[netcore] Ongoing work. (#13391)
[mono-project.git] / mono / tests / stress-runner.pl
blobaa6301a9547e6329ee92635358854ea5cd3bd0a4
1 #!/usr/bin/perl -w
3 # mono stress test tool
4 # This stress test runner is designed to detect possible
5 # leaks, runtime slowdowns and crashes when a task is performed
6 # repeatedly.
7 # A stress program should be written to repeat for a number of times
8 # a specific task: it is run a first time to collect info about memory
9 # and cpu usage: this run should last a couple of seconds or so.
10 # Then, the same program is run with a number of iterations that is at least
11 # 2 orders of magnitude bigger than the first run (3 orders should be used,
12 # eventually, to detect smaller leaks).
13 # Of course the right time for the test and the ratio depends on the test
14 # itself, so it's configurable per-test.
15 # The test driver will then check that the second run has used roughly the
16 # same amount of memory as the first and a proportionally bigger cpu time.
17 # Note: with a conservative GC there may be more false positives than
18 # with a precise one, because heap size may grow depending on timing etc.
19 # so failing results need to be checked carefully. In some cases a solution
20 # is to increase the number of runs in the dry run.
22 use POSIX ":sys_wait_h";
23 use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
25 # in milliseconds between checks of resource usage
26 my $interval = 50;
27 # multiplier to allow some wiggle room
28 my $wiggle_ratio = 1.05;
29 # if the test computer is too fast or if we want to stress test more,
30 # we multiply the test ratio by this number. Use the --times=x option.
31 my $extra_strong = 1;
33 # descriptions of the tests to run
34 # for each test:
35 # program is the program to run
36 # args an array ref of argumenst to pass to program
37 # arg-knob is the index of the argument in args that changes the number of iterations
38 # ratio is the multiplier applied to the arg-knob argument
39 my %tests = (
40 'domain-stress' => {
41 'program' => 'domain-stress.exe',
42 # threads, domains, allocs, loops
43 'args' => [2, 10, 1000, 1],
44 'arg-knob' => 3, # loops
45 'ratio' => 30,
47 'gchandle-stress' => {
48 'program' => 'gchandle-stress.exe',
49 # allocs, loops
50 'args' => [80000, 2],
51 'arg-knob' => 1, # loops
52 'ratio' => 20,
54 'monitor-stress' => {
55 'program' => 'monitor-stress.exe',
56 # loops
57 'args' => [10],
58 'arg-knob' => 0, # loops
59 'ratio' => 20,
61 'gc-stress' => {
62 'program' => 'gc-stress.exe',
63 # loops
64 'args' => [25],
65 'arg-knob' => 0, # loops
66 'ratio' => 20,
68 'gc-graystack-stress' => {
69 'program' => 'gc-graystack-stress.exe',
70 # width, depth, collections
71 'args' => [125, 10000, 100],
72 'arg-knob' => 2, # loops
73 'ratio' => 10,
75 'gc-copy-stress' => {
76 'program' => 'gc-copy-stress.exe',
77 # loops, count, persist_factor
78 'args' => [250, 500000, 10],
79 'arg-knob' => 1, # count
80 'ratio' => 4,
82 'thread-stress' => {
83 'program' => 'thread-stress.exe',
84 # loops
85 'args' => [20],
86 'arg-knob' => 0, # loops
87 'ratio' => 20,
89 'spinlock-stress' => {
90 'program' => 'spinlock-stress.exe',
91 'args' => [20],
92 'arg-knob' => 0,
93 'ratio' => 4,
95 'abort-stress-1' => {
96 'program' => 'abort-stress-1.exe',
97 # loops,
98 'args' => [20],
99 'arg-knob' => 0, # loops
100 'ratio' => 20,
102 # FIXME: This tests exits, so it has no loops, instead it should be run more times
103 'exit-stress' => {
104 'program' => 'exit-stress.exe',
105 # loops,
106 'args' => [10],
107 'arg-knob' => 0, # loops
108 'ratio' => 20,
110 # FIXME: This test deadlocks, bug 72740.
111 # We need hang detection
112 #'abort-stress-2' => {
113 # 'program' => 'abort-stress-2.exe',
114 # # loops,
115 # 'args' => [20],
116 # 'arg-knob' => 0, # loops
117 # 'ratio' => 20,
121 # poor man option handling
122 while (@ARGV) {
123 my $arg = shift @ARGV;
124 if ($arg =~ /^--times=(\d+)$/) {
125 $extra_strong = $1;
126 next;
128 if ($arg =~ /^--interval=(\d+)$/) {
129 $interval = $1;
130 next;
132 unshift @ARGV, $arg;
133 last;
135 my $test_rx = shift (@ARGV) || '.';
136 # the mono runtime to use and the arguments to pass to it
137 my @mono_args = @ARGV;
138 my @results = ();
139 my %vmmap = qw(VmSize 0 VmLck 1 VmRSS 2 VmData 3 VmStk 4 VmExe 5 VmLib 6 VmHWM 7 VmPTE 8 VmPeak 9);
140 my @vmnames = sort {$vmmap{$a} <=> $vmmap{$b}} keys %vmmap;
141 # VmRSS depends on the operating system's decisions
142 my %vmignore = qw(VmRSS 1);
143 my $errorcount = 0;
144 my $numtests = 0;
146 @mono_args = 'mono' unless @mono_args;
148 apply_options ();
150 foreach my $test (sort keys %tests) {
151 next unless ($tests{$test}->{'program'} =~ /$test_rx/);
152 $numtests++;
153 run_test ($test, 'dry');
154 run_test ($test, 'stress');
157 # print all the reports at the end
158 foreach my $test (sort keys %tests) {
159 next unless ($tests{$test}->{'program'} =~ /$test_rx/);
160 print_test_report ($test);
163 print "No tests matched '$test_rx'.\n" unless $numtests;
165 if ($errorcount) {
166 print "Total issues: $errorcount\n";
167 exit (1);
168 } else {
169 exit (0);
172 sub run_test {
173 my ($name, $mode) = @_;
174 my $test = $tests {$name};
175 my @targs = (@mono_args, $test->{program});
176 my @results = ();
177 my @rargs = @{$test->{"args"}};
179 if ($mode ne "dry") {
180 # FIXME: set also a timeout
181 $rargs [$test->{"arg-knob"}] *= $test->{"ratio"};
183 push @targs, @rargs;
184 print "Running test '$name' in $mode mode\n";
185 my $start_time = [gettimeofday];
186 my $pid = fork ();
187 if ($pid == 0) {
188 exec @targs;
189 die "Cannot exec: $! (@targs)\n";
190 } else {
191 my $kid;
192 do {
193 $kid = waitpid (-1, WNOHANG);
194 my $sample = collect_memusage ($pid);
195 push @results, $sample if (defined ($sample) && @{$sample});
196 # sleep for a few ms
197 usleep ($interval * 1000) unless $kid > 0;
198 } until $kid > 0;
200 my $end_time = [gettimeofday];
201 $test->{"$mode-cputime"} = tv_interval ($start_time, $end_time);
202 $test->{"$mode-memusage"} = [summarize_result (@results)];
205 sub print_test_report {
206 my ($name) = shift;
207 my $test = $tests {$name};
208 my ($cpu_dry, $cpu_test) = ($test->{'dry-cputime'}, $test->{'stress-cputime'});
209 my @dry_mem = @{$test->{'dry-memusage'}};
210 my @test_mem = @{$test->{'stress-memusage'}};
211 my $ratio = $test->{'ratio'};
212 print "Report for test: $name\n";
213 print "Cpu usage: dry: $cpu_dry, stress: $cpu_test\n";
214 print "Memory usage (KB):\n";
215 print "\t ",join ("\t", @vmnames), "\n";
216 print "\t dry: ", join ("\t", @dry_mem), "\n";
217 print "\tstress: ", join ("\t", @test_mem), "\n";
218 if ($cpu_test > ($cpu_dry * $ratio) * $wiggle_ratio) {
219 print "Cpu usage not proportional to ratio $ratio.\n";
220 $errorcount++;
222 my $i;
223 for ($i = 0; $i < @dry_mem; ++$i) {
224 next if exists $vmignore {$vmnames [$i]};
225 if ($test_mem [$i] > $dry_mem [$i] * $wiggle_ratio) {
226 print "Memory usage $vmnames[$i] not constant.\n";
227 $errorcount++;
232 sub collect_memusage {
233 my ($pid) = @_;
234 open (PROC, "</proc/$pid/status") || return undef; # might be dead already
235 my @sample = ();
236 while (<PROC>) {
237 next unless /^(Vm.*?):\s+(\d+)\s+kB/;
238 $sample [$vmmap {$1}] = $2;
240 close (PROC);
241 return \@sample;
244 sub summarize_result {
245 my (@data) = @_;
246 my (@result) = (0) x 7;
247 my $i;
248 foreach my $sample (@data) {
249 for ($i = 0; $i < 7; ++$i) {
250 if ($sample->[$i] > $result [$i]) {
251 $result [$i] = $sample->[$i];
255 return @result;
258 sub apply_options {
259 foreach my $test (values %tests) {
260 $test->{args}->[$test->{'arg-knob'}] *= $extra_strong;