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.

1241 lines
36 KiB

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