From 222cf7fc55c14c56fd3a4fa7fa1e361d33172c34 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Tue, 18 Jul 2017 20:55:24 +0200 Subject: [PATCH] proc/eval: optimize variable lookup (#925) Variable lookup is slow because it requires a full scan of debug_info to check for package variables, this doesn't matter much in interactive use but can slow down evaluation of breakpoint conditions significantly. Providing benchmark proof for this is hard since this effect doesn't show for small programs with small debug_info sections. --- pkg/proc/bininfo.go | 1 + pkg/proc/types.go | 27 ++++++++++++++------------- pkg/proc/variables.go | 18 +++++++----------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index 0df7b0da..35afac48 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -34,6 +34,7 @@ type BinaryInfo struct { lineInfo line.DebugLines goSymTable *gosym.Table types map[string]dwarf.Offset + packageVars map[string]dwarf.Offset functions []functionDebugInfo gStructOffset uint64 diff --git a/pkg/proc/types.go b/pkg/proc/types.go index 32aea044..96f2b52e 100644 --- a/pkg/proc/types.go +++ b/pkg/proc/types.go @@ -132,6 +132,7 @@ func (v sortFunctionsDebugInfoByLowpc) Swap(i, j int) { func (bi *BinaryInfo) loadDebugInfoMaps(wg *sync.WaitGroup) { defer wg.Done() bi.types = make(map[string]dwarf.Offset) + bi.packageVars = make(map[string]dwarf.Offset) bi.functions = []functionDebugInfo{} reader := bi.DwarfReader() for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { @@ -140,23 +141,23 @@ func (bi *BinaryInfo) loadDebugInfoMaps(wg *sync.WaitGroup) { } switch entry.Tag { case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType: - name, ok := entry.Val(dwarf.AttrName).(string) - if !ok { - continue + if name, ok := entry.Val(dwarf.AttrName).(string); ok { + if _, exists := bi.types[name]; !exists { + bi.types[name] = entry.Offset + } } - if _, exists := bi.types[name]; !exists { - bi.types[name] = entry.Offset + reader.SkipChildren() + case dwarf.TagVariable: + if n, ok := entry.Val(dwarf.AttrName).(string); ok { + bi.packageVars[n] = entry.Offset } case dwarf.TagSubprogram: - lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64) - if !ok { - continue + lowpc, ok1 := entry.Val(dwarf.AttrLowpc).(uint64) + highpc, ok2 := entry.Val(dwarf.AttrHighpc).(uint64) + if ok1 && ok2 { + bi.functions = append(bi.functions, functionDebugInfo{lowpc, highpc, entry.Offset}) } - highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64) - if !ok { - continue - } - bi.functions = append(bi.functions, functionDebugInfo{lowpc, highpc, entry.Offset}) + reader.SkipChildren() } } sort.Sort(sortFunctionsDebugInfoByLowpc(bi.functions)) diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 5dbbff7d..5f33fa81 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -650,18 +650,14 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) { } func (scope *EvalScope) packageVarAddr(name string) (*Variable, error) { - reader := scope.DwarfReader() - for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() { - if err != nil { - return nil, err - } - - n, ok := entry.Val(dwarf.AttrName).(string) - if !ok { - continue - } - + for n, off := range scope.BinInfo.packageVars { if n == name || strings.HasSuffix(n, "/"+name) { + reader := scope.DwarfReader() + reader.Seek(off) + entry, err := reader.Next() + if err != nil { + return nil, err + } return scope.extractVarInfoFromEntry(entry, reader) } }