]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/pprof: use DWARF info to lookup unknown PC addresses
authorIan Lance Taylor <iant@golang.org>
Thu, 24 Mar 2016 00:10:18 +0000 (17:10 -0700)
committerIan Lance Taylor <iant@golang.org>
Thu, 31 Mar 2016 04:05:06 +0000 (04:05 +0000)
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>

src/cmd/internal/objfile/elf.go
src/cmd/internal/objfile/goobj.go
src/cmd/internal/objfile/macho.go
src/cmd/internal/objfile/objfile.go
src/cmd/internal/objfile/pe.go
src/cmd/internal/objfile/plan9obj.go
src/cmd/pprof/pprof.go

index 6369f7e259af750946ce7f5482d9140d7aeb09ae..3bad03409780a4e636227b1718f86e41d2751d92 100644 (file)
@@ -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()
+}
index 46e5f4e86651cee528c5b04437f3a265d0b9e906..5a084a94bec2d1f298556f18a0fb09255e946b4a 100644 (file)
@@ -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")
+}
index c98d9520c20d96bd32106640ec2d6ff5177b9f2f..754674d7574857026e8f6da40c06a3332dee36a1 100644 (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()
+}
index cf58e9c6aa337a54a9452afb90e720c1a733e21a..48ed9ed4896f522e1fdd67c2d5cf57fe400ea4bb 100644 (file)
@@ -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()
+}
index 503d6576476b56c633a25de0bef534c6d2940867..1b319941ac6f02836edcb7b51063386b6c6450e2 100644 (file)
@@ -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()
+}
index 45a6d02748b353b99ac3a35d231cc2a30224ddab..1d808f77eb4dc43ca9567432faabe4cde79d1b52 100644 (file)
@@ -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")
+}
index 2b20f1da771aa19016cec6d707d956b590997f68..1c55d05d5d4f5c1efe4ddc640707e36b3ec47395 100644 (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,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) {