-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtransfuzz.cc
460 lines (394 loc) · 14 KB
/
transfuzz.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
#include "config.h"
#include <iostream>
#include <chrono>
#ifndef HAVE_BOOST_REGEX
#include <regex>
#else
#include <boost/regex.hpp>
using boost::regex;
using boost::smatch;
using boost::regex_match;
#endif
#include <thread>
#include <typeinfo>
#include "random.hh"
#include "grammar.hh"
#include "relmodel.hh"
#include "schema.hh"
#include "gitrev.h"
#include "log.hh"
#include "dump.hh"
#include "impedance.hh"
#include "dut.hh"
#include <sys/time.h>
#include <sys/wait.h>
using namespace std;
using namespace std::chrono;
extern "C" {
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
}
#include "transaction_test.hh"
#define NORMAL_EXIT 0
#define FIND_BUG_EXIT 7
#define MAX_TIMEOUT_TIME 3
#define MAX_SETUP_TRY_TIME 3
pthread_mutex_t mutex_timeout;
pthread_cond_t cond_timeout;
int child_pid = 0;
bool child_timed_out = false;
extern int write_op_id;
void kill_process_signal(int signal)
{
if(signal != SIGALRM) {
printf("unexpect signal %d\n", signal);
exit(1);
}
if (child_pid > 0) {
printf("child pid timeout, kill it\n");
child_timed_out = true;
kill(child_pid, SIGKILL);
// also kill server process to restart
while (transaction_test::try_to_kill_server() == false) {}
}
cerr << "get SIGALRM, stop the process" << endl;
return;
}
int fork_for_generating_database(dbms_info& d_info)
{
static itimerval itimer;
transaction_test::fork_if_server_closed(d_info);
write_op_id = 0;
child_pid = fork();
if (child_pid == 0) { // in child process
generate_database(d_info);
ofstream output_wkey("wkey.txt");
output_wkey << write_op_id << endl;
output_wkey.close();
exit(NORMAL_EXIT);
}
itimer.it_value.tv_sec = TRANSACTION_TIMEOUT;
itimer.it_value.tv_usec = 0; // us limit
setitimer(ITIMER_REAL, &itimer, NULL);
int status;
auto res = waitpid(child_pid, &status, 0);
if (res <= 0) {
cerr << "waitpid() fail: " << res << endl;
throw runtime_error(string("waitpid() fail"));
}
if (!WIFSTOPPED(status))
child_pid = 0;
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &itimer, NULL);
if (WIFEXITED(status)) {
auto exit_code = WEXITSTATUS(status); // only low 8 bit (max 255)
// cerr << "exit code: " << exit_code << endl;
if (exit_code == FIND_BUG_EXIT) {
cerr << RED << "a bug is found in fork process" << RESET << endl;
transaction_test::record_bug_num++;
exit(-1);
}
if (exit_code == 255)
exit(-1);
}
if (WIFSIGNALED(status)) {
auto killSignal = WTERMSIG(status);
if (child_timed_out && killSignal == SIGKILL) {
// cerr << "timeout in generating stmt, reset the seed" << endl;
// transaction_test::try_to_kill_server();
// auto just_check_server = make_shared<transaction_test>(d_info);
// auto restart = just_check_server->fork_if_server_closed();
// if (restart)
// throw runtime_error(string("restart server")); // need to generate database again
// smith::rng.seed(time(NULL));
throw runtime_error(string("transaction test timeout"));
}
else {
cerr << RED << "find memory bug" << RESET << endl;
cerr << "killSignal: " << killSignal << endl;
throw runtime_error(string("memory bug"));
}
}
ifstream input_wkey("wkey.txt");
input_wkey >> write_op_id;
input_wkey.close();
write_op_id++;
// cerr << "updating write_op_id: "<< write_op_id << endl;
return 0;
}
int fork_for_transaction_test(dbms_info& d_info)
{
static itimerval itimer;
transaction_test::fork_if_server_closed(d_info);
child_pid = fork();
if (child_pid == 0) { // in child process
try {
// cerr << "write_op_id: " << write_op_id << endl;
transaction_test tt(d_info);
auto ret = tt.test();
if (ret == 1) {
cerr << RED << "Find a bug !!!" << RESET << endl;
exit(FIND_BUG_EXIT);
}
} catch(std::exception &e) { // ignore runtime error
cerr << "in test: " << e.what() << endl;
}
exit(NORMAL_EXIT);
}
itimer.it_value.tv_sec = TRANSACTION_TIMEOUT;
itimer.it_value.tv_usec = 0; // us limit
setitimer(ITIMER_REAL, &itimer, NULL);
int status;
auto res = waitpid(child_pid, &status, 0);
if (res <= 0) {
cerr << "waitpid() fail: " << res << endl;
throw runtime_error(string("waitpid() fail"));
}
if (!WIFSTOPPED(status))
child_pid = 0;
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &itimer, NULL);
if (WIFEXITED(status)) {
auto exit_code = WEXITSTATUS(status); // only low 8 bit (max 255)
// cerr << "exit code: " << exit_code << endl;
if (exit_code == FIND_BUG_EXIT) {
cerr << RED << "a bug is found in fork process" << RESET << endl;
transaction_test::record_bug_num++;
// abort();
}
if (exit_code == 255)
abort();
}
if (WIFSIGNALED(status)) {
auto killSignal = WTERMSIG(status);
if (child_timed_out && killSignal == SIGKILL) {
// cerr << "timeout in generating stmt, reset the seed" << endl;
// smith::rng.seed(time(NULL));
throw runtime_error(string("transaction test timeout"));
}
else {
cerr << RED << "find memory bug" << RESET << endl;
cerr << "killSignal: " << killSignal << endl;
abort();
// throw runtime_error(string("memory bug"));
}
}
return 0;
}
int random_test(dbms_info& d_info)
{
random_device rd;
auto rand_seed = rd();
cerr << "\n\n";
cerr << "random seed for db: " << rand_seed << endl;
smith::rng.seed(rand_seed);
// reset the target DBMS to initial state
int setup_try_time = 0;
while (1) {
if (setup_try_time > MAX_SETUP_TRY_TIME) {
kill_process_with_SIGTERM(transaction_test::server_process_id);
setup_try_time = 0;
}
try {
// donot fork, so that the static schema can be used in each test case
transaction_test::fork_if_server_closed(d_info);
generate_database(d_info);
// fork_for_generating_database(d_info);
break;
} catch(std::exception &e) {
cerr << e.what() << " in setup stage" << endl;
setup_try_time++;
}
}
int i = TEST_TIME_FOR_EACH_DB;
while (i--) {
// each round, generate random seed again, otherwise it will perform the same tests
rand_seed = rd();
cerr << "\n\n";
cerr << "random seed for tests: " << rand_seed << endl;
smith::rng.seed(rand_seed);
try {
fork_for_transaction_test(d_info);
} catch (exception &e) {
string err = e.what();
cerr << "ERROR in random_test: " << err << endl;
if (err == "restart server")
break;
else if (err == "transaction test timeout") {
break; // break the test and begin a new test
// after killing and starting a new server, created tables might be lost
// so it needs to begin a new test to generate tables
}
else {
cerr << "the exception cannot be handled" << endl;
throw e;
}
}
}
return 0;
}
int main(int argc, char *argv[])
{
// analyze the options
map<string,string> options;
regex optregex("--\
(help|min|\
tidb-db|tidb-port|\
mysql-db|mysql-port|\
mariadb-db|mariadb-port|\
output-or-affect-num|\
reproduce-sql|reproduce-tid|reproduce-usage|reproduce-backup)(?:=((?:.|\n)*))?");
for(char **opt = argv + 1 ;opt < argv + argc; opt++) {
smatch match;
string s(*opt);
if (regex_match(s, match, optregex)) {
options[string(match[1])] = match[2];
} else {
cerr << "Cannot parse option: " << *opt << endl;
options["help"] = "";
}
}
if (options.count("help")) {
cerr <<
#ifdef HAVE_TIDB
" --tidb-db=constr tidb database name to send queries to" << endl <<
" --tidb-port=int tidb server port number" << endl <<
#endif
#ifdef HAVE_MARIADB
" --mariadb-db=constr mariadb database name to send queries to" << endl <<
" --mariadb-port=int mariadb server port number" << endl <<
#endif
#ifdef HAVE_MYSQL
" --mysql-db=constr mysql database name to send queries to" << endl <<
" --mysql-port=int mysql server port number" << endl <<
#endif
" --output-or-affect-num=int generating statement that output num rows or affect num rows" << endl <<
" --reproduce-sql=filename sql file to reproduce the problem" << endl <<
" --reproduce-tid=filename tid file to reproduce the problem" << endl <<
" --reproduce-usage=filename stmt usage file to reproduce the problem" << endl <<
" --reproduce-backup=filename backup file to reproduce the problem" << endl <<
" --min minimize the reproduce test case" << endl <<
" --help print available command line options and exit" << endl;
return 0;
} else if (options.count("version")) {
return 0;
}
// set timeout action
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = user_signal;
if (sigaction(SIGUSR1, &action, NULL)) {
cerr << "sigaction error" << endl;
exit(1);
}
// set timeout action for fork
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = kill_process_signal;
if (sigaction(SIGALRM, &sa, NULL)) {
cerr << "sigaction error" << endl;
exit(1);
}
// init the lock
pthread_mutex_init(&mutex_timeout, NULL);
pthread_cond_init(&cond_timeout, NULL);
dbms_info d_info(options);
cerr << "-------------Test Info------------" << endl;
cerr << "Test DBMS: " << d_info.dbms_name << endl;
cerr << "Test database: " << d_info.test_db << endl;
cerr << "Test port: " << d_info.test_port << endl;
cerr << "Can trigger error in transaction: " << d_info.can_trigger_error_in_txn << endl;
cerr << "Output or affect num: " << d_info.ouput_or_affect_num << endl;
cerr << "----------------------------------" << endl;
if (options.count("reproduce-sql")) {
cerr << "enter reproduce mode" << endl;
if (!options.count("reproduce-tid")) {
cerr << "should also provide tid file" << endl;
return 0;
}
// get stmt queue
vector<shared_ptr<prod>> stmt_queue;
ifstream stmt_file(options["reproduce-sql"]);
stringstream buffer;
buffer << stmt_file.rdbuf();
stmt_file.close();
string stmts(buffer.str());
int old_off = 0;
while (1) {
int new_off = stmts.find(";\n\n", old_off);
if (new_off == string::npos)
break;
auto each_sql = stmts.substr(old_off, new_off - old_off); // not include ;\n\n
old_off = new_off + string(";\n\n").size();
stmt_queue.push_back(make_shared<txn_string_stmt>((prod *)0, each_sql));
}
// get tid queue
vector<int> tid_queue;
ifstream tid_file(options["reproduce-tid"]);
int tid;
int max_tid = -1;
while (tid_file >> tid) {
tid_queue.push_back(tid);
if (tid > max_tid)
max_tid = tid;
}
tid_file.close();
// get stmt use queue
vector<stmt_usage> stmt_usage_queue;
ifstream stmt_usage_file(options["reproduce-usage"]);
int use;
while (stmt_usage_file >> use) {
switch (use) {
case 0:
stmt_usage_queue.push_back(stmt_usage(INIT_TYPE, false, "t_***"));
break;
case 1:
stmt_usage_queue.push_back(stmt_usage(SELECT_READ, false, "t_***"));
break;
case 2:
stmt_usage_queue.push_back(stmt_usage(UPDATE_WRITE, false, "t_***"));
break;
case 3:
stmt_usage_queue.push_back(stmt_usage(INSERT_WRITE, false, "t_***"));
break;
case 4:
stmt_usage_queue.push_back(stmt_usage(DELETE_WRITE, false, "t_***"));
break;
case 5:
stmt_usage_queue.push_back(stmt_usage(BEFORE_WRITE_READ, true, "t_***"));
break;
case 6:
stmt_usage_queue.push_back(stmt_usage(AFTER_WRITE_READ, true, "t_***"));
break;
case 7:
stmt_usage_queue.push_back(stmt_usage(VERSION_SET_READ, true, "t_***"));
break;
default:
cerr << "unknown stmt usage: " << use << endl;
exit(-1);
break;
}
}
stmt_usage_file.close();
auto backup_file = options["reproduce-backup"];
use_backup_file(backup_file, d_info);
if (options.count("min"))
minimize_testcase(d_info, stmt_queue, tid_queue, stmt_usage_queue);
else {
string empty_str;
reproduce_routine(d_info, stmt_queue, tid_queue, stmt_usage_queue, empty_str);
}
return 0;
}
while (1) {
random_test(d_info);
}
return 0;
}