Bug 812070 (part 1) - Merge %pipes and %address_adjustment in fix-linux-stack.pl.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 15 Nov 2012 14:38:21 -0800
changeset 118286 a702c73d82a6e537f8712ecb97500783729d228d
parent 118285 0dc4a6299ba6f952e35e793966a537af6f0e7520
child 118287 e72aeaefb691a9bb1cfb7f6143cdd7918f0a4630
push idunknown
push userunknown
push dateunknown
bugs812070
milestone19.0a1
Bug 812070 (part 1) - Merge %pipes and %address_adjustment in fix-linux-stack.pl.
tools/rb/fix-linux-stack.pl
--- a/tools/rb/fix-linux-stack.pl
+++ b/tools/rb/fix-linux-stack.pl
@@ -21,54 +21,59 @@
 
 use strict;
 use IPC::Open2;
 use File::Basename;
 
 # XXX Hard-coded to gdb defaults (works on Fedora).
 my $global_debug_dir = '/usr/lib/debug';
 
-# addr2line wants offsets relative to the base address for shared
-# libraries, but it wants addresses including the base address offset
-# for executables.  This function returns the appropriate address
-# adjustment to add to an offset within file.  See bug 230336.
-my %address_adjustments;
-sub address_adjustment($) {
-    my ($file) = @_;
-    unless (exists $address_adjustments{$file}) {
-        # find out if it's an executable (as opposed to a shared library)
-        my $elftype;
-        open(ELFHDR, '-|', 'readelf', '-h', $file);
-        while (<ELFHDR>) {
-            if (/^\s*Type:\s+(\S+)/) {
-                $elftype = $1;
+# We record several things for each file encountered.
+#
+# - {pipe_read}, {pipe_write}: these constitute a bidirectional pipe to an
+#   addr2line process that gives symbol information for a file.
+#
+# - {address_adjustment}: addr2line wants offsets relative to the base address
+#   for shared libraries, but it wants addresses including the base address
+#   offset for executables.  This holds the appropriate address adjustment to
+#   add to an offset within file.  See bug 230336.
+#
+my %file_infos;
+
+sub set_address_adjustment($$) {
+    my ($file, $file_info) = @_;
+
+    # find out if it's an executable (as opposed to a shared library)
+    my $elftype;
+    open(ELFHDR, '-|', 'readelf', '-h', $file);
+    while (<ELFHDR>) {
+        if (/^\s*Type:\s+(\S+)/) {
+            $elftype = $1;
+            last;
+        }
+    }
+    close(ELFHDR);
+
+    # If it's an executable, make adjustment the base address.
+    # Otherwise, leave it zero.
+    my $adjustment = 0;
+    if ($elftype eq 'EXEC') {
+        open(ELFSECS, '-|', 'readelf', '-S', $file);
+        while (<ELFSECS>) {
+            if (/^\s*\[\s*\d+\]\s+\.text\s+\w+\s+(\w+)\s+(\w+)\s+/) {
+                # Subtract the .text section's offset within the
+                # file from its base address.
+                $adjustment = hex($1) - hex($2);
                 last;
             }
         }
-        close(ELFHDR);
+        close(ELFSECS);
+    }
 
-        # If it's an executable, make adjustment the base address.
-        # Otherwise, leave it zero.
-        my $adjustment = 0;
-        if ($elftype eq 'EXEC') {
-            open(ELFSECS, '-|', 'readelf', '-S', $file);
-            while (<ELFSECS>) {
-                if (/^\s*\[\s*\d+\]\s+\.text\s+\w+\s+(\w+)\s+(\w+)\s+/) {
-                    # Subtract the .text section's offset within the
-                    # file from its base address.
-                    $adjustment = hex($1) - hex($2);
-                    last;
-                }
-            }
-            close(ELFSECS);
-        }
-
-        $address_adjustments{$file} = $adjustment;
-    }
-    return $address_adjustments{$file};
+    $file_info->{address_adjustment} = $adjustment;
 }
 
 # Files sometimes contain a link to a separate object file that contains
 # the debug sections of the binary, removed so that a smaller file can
 # be shipped, but kept separately so that it can be obtained by those
 # who want it.
 # See http://sources.redhat.com/gdb/current/onlinedocs/gdb_16.html#SEC154
 # for documentation of debugging information in separate files.
@@ -186,55 +191,52 @@ sub separate_debug_file_for($) {
             # XXX We should check the hash.
             return $result;
         }
     }
 
     return '';
 }
 
-# Return a reference to a hash whose {read} and {write} entries are a
-# bidirectional pipe to an addr2line process that gives symbol
-# information for a file.
-my %pipes;
-sub addr2line_pipe($) {
+sub get_file_info($) {
     my ($file) = @_;
-    my $pipe;
-    unless (exists $pipes{$file}) {
+    my $file_info = $file_infos{$file};
+    unless (defined $file_info) {
         my $debug_file = separate_debug_file_for($file);
         $debug_file = $file if ($debug_file eq '');
 
-        my $pid = open2($pipe->{read}, $pipe->{write},
+        my $pid = open2($file_info->{pipe_read}, $file_info->{pipe_write},
                         '/usr/bin/addr2line', '-C', '-f', '-e', $debug_file);
-        $pipes{$file} = $pipe;
-    } else {
-        $pipe = $pipes{$file};
+
+        set_address_adjustment($file, $file_info);
+
+        $file_infos{$file} = $file_info;
     }
-    return $pipe;
+    return $file_info;
 }
 
 # Ignore SIGPIPE as a workaround for addr2line crashes in some situations.
 $SIG{PIPE} = 'IGNORE';
 
 select STDOUT; $| = 1; # make STDOUT unbuffered
 while (<>) {
     my $line = $_;
     if ($line =~ /^([ \|0-9-]*)(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,8})\](.*)$/) {
         my $before = $1; # allow preservation of balance trees
         my $badsymbol = $2;
         my $file = $3;
         my $address = hex($4);
         my $after = $5; # allow preservation of counts
 
         if (-f $file) {
-            my $pipe = addr2line_pipe($file);
-            $address += address_adjustment($file);
+            my $file_info = get_file_info($file);
+            $address += $file_info->{address_adjustment};
 
-            my $out = $pipe->{write};
-            my $in = $pipe->{read};
+            my $out = $file_info->{pipe_write};
+            my $in = $file_info->{pipe_read};
             printf {$out} "0x%X\n", $address;
             chomp(my $symbol = <$in>);
             chomp(my $fileandline = <$in>);
             if (!$symbol || $symbol eq '??') { $symbol = $badsymbol; }
             if (!$fileandline || $fileandline eq '??:0') {
                 $fileandline = $file;
             }
             print "$before$symbol ($fileandline)$after\n";