1#include <stdlib.h>
2#include <time.h>
3#include <stdio.h>
4#include <fcntl.h>
5#include <unistd.h>
6#include <string.h>
7#include <fcntl.h>
8#include <signal.h>
9#include <stdarg.h>
10#include <sys/mman.h>
11#include <sys/stat.h>
12#include <sys/time.h>
13#include <sys/wait.h>
14#include "tdb.h"
15
16/* this tests tdb by doing lots of ops from several simultaneous
17   writers - that stresses the locking code. Build with TDB_DEBUG=1
18   for best effect */
19
20
21
22#define REOPEN_PROB 30
23#define DELETE_PROB 8
24#define STORE_PROB 4
25#define APPEND_PROB 6
26#define LOCKSTORE_PROB 0
27#define TRAVERSE_PROB 20
28#define CULL_PROB 100
29#define KEYLEN 3
30#define DATALEN 100
31#define LOCKLEN 20
32
33static TDB_CONTEXT *db;
34
35static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
36{
37	va_list ap;
38
39	va_start(ap, format);
40	vfprintf(stdout, format, ap);
41	va_end(ap);
42	fflush(stdout);
43#if 0
44	{
45		char *ptr;
46		asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
47		system(ptr);
48		free(ptr);
49	}
50#endif
51}
52
53static void fatal(char *why)
54{
55	perror(why);
56	exit(1);
57}
58
59static char *randbuf(int len)
60{
61	char *buf;
62	int i;
63	buf = (char *)malloc(len+1);
64
65	for (i=0;i<len;i++) {
66		buf[i] = 'a' + (rand() % 26);
67	}
68	buf[i] = 0;
69	return buf;
70}
71
72static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
73			 void *state)
74{
75	if (random() % CULL_PROB == 0) {
76		tdb_delete(tdb, key);
77	}
78	return 0;
79}
80
81static void addrec_db(void)
82{
83	int klen, dlen, slen;
84	char *k, *d, *s;
85	TDB_DATA key, data, lockkey;
86
87	klen = 1 + (rand() % KEYLEN);
88	dlen = 1 + (rand() % DATALEN);
89	slen = 1 + (rand() % LOCKLEN);
90
91	k = randbuf(klen);
92	d = randbuf(dlen);
93	s = randbuf(slen);
94
95	key.dptr = k;
96	key.dsize = klen+1;
97
98	data.dptr = d;
99	data.dsize = dlen+1;
100
101	lockkey.dptr = s;
102	lockkey.dsize = slen+1;
103
104#if REOPEN_PROB
105	if (random() % REOPEN_PROB == 0) {
106		tdb_reopen_all();
107		goto next;
108	}
109#endif
110
111#if DELETE_PROB
112	if (random() % DELETE_PROB == 0) {
113		tdb_delete(db, key);
114		goto next;
115	}
116#endif
117
118#if STORE_PROB
119	if (random() % STORE_PROB == 0) {
120		if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
121			fatal("tdb_store failed");
122		}
123		goto next;
124	}
125#endif
126
127#if APPEND_PROB
128	if (random() % APPEND_PROB == 0) {
129		if (tdb_append(db, key, data) != 0) {
130			fatal("tdb_append failed");
131		}
132		goto next;
133	}
134#endif
135
136#if LOCKSTORE_PROB
137	if (random() % LOCKSTORE_PROB == 0) {
138		tdb_chainlock(db, lockkey);
139		data = tdb_fetch(db, key);
140		if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
141			fatal("tdb_store failed");
142		}
143		if (data.dptr) free(data.dptr);
144		tdb_chainunlock(db, lockkey);
145		goto next;
146	}
147#endif
148
149#if TRAVERSE_PROB
150	if (random() % TRAVERSE_PROB == 0) {
151		tdb_traverse(db, cull_traverse, NULL);
152		goto next;
153	}
154#endif
155
156	data = tdb_fetch(db, key);
157	if (data.dptr) free(data.dptr);
158
159next:
160	free(k);
161	free(d);
162	free(s);
163}
164
165static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
166                       void *state)
167{
168	tdb_delete(tdb, key);
169	return 0;
170}
171
172#ifndef NPROC
173#define NPROC 6
174#endif
175
176#ifndef NLOOPS
177#define NLOOPS 200000
178#endif
179
180int main(int argc, char *argv[])
181{
182	int i, seed=0;
183	int loops = NLOOPS;
184	pid_t pids[NPROC];
185
186	pids[0] = getpid();
187
188	for (i=0;i<NPROC-1;i++) {
189		if ((pids[i+1]=fork()) == 0) break;
190	}
191
192	db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST,
193		      O_RDWR | O_CREAT, 0600);
194	if (!db) {
195		fatal("db open failed");
196	}
197	tdb_logging_function(db, tdb_log);
198
199	srand(seed + getpid());
200	srandom(seed + getpid() + time(NULL));
201	for (i=0;i<loops;i++) addrec_db();
202
203	tdb_traverse(db, NULL, NULL);
204	tdb_traverse(db, traverse_fn, NULL);
205	tdb_traverse(db, traverse_fn, NULL);
206
207	tdb_close(db);
208
209	if (getpid() == pids[0]) {
210		for (i=0;i<NPROC-1;i++) {
211			int status;
212			if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
213				printf("failed to wait for %d\n",
214				       (int)pids[i+1]);
215				exit(1);
216			}
217			if (WEXITSTATUS(status) != 0) {
218				printf("child %d exited with status %d\n",
219				       (int)pids[i+1], WEXITSTATUS(status));
220				exit(1);
221			}
222		}
223		printf("OK\n");
224	}
225
226	return 0;
227}
228