diff --git a/dwarf/reader/reader.go b/dwarf/reader/reader.go index b711b6c1..ee139157 100755 --- a/dwarf/reader/reader.go +++ b/dwarf/reader/reader.go @@ -63,27 +63,21 @@ func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) { return nil, fmt.Errorf("unable to find function context") } -// SeekToTypeNamed moves the reader to the type specified by the name. -// If the reader is set to a struct type the NextMemberVariable call -// can be used to walk all member data. -func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) { - // Walk the types to the base - for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { - if err != nil { - return nil, err - } - - n, ok := entry.Val(dwarf.AttrName).(string) - if !ok { - continue - } - - if n == name { - return entry, nil - } +// Returns the address for the named entry. +func (reader *Reader) AddrFor(name string) (uint64, error) { + entry, err := reader.FindEntryNamed(name, false) + if err != nil { + return 0, err } - - return nil, errors.New("no type entry found") + instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) + if !ok { + return 0, fmt.Errorf("type assertion failed") + } + addr, err := op.ExecuteStackProgram(0, instructions) + if err != nil { + return 0, err + } + return uint64(addr), nil } // Returns the address for the named struct member. @@ -143,6 +137,105 @@ func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resol return nil, fmt.Errorf("no type entry found") } +// SeekToTypeNamed moves the reader to the type specified by the name. +// If the reader is set to a struct type the NextMemberVariable call +// can be used to walk all member data. +func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) { + // Walk the types to the base + for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { + if err != nil { + return nil, err + } + + n, ok := entry.Val(dwarf.AttrName).(string) + if !ok { + continue + } + + if n == name { + return entry, nil + } + } + + return nil, errors.New("no type entry found") +} + +// Finds the entry for 'name'. +func (reader *Reader) FindEntryNamed(name string, member bool) (*dwarf.Entry, error) { + depth := 1 + for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { + if err != nil { + return nil, err + } + + if entry.Children { + depth++ + } + + if entry.Tag == 0 { + depth-- + if depth <= 0 { + return nil, fmt.Errorf("could not find symbol value for %s", name) + } + } + + if member { + if entry.Tag != dwarf.TagMember { + continue + } + } else { + if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType { + continue + } + } + + n, ok := entry.Val(dwarf.AttrName).(string) + if !ok || n != name { + continue + } + return entry, nil + } + return nil, fmt.Errorf("could not find symbol value for %s", name) +} + +func (reader *Reader) InstructionsForEntryNamed(name string, member bool) ([]byte, error) { + entry, err := reader.FindEntryNamed(name, member) + if err != nil { + return nil, err + } + var attr dwarf.Attr + if member { + attr = dwarf.AttrDataMemberLoc + } else { + attr = dwarf.AttrLocation + } + instr, ok := entry.Val(attr).([]byte) + if !ok { + return nil, errors.New("invalid typecast for Dwarf instructions") + } + return instr, nil +} + +func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) { + if entry.Tag == dwarf.TagMember { + instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte) + if !ok { + return nil, fmt.Errorf("member data has no data member location attribute") + } + // clone slice to prevent stomping on the dwarf data + return append([]byte{}, instructions...), nil + } + + // non-member + instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) + if !ok { + return nil, fmt.Errorf("entry has no location attribute") + } + + // clone slice to prevent stomping on the dwarf data + return append([]byte{}, instructions...), nil +} + // NextScopeVariable moves the reader to the next debug entry that describes a local variable and returns the entry. func (reader *Reader) NextScopeVariable() (*dwarf.Entry, error) { for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { diff --git a/proctl/proctl.go b/proctl/proctl.go index 121c5548..6e12e475 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -393,16 +393,22 @@ func (dbp *DebuggedProcess) SwitchThread(tid int) error { // Delve cares about from the internal runtime G structure. func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) { var ( - allg []*G - reader = dbp.dwarf.Reader() + allg []*G + rdr = dbp.DwarfReader() ) - allglen, err := allglenval(dbp, reader) + addr, err := rdr.AddrFor("runtime.allglen") if err != nil { return nil, err } - reader.Seek(0) - allgentryaddr, err := addressFor(dbp, "runtime.allg", reader) + allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8) + if err != nil { + return nil, err + } + allglen := binary.LittleEndian.Uint64(allglenBytes) + + rdr.Seek(0) + allgentryaddr, err := rdr.AddrFor("runtime.allg") if err != nil { return nil, err } diff --git a/proctl/variables.go b/proctl/variables.go index b062b0d7..1de15aa4 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -68,172 +68,6 @@ func (g *G) chanRecvReturnAddr(dbp *DebuggedProcess) (uint64, error) { return topLoc.addr, nil } -// Parses and returns select info on the internal M -// data structures used by the Go scheduler. -func (thread *ThreadContext) AllM() ([]*M, error) { - reader := thread.Process.dwarf.Reader() - - allmaddr, err := parseAllMPtr(thread.Process, reader) - if err != nil { - return nil, err - } - mptr, err := thread.readMemory(uintptr(allmaddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, err - } - m := binary.LittleEndian.Uint64(mptr) - if m == 0 { - return nil, fmt.Errorf("allm contains no M pointers") - } - - procidInstructions, err := instructionsFor("procid", thread.Process, reader, true) - if err != nil { - return nil, err - } - spinningInstructions, err := instructionsFor("spinning", thread.Process, reader, true) - if err != nil { - return nil, err - } - alllinkInstructions, err := instructionsFor("alllink", thread.Process, reader, true) - if err != nil { - return nil, err - } - blockedInstructions, err := instructionsFor("blocked", thread.Process, reader, true) - if err != nil { - return nil, err - } - curgInstructions, err := instructionsFor("curg", thread.Process, reader, true) - if err != nil { - return nil, err - } - - var allm []*M - for { - // curg - curgAddr, err := executeMemberStackProgram(mptr, curgInstructions) - if err != nil { - return nil, err - } - curgBytes, err := thread.readMemory(uintptr(curgAddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, fmt.Errorf("could not read curg %#v %s", curgAddr, err) - } - curg := binary.LittleEndian.Uint64(curgBytes) - - // procid - procidAddr, err := executeMemberStackProgram(mptr, procidInstructions) - if err != nil { - return nil, err - } - procidBytes, err := thread.readMemory(uintptr(procidAddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, fmt.Errorf("could not read procid %#v %s", procidAddr, err) - } - procid := binary.LittleEndian.Uint64(procidBytes) - - // spinning - spinningAddr, err := executeMemberStackProgram(mptr, spinningInstructions) - if err != nil { - return nil, err - } - spinBytes, err := thread.readMemory(uintptr(spinningAddr), 1) - if err != nil { - return nil, fmt.Errorf("could not read spinning %#v %s", spinningAddr, err) - } - - // blocked - blockedAddr, err := executeMemberStackProgram(mptr, blockedInstructions) - if err != nil { - return nil, err - } - blockBytes, err := thread.readMemory(uintptr(blockedAddr), 1) - if err != nil { - return nil, fmt.Errorf("could not read blocked %#v %s", blockedAddr, err) - } - - allm = append(allm, &M{ - procid: int(procid), - blocked: blockBytes[0], - spinning: spinBytes[0], - curg: uintptr(curg), - }) - - // Follow the linked list - alllinkAddr, err := executeMemberStackProgram(mptr, alllinkInstructions) - if err != nil { - return nil, err - } - mptr, err = thread.readMemory(uintptr(alllinkAddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, fmt.Errorf("could not read alllink %#v %s", alllinkAddr, err) - } - m = binary.LittleEndian.Uint64(mptr) - - if m == 0 { - break - } - } - - return allm, nil -} - -func instructionsFor(name string, dbp *DebuggedProcess, reader *dwarf.Reader, member bool) ([]byte, error) { - reader.Seek(0) - entry, err := findDwarfEntry(name, reader, member) - if err != nil { - return nil, err - } - return instructionsForEntry(entry) -} - -func instructionsForEntry(entry *dwarf.Entry) ([]byte, error) { - if entry.Tag == dwarf.TagMember { - instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte) - if !ok { - return nil, fmt.Errorf("member data has no data member location attribute") - } - // clone slice to prevent stomping on the dwarf data - return append([]byte{}, instructions...), nil - } - - // non-member - instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) - if !ok { - return nil, fmt.Errorf("entry has no location attribute") - } - - // clone slice to prevent stomping on the dwarf data - return append([]byte{}, instructions...), nil -} - -func executeMemberStackProgram(base, instructions []byte) (uint64, error) { - parentInstructions := append([]byte{op.DW_OP_addr}, base...) - addr, err := op.ExecuteStackProgram(0, append(parentInstructions, instructions...)) - if err != nil { - return 0, err - } - - return uint64(addr), nil -} - -func parseAllMPtr(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) { - entry, err := findDwarfEntry("runtime.allm", reader, false) - if err != nil { - return 0, err - } - - instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) - if !ok { - return 0, fmt.Errorf("type assertion failed") - } - addr, err := op.ExecuteStackProgram(0, instructions) - if err != nil { - return 0, err - } - - return uint64(addr), nil -} - type NoGError struct { tid int } @@ -319,45 +153,6 @@ func parseG(thread *ThreadContext, addr uint64) (*G, error) { return g, nil } -func allglenval(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) { - entry, err := findDwarfEntry("runtime.allglen", reader, false) - if err != nil { - return 0, err - } - - instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) - if !ok { - return 0, fmt.Errorf("type assertion failed") - } - addr, err := op.ExecuteStackProgram(0, instructions) - if err != nil { - return 0, err - } - val, err := dbp.CurrentThread.readMemory(uintptr(addr), 8) - if err != nil { - return 0, err - } - return binary.LittleEndian.Uint64(val), nil -} - -func addressFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader) (uint64, error) { - entry, err := findDwarfEntry(name, reader, false) - if err != nil { - return 0, err - } - - instructions, ok := entry.Val(dwarf.AttrLocation).([]byte) - if !ok { - return 0, fmt.Errorf("type assertion failed") - } - addr, err := op.ExecuteStackProgram(0, instructions) - if err != nil { - return 0, err - } - - return uint64(addr), nil -} - // Returns the value of the named symbol. func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) { pc, err := thread.PC() @@ -433,45 +228,8 @@ func (thread *ThreadContext) PackageVariables() ([]*Variable, error) { return vars, nil } -func findDwarfEntry(name string, reader *dwarf.Reader, member bool) (*dwarf.Entry, error) { - depth := 1 - for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { - if err != nil { - return nil, err - } - - if entry.Children { - depth++ - } - - if entry.Tag == 0 { - depth-- - if depth <= 0 { - return nil, fmt.Errorf("could not find symbol value for %s", name) - } - } - - if member { - if entry.Tag != dwarf.TagMember { - continue - } - } else { - if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType { - continue - } - } - - n, ok := entry.Val(dwarf.AttrName).(string) - if !ok || n != name { - continue - } - return entry, nil - } - return nil, fmt.Errorf("could not find symbol value for %s", name) -} - -func (thread *ThreadContext) evaluateStructMember(parentEntry *dwarf.Entry, reader *reader.Reader, memberName string) (*Variable, error) { - parentAddr, err := thread.extractVariableDataAddress(parentEntry, reader) +func (thread *ThreadContext) evaluateStructMember(parentEntry *dwarf.Entry, rdr *reader.Reader, memberName string) (*Variable, error) { + parentAddr, err := thread.extractVariableDataAddress(parentEntry, rdr) if err != nil { return nil, err } @@ -483,13 +241,13 @@ func (thread *ThreadContext) evaluateStructMember(parentEntry *dwarf.Entry, read } // Seek reader to the type information so members can be iterated - _, err = reader.SeekToType(parentEntry, true, true) + _, err = rdr.SeekToType(parentEntry, true, true) if err != nil { return nil, err } // Iterate to find member by name - for memberEntry, err := reader.NextMemberVariable(); memberEntry != nil; memberEntry, err = reader.NextMemberVariable() { + for memberEntry, err := rdr.NextMemberVariable(); memberEntry != nil; memberEntry, err = rdr.NextMemberVariable() { if err != nil { return nil, err } @@ -505,7 +263,7 @@ func (thread *ThreadContext) evaluateStructMember(parentEntry *dwarf.Entry, read return nil, fmt.Errorf("%s is nil", parentName) } - memberInstr, err := instructionsForEntry(memberEntry) + memberInstr, err := rdr.InstructionsForEntry(memberEntry) if err != nil { return nil, err } @@ -597,8 +355,8 @@ func (thread *ThreadContext) executeStackProgram(instructions []byte) (int64, er } // Extracts the address of a variable, dereferencing any pointers -func (thread *ThreadContext) extractVariableDataAddress(entry *dwarf.Entry, reader *reader.Reader) (int64, error) { - instructions, err := instructionsForEntry(entry) +func (thread *ThreadContext) extractVariableDataAddress(entry *dwarf.Entry, rdr *reader.Reader) (int64, error) { + instructions, err := rdr.InstructionsForEntry(entry) if err != nil { return 0, err } @@ -609,7 +367,7 @@ func (thread *ThreadContext) extractVariableDataAddress(entry *dwarf.Entry, read } // Dereference pointers to get down the concrete type - for typeEntry, err := reader.SeekToType(entry, true, false); typeEntry != nil; typeEntry, err = reader.SeekToType(typeEntry, true, false) { + for typeEntry, err := rdr.SeekToType(entry, true, false); typeEntry != nil; typeEntry, err = rdr.SeekToType(typeEntry, true, false) { if err != nil { return 0, err }