3
# ***** BEGIN LICENSE BLOCK *****
4
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
# The contents of this file are subject to the Mozilla Public License Version
7
# 1.1 (the "License"); you may not use this file except in compliance with
8
# the License. You may obtain a copy of the License at
9
# http://www.mozilla.org/MPL/
11
# Software distributed under the License is distributed on an "AS IS" basis,
12
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
# for the specific language governing rights and limitations under the
16
# The Original Code is fix-linux-stack.pl.
18
# The Initial Developer of the Original Code is L. David Baron.
19
# Portions created by the Initial Developer are Copyright (C) 2003
20
# the Initial Developer. All Rights Reserved.
23
# L. David Baron <dbaron@dbaron.org> (original author)
25
# Alternatively, the contents of this file may be used under the terms of
26
# either the GNU General Public License Version 2 or later (the "GPL"), or
27
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
# in which case the provisions of the GPL or the LGPL are applicable instead
29
# of those above. If you wish to allow use of your version of this file only
30
# under the terms of either the GPL or the LGPL, and not to allow others to
31
# use your version of this file under the terms of the MPL, indicate your
32
# decision by deleting the provisions above and replace them with the notice
33
# and other provisions required by the GPL or the LGPL. If you do not delete
34
# the provisions above, a recipient may use your version of this file under
35
# the terms of any one of the MPL, the GPL or the LGPL.
37
# ***** END LICENSE BLOCK *****
39
# $Id: fix-macosx-stack.pl,v 1.6 2007/08/17 06:26:09 dbaron%dbaron.org Exp $
41
# This script processes the output of nsTraceRefcnt's Mac OS X stack
42
# walking code. This is useful for two things:
43
# (1) Getting line number information out of
44
# |nsTraceRefcntImpl::WalkTheStack|'s output in debug builds.
45
# (2) Getting function names out of |nsTraceRefcntImpl::WalkTheStack|'s
46
# output on all builds (where it mostly prints UNKNOWN because only
47
# a handful of symbols are exported from component libraries).
49
# Use the script by piping output containing stacks (such as raw stacks
50
# or make-tree.pl balance trees) through this script.
55
sub separate_debug_file_for($) {
60
my %address_adjustments;
61
sub address_adjustment($) {
63
unless (exists $address_adjustments{$file}) {
66
open(OTOOL, '-|', 'otool', '-l', $file);
68
if (/^ segname __TEXT$/) {
69
if (<OTOOL> =~ /^ vmaddr (0x[0-9a-f]{8})$/) {
73
die "Bad output from otool";
79
$result >= 0 || die "Bad output from otool";
81
$address_adjustments{$file} = $result;
84
return $address_adjustments{$file};
88
my ($array, $address, $data) = @_;
90
# only remember the last item at a given address
91
pop @{$array} if ($#{$array} >= 0 && $array->[$#{$array}]->[0] == $address);
93
push @{$array}, [ $address, $data ];
96
sub sort_by_address() {
97
return $a->[0] <=> $b->[0];
100
# Return a reference to a hash whose {read} and {write} entries are a
101
# bidirectional pipe to an addr2line process that gives symbol
102
# information for a file.
104
sub nmstruct_for($) {
108
unless (exists $nmstructs{$file}) {
109
$nmstruct = { symbols => [], files => [], lines => [] };
111
my $debug_file = separate_debug_file_for($file);
112
$debug_file = $file if ($debug_file eq '');
114
open(NM, '-|', 'nm', '-an', $debug_file);
117
my ($addr, $ty, $rest) = ($_ =~ /^([0-9a-f ]{8}) (.) (.*)$/);
119
if ($ty eq 't' || $ty eq 'T') {
121
if (substr($sym, 0, 1) eq '_') {
122
# symbols on Mac have an extra leading _
123
$sym = substr($sym, 1);
125
add_info($nmstruct->{symbols}, $addr, $sym);
126
} elsif ($ty eq '-') {
127
# nm gives us stabs debugging information
128
my ($n1, $n2, $ty2, $rest2) =
129
($rest =~ /^([0-9a-f]{2}) ([0-9a-f]{4}) (.{5}) (.*)$/);
130
# ignore $ty2 == ' FUN'
131
if ($ty2 eq 'SLINE') {
132
add_info($nmstruct->{lines}, $addr, hex($n2));
133
} elsif ($ty2 eq ' SOL') {
134
# We get SOL lines within the code for a source
135
# file. They always have file names.
137
if (!($file =~ /^\//)) {
138
# resolve relative paths
139
$file = $curdir . $file;
141
add_info($nmstruct->{files}, $addr, $file);
142
} elsif ($ty2 eq ' SO') {
143
# We get SO lines at the beginning of the code for a
145
# * the directory of the compilation
147
# * sometimes a blank line
148
if ($rest2 =~ /\/$/) {
150
} elsif ($rest2 ne '') {
151
add_info($nmstruct->{files}, $addr, $rest2);
158
# nm -n Doesn't sort across .o files.
159
@{$nmstruct->{symbols}} = sort sort_by_address @{$nmstruct->{symbols}};
160
@{$nmstruct->{lines}} = sort sort_by_address @{$nmstruct->{lines}};
161
@{$nmstruct->{files}} = sort sort_by_address @{$nmstruct->{files}};
163
$nmstructs{$file} = $nmstruct;
165
$nmstruct = $nmstructs{$file};
174
unless($cxxfilt_pipe) {
175
my $pid = open2($cxxfilt_pipe->{read}, $cxxfilt_pipe->{write},
176
'c++filt', '--no-strip-underscores',
177
'--format', 'gnu-v3');
179
my $out = $cxxfilt_pipe->{write};
180
my $in = $cxxfilt_pipe->{read};
181
print {$out} $sym . "\n";
182
chomp(my $fixedsym = <$in>);
186
# binary search the array for the address
187
sub array_lookup($$) {
188
my ($array, $address) = @_;
191
my $end = $#{$array};
193
return [ -1 , "" ] if ($end == -1);
195
while ($start != $end) {
196
my $test = int(($start + $end + 1) / 2); # may equal $end
197
# Since we're processing stack traces, and the addresses in
198
# stack traces are the instructions to return to, and we really
199
# want the instruction that made the call (the previous
200
# instruction), use > instead of >=.
201
if ($address > $array->[$test]->[0]) {
208
return $array->[$start];
212
my ($nmstruct, $address) = @_;
213
my $sym = array_lookup($nmstruct->{symbols}, $address);
215
symbol => cxxfilt($sym->[1]),
216
symbol_offset => ($address - $sym->[0]),
217
file => array_lookup($nmstruct->{files}, $address)->[1],
218
line => array_lookup($nmstruct->{lines}, $address)->[1]
222
select STDOUT; $| = 1; # make STDOUT unbuffered
225
if ($line =~ /^([ \|0-9-]*)(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,8})\](.*)$/) {
226
my $before = $1; # allow preservation of balance trees
229
my $address = hex($4);
230
my $after = $5; # allow preservation of counts
233
my $nmstruct = nmstruct_for($file);
234
$address += address_adjustment($file);
236
my $info = nm_lookup($nmstruct, $address);
237
my $symbol = $info->{symbol};
238
my $fileandline = $info->{file} . ':' . $info->{line};
240
# I'm not sure if it's possible for dlsym to have gotten
241
# better information, but just in case:
242
if (my ($offset) = ($badsymbol =~ /\+0x([0-9A-F]{8})/)) { # FIXME: add $
243
if (hex($offset) < $info->{symbol_offset}) {
244
$symbol = $badsymbol;
248
if ($fileandline eq ':') { $fileandline = $file; }
249
print "$before$symbol ($fileandline)$after\n";
251
print STDERR "Warning: File \"$file\" does not exist.\n";