~drizzle-trunk/drizzle/development

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