#!/bin/perl # # Copyright 2006 Sun Microsystems, Inc. ALL RIGHTS RESERVED # Use of this software is authorized pursuant to the terms of the license found at # http://developers.sun.com/berkeley_license.html # # unstrip_traceback: replaces "????????" (pstack) or hex addresses (dbx) with # function names in traceback from pstack or dbx, given the unstripped # version of the same executable. # Automatically detects whether the traceback was produced by dbx or pstack. # By Greg Nakhimovsky, Sun Microsystems, August 1999. # GN Sept 2002: fixed a minor bug triggered by a minor bug in pstack # GN Feb 2006: adjusted for 64-bit executables; switched to /usr/ccs/bin/nm # use integer; use strict; use warnings; # Suppress warnings to stop oct() complaining about hex numbers > 0xffffffff no warnings qw(portable); my $traceback; my $exe; my $start; my $end; my $i; my $i1; my @nm_fields; my $nm_size; my @func_addr; my @func_name; my @words; my $pc_addr; my $low; my $high; my $mid; my $index; my $dbx_traceback; my $pstack_traceback; my $search_for; my $pc_addr_hex; my $offset; # Auto-flush STDOUT and STDERR: select(STDOUT); $| = 1; select(STDERR); $| = 1; if (@ARGV != 2) { print "Usage: unstrip_traceback traceback.txt unstripped_executable\n"; exit(1); } $traceback = $ARGV[0]; $exe = $ARGV[1]; # Find out which format the given traceback file is in: $dbx_traceback = 0; $pstack_traceback = 0; open (TRACE, $traceback) || die "Cannot open file $traceback\n"; while () { if (/\Q????????\E/) { $pstack_traceback = 1; print " This is a pstack traceback\n"; last; } elsif (/\Q] 0x\E/) { $dbx_traceback = 1; print " This is a dbx traceback\n"; last; } } if($dbx_traceback == 0 && $pstack_traceback == 0) { print "Missing function names not found\n"; exit(1); } close(TRACE); # Run nm and store addresses and function names: print " Running nm for $exe ...\n"; print " (may take a while for a large executable)\n"; $start = (times)[0]; # nm agruments used: # -C demangle C++ names # -h no heading # -v sort by value (address) # -x print address in hex # Pre-extend arrays @func_addr and @func_name for efficiency: $#func_addr = 200_000; $#func_name = 200_000; $i = 0; # Unset LD_NOEXEC_64 to make sure /usr/ccs/bin/sparcv9/nm is used for sparcv9: foreach $_ (`LD_NOEXEC_64= /usr/ccs/bin/nm -Chvx $exe | /bin/grep -v '|UNDEF |' | /bin/grep '|FUNC |'`) { chomp; @nm_fields = split (/\|/); # use "oct()" to convert the "0x"-formatted hex string to number: $func_addr[$i] = oct($nm_fields[1]); $func_name[$i] = $nm_fields[7]; $i++; } $nm_size = $i; if($nm_size < 2) { printf "Executable $exe has too few functions: %d. Is it stripped?\n", $nm_size; exit(1); } $end = (times)[0]; no integer; printf " That took %.2f sec\n", ($end-$start); use integer; # printf "Debug: nm_size = %d size of func_addr = %d size of func_name = %d\n", # $nm_size, scalar(@func_addr), scalar(@func_name); open (TRACE, $traceback) || die "Cannot open file $traceback\n"; print " Searching for the functions missing from traceback ...\n"; $start = (times)[0]; # Look for lines containing "????????" (pstack format) or " 0x" (dbx format): while () { if($pstack_traceback == 1) { $search_for = "\Q????????\E"; } else { $search_for = "] 0x"; } if (/$search_for/) { if($pstack_traceback == 1) { @words = split; $pc_addr = oct("0x".$words[0]); } else # dbx format: { @words = split(/\(/, $_); $i1 = index($words[0], "0x"); # index to the hex value $pc_addr_hex = substr($words[0], $i1); # to the end of the word $pc_addr = oct($pc_addr_hex); } # Leave it alone if the address is outside of the range produced by nm if ($pc_addr >= $func_addr[0] && $pc_addr <= $func_addr[$nm_size-1]) { # Do a binary search thru the function addresses that "nm" produced: $low = 0; $high = $nm_size - 1; while ($low <= $high) { $mid = ($low + $high)/2; if ($pc_addr < $func_addr[$mid]) { $high = $mid - 1; } elsif($pc_addr > $func_addr[$mid]) { $low = $mid + 1; } else # found exact match { # print "Debug: found exact match mid=$mid\n"; $high = $mid; last; # break out of the "while" loop } } # print "Debug: high=$high low=$low mid=$mid\n"; $offset = sprintf "0x%x", ($pc_addr - $func_addr[$high]); if($pstack_traceback == 1) # pstack format { $words[1] = $func_name[$high]; printf " %s + %s\n",join(' ', @words), $offset; } else # dbx format: { # print "Debug: replacing $pc_addr_hex with $func_name[$high]\n"; s/$pc_addr_hex/$func_name[$high]/; chomp; print ($_, ", offset $offset\n"); } } else { print $_; } } else { print $_; } } print "\n"; $end = (times)[0]; no integer; printf " The search took %.2f sec\n", ($end-$start);