if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) {
print <<'EOF';
backtrace, for converting raw addresses into symbolic backtraces
-usage: backtrace [BINARY] ADDRESS...
-where BINARY is the binary file from which to obtain symbols
+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 BINARY is unspecified, the default is the first of kernel.o or
-build/kernel.o that exists.
+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
die "backtrace: at least one argument required (use --help for help)\n"
if @ARGV == 0;
-# Drop leading and trailing garbage inserted by kernel.
-shift while grep (/^(call|stack:?)$/i, $ARGV[0]);
+# Drop garbage inserted by kernel.
+@ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV);
s/\.$// foreach @ARGV;
-# Find binary file.
-my ($bin) = $ARGV[0];
-if (-e $bin) {
- shift @ARGV;
-} elsif ($bin !~ /^0/) {
- die "backtrace: $bin: not found (use --help for help)\n";
-} elsif (-e 'kernel.o') {
- $bin = 'kernel.o';
-} elsif (-e 'build/kernel.o') {
- $bin = 'build/kernel.o';
-} else {
- die "backtrace: can't find binary for backtrace (use --help for help)\n";
+# 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.
return undef;
}
-# Do backtrace.
-open (A2L, "$a2l -fe $bin " . join (' ', @ARGV) . "|");
-while (<A2L>) {
- my ($function, $line);
- chomp ($function = $_);
- chomp ($line = <A2L>);
- $line = "..." . substr ($line, -25) if length ($line) > 28;
- print shift (@ARGV), ": $function ($line)\n";
+# 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};
+
+ 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);
-