pintos: Avoid literal control character in Perl variable name.
[pintos-anon] / src / utils / backtrace
1 #! /usr/bin/perl -w
2
3 use strict;
4
5 # Check command line.
6 if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) {
7     print <<'EOF';
8 backtrace, for converting raw addresses into symbolic backtraces
9 usage: backtrace [BINARY]... ADDRESS...
10 where BINARY is the binary file or files from which to obtain symbols
11  and ADDRESS is a raw address to convert to a symbol name.
12
13 If no BINARY is unspecified, the default is the first of kernel.o or
14 build/kernel.o that exists.  If multiple binaries are specified, each
15 symbol printed is from the first binary that contains a match.
16
17 The ADDRESS list should be taken from the "Call stack:" printed by the
18 kernel.  Read "Backtraces" in the "Debugging Tools" chapter of the
19 Pintos documentation for more information.
20 EOF
21     exit 0;
22 }
23 die "backtrace: at least one argument required (use --help for help)\n"
24     if @ARGV == 0;
25
26 # Drop garbage inserted by kernel.
27 @ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV);
28 s/\.$// foreach @ARGV;
29
30 # Find binaries.
31 my (@binaries);
32 while ($ARGV[0] !~ /^0x/) {
33     my ($bin) = shift @ARGV;
34     die "backtrace: $bin: not found (use --help for help)\n" if ! -e $bin;
35     push (@binaries, $bin);
36 }
37 if (!@binaries) {
38     my ($bin);
39     if (-e 'kernel.o') {
40         $bin = 'kernel.o';
41     } elsif (-e 'build/kernel.o') {
42         $bin = 'build/kernel.o';
43     } else {
44         die "backtrace: no binary specified and neither \"kernel.o\" nor \"build/kernel.o\" exists (use --help for help)\n";
45     }
46     push (@binaries, $bin);
47 }
48
49 # Find addr2line.
50 my ($a2l) = search_path ("i386-elf-addr2line") || search_path ("addr2line");
51 if (!$a2l) {
52     die "backtrace: neither `i386-elf-addr2line' nor `addr2line' in PATH\n";
53 }
54 sub search_path {
55     my ($target) = @_;
56     for my $dir (split (':', $ENV{PATH})) {
57         my ($file) = "$dir/$target";
58         return $file if -e $file;
59     }
60     return undef;
61 }
62
63 # Figure out backtrace.
64 my (@locs) = map ({ADDR => $_}, @ARGV);
65 for my $bin (@binaries) {
66     open (A2L, "$a2l -fe $bin " . join (' ', map ($_->{ADDR}, @locs)) . "|");
67     for (my ($i) = 0; <A2L>; $i++) {
68         my ($function, $line);
69         chomp ($function = $_);
70         chomp ($line = <A2L>);
71         next if defined $locs[$i]{BINARY};
72
73         if ($function ne '??' || $line ne '??:0') {
74             $locs[$i]{FUNCTION} = $function;
75             $locs[$i]{LINE} = $line;
76             $locs[$i]{BINARY} = $bin;
77         }
78     }
79     close (A2L);
80 }
81
82 # Print backtrace.
83 my ($cur_binary);
84 for my $loc (@locs) {
85     if (defined ($loc->{BINARY})
86         && @binaries > 1
87         && (!defined ($cur_binary) || $loc->{BINARY} ne $cur_binary)) {
88         $cur_binary = $loc->{BINARY};
89         print "In $cur_binary:\n";
90     }
91
92     my ($addr) = $loc->{ADDR};
93     $addr = sprintf ("0x%08x", hex ($addr)) if $addr =~ /^0x[0-9a-f]+$/i;
94
95     print $addr, ": ";
96     if (defined ($loc->{BINARY})) {
97         my ($function) = $loc->{FUNCTION};
98         my ($line) = $loc->{LINE};
99         $line =~ s/^(\.\.\/)*//;
100         $line = "..." . substr ($line, -25) if length ($line) > 28;
101         print "$function ($line)";
102     } else {
103         print "(unknown)";
104     }
105     print "\n";
106 }