Bug 496271, automation config for Tb2.0.0.22 build1, p=joduinn, r=me
[mozilla-1.9.git] / tools / rb / fix-macosx-stack.pl
blobdecb17e5bf1719806afa5f65be3c7f2718a0b082
1 #!/usr/bin/perl
2 # vim:sw=4:ts=4:et:
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
14 # License.
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.
22 # Contributor(s):
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.
52 use strict;
53 use IPC::Open2;
55 sub separate_debug_file_for($) {
56 my ($file) = @_;
57 return '';
60 my %address_adjustments;
61 sub address_adjustment($) {
62 my ($file) = @_;
63 unless (exists $address_adjustments{$file}) {
64 my $result = -1;
66 open(OTOOL, '-|', 'otool', '-l', $file);
67 while (<OTOOL>) {
68 if (/^ segname __TEXT$/) {
69 if (<OTOOL> =~ /^ vmaddr (0x[0-9a-f]{8})$/) {
70 $result = hex($1);
71 last;
72 } else {
73 die "Bad output from otool";
77 close(OTOOL);
79 $result >= 0 || die "Bad output from otool";
81 $address_adjustments{$file} = $result;
84 return $address_adjustments{$file};
87 sub add_info($$$) {
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.
103 my %nmstructs;
104 sub nmstruct_for($) {
105 my ($file) = @_;
106 my $nmstruct;
107 my $curdir;
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);
115 while (<NM>) {
116 chomp;
117 my ($addr, $ty, $rest) = ($_ =~ /^([0-9a-f ]{8}) (.) (.*)$/);
118 $addr = hex($addr);
119 if ($ty eq 't' || $ty eq 'T') {
120 my $sym = $rest;
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.
136 my $file = $rest2;
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
144 # source file, for:
145 # * the directory of the compilation
146 # * the file
147 # * sometimes a blank line
148 if ($rest2 =~ /\/$/) {
149 $curdir = $rest2;
150 } elsif ($rest2 ne '') {
151 add_info($nmstruct->{files}, $addr, $rest2);
156 close(NM);
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;
164 } else {
165 $nmstruct = $nmstructs{$file};
167 return $nmstruct;
170 my $cxxfilt_pipe;
171 sub cxxfilt($) {
172 my ($sym) = @_;
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>);
183 return $fixedsym;
186 # binary search the array for the address
187 sub array_lookup($$) {
188 my ($array, $address) = @_;
190 my $start = 0;
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]) {
202 $start = $test;
203 } else {
204 $end = $test - 1;
208 return $array->[$start];
211 sub nm_lookup($$) {
212 my ($nmstruct, $address) = @_;
213 my $sym = array_lookup($nmstruct->{symbols}, $address);
214 return {
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 while (<>) {
223 my $line = $_;
224 if ($line =~ /^([ \|0-9-]*)(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,8})\](.*)$/) {
225 my $before = $1; # allow preservation of balance trees
226 my $badsymbol = $2;
227 my $file = $3;
228 my $address = hex($4);
229 my $after = $5; # allow preservation of counts
231 if (-f $file) {
232 my $nmstruct = nmstruct_for($file);
233 $address += address_adjustment($file);
235 my $info = nm_lookup($nmstruct, $address);
236 my $symbol = $info->{symbol};
237 my $fileandline = $info->{file} . ':' . $info->{line};
239 # I'm not sure if it's possible for dlsym to have gotten
240 # better information, but just in case:
241 if (my ($offset) = ($badsymbol =~ /\+0x([0-9A-F]{8})/)) { # FIXME: add $
242 if (hex($offset) < $info->{symbol_offset}) {
243 $symbol = $badsymbol;
247 if ($fileandline eq ':') { $fileandline = $file; }
248 print "$before$symbol ($fileandline)$after\n";
249 } else {
250 print STDERR "Warning: File \"$file\" does not exist.\n";
251 print $line;
254 } else {
255 print $line;