blob: 045f5e488b71fa8c87919da89ea2e71bc4c382c0 [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
Elliott Hughesdc75b012017-07-05 13:54:44 -070012# Copyright (c) 1998 by Richard Braakman <dark@xs4all.nl>.
13# Copyright (c) 1998-2017 The strace developers.
Wichert Akkerman7b27ba01999-08-30 23:26:53 +000014
15# Redistribution and use in source and binary forms, with or without
16# modification, are permitted provided that the following conditions
17# are met:
18# 1. Redistributions of source code must retain the above copyright
19# notice, this list of conditions and the following disclaimer.
20# 2. Redistributions in binary form must reproduce the above copyright
21# notice, this list of conditions and the following disclaimer in the
22# documentation and/or other materials provided with the distribution.
23# 3. The name of the author may not be used to endorse or promote products
24# derived from this software without specific prior written permission.
25#
26# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000036
Keith Owensa0bc25f2016-01-18 12:57:41 +110037use strict;
38use warnings;
39
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000040my %unfinished;
Keith Owensa0bc25f2016-01-18 12:57:41 +110041my $floatform;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000042
43# Scales for strace slowdown. Make configurable!
44my $scale_factor = 3.5;
Elliott Hughes39bac052017-05-25 16:56:11 -070045my %running_fqname;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000046
47while (<>) {
Keith Owensdbc1ffb2016-01-18 13:03:41 +110048 my ($pid, $call, $args, $result, $time, $time_spent);
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000049 chop;
Keith Owensa0bc25f2016-01-18 12:57:41 +110050 $floatform = 0;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000051
52 s/^(\d+)\s+//;
53 $pid = $1;
54
55 if (s/^(\d\d):(\d\d):(\d\d)(?:\.(\d\d\d\d\d\d))? //) {
56 $time = $1 * 3600 + $2 * 60 + $3;
57 if (defined $4) {
58 $time = $time + $4 / 1000000;
59 $floatform = 1;
60 }
61 } elsif (s/^(\d+)\.(\d\d\d\d\d\d) //) {
62 $time = $1 + ($2 / 1000000);
63 $floatform = 1;
64 }
65
66 if (s/ <unfinished ...>$//) {
67 $unfinished{$pid} = $_;
68 next;
69 }
70
71 if (s/^<... \S+ resumed> //) {
72 unless (exists $unfinished{$pid}) {
73 print STDERR "$0: $ARGV: cannot find start of resumed call on line $.";
74 next;
75 }
76 $_ = $unfinished{$pid} . $_;
77 delete $unfinished{$pid};
78 }
79
Keith Owensef445b52016-01-18 13:09:59 +110080 if (/^--- SIG(\S+) (.*) ---$/) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000081 # $pid received signal $1
82 # currently we don't do anything with this
83 next;
84 }
85
86 if (/^\+\+\+ killed by SIG(\S+) \+\+\+$/) {
87 # $pid received signal $1
88 handle_killed($pid, $time);
89 next;
90 }
91
Keith Owensef445b52016-01-18 13:09:59 +110092 if (/^\+\+\+ exited with (\d+) \+\+\+$/) {
93 # $pid exited $1
94 # currently we don't do anything with this
95 next;
96 }
97
Wichert Akkerman9ce1a631999-08-29 23:15:07 +000098 ($call, $args, $result) = /(\S+)\((.*)\)\s+= (.*)$/;
Keith Owensdbc1ffb2016-01-18 13:03:41 +110099 if ($result =~ /^(.*) <([0-9.]*)>$/) {
100 ($result, $time_spent) = ($1, $2);
101 }
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000102 unless (defined $result) {
103 print STDERR "$0: $ARGV: $.: cannot parse line.\n";
104 next;
105 }
106
107 handle_trace($pid, $call, $args, $result, $time);
108}
109
110display_trace();
111
112exit 0;
113
114sub parse_str {
115 my ($in) = @_;
116 my $result = "";
117
118 while (1) {
119 if ($in =~ s/^\\(.)//) {
120 $result .= $1;
121 } elsif ($in =~ s/^\"//) {
122 if ($in =~ s/^\.\.\.//) {
123 return ("$result...", $in);
124 }
125 return ($result, $in);
126 } elsif ($in =~ s/([^\\\"]*)//) {
127 $result .= $1;
128 } else {
129 return (undef, $in);
130 }
131 }
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000132}
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000133
134sub parse_one {
135 my ($in) = @_;
136
137 if ($in =~ s/^\"//) {
Keith Owensa0bc25f2016-01-18 12:57:41 +1100138 my $tmp;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000139 ($tmp, $in) = parse_str($in);
140 if (not defined $tmp) {
141 print STDERR "$0: $ARGV: $.: cannot parse string.\n";
142 return (undef, $in);
143 }
144 return ($tmp, $in);
Keith Owensa0bc25f2016-01-18 12:57:41 +1100145 } elsif ($in =~ s/^0x([[:xdigit:]]+)//) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000146 return (hex $1, $in);
147 } elsif ($in =~ s/^(\d+)//) {
148 return (int $1, $in);
149 } else {
150 print STDERR "$0: $ARGV: $.: unrecognized element.\n";
151 return (undef, $in);
152 }
153}
154
155sub parseargs {
156 my ($in) = @_;
157 my @args = ();
158 my $tmp;
159
160 while (length $in) {
161 if ($in =~ s/^\[//) {
162 my @subarr = ();
163 if ($in =~ s,^/\* (\d+) vars \*/\],,) {
164 push @args, $1;
165 } else {
166 while ($in !~ s/^\]//) {
167 ($tmp, $in) = parse_one($in);
168 defined $tmp or return undef;
169 push @subarr, $tmp;
170 unless ($in =~ /^\]/ or $in =~ s/^, //) {
171 print STDERR "$0: $ARGV: $.: missing comma in array.\n";
172 return undef;
173 }
174 if ($in =~ s/^\.\.\.//) {
175 push @subarr, "...";
176 }
177 }
178 push @args, \@subarr;
179 }
180 } elsif ($in =~ s/^\{//) {
181 my %subhash = ();
182 while ($in !~ s/^\}//) {
183 my $key;
184 unless ($in =~ s/^(\w+)=//) {
185 print STDERR "$0: $ARGV: $.: struct field expected.\n";
186 return undef;
187 }
188 $key = $1;
189 ($tmp, $in) = parse_one($in);
190 defined $tmp or return undef;
191 $subhash{$key} = $tmp;
192 unless ($in =~ s/, //) {
193 print STDERR "$0: $ARGV: $.: missing comma in struct.\n";
194 return undef;
195 }
196 }
197 push @args, \%subhash;
198 } else {
199 ($tmp, $in) = parse_one($in);
200 defined $tmp or return undef;
201 push @args, $tmp;
202 }
203 unless (length($in) == 0 or $in =~ s/^, //) {
204 print STDERR "$0: $ARGV: $.: missing comma.\n";
205 return undef;
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000206 }
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000207 }
208 return @args;
209}
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000210
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000211
212my $depth = "";
213
214# process info, indexed by pid.
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000215# fields:
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000216# parent pid number
Keith Owens7e0ba4b2016-01-18 13:12:26 +1100217# seq clones, forks and execs for this pid, in sequence (array)
Denys Vlasenko402eeb62009-01-02 13:03:44 +0000218
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000219# filename and argv (from latest exec)
220# basename (derived from filename)
221# argv[0] is modified to add the basename if it differs from the 0th argument.
222
223my %pr;
224
225sub handle_trace {
226 my ($pid, $call, $args, $result, $time) = @_;
Elliott Hughes39bac052017-05-25 16:56:11 -0700227 my $pid_fqname = $pid . "-" . $time;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000228
Elliott Hughes39bac052017-05-25 16:56:11 -0700229 if (defined $time and not defined $running_fqname{$pid}) {
230 $pr{$pid_fqname}{start} = $time;
231 $running_fqname{$pid} = $pid_fqname;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000232 }
233
Elliott Hughes39bac052017-05-25 16:56:11 -0700234 $pid_fqname = $running_fqname{$pid};
235
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000236 if ($call eq 'execve') {
Keith Owensa0bc25f2016-01-18 12:57:41 +1100237 return if $result ne '0';
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000238
239 my ($filename, $argv) = parseargs($args);
Keith Owensa0bc25f2016-01-18 12:57:41 +1100240 my ($basename) = $filename =~ m/([^\/]*)$/;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000241 if ($basename ne $$argv[0]) {
242 $$argv[0] = "$basename($$argv[0])";
Elliott Hughes39bac052017-05-25 16:56:11 -0700243 }
244 my $seq = $pr{$pid_fqname}{seq};
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000245 $seq = [] if not defined $seq;
246
247 push @$seq, ['EXEC', $filename, $argv];
248
Elliott Hughes39bac052017-05-25 16:56:11 -0700249 $pr{$pid_fqname}{seq} = $seq;
Roland McGrathec407e32005-12-02 04:27:26 +0000250 } elsif ($call eq 'fork' || $call eq 'clone' || $call eq 'vfork') {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000251 return if $result == 0;
252
Elliott Hughes39bac052017-05-25 16:56:11 -0700253 my $seq = $pr{$pid_fqname}{seq};
254 my $result_fqname= $result . "-" . $time;
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000255 $seq = [] if not defined $seq;
Elliott Hughes39bac052017-05-25 16:56:11 -0700256 push @$seq, ['FORK', $result_fqname];
257 $pr{$pid_fqname}{seq} = $seq;
258 $pr{$result_fqname}{start} = $time;
259 $pr{$result_fqname}{parent} = $pid_fqname;
260 $pr{$result_fqname}{seq} = [];
261 $running_fqname{$result} = $result_fqname;
Keith Owensef445b52016-01-18 13:09:59 +1100262 } elsif ($call eq '_exit' || $call eq 'exit_group') {
Elliott Hughes39bac052017-05-25 16:56:11 -0700263 $pr{$running_fqname{$pid}}{end} = $time if defined $time and not defined $pr{$running_fqname{$pid}}{end};
264 delete $running_fqname{$pid};
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000265 }
266}
267
268sub handle_killed {
269 my ($pid, $time) = @_;
Elliott Hughes39bac052017-05-25 16:56:11 -0700270 $pr{$pid}{end} = $time if defined $time and not defined $pr{$pid}{end};
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000271}
272
273sub straight_seq {
274 my ($pid) = @_;
275 my $seq = $pr{$pid}{seq};
276
Keith Owensa0bc25f2016-01-18 12:57:41 +1100277 for my $elem (@$seq) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000278 if ($$elem[0] eq 'EXEC') {
279 my $argv = $$elem[2];
280 print "$$elem[0] $$elem[1] @$argv\n";
281 } elsif ($$elem[0] eq 'FORK') {
282 print "$$elem[0] $$elem[1]\n";
283 } else {
284 print "$$elem[0]\n";
285 }
286 }
287}
288
289sub first_exec {
290 my ($pid) = @_;
291 my $seq = $pr{$pid}{seq};
292
Keith Owensa0bc25f2016-01-18 12:57:41 +1100293 for my $elem (@$seq) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000294 if ($$elem[0] eq 'EXEC') {
295 return $elem;
296 }
297 }
298 return undef;
299}
300
301sub display_pid_trace {
302 my ($pid, $lead) = @_;
303 my $i = 0;
304 my @seq = @{$pr{$pid}{seq}};
305 my $elapsed;
306
307 if (not defined first_exec($pid)) {
308 unshift @seq, ['EXEC', '', ['(anon)'] ];
309 }
310
311 if (defined $pr{$pid}{start} and defined $pr{$pid}{end}) {
312 $elapsed = $pr{$pid}{end} - $pr{$pid}{start};
313 $elapsed /= $scale_factor;
314 if ($floatform) {
315 $elapsed = sprintf("%0.02f", $elapsed);
316 } else {
317 $elapsed = int $elapsed;
318 }
319 }
320
Keith Owensa0bc25f2016-01-18 12:57:41 +1100321 for my $elem (@seq) {
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000322 $i++;
323 if ($$elem[0] eq 'EXEC') {
324 my $argv = $$elem[2];
325 if (defined $elapsed) {
Keith Owens320fb412016-01-18 13:15:36 +1100326 print "$lead [$elapsed] $pid @$argv\n";
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000327 undef $elapsed;
328 } else {
Keith Owens320fb412016-01-18 13:15:36 +1100329 print "$lead $pid @$argv\n";
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000330 }
331 } elsif ($$elem[0] eq 'FORK') {
332 if ($i == 1) {
Elliott Hughes39bac052017-05-25 16:56:11 -0700333 if ($lead =~ /-$/) {
Dmitry V. Levin414fe7d2009-07-08 11:21:17 +0000334 display_pid_trace($$elem[1], "$lead--+--");
Elliott Hughes39bac052017-05-25 16:56:11 -0700335 } else {
Dmitry V. Levin414fe7d2009-07-08 11:21:17 +0000336 display_pid_trace($$elem[1], "$lead +--");
Elliott Hughes39bac052017-05-25 16:56:11 -0700337 }
Wichert Akkerman9ce1a631999-08-29 23:15:07 +0000338 } elsif ($i == @seq) {
339 display_pid_trace($$elem[1], "$lead `--");
340 } else {
341 display_pid_trace($$elem[1], "$lead +--");
342 }
343 }
344 if ($i == 1) {
345 $lead =~ s/\`--/ /g;
346 $lead =~ s/-/ /g;
347 $lead =~ s/\+/|/g;
348 }
349 }
350}
351
352sub display_trace {
353 my ($startpid) = @_;
354
355 $startpid = (keys %pr)[0];
356 while ($pr{$startpid}{parent}) {
357 $startpid = $pr{$startpid}{parent};
358 }
359
360 display_pid_trace($startpid, "");
361}