From ed6d4049b646fab859b013d4275d3bc71796bef0 Mon Sep 17 00:00:00 2001 From: epipho Date: Wed, 31 Dec 2014 08:34:41 -0500 Subject: [PATCH] Extracting common dwarf reader functionality into its own area --- dwarf/reader/reader.go | 85 ++++++++++++++++++++++++++++++++++++++++++ proctl/proctl.go | 6 +++ proctl/variables.go | 60 +++++++---------------------- 3 files changed, 104 insertions(+), 47 deletions(-) create mode 100755 dwarf/reader/reader.go diff --git a/dwarf/reader/reader.go b/dwarf/reader/reader.go new file mode 100755 index 00000000..d466e48a --- /dev/null +++ b/dwarf/reader/reader.go @@ -0,0 +1,85 @@ +package reader + +import ( + "debug/dwarf" + "fmt" +) + +type Reader struct { + *dwarf.Reader + depth int +} + +// New returns a reader for the specified dwarf data +func New(data *dwarf.Data) *Reader { + return &Reader{data.Reader(), 0} +} + +// Seek moves the reader to an arbitrary offset +func (reader *Reader) Seek(off dwarf.Offset) { + reader.depth = 0 + reader.Reader.Seek(off) +} + +// SeekToEntry moves the reader to an arbitrary entry +func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error { + reader.Seek(entry.Offset) + // Consume the current entry so .Next works as intended + _, err := reader.Next() + return err +} + +// SeekToFunctionEntry moves the reader to the function that includes the +// specified program counter. +func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) { + reader.Seek(0) + for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { + if err != nil { + return nil, err + } + + if entry.Tag != dwarf.TagSubprogram { + continue + } + + lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64) + if !ok { + continue + } + + highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64) + if !ok { + continue + } + + if lowpc <= pc && highpc >= pc { + return entry, nil + } + } + + return nil, fmt.Errorf("unable to find function context") +} + +// NextScopeVariable moves the reader to the next debug entry that describes a local variable +func (reader *Reader) NextScopeVariable() (*dwarf.Entry, error) { + for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { + if err != nil { + return nil, err + } + + // All scope variables will be at the same depth + reader.SkipChildren() + + // End of the current depth + if entry.Tag == 0 { + break + } + + if entry.Tag == dwarf.TagVariable || entry.Tag == dwarf.TagFormalParameter { + return entry, nil + } + } + + // No more items + return nil, nil +} diff --git a/proctl/proctl.go b/proctl/proctl.go index 794c4566..aa80a4f6 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -11,6 +11,7 @@ import ( "syscall" "github.com/derekparker/delve/dwarf/frame" + "github.com/derekparker/delve/dwarf/reader" ) // Struct representing a debugged process. Holds onto pid, register values, @@ -298,6 +299,11 @@ func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) { return dbp.CurrentThread.EvalSymbol(name) } +// Returns a reader for the dwarf data +func (dbp *DebuggedProcess) DwarfReader() *reader.Reader { + return reader.New(dbp.Dwarf) +} + type ProcessExitedError struct { pid int } diff --git a/proctl/variables.go b/proctl/variables.go index eb565eaf..e7fc8c35 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -302,48 +302,27 @@ func offsetFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader, parentin // Returns the value of the named symbol. func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) { - data := thread.Process.Dwarf - pc, err := thread.CurrentPC() if err != nil { return nil, err } - fn := thread.Process.GoSymTable.PCToFunc(pc) - if fn == nil { - return nil, fmt.Errorf("could not find function scope") - } + reader := thread.Process.DwarfReader() - reader := data.Reader() - if err = seekToFunctionEntry(fn.Name, reader); err != nil { + _, err = reader.SeekToFunction(pc) + if err != nil { return nil, err } if strings.Contains(name, ".") { idx := strings.Index(name, ".") - return evaluateStructMember(thread, data, reader, name[:idx], name[idx+1:]) + data := thread.Process.Dwarf + return evaluateStructMember(thread, data, reader.Reader, name[:idx], name[idx+1:]) } - entry, err := findDwarfEntry(name, reader, false) - if err != nil { - return nil, err - } - - return thread.extractVariableFromEntry(entry) -} - -// seekToFunctionEntry is basically used to seek the dwarf.Reader to -// the function entry that represents our current scope. From there -// we can find the first child entry that matches the var name and -// use it to determine the value of the variable. -func seekToFunctionEntry(name string, reader *dwarf.Reader) error { - for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { + for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() { if err != nil { - return err - } - - if entry.Tag != dwarf.TagSubprogram { - continue + return nil, err } n, ok := entry.Val(dwarf.AttrName).(string) @@ -352,11 +331,11 @@ func seekToFunctionEntry(name string, reader *dwarf.Reader) error { } if n == name { - break + return thread.extractVariableFromEntry(entry) } } - return nil + return nil, fmt.Errorf("could not find symbol value for %s", name) } func findDwarfEntry(name string, reader *dwarf.Reader, member bool) (*dwarf.Entry, error) { @@ -656,35 +635,25 @@ func (thread *ThreadContext) readMemory(addr uintptr, size uintptr) ([]byte, err // Fetches all variables of a specific type in the current function scope func (thread *ThreadContext) variablesByTag(tag dwarf.Tag) ([]*Variable, error) { - data := thread.Process.Dwarf - pc, err := thread.CurrentPC() if err != nil { return nil, err } - fn := thread.Process.GoSymTable.PCToFunc(pc) - if fn == nil { - return nil, fmt.Errorf("could not find function scope") - } + reader := thread.Process.DwarfReader() - reader := data.Reader() - if err = seekToFunctionEntry(fn.Name, reader); err != nil { + _, err = reader.SeekToFunction(pc) + if err != nil { return nil, err } vars := make([]*Variable, 0) - for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { + for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() { if err != nil { return nil, err } - // End of function - if entry.Tag == 0 { - break - } - if entry.Tag == tag { val, err := thread.extractVariableFromEntry(entry) if err != nil { @@ -693,9 +662,6 @@ func (thread *ThreadContext) variablesByTag(tag dwarf.Tag) ([]*Variable, error) vars = append(vars, val) } - - // Only care about top level - reader.SkipChildren() } return vars, nil