proc: Caching type offsets

Caches the mapping of type names to offset in debug_info to speed up
variable evaluation.

BEFORE:
	BenchmarkArray-4         	     100	  13'238'441 ns/op	   0.62 MB/s
	BenchmarkArrayPointer-4  	     200	  10'044'093 ns/op	   0.87 MB/s
	BenchmarkMap-4           	    1000	   1'332'530 ns/op	   0.77 MB/s
	BenchmarkGoroutinesInfo-4	      10	 114'677'462 ns/op
	BenchmarkLocalVariables-4	    2000	   1'223'975 ns/op
AFTER:
	BenchmarkArray-4         	     200	   9'925'686 ns/op	   0.83 MB/s
	BenchmarkArrayPointer-4  	     100	  11'143'930 ns/op	   0.78 MB/s
	BenchmarkMap-4           	    2000	   1'302'520 ns/op	   0.79 MB/s
	BenchmarkGoroutinesInfo-4	      30	  35'079'549 ns/op
	BenchmarkLocalVariables-4	    1000	   1'137'299 ns/op

Note in particular the speedup of BenchmarkGoroutinesInfo, since
proc.(*Variable).parseG is a function we call a lot.
This commit is contained in:
aarzilli
2016-03-05 13:04:11 +01:00
parent 758f76ffee
commit c66c6408a5
3 changed files with 31 additions and 20 deletions

View File

@ -58,6 +58,7 @@ type Process struct {
exited bool exited bool
ptraceChan chan func() ptraceChan chan func()
ptraceDoneChan chan interface{} ptraceDoneChan chan interface{}
types map[string]dwarf.Offset
} }
// New returns an initialized Process struct. Before returning, // New returns an initialized Process struct. Before returning,
@ -149,11 +150,12 @@ func (dbp *Process) LoadInformation(path string) error {
return err return err
} }
wg.Add(4) wg.Add(5)
go dbp.loadProcessInformation(&wg) go dbp.loadProcessInformation(&wg)
go dbp.parseDebugFrame(exe, &wg) go dbp.parseDebugFrame(exe, &wg)
go dbp.obtainGoSymbols(exe, &wg) go dbp.obtainGoSymbols(exe, &wg)
go dbp.parseDebugLineInfo(exe, &wg) go dbp.parseDebugLineInfo(exe, &wg)
go dbp.loadTypeMap(&wg)
wg.Wait() wg.Wait()
return nil return nil
@ -658,19 +660,9 @@ func (dbp *Process) Funcs() []gosym.Func {
// Types returns list of types present in the debugged program. // Types returns list of types present in the debugged program.
func (dbp *Process) Types() ([]string, error) { func (dbp *Process) Types() ([]string, error) {
reader := dbp.DwarfReader() types := make([]string, 0, len(dbp.types))
types := []string{} for k := range dbp.types {
seen := map[string]struct{}{} types = append(types, k)
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
if err != nil {
return nil, err
}
if n, ok := entry.Val(dwarf.AttrName).(string); ok {
if _, isseen := seen[n]; !isseen {
seen[n] = struct{}{}
types = append(types, n)
}
}
} }
return types, nil return types, nil
} }

View File

@ -1,20 +1,21 @@
package proc package proc
import ( import (
"github.com/derekparker/delve/dwarf/reader"
"go/ast" "go/ast"
"golang.org/x/debug/dwarf" "golang.org/x/debug/dwarf"
"reflect" "reflect"
"strings" "strings"
"sync"
) )
// Do not call this function directly it isn't able to deal correctly with package paths // Do not call this function directly it isn't able to deal correctly with package paths
func (dbp *Process) findType(name string) (dwarf.Type, error) { func (dbp *Process) findType(name string) (dwarf.Type, error) {
reader := dbp.DwarfReader() off, found := dbp.types[name]
typentry, err := reader.SeekToTypeNamed(name) if !found {
if err != nil { return nil, reader.TypeNotFoundErr
return nil, err
} }
return dbp.dwarf.Type(typentry.Offset) return dbp.dwarf.Type(off)
} }
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type { func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
@ -83,6 +84,24 @@ func (dbp *Process) loadPackageMap() error {
return nil return nil
} }
func (dbp *Process) loadTypeMap(wg *sync.WaitGroup) {
defer wg.Done()
dbp.types = make(map[string]dwarf.Offset)
reader := dbp.DwarfReader()
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
if err != nil {
break
}
name, ok := entry.Val(dwarf.AttrName).(string)
if !ok {
continue
}
if _, exists := dbp.types[name]; !exists {
dbp.types[name] = entry.Offset
}
}
}
func (dbp *Process) expandPackagesInType(expr ast.Expr) { func (dbp *Process) expandPackagesInType(expr ast.Expr) {
switch e := expr.(type) { switch e := expr.(type) {
case *ast.ArrayType: case *ast.ArrayType: