mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 04:35:19 +08:00
proc,dwarf: cache debug.Entry objects (#1931)
Instead of rescanning debug_info every time we want to read a function (either to find inlined calls or its variables) cache the tree of dwarf.Entry that we would generate and use that. Benchmark before: BenchmarkConditionalBreakpoints-4 1 5164689165 ns/op Benchmark after: BenchmarkConditionalBreakpoints-4 1 4817425836 ns/op Updates #1549
This commit is contained in:
committed by
GitHub
parent
3c683ae30f
commit
7cd12c34fd
@ -1,123 +1,41 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"debug/dwarf"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
||||
)
|
||||
|
||||
// RelAddr is an address relative to the static base. For normal executables
|
||||
// this is just a normal memory address, for PIE it's a relative address.
|
||||
type RelAddr uint64
|
||||
|
||||
func ToRelAddr(addr uint64, staticBase uint64) RelAddr {
|
||||
return RelAddr(addr - staticBase)
|
||||
type Variable struct {
|
||||
*godwarf.Tree
|
||||
Depth int
|
||||
}
|
||||
|
||||
// VariableReader provides a way of reading the local variables and formal
|
||||
// parameters of a function that are visible at the specified PC address.
|
||||
type VariableReader struct {
|
||||
dwarf *dwarf.Data
|
||||
reader *dwarf.Reader
|
||||
entry *dwarf.Entry
|
||||
depth int
|
||||
pc uint64
|
||||
line int
|
||||
err error
|
||||
|
||||
onlyVisible bool
|
||||
skipInlinedSubroutines bool
|
||||
// Variables returns a list of variables contained inside 'root'.
|
||||
// If onlyVisible is true only variables visible at pc will be returned.
|
||||
// If skipInlinedSubroutines is true inlined subroutines will be skipped
|
||||
func Variables(root *godwarf.Tree, pc uint64, line int, onlyVisible, skipInlinedSubroutines bool) []Variable {
|
||||
return variablesInternal(nil, root, 0, pc, line, onlyVisible, skipInlinedSubroutines)
|
||||
}
|
||||
|
||||
// Variables returns a VariableReader for the function or lexical block at off.
|
||||
// If onlyVisible is true only variables visible at pc will be returned by
|
||||
// the VariableReader.
|
||||
func Variables(dwarf *dwarf.Data, off dwarf.Offset, pc RelAddr, line int, onlyVisible, skipInlinedSubroutines bool) *VariableReader {
|
||||
reader := dwarf.Reader()
|
||||
reader.Seek(off)
|
||||
return &VariableReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, onlyVisible: onlyVisible, skipInlinedSubroutines: skipInlinedSubroutines, pc: uint64(pc), line: line, err: nil}
|
||||
}
|
||||
|
||||
// Next reads the next variable entry, returns false if there aren't any.
|
||||
func (vrdr *VariableReader) Next() bool {
|
||||
if vrdr.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
vrdr.entry, vrdr.err = vrdr.reader.Next()
|
||||
if vrdr.entry == nil || vrdr.err != nil {
|
||||
return false
|
||||
func variablesInternal(v []Variable, root *godwarf.Tree, depth int, pc uint64, line int, onlyVisible, skipInlinedSubroutines bool) []Variable {
|
||||
switch root.Tag {
|
||||
case dwarf.TagInlinedSubroutine:
|
||||
if skipInlinedSubroutines {
|
||||
return v
|
||||
}
|
||||
|
||||
switch vrdr.entry.Tag {
|
||||
case 0:
|
||||
vrdr.depth--
|
||||
if vrdr.depth == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
case dwarf.TagInlinedSubroutine:
|
||||
if vrdr.skipInlinedSubroutines {
|
||||
vrdr.reader.SkipChildren()
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
case dwarf.TagLexDwarfBlock, dwarf.TagSubprogram:
|
||||
|
||||
recur := true
|
||||
if vrdr.onlyVisible {
|
||||
recur, vrdr.err = entryRangesContains(vrdr.dwarf, vrdr.entry, vrdr.pc)
|
||||
if vrdr.err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if recur && vrdr.entry.Children {
|
||||
vrdr.depth++
|
||||
} else {
|
||||
if vrdr.depth == 0 {
|
||||
return false
|
||||
}
|
||||
vrdr.reader.SkipChildren()
|
||||
}
|
||||
|
||||
default:
|
||||
if vrdr.depth == 0 {
|
||||
vrdr.err = errors.New("offset was not lexical block or subprogram")
|
||||
return false
|
||||
}
|
||||
if declLine, ok := vrdr.entry.Val(dwarf.AttrDeclLine).(int64); !ok || vrdr.line >= int(declLine) {
|
||||
return true
|
||||
fallthrough
|
||||
case dwarf.TagLexDwarfBlock, dwarf.TagSubprogram:
|
||||
if !onlyVisible || root.ContainsPC(pc) {
|
||||
for _, child := range root.Children {
|
||||
v = variablesInternal(v, child, depth+1, pc, line, onlyVisible, skipInlinedSubroutines)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func entryRangesContains(dwarf *dwarf.Data, entry *dwarf.Entry, pc uint64) (bool, error) {
|
||||
rngs, err := dwarf.Ranges(entry)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, rng := range rngs {
|
||||
if pc >= rng[0] && pc < rng[1] {
|
||||
return true, nil
|
||||
return v
|
||||
default:
|
||||
if declLine, ok := root.Val(dwarf.AttrDeclLine).(int64); !ok || line >= int(declLine) {
|
||||
return append(v, Variable{root, depth})
|
||||
}
|
||||
return v
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Entry returns the current variable entry.
|
||||
func (vrdr *VariableReader) Entry() *dwarf.Entry {
|
||||
return vrdr.entry
|
||||
}
|
||||
|
||||
// Depth returns the depth of the current scope
|
||||
func (vrdr *VariableReader) Depth() int {
|
||||
return vrdr.depth
|
||||
}
|
||||
|
||||
// Err returns the error if there was one.
|
||||
func (vrdr *VariableReader) Err() error {
|
||||
return vrdr.err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user