Commit b4117995 authored by Ian Lance Taylor's avatar Ian Lance Taylor

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/21055Reviewed-by: default avatarAustin Clements <austin@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
parent 4b209dbf
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package objfile package objfile
import ( import (
"debug/dwarf"
"debug/elf" "debug/elf"
"fmt" "fmt"
"os" "os"
...@@ -104,3 +105,7 @@ func (f *elfFile) goarch() string { ...@@ -104,3 +105,7 @@ func (f *elfFile) goarch() string {
} }
return "" return ""
} }
func (f *elfFile) dwarf() (*dwarf.Data, error) {
return f.elf.DWARF()
}
...@@ -8,6 +8,8 @@ package objfile ...@@ -8,6 +8,8 @@ package objfile
import ( import (
"cmd/internal/goobj" "cmd/internal/goobj"
"debug/dwarf"
"errors"
"fmt" "fmt"
"os" "os"
) )
...@@ -91,3 +93,7 @@ func (f *goobjFile) text() (textStart uint64, text []byte, err error) { ...@@ -91,3 +93,7 @@ func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
func (f *goobjFile) goarch() string { func (f *goobjFile) goarch() string {
return "GOARCH unimplemented for debug/goobj files" return "GOARCH unimplemented for debug/goobj files"
} }
func (f *goobjFile) dwarf() (*dwarf.Data, error) {
return nil, errors.New("no DWARF data in go object file")
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package objfile package objfile
import ( import (
"debug/dwarf"
"debug/macho" "debug/macho"
"fmt" "fmt"
"os" "os"
...@@ -123,3 +124,7 @@ type uint64s []uint64 ...@@ -123,3 +124,7 @@ type uint64s []uint64
func (x uint64s) Len() int { return len(x) } func (x uint64s) Len() int { return len(x) }
func (x uint64s) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x uint64s) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x uint64s) Less(i, j int) bool { return x[i] < x[j] } func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
func (f *machoFile) dwarf() (*dwarf.Data, error) {
return f.macho.DWARF()
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package objfile package objfile
import ( import (
"debug/dwarf"
"debug/gosym" "debug/gosym"
"fmt" "fmt"
"os" "os"
...@@ -17,6 +18,7 @@ type rawFile interface { ...@@ -17,6 +18,7 @@ type rawFile interface {
pcln() (textStart uint64, symtab, pclntab []byte, err error) pcln() (textStart uint64, symtab, pclntab []byte, err error)
text() (textStart uint64, text []byte, err error) text() (textStart uint64, text []byte, err error)
goarch() string goarch() string
dwarf() (*dwarf.Data, error)
} }
// A File is an opened executable file. // A File is an opened executable file.
...@@ -92,3 +94,9 @@ func (f *File) Text() (uint64, []byte, error) { ...@@ -92,3 +94,9 @@ func (f *File) Text() (uint64, []byte, error) {
func (f *File) GOARCH() string { func (f *File) GOARCH() string {
return f.raw.goarch() return f.raw.goarch()
} }
// DWARF returns DWARF debug data for the file, if any.
// This is for cmd/pprof to locate cgo functions.
func (f *File) DWARF() (*dwarf.Data, error) {
return f.raw.dwarf()
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package objfile package objfile
import ( import (
"debug/dwarf"
"debug/pe" "debug/pe"
"fmt" "fmt"
"os" "os"
...@@ -199,3 +200,7 @@ func (f *peFile) goarch() string { ...@@ -199,3 +200,7 @@ func (f *peFile) goarch() string {
} }
return "" return ""
} }
func (f *peFile) dwarf() (*dwarf.Data, error) {
return f.pe.DWARF()
}
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
package objfile package objfile
import ( import (
"debug/dwarf"
"debug/plan9obj" "debug/plan9obj"
"errors"
"fmt" "fmt"
"os" "os"
"sort" "sort"
...@@ -144,3 +146,7 @@ func (f *plan9File) goarch() string { ...@@ -144,3 +146,7 @@ func (f *plan9File) goarch() string {
} }
return "" return ""
} }
func (f *plan9File) dwarf() (*dwarf.Data, error) {
return nil, errors.New("no DWARF data in Plan 9 file")
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package main package main
import ( import (
"debug/dwarf"
"debug/gosym" "debug/gosym"
"flag" "flag"
"fmt" "fmt"
...@@ -172,6 +173,9 @@ type file struct { ...@@ -172,6 +173,9 @@ type file struct {
sym []objfile.Sym sym []objfile.Sym
file *objfile.File file *objfile.File
pcln *gosym.Table pcln *gosym.Table
triedDwarf bool
dwarf *dwarf.Data
} }
func (f *file) Name() string { func (f *file) Name() string {
...@@ -197,9 +201,7 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) { ...@@ -197,9 +201,7 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
f.pcln = pcln f.pcln = pcln
} }
file, line, fn := f.pcln.PCToLine(addr) file, line, fn := f.pcln.PCToLine(addr)
if fn == nil { if fn != nil {
return nil, fmt.Errorf("no line information for PC=%#x", addr)
}
frame := []plugin.Frame{ frame := []plugin.Frame{
{ {
Func: fn.Name, Func: fn.Name,
...@@ -208,6 +210,85 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) { ...@@ -208,6 +210,85 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
}, },
} }
return frame, nil 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
}
// 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: name,
File: lentry.File.Name,
Line: lentry.Line,
},
}
return frames
} }
func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment