Gwinel / gperftools

Automatically exported from code.google.com/p/gperftools
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

pprof incorrectly reports addresses where .text is not in the first loadable segment #654

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Build chromium for Android with packed relocations.
2. Capture a .prof file.
3. Process with pprof.

Relocation packing splits a single executable load segment into two.  Before:

  LOAD           0x000000 0x00000000 0x00000000 0x2034d28 0x2034d28 R E 0x1000
  LOAD           0x2035888 0x02036888 0x02036888 0x182d38 0x1a67d0 RW  0x1000

After:
  LOAD           0x000000 0x00000000 0x00000000 0x14648 0x14648 R E 0x1000
  LOAD           0x014648 0x0020c648 0x0020c648 0x1e286e0 0x1e286e0 R E 0x1000
  ...
  LOAD           0x1e3d888 0x02036888 0x02036888 0x182d38 0x1a67d0 RW  0x1000

The .text section is in the second LOAD, and this is not at offset/address 
zero.  The result is that this library shows up in /proc/self/maps as multiple 
executable entries, for example (note: this trace is not from the library 
dissected above, but rather from an earlier version of it):

  73b0c000-73b21000 r-xp 00000000 b3:19 786460 /data/.../libchrome.2160.0.so
  73b21000-73d12000 ---p 00000000 00:00 0
  73d12000-75a90000 r-xp 00014000 b3:19 786460 /data/.../libchrome.2160.0.so
  75a90000-75c0d000 rw-p 01d91000 b3:19 786460 /data/.../libchrome.2160.0.so

When parsing this, pprof needs to merge the two r-xp entries above into a 
single entry, otherwise the addresses it prints are incorrect.

The following fix against 2.2.1 was sufficient to make pprof --text print the 
correct output.  Untested with other pprof options.

$ diff -c ./gperftools-2.2.1/src/pprof ./pprof
*** ./gperftools-2.2.1/src/pprof    2014-04-13 02:35:40.000000000 +0100
--- ./pprof 2014-10-13 16:48:37.039256051 +0100
***************
*** 4352,4357 ****
--- 4352,4358 ----
    my $zero_offset = HexExtend("0");

    my $buildvar = "";
+   my $priorlib = "";
    foreach my $l (split("\n", $map)) {
      if ($l =~ m/^\s*build=(.*)$/) {
        $buildvar = $1;
***************
*** 4408,4414 ****
--- 4409,4424 ----
        }
      }

+     # If we find multiple executable segments for a single library, merge them
+     # into a single entry that spans the complete address range.
+     if ($lib eq $priorlib) {
+       my $prior = pop(@{$result});
+       $start = @$prior[1];
+       # TODO $offset may be wrong if .text is not in the final segment.
+     }
+ 
      push(@{$result}, [$lib, $start, $finish, $offset]);
+     $priorlib = $lib;
    }

    # Append special entry for additional library (not relocated)

Original issue reported on code.google.com by sim...@google.com on 15 Oct 2014 at 1:30

GoogleCodeExporter commented 9 years ago
Thanks. Applied.

Original comment by alkondratenko on 18 Oct 2014 at 11:46