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 @@
package objfile
import (
"debug/dwarf"
"debug/elf"
"fmt"
"os"
......@@ -104,3 +105,7 @@ func (f *elfFile) goarch() string {
}
return ""
}
func (f *elfFile) dwarf() (*dwarf.Data, error) {
return f.elf.DWARF()
}
......@@ -8,6 +8,8 @@ package objfile
import (
"cmd/internal/goobj"
"debug/dwarf"
"errors"
"fmt"
"os"
)
......@@ -91,3 +93,7 @@ func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
func (f *goobjFile) goarch() string {
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 @@
package objfile
import (
"debug/dwarf"
"debug/macho"
"fmt"
"os"
......@@ -123,3 +124,7 @@ type uint64s []uint64
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) 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 @@
package objfile
import (
"debug/dwarf"
"debug/gosym"
"fmt"
"os"
......@@ -17,6 +18,7 @@ type rawFile interface {
pcln() (textStart uint64, symtab, pclntab []byte, err error)
text() (textStart uint64, text []byte, err error)
goarch() string
dwarf() (*dwarf.Data, error)
}
// A File is an opened executable file.
......@@ -92,3 +94,9 @@ func (f *File) Text() (uint64, []byte, error) {
func (f *File) GOARCH() string {
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 @@
package objfile
import (
"debug/dwarf"
"debug/pe"
"fmt"
"os"
......@@ -199,3 +200,7 @@ func (f *peFile) goarch() string {
}
return ""
}
func (f *peFile) dwarf() (*dwarf.Data, error) {
return f.pe.DWARF()
}
......@@ -7,7 +7,9 @@
package objfile
import (
"debug/dwarf"
"debug/plan9obj"
"errors"
"fmt"
"os"
"sort"
......@@ -144,3 +146,7 @@ func (f *plan9File) goarch() string {
}
return ""
}
func (f *plan9File) dwarf() (*dwarf.Data, error) {
return nil, errors.New("no DWARF data in Plan 9 file")
}
......@@ -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,9 +201,7 @@ 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,
......@@ -208,6 +210,85 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
},
}
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) {
......
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