2014-12-31 13:34:41 +00:00
|
|
|
package reader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"debug/dwarf"
|
2015-05-04 22:31:13 +00:00
|
|
|
"fmt"
|
2014-12-31 13:34:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Reader struct {
|
|
|
|
*dwarf.Reader
|
|
|
|
depth int
|
|
|
|
}
|
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// New returns a reader for the specified dwarf data.
|
2014-12-31 13:34:41 +00:00
|
|
|
func New(data *dwarf.Data) *Reader {
|
|
|
|
return &Reader{data.Reader(), 0}
|
|
|
|
}
|
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// Seek moves the reader to an arbitrary offset.
|
2014-12-31 13:34:41 +00:00
|
|
|
func (reader *Reader) Seek(off dwarf.Offset) {
|
|
|
|
reader.depth = 0
|
|
|
|
reader.Reader.Seek(off)
|
|
|
|
}
|
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// SeekToEntry moves the reader to an arbitrary entry.
|
2014-12-31 13:34:41 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("unable to find function context")
|
2014-12-31 13:34:41 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 21:13:28 +00:00
|
|
|
// SeekToType moves the reader to the type specified by the entry,
|
|
|
|
// optionally resolving typedefs and pointer types. If the reader is set
|
2015-02-04 04:20:25 +00:00
|
|
|
// to a struct type the NextMemberVariable call can be used to walk all member data.
|
2014-12-31 21:13:28 +00:00
|
|
|
func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resolvePointerTypes bool) (*dwarf.Entry, error) {
|
|
|
|
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("entry does not have a type attribute")
|
2014-12-31 21:13:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Seek to the first type offset
|
|
|
|
reader.Seek(offset)
|
|
|
|
|
|
|
|
// Walk the types to the base
|
|
|
|
for typeEntry, err := reader.Next(); typeEntry != nil; typeEntry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if typeEntry.Tag == dwarf.TagTypedef && !resolveTypedefs {
|
|
|
|
return typeEntry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if typeEntry.Tag == dwarf.TagPointerType && !resolvePointerTypes {
|
|
|
|
return typeEntry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
offset, ok = typeEntry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
|
|
|
return typeEntry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
reader.Seek(offset)
|
|
|
|
}
|
|
|
|
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("no type entry found")
|
2014-12-31 21:13:28 +00:00
|
|
|
}
|
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// NextScopeVariable moves the reader to the next debug entry that describes a local variable and returns the entry.
|
2014-12-31 13:34:41 +00:00
|
|
|
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
|
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// NextMememberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
|
2014-12-31 21:13:28 +00:00
|
|
|
func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// All member variables will be at the same depth
|
|
|
|
reader.SkipChildren()
|
|
|
|
|
|
|
|
// End of the current depth
|
|
|
|
if entry.Tag == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == dwarf.TagMember {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more items
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-01-21 05:47:59 +00:00
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// NextPackageVariable moves the reader to the next debug entry that describes a package variable.
|
|
|
|
// Any TagVariable entry that is not inside a sub prgram entry and is marked external is considered a package variable.
|
2015-01-21 05:47:59 +00:00
|
|
|
func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == dwarf.TagVariable {
|
|
|
|
ext, ok := entry.Val(dwarf.AttrExternal).(bool)
|
|
|
|
if ok && ext {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore everything inside sub programs
|
|
|
|
if entry.Tag == dwarf.TagSubprogram {
|
|
|
|
reader.SkipChildren()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more items
|
|
|
|
return nil, nil
|
|
|
|
}
|