blob: 5435e864b859eaffea4ec0f273179ba28ccf50f3 [file] [log] [blame]
Wichert Akkerman9ce1a631999-08-29 23:15:07 +00001#!/usr/bin/perl
2
3# This script processes strace -f output. It displays a graph of invoked
4# subprocesses, and is useful for finding out what complex commands do.
5
6# You will probably want to invoke strace with -q as well, and with
7# -s 100 to get complete filenames.
8
9# The script can also handle the output with strace -t, -tt, or -ttt.
10# It will add elapsed time for each process in that case.
11
12# This script is Copyright (C) 1998 by Richard Braakman <dark@xs4all.nl>.
Wichert Akkerman7b27ba01999-08-30 23:26:53 +000013
14# Redistribution and use in source and binary forms, with or without
15# modification, are permitted provided that the following conditions
16# are met:
17# 1. Redistributions of source code must retain the above copyright
18# notice, this list of conditions and the following disclaimer.
19# 2. Redistributions in binary form must reproduce the above copyright
20# notice, this list of conditions and the following disclaimer in the
21# documentation and/or other materials provided with the distribution.
22# 3. The name of the author may not be used to endorse or promote products
23# derived from this software without specific prior written permission.
24#
25# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000035
Keith Owensa0bc25f2016-01-18 12:57:41 +110036use strict;
37use warnings;
38
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000039my %unfinished;
Keith Owensa0bc25f2016-01-18 12:57:41 +110040my $floatform;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000041
42# Scales for strace slowdown. Make configurable!
43my $scale_factor = 3.5;
44
45while (<>) {
Keith Owensdbc1ffb2016-01-18 13:03:41 +110046 my ($pid, $call, $args, $result, $time, $time_spent);
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000047 chop;
Keith Owensa0bc25f2016-01-18 12:57:41 +110048 $floatform = 0;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000049
50 s/^(\d+)\s+//;
51 $pid = $1;
52
53 if (s/^(\d\d):(\d\d):(\d\d)(?:\.(\d\d\d\d\d\d))? //) {
54 $time = $1 * 3600 + $2 * 60 + $3;
55 if (defined $4) {
56 $time = $time + $4 / 1000000;
57 $floatform = 1;
58 }
59 } elsif (s/^(\d+)\.(\d\d\d\d\d\d) //) {
60 $time = $1 + ($2 / 1000000);
61 $floatform = 1;
62 }
63
64 if (s/ <unfinished ...>$//) {
65 $unfinished{$pid} = $_;
66 next;
67 }
68
69 if (s/^<... \S+ resumed> //) {
70 unless (exists $unfinished{$pid}) {
71 print STDERR "$0: $ARGV: cannot find start of resumed call on line $.";
72 next;
73 }
74 $_ = $unfinished{$pid} . $_;
75 delete $unfinished{$pid};
76 }
77
Keith Owensef445b52016-01-18 13:09:59 +110078 if (/^--- SIG(\S+) (.*) ---$/) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000079 # $pid received signal $1
80 # currently we don't do anything with this
81 next;
82 }
83
84 if (/^\+\+\+ killed by SIG(\S+) \+\+\+$/) {
85 # $pid received signal $1
86 handle_killed($pid, $time);
87 next;
88 }
89
Keith Owensef445b52016-01-18 13:09:59 +110090 if (/^\+\+\+ exited with (\d+) \+\+\+$/) {
91 # $pid exited $1
92 # currently we don't do anything with this
93 next;
94 }
95
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000096 ($call, $args, $result) = /(\S+)\((.*)\)\s+= (.*)$/;
Keith Owensdbc1ffb2016-01-18 13:03:41 +110097 if ($result =~ /^(.*) <([0-9.]*)>$/) {
98 ($result, $time_spent) = ($1, $2);
99 }
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000100 unless (defined $result) {
101 print STDERR "$0: $ARGV: $.: cannot parse line.\n";
102 next;
103 }
104
105 handle_trace($pid, $call, $args, $result, $time);
106}
107
108display_trace();
109
110exit 0;
111
112sub parse_str {
113 my ($in) = @_;
114 my $result = "";
115
116 while (1) {
117 if ($in =~ s/^\\(.)//) {
118 $result .= $1;
119 } elsif ($in =~ s/^\"//) {
120 if ($in =~ s/^\.\.\.//) {
121 return ("$result...", $in);
122 }
123 return ($result, $in);
124 } elsif ($in =~ s/([^\\\"]*)//) {
125 $result .= $1;
126 } else {
127 return (undef, $in);
128 }
129 }
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000130}
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000131
132sub parse_one {
133 my ($in) = @_;
134
135 if ($in =~ s/^\"//) {
Keith Owensa0bc25f2016-01-18 12:57:41 +1100136 my $tmp;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000137 ($tmp, $in) = parse_str($in);
138 if (not defined $tmp) {
139 print STDERR "$0: $ARGV: $.: cannot parse string.\n";
140 return (undef, $in);
141 }
142 return ($tmp, $in);
Keith Owensa0bc25f2016-01-18 12:57:41 +1100143 } elsif ($in =~ s/^0x([[:xdigit:]]+)//) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000144 return (hex $1, $in);
145 } elsif ($in =~ s/^(\d+)//) {
146 return (int $1, $in);
147 } else {
148 print STDERR "$0: $ARGV: $.: unrecognized element.\n";
149 return (undef, $in);
150 }
151}
152
153sub parseargs {
154 my ($in) = @_;
155 my @args = ();
156 my $tmp;
157
158 while (length $in) {
159 if ($in =~ s/^\[//) {
160 my @subarr = ();
161 if ($in =~ s,^/\* (\d+) vars \*/\],,) {
162 push @args, $1;
163 } else {
164 while ($in !~ s/^\]//) {
165 ($tmp, $in) = parse_one($in);
166 defined $tmp or return undef;
167 push @subarr, $tmp;
168 unless ($in =~ /^\]/ or $in =~ s/^, //) {
169 print STDERR "$0: $ARGV: $.: missing comma in array.\n";
170 return undef;
171 }
172 if ($in =~ s/^\.\.\.//) {
173 push @subarr, "...";
174 }
175 }
176 push @args, \@subarr;
177 }
178 } elsif ($in =~ s/^\{//) {
179 my %subhash = ();
180 while ($in !~ s/^\}//) {
181 my $key;
182 unless ($in =~ s/^(\w+)=//) {
183 print STDERR "$0: $ARGV: $.: struct field expected.\n";
184 return undef;
185 }
186 $key = $1;
187 ($tmp, $in) = parse_one($in);
188 defined $tmp or return undef;
189 $subhash{$key} = $tmp;
190 unless ($in =~ s/, //) {
191 print STDERR "$0: $ARGV: $.: missing comma in struct.\n";
192 return undef;
193 }
194 }
195 push @args, \%subhash;
196 } else {
197 ($tmp, $in) = parse_one($in);
198 defined $tmp or return undef;
199 push @args, $tmp;
200 }
201 unless (length($in) == 0 or $in =~ s/^, //) {
202 print STDERR "$0: $ARGV: $.: missing comma.\n";
203 return undef;
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000204 }
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000205 }
206 return @args;
207}
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000208
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000209
210my $depth = "";
211
212# process info, indexed by pid.
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000213# fields:
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000214# parent pid number
Keith Owens7e0ba4b2016-01-18 13:12:26 +1100215# seq clones, forks and execs for this pid, in sequence (array)
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000216
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000217# filename and argv (from latest exec)
218# basename (derived from filename)
219# argv[0] is modified to add the basename if it differs from the 0th argument.
220
221my %pr;
222
223sub handle_trace {
224 my ($pid, $call, $args, $result, $time) = @_;
225 my $p;
226
227 if (defined $time and not defined $pr{$pid}{start}) {
228 $pr{$pid}{start} = $time;
229 }
230
231 if ($call eq 'execve') {
Keith Owensa0bc25f2016-01-18 12:57:41 +1100232 return if $result ne '0';
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000233
234 my ($filename, $argv) = parseargs($args);
Keith Owensa0bc25f2016-01-18 12:57:41 +1100235 my ($basename) = $filename =~ m/([^\/]*)$/;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000236 if ($basename ne $$argv[0]) {
237 $$argv[0] = "$basename($$argv[0])";
238 }
239 my $seq = $pr{$pid}{seq};
240 $seq = [] if not defined $seq;
241
242 push @$seq, ['EXEC', $filename, $argv];
243
244 $pr{$pid}{seq} = $seq;
Roland McGrathec407e32005-12-02 04:27:26 +0000245 } elsif ($call eq 'fork' || $call eq 'clone' || $call eq 'vfork') {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000246 return if $result == 0;
247
248 my $seq = $pr{$pid}{seq};
249 $seq = [] if not defined $seq;
250 push @$seq, ['FORK', $result];
251 $pr{$pid}{seq} = $seq;
252 $pr{$result}{parent} = $pid;
Keith Owens7e0ba4b2016-01-18 13:12:26 +1100253 $pr{$result}{seq} = [];
Keith Owensef445b52016-01-18 13:09:59 +1100254 } elsif ($call eq '_exit' || $call eq 'exit_group') {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000255 $pr{$pid}{end} = $time if defined $time;
256 }
257}
258
259sub handle_killed {
260 my ($pid, $time) = @_;
261 $pr{$pid}{end} = $time if defined $time;
262}
263
264sub straight_seq {
265 my ($pid) = @_;
266 my $seq = $pr{$pid}{seq};
267
Keith Owensa0bc25f2016-01-18 12:57:41 +1100268 for my $elem (@$seq) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000269 if ($$elem[0] eq 'EXEC') {
270 my $argv = $$elem[2];
271 print "$$elem[0] $$elem[1] @$argv\n";
272 } elsif ($$elem[0] eq 'FORK') {
273 print "$$elem[0] $$elem[1]\n";
274 } else {
275 print "$$elem[0]\n";
276 }
277 }
278}
279
280sub first_exec {
281 my ($pid) = @_;
282 my $seq = $pr{$pid}{seq};
283
Keith Owensa0bc25f2016-01-18 12:57:41 +1100284 for my $elem (@$seq) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000285 if ($$elem[0] eq 'EXEC') {
286 return $elem;
287 }
288 }
289 return undef;
290}
291
292sub display_pid_trace {
293 my ($pid, $lead) = @_;
294 my $i = 0;
295 my @seq = @{$pr{$pid}{seq}};
296 my $elapsed;
297
298 if (not defined first_exec($pid)) {
299 unshift @seq, ['EXEC', '', ['(anon)'] ];
300 }
301
302 if (defined $pr{$pid}{start} and defined $pr{$pid}{end}) {
303 $elapsed = $pr{$pid}{end} - $pr{$pid}{start};
304 $elapsed /= $scale_factor;
305 if ($floatform) {
306 $elapsed = sprintf("%0.02f", $elapsed);
307 } else {
308 $elapsed = int $elapsed;
309 }
310 }
311
Keith Owensa0bc25f2016-01-18 12:57:41 +1100312 for my $elem (@seq) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000313 $i++;
314 if ($$elem[0] eq 'EXEC') {
315 my $argv = $$elem[2];
316 if (defined $elapsed) {
Keith Owens320fb412016-01-18 13:15:36 +1100317 print "$lead [$elapsed] $pid @$argv\n";
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000318 undef $elapsed;
319 } else {
Keith Owens320fb412016-01-18 13:15:36 +1100320 print "$lead $pid @$argv\n";
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000321 }
322 } elsif ($$elem[0] eq 'FORK') {
323 if ($i == 1) {
324 if ($lead =~ /-$/) {
Dmitry V. Levin414fe7d2009-07-08 11:21:17 +0000325 display_pid_trace($$elem[1], "$lead--+--");
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000326 } else {
Dmitry V. Levin414fe7d2009-07-08 11:21:17 +0000327 display_pid_trace($$elem[1], "$lead +--");
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000328 }
329 } elsif ($i == @seq) {
330 display_pid_trace($$elem[1], "$lead `--");
331 } else {
332 display_pid_trace($$elem[1], "$lead +--");
333 }
334 }
335 if ($i == 1) {
336 $lead =~ s/\`--/ /g;
337 $lead =~ s/-/ /g;
338 $lead =~ s/\+/|/g;
339 }
340 }
341}
342
343sub display_trace {
344 my ($startpid) = @_;
345
346 $startpid = (keys %pr)[0];
347 while ($pr{$startpid}{parent}) {
348 $startpid = $pr{$startpid}{parent};
349 }
350
351 display_pid_trace($startpid, "");
352}