diff options
author | Ian Lance Taylor <iant@golang.org> | 2016-03-23 17:10:18 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2016-03-31 04:05:06 +0000 |
commit | b4117995e3e01a669be737c36033c2393858d555 (patch) | |
tree | c01edce64a80a4d284a398d3a94efc0873196113 /src/cmd/pprof | |
parent | 4b209dbf0bf3e5fd4cffda1e11f11bf45ddf212d (diff) | |
download | go-b4117995e3e01a669be737c36033c2393858d555.tar.gz go-b4117995e3e01a669be737c36033c2393858d555.zip |
cmd/pprof: use DWARF info to lookup unknown PC addresses
Test to follow in a separate CL that arranges for the runtime package to
store non-Go addresses in a CPU profile.
Change-Id: I33ce1d66b77340b1e62b54505fc9b1abcec108a9
Reviewed-on: https://go-review.googlesource.com/21055
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/cmd/pprof')
-rw-r--r-- | src/cmd/pprof/pprof.go | 95 |
1 files changed, 88 insertions, 7 deletions
diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go index 2b20f1da77..1c55d05d5d 100644 --- a/src/cmd/pprof/pprof.go +++ b/src/cmd/pprof/pprof.go @@ -5,6 +5,7 @@ package main import ( + "debug/dwarf" "debug/gosym" "flag" "fmt" @@ -172,6 +173,9 @@ type file struct { sym []objfile.Sym file *objfile.File pcln *gosym.Table + + triedDwarf bool + dwarf *dwarf.Data } func (f *file) Name() string { @@ -197,17 +201,94 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) { f.pcln = pcln } file, line, fn := f.pcln.PCToLine(addr) - if fn == nil { - return nil, fmt.Errorf("no line information for PC=%#x", addr) + if fn != nil { + frame := []plugin.Frame{ + { + Func: fn.Name, + File: file, + Line: line, + }, + } + return frame, nil + } + + frames := f.dwarfSourceLine(addr) + if frames != nil { + return frames, nil + } + + return nil, fmt.Errorf("no line information for PC=%#x", addr) +} + +// dwarfSourceLine tries to get file/line information using DWARF. +// This is for C functions that appear in the profile. +// Returns nil if there is no information available. +func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame { + if f.dwarf == nil && !f.triedDwarf { + // Ignore any error--we don't care exactly why there + // is no DWARF info. + f.dwarf, _ = f.file.DWARF() + f.triedDwarf = true + } + + if f.dwarf != nil { + r := f.dwarf.Reader() + unit, err := r.SeekPC(addr) + if err == nil { + if frames := f.dwarfSourceLineEntry(r, unit, addr); frames != nil { + return frames + } + } + } + + return nil +} + +// dwarfSourceLineEntry tries to get file/line information from a +// DWARF compilation unit. Returns nil if it doesn't find anything. +func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []plugin.Frame { + lines, err := f.dwarf.LineReader(entry) + if err != nil { + return nil + } + var lentry dwarf.LineEntry + if err := lines.SeekPC(addr, &lentry); err != nil { + return nil } - frame := []plugin.Frame{ + + // Try to find the function name. + name := "" +FindName: + for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { + if entry.Tag == dwarf.TagSubprogram { + ranges, err := f.dwarf.Ranges(entry) + if err != nil { + return nil + } + for _, pcs := range ranges { + if pcs[0] <= addr && addr < pcs[1] { + var ok bool + // TODO: AT_linkage_name, AT_MIPS_linkage_name. + name, ok = entry.Val(dwarf.AttrName).(string) + if ok { + break FindName + } + } + } + } + } + + // TODO: Report inlined functions. + + frames := []plugin.Frame{ { - Func: fn.Name, - File: file, - Line: line, + Func: name, + File: lentry.File.Name, + Line: lentry.Line, }, } - return frame, nil + + return frames } func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { |