Skip to content

Commit ba02264

Browse files
committed
runtime/pprof: add a fake mapping when /proc/self/maps is unavailable
Profile's Mapping field is currently populated by reading /proc/self/maps. On systems where /proc/self/maps is not available, the profile generated by Go's runtime will not have any Mapping entry. Pprof command then adds a fake entry and links all Location entries in the profile with the fake entry to be used during symbolization. https://github.com/google/pprof/blob/a8644067d5a3c9a6386e7c88fa4a3d9d37877ca3/internal/driver/fetch.go#L437 The fake entry is not enough to suppress the error or warning messages pprof command produces. We need to tell pprof that Location entries are symbolized already by Go runtime and pprof does not have to attempt to perform further symbolization. In #25743, we made Go runtime mark Mapping entries with HasFunctions=true when all Location entries from the Mapping entries are successfully symbolized. This change makes the Go runtime add a fake mapping entry, otherwise the pprof command tool would add, and set the HasFunctions=true following the same logic taken when the real mapping information is available. Updates #19790. Fixes #26255. Tested pprof doesn't report the error message any more for pure Go program. Change-Id: Ib12b62e15073f5d6c80967e44b3e8709277c11bd Reviewed-on: https://go-review.googlesource.com/123779 Run-TryBot: Hyang-Ah Hana Kim <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 22e17d0 commit ba02264

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/runtime/pprof/proto.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type memMap struct {
5454
file, buildID string
5555

5656
funcs symbolizeFlag
57+
fake bool // map entry was faked; /proc/self/maps wasn't available
5758
}
5859

5960
// symbolizeFlag keeps track of symbolization result.
@@ -267,7 +268,7 @@ func (b *profileBuilder) locForPC(addr uintptr) uint64 {
267268
frame, more = frames.Next()
268269
}
269270
for i := range b.mem {
270-
if b.mem[i].start <= addr && addr < b.mem[i].end {
271+
if b.mem[i].start <= addr && addr < b.mem[i].end || b.mem[i].fake {
271272
b.pb.uint64Opt(tagLocation_MappingID, uint64(i+1))
272273

273274
m := b.mem[i]
@@ -440,6 +441,12 @@ func (b *profileBuilder) build() {
440441
func (b *profileBuilder) readMapping() {
441442
data, _ := ioutil.ReadFile("/proc/self/maps")
442443
parseProcSelfMaps(data, b.addMapping)
444+
if len(b.mem) == 0 { // pprof expects a map entry, so fake one.
445+
b.addMappingEntry(0, 0, 0, "", "", true)
446+
// TODO(hyangah): make addMapping return *memMap or
447+
// take a memMap struct, and get rid of addMappingEntry
448+
// that takes a bunch of positional arguments.
449+
}
443450
}
444451

445452
func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, buildID string)) {
@@ -540,11 +547,16 @@ func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file,
540547
}
541548

542549
func (b *profileBuilder) addMapping(lo, hi, offset uint64, file, buildID string) {
550+
b.addMappingEntry(lo, hi, offset, file, buildID, false)
551+
}
552+
553+
func (b *profileBuilder) addMappingEntry(lo, hi, offset uint64, file, buildID string, fake bool) {
543554
b.mem = append(b.mem, memMap{
544555
start: uintptr(lo),
545556
end: uintptr(hi),
546557
offset: offset,
547558
file: file,
548559
buildID: buildID,
560+
fake: fake,
549561
})
550562
}

src/runtime/pprof/proto_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,16 @@ func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) {
9797
addr2 = mprof.Mapping[1].Start
9898
map2 = mprof.Mapping[1]
9999
map2.BuildID, _ = elfBuildID(map2.File)
100+
case "js":
101+
addr1 = uint64(funcPC(f1))
102+
addr2 = uint64(funcPC(f2))
100103
default:
101104
addr1 = uint64(funcPC(f1))
102105
addr2 = uint64(funcPC(f2))
106+
// Fake mapping - HasFunctions will be true because two PCs from Go
107+
// will be fully symbolized.
108+
fake := &profile.Mapping{ID: 1, HasFunctions: true}
109+
map1, map2 = fake, fake
103110
}
104111
return
105112
}
@@ -301,3 +308,41 @@ func symbolized(loc *profile.Location) bool {
301308
}
302309
return true
303310
}
311+
312+
// TestFakeMapping tests if at least one mapping exists
313+
// (including a fake mapping), and their HasFunctions bits
314+
// are set correctly.
315+
func TestFakeMapping(t *testing.T) {
316+
var buf bytes.Buffer
317+
if err := Lookup("heap").WriteTo(&buf, 0); err != nil {
318+
t.Fatalf("failed to write heap profile: %v", err)
319+
}
320+
prof, err := profile.Parse(&buf)
321+
if err != nil {
322+
t.Fatalf("failed to parse the generated profile data: %v", err)
323+
}
324+
t.Logf("Profile: %s", prof)
325+
if len(prof.Mapping) == 0 {
326+
t.Fatal("want profile with at least one mapping entry, got 0 mapping")
327+
}
328+
329+
hit := make(map[*profile.Mapping]bool)
330+
miss := make(map[*profile.Mapping]bool)
331+
for _, loc := range prof.Location {
332+
if symbolized(loc) {
333+
hit[loc.Mapping] = true
334+
} else {
335+
miss[loc.Mapping] = true
336+
}
337+
}
338+
for _, m := range prof.Mapping {
339+
if miss[m] && m.HasFunctions {
340+
t.Errorf("mapping %+v has HasFunctions=true, but contains locations with failed symbolization", m)
341+
continue
342+
}
343+
if !miss[m] && hit[m] && !m.HasFunctions {
344+
t.Errorf("mapping %+v has HasFunctions=false, but all referenced locations from this lapping were symbolized successfully", m)
345+
continue
346+
}
347+
}
348+
}

0 commit comments

Comments
 (0)