Extracting common dwarf reader functionality into its own area

This commit is contained in:
epipho 2014-12-31 08:34:41 -05:00 committed by Derek Parker
parent c0fd1a0295
commit ed6d4049b6
3 changed files with 104 additions and 47 deletions

85
dwarf/reader/reader.go Executable file

@ -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
}

@ -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
}

@ -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