pintos: Avoid literal control character in Perl variable name.
[pintos-anon] / src / utils / backtrace
index 487e224791388de739f57fdd512e75b69a579fd8..95e422f04793610960414529f543ae6d82132c6c 100755 (executable)
@@ -4,27 +4,47 @@ use strict;
 
 # Check command line.
 if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) {
-    print "backtrace, for converting raw addresses into symbolic backtraces\n";
-    print "\n";
-    print "usage: backtrace BINARY ADDRESS...\n";
-    print "where BINARY is the binary file from which to obtain symbols\n";
-    print "  and each ADDRESS is a raw address to convert to a symbol name.\n";
-    print "\n";
-    print "In use with Pintos, BINARY is usually kernel.o and the ADDRESS\n";
-    print "list is taken from the \"Call stack:\" printed by the kernel.\n";
-    print "Read \"Backtraces\" in the \"Debugging Tools\" chapter\n";
-    print "of the Pintos documentation for more information.\n";
+    print <<'EOF';
+backtrace, for converting raw addresses into symbolic backtraces
+usage: backtrace [BINARY]... ADDRESS...
+where BINARY is the binary file or files from which to obtain symbols
+ and ADDRESS is a raw address to convert to a symbol name.
+
+If no BINARY is unspecified, the default is the first of kernel.o or
+build/kernel.o that exists.  If multiple binaries are specified, each
+symbol printed is from the first binary that contains a match.
+
+The ADDRESS list should be taken from the "Call stack:" printed by the
+kernel.  Read "Backtraces" in the "Debugging Tools" chapter of the
+Pintos documentation for more information.
+EOF
     exit 0;
 }
-die "backtrace: binary file argument required (use --help for help)\n"
+die "backtrace: at least one argument required (use --help for help)\n"
     if @ARGV == 0;
-die "backtrace: at least one address argument required (use --help for help)\n"
-    if @ARGV == 1;
 
-# Find binary file.
-my ($bin) = shift @ARGV;
-die "backtrace: $bin: not found (use --help for help)\n"
-    if ! -e $bin;
+# Drop garbage inserted by kernel.
+@ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV);
+s/\.$// foreach @ARGV;
+
+# Find binaries.
+my (@binaries);
+while ($ARGV[0] !~ /^0x/) {
+    my ($bin) = shift @ARGV;
+    die "backtrace: $bin: not found (use --help for help)\n" if ! -e $bin;
+    push (@binaries, $bin);
+}
+if (!@binaries) {
+    my ($bin);
+    if (-e 'kernel.o') {
+       $bin = 'kernel.o';
+    } elsif (-e 'build/kernel.o') {
+       $bin = 'build/kernel.o';
+    } else {
+       die "backtrace: no binary specified and neither \"kernel.o\" nor \"build/kernel.o\" exists (use --help for help)\n";
+    }
+    push (@binaries, $bin);
+}
 
 # Find addr2line.
 my ($a2l) = search_path ("i386-elf-addr2line") || search_path ("addr2line");
@@ -40,17 +60,47 @@ sub search_path {
     return undef;
 }
 
-# Drop leading and trailing garbage inserted by kernel.
-shift while grep (/call|stack/i, $ARGV[0]);
-s/\.$// foreach @ARGV;
+# Figure out backtrace.
+my (@locs) = map ({ADDR => $_}, @ARGV);
+for my $bin (@binaries) {
+    open (A2L, "$a2l -fe $bin " . join (' ', map ($_->{ADDR}, @locs)) . "|");
+    for (my ($i) = 0; <A2L>; $i++) {
+       my ($function, $line);
+       chomp ($function = $_);
+       chomp ($line = <A2L>);
+       next if defined $locs[$i]{BINARY};
 
-# Do backtrace.
-open (A2L, "$a2l -fe $bin " . join (' ', @ARGV) . "|");
-while (<A2L>) {
-    my ($function, $line);
-    chomp ($function = $_);
-    chomp ($line = <A2L>);
-    print shift (@ARGV), ": $function ($line)\n";
+       if ($function ne '??' || $line ne '??:0') {
+           $locs[$i]{FUNCTION} = $function;
+           $locs[$i]{LINE} = $line;
+           $locs[$i]{BINARY} = $bin;
+       }
+    }
+    close (A2L);
+}
+
+# Print backtrace.
+my ($cur_binary);
+for my $loc (@locs) {
+    if (defined ($loc->{BINARY})
+       && @binaries > 1
+       && (!defined ($cur_binary) || $loc->{BINARY} ne $cur_binary)) {
+       $cur_binary = $loc->{BINARY};
+       print "In $cur_binary:\n";
+    }
+
+    my ($addr) = $loc->{ADDR};
+    $addr = sprintf ("0x%08x", hex ($addr)) if $addr =~ /^0x[0-9a-f]+$/i;
+
+    print $addr, ": ";
+    if (defined ($loc->{BINARY})) {
+       my ($function) = $loc->{FUNCTION};
+       my ($line) = $loc->{LINE};
+       $line =~ s/^(\.\.\/)*//;
+       $line = "..." . substr ($line, -25) if length ($line) > 28;
+       print "$function ($line)";
+    } else {
+       print "(unknown)";
+    }
+    print "\n";
 }
-close (A2L);
-