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.

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