You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1149 lines
32 KiB

  1. #!/usr/bin/perl
  2. # ======================================================================
  3. # MySQL server stress test system
  4. # ======================================================================
  5. #
  6. ##########################################################################
  7. #
  8. # SCENARIOS AND REQUIREMENTS
  9. #
  10. # The system should perform stress testing of MySQL server with
  11. # following requirements and basic scenarios:
  12. #
  13. # Basic requirements:
  14. #
  15. # Design of stress script should allow one:
  16. #
  17. # - To stress test the mysqltest binary test engine.
  18. # - To stress test the regular test suite and any additional test suites
  19. # (such as mysql-test-extra-5.0).
  20. # - To specify files with lists of tests both for initialization of
  21. # stress db and for further testing itself.
  22. # - To define the number of threads to be concurrently used in testing.
  23. # - To define limitations for the test run. such as the number of tests or
  24. # loops for execution or duration of testing, delay between test
  25. # executions, and so forth.
  26. # - To get a readable log file that can be used for identification of
  27. # errors that occur during testing.
  28. #
  29. # Basic scenarios:
  30. #
  31. # * It should be possible to run stress script in standalone mode
  32. # which will allow to create various scenarios of stress workloads:
  33. #
  34. # simple ones:
  35. #
  36. # box #1:
  37. # - one instance of script with list of tests #1
  38. #
  39. # and more advanced ones:
  40. #
  41. # box #1:
  42. # - one instance of script with list of tests #1
  43. # - another instance of script with list of tests #2
  44. # box #2:
  45. # - one instance of script with list of tests #3
  46. # - another instance of script with list of tests #4
  47. # that will recreate whole database to back it to clean
  48. # state
  49. #
  50. # One kind of such complex scenarios maybe continued testing
  51. # when we want to run stress tests from many boxes with various
  52. # lists of tests that will last very long time. And in such case
  53. # we need some wrapper for MySQL server that will restart it in
  54. # case of crashes.
  55. #
  56. # * It should be possible to run stress script in ad-hoc mode from
  57. # shell or perl versions of mysql-test-run. This allows developers
  58. # to reproduce and debug errors that was found in continued stress
  59. # testing
  60. #
  61. ########################################################################
  62. use Config;
  63. if (!defined($Config{useithreads}))
  64. {
  65. die <<EOF;
  66. It is unable to run threaded version of stress test on this system
  67. due to disabled ithreads. Please check that installed perl binary
  68. was built with support of ithreads.
  69. EOF
  70. }
  71. use threads;
  72. use threads::shared;
  73. use IO::Socket;
  74. use Sys::Hostname;
  75. use File::Copy;
  76. use File::Spec;
  77. use File::Find;
  78. use File::Basename;
  79. use File::Path;
  80. use Cwd;
  81. use Data::Dumper;
  82. use Getopt::Long;
  83. my $stress_suite_version="1.0";
  84. $|=1;
  85. $opt_server_host="";
  86. $opt_server_logs_dir="";
  87. $opt_help="";
  88. $opt_server_port="";
  89. $opt_server_socket="";
  90. $opt_server_user="";
  91. $opt_server_password="";
  92. $opt_server_database="";
  93. $opt_cleanup="";
  94. $opt_verbose="";
  95. $opt_log_error_details="";
  96. $opt_suite="main";
  97. $opt_stress_suite_basedir="";
  98. $opt_stress_basedir="";
  99. $opt_stress_datadir="";
  100. $opt_test_suffix="";
  101. $opt_stress_mode="random";
  102. $opt_loop_count=0;
  103. $opt_test_count=0;
  104. $opt_test_duration=0;
  105. $opt_abort_on_error=0;
  106. $opt_sleep_time = 0;
  107. $opt_threads=1;
  108. $pid_file="mysql_stress_test.pid";
  109. $opt_mysqltest= ($^O =~ /mswin32/i) ? "mysqltest.exe" : "mysqltest";
  110. $opt_check_tests_file="";
  111. @mysqltest_args=("--silent", "-v", "--skip-safemalloc");
  112. # Client ip address
  113. $client_ip=inet_ntoa((gethostbyname(hostname()))[4]);
  114. $client_ip=~ s/\.//g;
  115. %tests_files=(client => {mtime => 0, data => []},
  116. initdb => {mtime => 0, data => []});
  117. # Error codes and sub-strings with corresponding severity
  118. #
  119. # S1 - Critical errors - cause immediately abort of testing. These errors
  120. # could be caused by server crash or impossibility
  121. # of test execution
  122. #
  123. # S2 - Serious errors - these errors are bugs for sure as it knowns that
  124. # they shouldn't appear during stress testing
  125. #
  126. # S3 - Non-seriuos errros - these errors could be caused by fact that
  127. # we execute simultaneously statements that
  128. # affect tests executed by other threads
  129. %error_strings = ( 'Failed in mysql_real_connect()' => S1,
  130. 'not found (Errcode: 2)' => S1 );
  131. %error_codes = ( 1012 => S2, 1015 => S2, 1021 => S2,
  132. 1027 => S2, 1037 => S2, 1038 => S2,
  133. 1039 => S2, 1040 => S2, 1046 => S2,
  134. 1180 => S2, 1181 => S2, 1203 => S2,
  135. 1205 => S2, 1206 => S2, 1207 => S2,
  136. 1223 => S2, 2013 => S1);
  137. share(%test_counters);
  138. %test_counters=( loop_count => 0, test_count=>0);
  139. share($exiting);
  140. $exiting=0;
  141. share($test_counters_lock);
  142. $test_counters_lock=0;
  143. share($log_file_lock);
  144. $log_file_lock=0;
  145. $SIG{INT}= \&sig_INT_handler;
  146. $SIG{TERM}= \&sig_TERM_handler;
  147. GetOptions("server-host=s", "server-logs-dir=s", "server-port=s",
  148. "server-socket=s", "server-user=s", "server-password=s",
  149. "server-database=s",
  150. "stress-suite-basedir=s", "suite=s", "stress-init-file:s",
  151. "stress-tests-file:s", "stress-basedir=s", "stress-mode=s",
  152. "stress-datadir=s",
  153. "threads=s", "sleep-time=s", "loop-count=i", "test-count=i",
  154. "test-duration=i", "test-suffix=s", "check-tests-file",
  155. "verbose", "log-error-details", "cleanup", "mysqltest=s",
  156. "abort-on-error", "help") || usage();
  157. usage() if ($opt_help);
  158. #$opt_abort_on_error=1;
  159. $test_dirname=get_timestamp();
  160. $test_dirname.="-$opt_test_suffix" if ($opt_test_suffix ne '');
  161. print <<EOF;
  162. #############################################################
  163. CONFIGURATION STAGE
  164. #############################################################
  165. EOF
  166. if ($opt_stress_basedir eq '' || $opt_stress_suite_basedir eq '' ||
  167. $opt_server_logs_dir eq '')
  168. {
  169. die <<EOF;
  170. Options --stress-basedir, --stress-suite-basedir and --server-logs-dir are
  171. required. Please use these options to specify proper basedir for
  172. client, test suite and location of server logs.
  173. stress-basedir: '$opt_stress_basedir'
  174. stress-suite-basedir: '$opt_stress_suite_basedir'
  175. server-logs-dir: '$opt_server_logs_dir'
  176. EOF
  177. }
  178. #Workaround for case when we got relative but not absolute path
  179. $opt_stress_basedir=File::Spec->rel2abs($opt_stress_basedir);
  180. $opt_stress_suite_basedir=File::Spec->rel2abs($opt_stress_suite_basedir);
  181. $opt_server_logs_dir=File::Spec->rel2abs($opt_server_logs_dir);
  182. if ($opt_stress_datadir ne '')
  183. {
  184. $opt_stress_datadir=File::Spec->rel2abs($opt_stress_datadir);
  185. }
  186. if (! -d "$opt_stress_basedir")
  187. {
  188. die <<EOF;
  189. Directory '$opt_stress_basedir' does not exist.
  190. Use --stress-basedir option to specify proper basedir for client
  191. EOF
  192. }
  193. if (!-d $opt_stress_suite_basedir)
  194. {
  195. die <<EOF;
  196. Directory '$opt_stress_suite_basedir' does not exist.
  197. Use --stress-suite-basedir option to specify proper basedir for test suite
  198. EOF
  199. }
  200. $test_dataset_dir=$opt_stress_suite_basedir;
  201. if ($opt_stress_datadir ne '')
  202. {
  203. if (-d $opt_stress_datadir)
  204. {
  205. $test_dataset_dir=$opt_stress_datadir;
  206. }
  207. else
  208. {
  209. die <<EOF;
  210. Directory '$opt_stress_datadir' not exists. Please specify proper one
  211. with --stress-datadir option.
  212. EOF
  213. }
  214. }
  215. if ($^O =~ /mswin32/i)
  216. {
  217. $test_dataset_dir=~ s/\\/\\\\/g;
  218. }
  219. else
  220. {
  221. $test_dataset_dir.="/";
  222. }
  223. if (!-d $opt_server_logs_dir)
  224. {
  225. die <<EOF;
  226. Directory server-logs-dir '$opt_server_logs_dir' does not exist.
  227. Use --server-logs-dir option to specify proper directory for storing
  228. logs
  229. EOF
  230. }
  231. else
  232. {
  233. #Create sub-directory for test session logs
  234. mkpath(File::Spec->catdir($opt_server_logs_dir, $test_dirname), 0, 0755);
  235. #Define filename of global session log file
  236. $stress_log_file=File::Spec->catfile($opt_server_logs_dir, $test_dirname,
  237. "mysql-stress-test.log");
  238. }
  239. if ($opt_suite ne '' && $opt_suite ne 'main' && $opt_suite ne 'default')
  240. {
  241. $test_suite_dir=File::Spec->catdir($opt_stress_suite_basedir, "suite", $opt_suite);
  242. }
  243. else
  244. {
  245. $test_suite_dir= $opt_stress_suite_basedir;
  246. }
  247. if (!-d $test_suite_dir)
  248. {
  249. die <<EOF
  250. Directory '$test_suite_dir' does not exist.
  251. Use --suite options to specify proper dir for test suite
  252. EOF
  253. }
  254. $test_suite_t_path=File::Spec->catdir($test_suite_dir,'t');
  255. $test_suite_r_path=File::Spec->catdir($test_suite_dir,'r');
  256. foreach my $suite_dir ($test_suite_t_path, $test_suite_r_path)
  257. {
  258. if (!-d $suite_dir)
  259. {
  260. die <<EOF;
  261. Directory '$suite_dir' does not exist.
  262. Please ensure that you specified proper source location for
  263. test/result files with --stress-suite-basedir option and name
  264. of test suite with --suite option
  265. EOF
  266. }
  267. }
  268. $test_t_path=File::Spec->catdir($opt_stress_basedir,'t');
  269. $test_r_path=File::Spec->catdir($opt_stress_basedir,'r');
  270. foreach $test_dir ($test_t_path, $test_r_path)
  271. {
  272. if (-d $test_dir)
  273. {
  274. if ($opt_cleanup)
  275. {
  276. #Delete existing 't', 'r', 'r/*' subfolders in $stress_basedir
  277. rmtree("$test_dir", 0, 0);
  278. print "Cleanup $test_dir\n";
  279. }
  280. else
  281. {
  282. die <<EOF;
  283. Directory '$test_dir' already exist.
  284. Please ensure that you specified proper location of working dir
  285. for current test run with --stress-basedir option or in case of staled
  286. directories use --cleanup option to remove ones
  287. EOF
  288. }
  289. }
  290. #Create empty 't', 'r' subfolders that will be filled later
  291. mkpath("$test_dir", 0, 0777);
  292. }
  293. if (!defined($opt_stress_tests_file) && !defined($opt_stress_init_file))
  294. {
  295. die <<EOF;
  296. You should run stress script either with --stress-tests-file or with
  297. --stress-init-file otions. See help for details.
  298. EOF
  299. }
  300. if (defined($opt_stress_tests_file))
  301. {
  302. if ($opt_stress_tests_file eq '')
  303. {
  304. #Default location of file with set of tests for current test run
  305. $tests_files{client}->{filename}= File::Spec->catfile($opt_stress_suite_basedir,
  306. "testslist_client.txt");
  307. }
  308. else
  309. {
  310. $tests_files{client}->{filename}= $opt_stress_tests_file;
  311. }
  312. if (!-f $tests_files{client}->{filename})
  313. {
  314. die <<EOF;
  315. File '$tests_files{client}->{filename}' with list of tests not exists.
  316. Please ensure that this file exists, readable or specify another one with
  317. --stress-tests-file option.
  318. EOF
  319. }
  320. }
  321. if (defined($opt_stress_init_file))
  322. {
  323. if ($opt_stress_init_file eq '')
  324. {
  325. #Default location of file with set of tests for current test run
  326. $tests_files{initdb}->{filename}= File::Spec->catfile($opt_stress_suite_basedir,
  327. "testslist_initdb.txt");
  328. }
  329. else
  330. {
  331. $tests_files{initdb}->{filename}= $opt_stress_init_file;
  332. }
  333. if (!-f $tests_files{initdb}->{filename})
  334. {
  335. die <<EOF;
  336. File '$tests_files{initdb}->{filename}' with list of tests for initialization of database
  337. for stress test not exists.
  338. Please ensure that this file exists, readable or specify another one with
  339. --stress-init-file option.
  340. EOF
  341. }
  342. }
  343. if ($opt_stress_mode !~ /^(random|seq)$/)
  344. {
  345. die <<EOF
  346. Was specified wrong --stress-mode. Correct values 'random' and 'seq'.
  347. EOF
  348. }
  349. if (open(TEST, "$opt_mysqltest -V |"))
  350. {
  351. $mysqltest_version=join("",<TEST>);
  352. close(TEST);
  353. print "FOUND MYSQLTEST BINARY: ", $mysqltest_version,"\n";
  354. }
  355. else
  356. {
  357. die <<EOF;
  358. ERROR: mysqltest binary $opt_mysqltest not found $!.
  359. You must either specify file location explicitly using --mysqltest
  360. option, or make sure path to mysqltest binary is listed
  361. in your PATH environment variable.
  362. EOF
  363. }
  364. #
  365. #Adding mysql server specific command line options for mysqltest binary
  366. #
  367. $opt_server_host= $opt_server_host ? $opt_server_host : "localhost";
  368. $opt_server_port= $opt_server_port ? $opt_server_port : "3306";
  369. $opt_server_user= $opt_server_user ? $opt_server_user : "root";
  370. $opt_server_socket= $opt_server_socket ? $opt_server_socket : "/tmp/mysql.sock";
  371. $opt_server_database= $opt_server_database ? $opt_server_database : "test";
  372. unshift @mysqltest_args, "--host=$opt_server_host";
  373. unshift @mysqltest_args, "--port=$opt_server_port";
  374. unshift @mysqltest_args, "--user=$opt_server_user";
  375. unshift @mysqltest_args, "--password=$opt_server_password";
  376. unshift @mysqltest_args, "--socket=$opt_server_socket";
  377. unshift @mysqltest_args, "--database=$opt_server_database";
  378. #Export variables that could be used in tests
  379. $ENV{MYSQL_TEST_DIR}=$test_dataset_dir;
  380. $ENV{MASTER_MYPORT}=$opt_server_port;
  381. $ENV{MASTER_MYSOCK}=$opt_server_socket;
  382. print <<EOF;
  383. TEST-SUITE-BASEDIR: $opt_stress_suite_basedir
  384. SUITE: $opt_suite
  385. TEST-BASE-DIR: $opt_stress_basedir
  386. TEST-DATADIR: $test_dataset_dir
  387. SERVER-LOGS-DIR: $opt_server_logs_dir
  388. THREADS: $opt_threads
  389. TEST-MODE: $opt_stress_mode
  390. EOF
  391. #-------------------------------------------------------------------------------
  392. #At this stage we've already checked all needed pathes/files
  393. #and ready to start the test
  394. #-------------------------------------------------------------------------------
  395. if (defined($opt_stress_tests_file) || defined($opt_stress_init_file))
  396. {
  397. print <<EOF;
  398. #############################################################
  399. PREPARATION STAGE
  400. #############################################################
  401. EOF
  402. #Copy Test files from network share to 't' folder
  403. print "\nCopying Test files from $test_suite_t_path to $test_t_path folder...";
  404. find({wanted=>\&copy_test_files, bydepth=>1}, "$test_suite_t_path");
  405. print "Done\n";
  406. #$test_r_path/r0 dir reserved for initdb
  407. $count_start= defined($opt_stress_init_file) ? 0 : 1;
  408. our $r_folder='';
  409. print "\nCreating 'r' folder and copying Protocol files to each 'r#' sub-folder...";
  410. for($count=$count_start; $count <= $opt_threads; $count++)
  411. {
  412. $r_folder = File::Spec->catdir($test_r_path, "r".$count);
  413. mkpath("$r_folder", 0, 0777);
  414. find(\&copy_result_files,"$test_suite_r_path");
  415. }
  416. print "Done\n\n";
  417. }
  418. if (defined($opt_stress_init_file))
  419. {
  420. print <<EOF;
  421. #############################################################
  422. INITIALIZATION STAGE
  423. #############################################################
  424. EOF
  425. #Set limits for stress db initialization
  426. %limits=(loop_count => 1, test_count => undef);
  427. #Read list of tests from $opt_stress_init_file
  428. read_tests_names($tests_files{initdb});
  429. test_loop($client_ip, 0, 'seq', $tests_files{initdb});
  430. #print Dumper($tests_files{initdb}),"\n";
  431. print <<EOF;
  432. Done initialization of stress database by tests from
  433. $tests_files{initdb}->{filename} file.
  434. EOF
  435. }
  436. if (defined($opt_stress_tests_file))
  437. {
  438. print <<EOF;
  439. #############################################################
  440. STRESS TEST RUNNING STAGE
  441. #############################################################
  442. EOF
  443. $exiting=0;
  444. #Read list of tests from $opt_stress_tests_file
  445. read_tests_names($tests_files{client});
  446. #Reset current counter and set limits
  447. %test_counters=( loop_count => 0, test_count=>0);
  448. %limits=(loop_count => $opt_loop_count, test_count => $opt_test_count);
  449. if (($opt_loop_count && $opt_threads > $opt_loop_count) ||
  450. ($opt_test_count && $opt_threads > $opt_test_count))
  451. {
  452. warn <<EOF;
  453. WARNING: Possible inaccuracies in number of executed loops or
  454. tests because number of threads bigger than number of
  455. loops or tests:
  456. Threads will be started: $opt_threads
  457. Loops will be executed: $opt_loop_count
  458. Tests will be executed: $opt_test_count
  459. EOF
  460. }
  461. #Create threads (number depending on the variable )
  462. for ($id=1; $id<=$opt_threads && !$exiting; $id++)
  463. {
  464. $thrd[$id] = threads->create("test_loop", $client_ip, $id,
  465. $opt_stress_mode, $tests_files{client});
  466. print "main: Thread ID $id TID ",$thrd[$id]->tid," started\n";
  467. select(undef, undef, undef, 0.5);
  468. }
  469. if ($opt_test_duration)
  470. {
  471. sleep($opt_test_duration);
  472. kill INT, $$; #Interrupt child threads
  473. }
  474. #Let other threads to process INT signal
  475. sleep(1);
  476. for ($id=1; $id<=$opt_threads;$id++)
  477. {
  478. if (defined($thrd[$id]))
  479. {
  480. $thrd[$id]->join();
  481. }
  482. }
  483. print "EXIT\n";
  484. }
  485. sub test_init
  486. {
  487. my ($env)=@_;
  488. $env->{session_id}=$env->{ip}."_".$env->{thread_id};
  489. $env->{r_folder}='r'.$env->{thread_id};
  490. $env->{screen_logs}=File::Spec->catdir($opt_server_logs_dir, $test_dirname,
  491. "screen_logs", $env->{session_id});
  492. $env->{reject_logs}=File::Spec->catdir($opt_server_logs_dir, $test_dirname,
  493. "reject_logs", $env->{session_id});
  494. mkpath($env->{screen_logs}, 0, 0755) unless (-d $env->{screen_logs});
  495. mkpath($env->{reject_logs}, 0, 0755) unless (-d $env->{reject_logs});
  496. $env->{session_log}= File::Spec->catfile($env->{screen_logs}, $env->{session_id}.".log");
  497. }
  498. sub test_execute
  499. {
  500. my $env= shift;
  501. my $test_name= shift;
  502. my $g_start= "";
  503. my $g_end= "";
  504. my $mysqltest_cmd= "";
  505. my @mysqltest_test_args=();
  506. my @stderr=();
  507. #Get time stamp
  508. $g_start = get_timestamp();
  509. $env->{errors}={};
  510. @{$env->{test_status}}=();
  511. my $test_file= $test_name.".test";
  512. my $result_file= $test_name.".result";
  513. my $reject_file = $test_name.'.reject';
  514. my $output_file = $env->{session_id}.'_'.$test_name.'_'.$g_start."_".$env->{test_count}.'.txt';
  515. my $test_filename = File::Spec->catfile($test_t_path, $test_file);
  516. my $result_filename = File::Spec->catdir($test_r_path, $env->{r_folder}, $result_file);
  517. my $reject_filename = File::Spec->catdir($test_r_path, $env->{r_folder}, $reject_file);
  518. my $output_filename = File::Spec->catfile($env->{screen_logs}, $output_file);
  519. push @mysqltest_test_args, "--basedir=$opt_stress_suite_basedir/",
  520. "--tmpdir=$opt_stress_basedir",
  521. "-x $test_filename",
  522. "-R $result_filename",
  523. "2>$output_filename";
  524. $cmd= "$opt_mysqltest --no-defaults ".join(" ", @mysqltest_args)." ".
  525. join(" ", @mysqltest_test_args);
  526. system($cmd);
  527. $exit_value = $? >> 8;
  528. $signal_num = $? & 127;
  529. $dumped_core = $? & 128;
  530. my $tid= threads->self->tid;
  531. if (-s $output_filename > 0)
  532. {
  533. #Read stderr for further analysis
  534. open (STDERR_LOG, $output_filename) or
  535. warn "Can't open file $output_filename";
  536. @stderr=<STDERR_LOG>;
  537. close(STDERR_LOG);
  538. if ($opt_verbose)
  539. {
  540. $session_debug_file="$opt_stress_basedir/error$tid.txt";
  541. stress_log($session_debug_file,
  542. "Something wrong happened during execution of this command line:");
  543. stress_log($session_debug_file, "MYSQLTEST CMD - $cmd");
  544. stress_log($session_debug_file, "STDERR:".join("",@stderr));
  545. stress_log($session_debug_file, "EXIT STATUS:\n1. EXIT: $exit_value \n".
  546. "2. SIGNAL: $signal_num\n".
  547. "3. CORE: $dumped_core\n");
  548. }
  549. }
  550. #If something wrong trying to analyse stderr
  551. if ($exit_value || $signal_num)
  552. {
  553. if (@stderr)
  554. {
  555. foreach my $line (@stderr)
  556. {
  557. #FIXME: we should handle case when for one sub-string/code
  558. # we have several different error messages
  559. # Now for both codes/substrings we assume that
  560. # first found message will represent error
  561. #Check line for error codes
  562. if (($err_msg, $err_code)= $line=~/failed: ((\d+):.+?$)/)
  563. {
  564. if (!exists($error_codes{$err_code}))
  565. {
  566. $severity="S3";
  567. $err_code=0;
  568. }
  569. else
  570. {
  571. $severity=$error_codes{$err_code};
  572. }
  573. if (!exists($env->{errors}->{$severity}->{$err_code}))
  574. {
  575. $env->{errors}->{$severity}->{$err_code}=[0, $err_msg];
  576. }
  577. $env->{errors}->{$severity}->{$err_code}->[0]++;
  578. $env->{errors}->{$severity}->{total}++;
  579. }
  580. #Check line for error patterns
  581. foreach $err_string (keys %error_strings)
  582. {
  583. $pattern= quotemeta $err_string;
  584. if ($line =~ /$pattern/i)
  585. {
  586. my $severity= $error_strings{$err_string};
  587. if (!exists($env->{errors}->{$severity}->{$err_string}))
  588. {
  589. $env->{errors}->{$severity}->{$err_string}=[0, $line];
  590. }
  591. $env->{errors}->{$severity}->{$err_string}->[0]++;
  592. $env->{errors}->{$severity}->{total}++;
  593. }
  594. }
  595. }
  596. }
  597. else
  598. {
  599. $env->{errors}->{S3}->{'Unknown error'}=
  600. [1,"Unknown error. Nothing was output to STDERR"];
  601. $env->{errors}->{S3}->{total}=1;
  602. }
  603. }
  604. #
  605. #FIXME: Here we can perform further analysis of recognized
  606. # error codes
  607. #
  608. foreach my $severity (sort {$a cmp $b} keys %{$env->{errors}})
  609. {
  610. my $total=$env->{errors}->{$severity}->{total};
  611. if ($total)
  612. {
  613. push @{$env->{test_status}}, "Severity $severity: $total";
  614. $env->{errors}->{total}=+$total;
  615. }
  616. }
  617. #FIXME: Should we take into account $exit_value here?
  618. # Now we assume that all stringified errors(i.e. errors without
  619. # error codes) which are not exist in %error_string structure
  620. # are OK
  621. if (!$env->{errors}->{total})
  622. {
  623. push @{$env->{test_status}},"No Errors. Test Passed OK";
  624. }
  625. log_session_errors($env, $test_file);
  626. if (!$exiting && ($signal_num == 2 || $signal_num == 15 ||
  627. ($opt_abort_on_error && $env->{errors}->{S1} > 0)))
  628. {
  629. #mysqltest was interrupted with INT or TERM signals or test was
  630. #ran with --abort-on-error option and we got errors with severity S1
  631. #so we assume that we should cancel testing and exit
  632. $exiting=1;
  633. print STDERR<<EOF;
  634. WARNING:
  635. mysqltest was interrupted with INT or TERM signals or test was
  636. ran with --abort-on-error option and we got errors with severity S1
  637. (test cann't connect to the server or server crashed) so we assume that
  638. we should cancel testing and exit. Please check log file for this thread
  639. in $stress_log_file or
  640. inspect below output of the last test case executed with mysqltest to
  641. find out cause of error.
  642. Output of mysqltest:
  643. @stderr
  644. EOF
  645. }
  646. if (-e $reject_filename)
  647. {
  648. move_to_logs($env->{reject_logs}, $reject_filename, $reject_file);
  649. }
  650. if (-e $output_filename)
  651. {
  652. move_to_logs($env->{screen_logs}, $output_filename, $output_file);
  653. }
  654. }
  655. sub test_loop
  656. {
  657. my %client_env=();
  658. my $test_name="";
  659. # KEY for session identification: IP-THREAD_ID
  660. $client_env{ip} = shift;
  661. $client_env{thread_id} = shift;
  662. $client_env{mode} = shift;
  663. $client_env{tests_file}=shift;
  664. $client_env{test_seq_idx}=0;
  665. #Initialize session variables
  666. test_init(\%client_env);
  667. LOOP:
  668. while(!$exiting)
  669. {
  670. if ($opt_check_tests_file)
  671. {
  672. #Check if tests_file was modified and reread it in this case
  673. read_tests_names($client_env{tests_file}, 0);
  674. }
  675. {
  676. lock($test_counters_lock);
  677. if (($limits{loop_count} && $limits{loop_count} <= $test_counters{loop_count}*1) ||
  678. ($limits{test_count} && $limits{test_count} <= $test_counters{test_count}*1) )
  679. {
  680. $exiting=1;
  681. next LOOP;
  682. }
  683. }
  684. #Get random file name
  685. if (($test_name = get_test(\%client_env)) ne '')
  686. {
  687. {
  688. lock($test_counters_lock);
  689. #Save current counters values
  690. $client_env{loop_count}=$test_counters{loop_count};
  691. $client_env{test_count}=$test_counters{test_count};
  692. }
  693. #Run test and analyze results
  694. test_execute(\%client_env, $test_name);
  695. print "test_loop[".$limits{loop_count}.":".
  696. $limits{test_count}." ".
  697. $client_env{loop_count}.":".
  698. $client_env{test_count}."]:".
  699. " TID ".$client_env{thread_id}.
  700. " test: '$test_name' ".
  701. " Errors: ".join(" ",@{$client_env{test_status}}),"\n";
  702. print "\n";
  703. }
  704. sleep($opt_sleep_time) if($opt_sleep_time);
  705. }
  706. }
  707. sub move_to_logs ($$$)
  708. {
  709. my $path_to_logs = shift;
  710. my $src_file = shift;
  711. my $random_filename = shift;
  712. my $dst_file = File::Spec->catfile($path_to_logs, $random_filename);
  713. move ($src_file, $dst_file) or warn<<EOF;
  714. ERROR: move_to_logs: File $src_file cannot be moved to $dst_file: $!
  715. EOF
  716. }
  717. sub copy_test_files ()
  718. {
  719. if (/\.test$/)
  720. {
  721. $src_file = $File::Find::name;
  722. #print "## $File::Find::topdir - $File::Find::dir - $src_file\n";
  723. if ($File::Find::topdir eq $File::Find::dir && $src_file !~ /SCCS/)
  724. {
  725. $test_filename = basename($src_file);
  726. $dst_file = File::Spec->catfile($test_t_path, $test_filename);
  727. copy($src_file, $dst_file) or die "ERROR: copy_test_files: File cannot be copied. $!";
  728. }
  729. }
  730. }
  731. sub copy_result_files ()
  732. {
  733. if (/\.result$/)
  734. {
  735. $src_file = $File::Find::name;
  736. if ($File::Find::topdir eq $File::Find::dir && $src_file !~ /SCCS/)
  737. {
  738. $result_filename = basename($src_file) ;
  739. $dst_file = File::Spec->catfile($r_folder, $result_filename);
  740. copy($src_file, $dst_file) or die "ERROR: copy_result_files: File cannot be copied. $!";
  741. }
  742. }
  743. }
  744. sub get_timestamp
  745. {
  746. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst) = localtime();
  747. return sprintf("%04d%02d%02d%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
  748. }
  749. sub read_tests_names
  750. {
  751. my $tests_file = shift;
  752. my $force_load = shift;
  753. if ($force_load || ( (stat($tests_file->{filename}))[9] != $tests_file->{mtime}) )
  754. {
  755. open (TEST, $tests_file->{filename}) || die ("Could not open file <".
  756. $tests_file->{filename}."> $!");
  757. @{$tests_file->{data}}= grep {!/^[#\r\n]|^$/} map { s/[\r\n]//g; $_ } <TEST>;
  758. close (TEST);
  759. $tests_file->{mtime}=(stat(_))[9];
  760. }
  761. }
  762. sub get_random_test
  763. {
  764. my $envt=shift;
  765. my $tests= $envt->{tests_file}->{data};
  766. my $random = int(rand(@{$tests}));
  767. my $test = $tests->[$random];
  768. return $test;
  769. }
  770. sub get_next_test
  771. {
  772. my $envt=shift;
  773. my $test;
  774. if (@{$envt->{tests_file}->{data}})
  775. {
  776. $test=${$envt->{tests_file}->{data}}[$envt->{test_seq_idx}];
  777. $envt->{test_seq_idx}++;
  778. }
  779. #If we reach bound of array, reset seq index and increment loop counter
  780. if ($envt->{test_seq_idx} == scalar(@{$envt->{tests_file}->{data}}))
  781. {
  782. $envt->{test_seq_idx}=0;
  783. {
  784. lock($test_counters_lock);
  785. $test_counters{loop_count}++;
  786. }
  787. }
  788. return $test;
  789. }
  790. sub get_test
  791. {
  792. my $envt=shift;
  793. {
  794. lock($test_counters_lock);
  795. $test_counters{test_count}++;
  796. }
  797. if ($envt->{mode} eq 'seq')
  798. {
  799. return get_next_test($envt);
  800. }
  801. elsif ($envt->{mode} eq 'random')
  802. {
  803. return get_random_test($envt);
  804. }
  805. }
  806. sub stress_log
  807. {
  808. my ($log_file, $line)=@_;
  809. {
  810. open(SLOG,">>$log_file") or warn "Error during opening log file $log_file";
  811. print SLOG $line,"\n";
  812. close(SLOG);
  813. }
  814. }
  815. sub log_session_errors
  816. {
  817. my ($env, $test_name) = @_;
  818. my $line='';
  819. {
  820. lock ($log_file_lock);
  821. #header in the begining of log file
  822. if (!-e $stress_log_file)
  823. {
  824. stress_log($stress_log_file,
  825. "TestID TID Suite TestFileName Found Errors");
  826. stress_log($stress_log_file,
  827. "=======================================================");
  828. }
  829. $line=sprintf('%6d %3d %10s %20s %s', $env->{test_count}, threads->self->tid,
  830. $opt_suite, $test_name,
  831. join(",", @{$env->{test_status}}));
  832. stress_log($stress_log_file, $line);
  833. #stress_log_with_lock($stress_log_file, "\n");
  834. if ($opt_log_error_details)
  835. {
  836. foreach $severity (sort {$a cmp $b} keys %{$env->{errors}})
  837. {
  838. stress_log($stress_log_file, "");
  839. foreach $error (keys %{$env->{errors}->{$severity}})
  840. {
  841. if ($error ne 'total')
  842. {
  843. stress_log($stress_log_file, "$severity: Count:".
  844. $env->{errors}->{$severity}->{$error}->[0].
  845. " Error:". $env->{errors}->{$severity}->{$error}->[1]);
  846. }
  847. }
  848. }
  849. }
  850. }
  851. }
  852. sub sig_INT_handler
  853. {
  854. $SIG{INT}= \&sig_INT_handler;
  855. $exiting=1;
  856. print STDERR "$$: Got INT signal-------------------------------------------\n";
  857. }
  858. sub sig_TERM_handler
  859. {
  860. $SIG{TERM}= \&sig_TERM_handler;
  861. $exiting=1;
  862. print STDERR "$$: Got TERM signal\n";
  863. }
  864. sub usage
  865. {
  866. print <<EOF;
  867. The MySQL Stress suite Ver $stress_suite_version
  868. mysql-stress-test.pl --stress-basedir=<dir> --stress-suite-basedir=<dir> --server-logs-dir=<dir>
  869. --server-host
  870. --server-port
  871. --server-socket
  872. --server-user
  873. --server-password
  874. --server-logs-dir
  875. Directory where all clients session logs will be stored. Usually
  876. this is shared directory associated with server that used
  877. in testing
  878. Required option.
  879. --stress-suite-basedir=<dir>
  880. Directory that has r/ t/ subfolders with test/result files
  881. which will be used for testing. Also by default we are looking
  882. in this directory for 'stress-tests.txt' file which contains
  883. list of tests. It is possible to specify other location of this
  884. file with --stress-tests-file option.
  885. Required option.
  886. --stress-basedir=<dir>
  887. Working directory for this test run. This directory will be used
  888. as temporary location for results tracking during testing
  889. Required option.
  890. --stress-datadir=<dir>
  891. Location of data files used which will be used in testing.
  892. By default we search for these files in <dir>/data where dir
  893. is value of --stress-suite-basedir option.
  894. --stress-init-file[=/path/to/file with tests for initialization of stress db]
  895. Using of this option allows to perform initialization of database
  896. by execution of test files. List of tests will be taken either from
  897. specified file or if it omited from default file 'stress-init.txt'
  898. located in <--stress-suite-basedir/--suite> dir
  899. --stress-tests-file[=/path/to/file with tests]
  900. Using of this option allows to run stress test itself. Tests for testing
  901. will be taken either from specified file or if it omited from default
  902. file 'stress-tests.txt' located in <--stress-suite-basedir/--suite> dir
  903. --stress-mode= [random|seq]
  904. There are two possible modes which affect order of selecting tests
  905. from the list:
  906. - in random mode tests will be selected in random order
  907. - in seq mode each thread will execute tests in the loop one by one as
  908. they specified in the list file.
  909. --sleep-time=<time in seconds>
  910. Delay between test execution. Could be usefull in continued testsing
  911. when one of instance of stress script perform periodical cleanup or
  912. recreating of some database objects
  913. --threads=#number of threads
  914. Define number of threads
  915. --check-tests-file
  916. Check file with list of tests. If file was modified it will force to
  917. reread list of tests. Could be usefull in continued testing for
  918. adding/removing tests without script interruption
  919. --mysqltest=/path/to/mysqltest binary
  920. --verbose
  921. --cleanup
  922. Force to clean up working directory (specified with --stress-basedir)
  923. --log-error-details
  924. Enable errors details in the global error log file. (Default: off)
  925. --test-count=<number of executed tests before we have to exit>
  926. --loop-count=<number of executed loops in sequential mode before we have to exit>
  927. --test-duration=<number of seconds that stress test should run>
  928. Example of tool usage:
  929. perl mysql-stress-test.pl \
  930. --stress-suite-basedir=/opt/qa/mysql-test-extra-5.0/mysql-test \
  931. --stress-basedir=/opt/qa/test \
  932. --server-logs-dir=/opt/qa/logs \
  933. --test-count=20 \
  934. --stress-tests-file=innodb-tests.txt \
  935. --stress-init-file=innodb-init.txt \
  936. --threads=5 \
  937. --suite=funcs_1 \
  938. --mysqltest=/opt/mysql/mysql-5.0/client/mysqltest \
  939. --server-user=root \
  940. --server-database=test \
  941. --cleanup \
  942. EOF
  943. exit(0);
  944. }