diff --git a/dwarf/reader/reader.go b/dwarf/reader/reader.go index 77f31693..b711b6c1 100755 --- a/dwarf/reader/reader.go +++ b/dwarf/reader/reader.go @@ -2,7 +2,10 @@ package reader import ( "debug/dwarf" + "errors" "fmt" + + "github.com/derekparker/delve/dwarf/op" ) type Reader struct { @@ -60,6 +63,49 @@ 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 + } + } + + return nil, errors.New("no type entry found") +} + +// Returns the address for the named struct member. +func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (uint64, error) { + for { + entry, err := reader.NextMemberVariable() + if err != nil { + return 0, err + } + name, ok := entry.Val(dwarf.AttrName).(string) + if !ok || name != member { + continue + } + instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte) + if !ok { + continue + } + addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...)) + return uint64(addr), err + } +} + // SeekToType moves the reader to the type specified by the entry, // optionally resolving typedefs and pointer types. If the reader is set // to a struct type the NextMemberVariable call can be used to walk all member data. diff --git a/proctl/proctl.go b/proctl/proctl.go index bb3fe176..121c5548 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -410,7 +410,7 @@ func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) { allgptr := binary.LittleEndian.Uint64(faddr) for i := uint64(0); i < allglen; i++ { - g, err := parseG(dbp.CurrentThread, allgptr+(i*uint64(dbp.arch.PtrSize())), reader) + g, err := parseG(dbp.CurrentThread, allgptr+(i*uint64(dbp.arch.PtrSize()))) if err != nil { return nil, err } diff --git a/proctl/threads.go b/proctl/threads.go index 7e4ded09..3c61b6c4 100644 --- a/proctl/threads.go +++ b/proctl/threads.go @@ -259,8 +259,7 @@ func (thread *ThreadContext) curG() (*G, error) { if err != nil { return err } - reader := thread.Process.dwarf.Reader() - g, err = parseG(thread, regs.SP()+uint64(thread.Process.arch.PtrSize()), reader) + g, err = parseG(thread, regs.SP()+uint64(thread.Process.arch.PtrSize())) return err }) return g, err diff --git a/proctl/variables.go b/proctl/variables.go index e9131d8a..b062b0d7 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -242,7 +242,7 @@ func (ng NoGError) Error() string { return fmt.Sprintf("no G executing on thread %d", ng.tid) } -func parseG(thread *ThreadContext, addr uint64, reader *dwarf.Reader) (*G, error) { +func parseG(thread *ThreadContext, addr uint64) (*G, error) { gaddrbytes, err := thread.readMemory(uintptr(addr), thread.Process.arch.PtrSize()) if err != nil { return nil, fmt.Errorf("error derefing *G %s", err) @@ -253,57 +253,64 @@ func parseG(thread *ThreadContext, addr uint64, reader *dwarf.Reader) (*G, error return nil, NoGError{tid: thread.Id} } - reader.Seek(0) - goidaddr, err := offsetFor("goid", reader, initialInstructions) - if err != nil { - return nil, err - } - goidbytes, err := thread.readMemory(uintptr(goidaddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, fmt.Errorf("error reading goid %s", err) - } - reader.Seek(0) - gopcaddr, err := offsetFor("gopc", reader, initialInstructions) - if err != nil { - return nil, err - } - gopcbytes, err := thread.readMemory(uintptr(gopcaddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, fmt.Errorf("error reading gopc %s", err) - } - reader.Seek(0) - schedaddr, err := offsetFor("sched", reader, initialInstructions) - if err != nil { - return nil, err - } - reader.Seek(0) - waitreasonaddr, err := offsetFor("waitreason", reader, initialInstructions) - if err != nil { - return nil, err - } - waitreason, err := thread.readString(uintptr(waitreasonaddr)) + rdr := thread.Process.DwarfReader() + rdr.Seek(0) + _, err = rdr.SeekToTypeNamed("runtime.g") if err != nil { return nil, err } - spbytes, err := thread.readMemory(uintptr(schedaddr), thread.Process.arch.PtrSize()) - if err != nil { - return nil, fmt.Errorf("error reading goroutine SP %s", err) - } - gosp := binary.LittleEndian.Uint64(spbytes) + // Let's parse all of the members we care about in order so that + // we don't have to spend any extra time seeking. - pcbytes, err := thread.readMemory(uintptr(schedaddr+uint64(thread.Process.arch.PtrSize())), thread.Process.arch.PtrSize()) + // Parse sched + schedAddr, err := rdr.AddrForMember("sched", initialInstructions) if err != nil { - return nil, fmt.Errorf("error reading goroutine PC %s", err) + return nil, err + } + // From sched, let's parse PC and SP. + sp, err := thread.readUintRaw(uintptr(schedAddr), 8) + if err != nil { + return nil, err + } + pc, err := thread.readUintRaw(uintptr(schedAddr+uint64(thread.Process.arch.PtrSize())), 8) + if err != nil { + return nil, err + } + // Parse goid + goidAddr, err := rdr.AddrForMember("goid", initialInstructions) + if err != nil { + return nil, err + } + goid, err := thread.readIntRaw(uintptr(goidAddr), 8) + if err != nil { + return nil, err + } + // Parse waitreason + waitReasonAddr, err := rdr.AddrForMember("waitreason", initialInstructions) + if err != nil { + return nil, err + } + waitreason, err := thread.readString(uintptr(waitReasonAddr)) + if err != nil { + return nil, err + } + // Parse gopc + gopcAddr, err := rdr.AddrForMember("gopc", initialInstructions) + if err != nil { + return nil, err + } + gopc, err := thread.readUintRaw(uintptr(gopcAddr), 8) + if err != nil { + return nil, err } - gopc := binary.LittleEndian.Uint64(pcbytes) f, l, fn := thread.Process.goSymTable.PCToLine(gopc) g := &G{ - Id: int(binary.LittleEndian.Uint64(goidbytes)), - GoPC: binary.LittleEndian.Uint64(gopcbytes), - PC: gopc, - SP: gosp, + Id: int(goid), + GoPC: gopc, + PC: pc, + SP: sp, File: f, Line: l, Func: fn, @@ -351,23 +358,6 @@ func addressFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader) (uint64 return uint64(addr), nil } -func offsetFor(name string, reader *dwarf.Reader, parentinstr []byte) (uint64, error) { - entry, err := findDwarfEntry(name, reader, true) - if err != nil { - return 0, err - } - instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte) - if !ok { - return 0, fmt.Errorf("type assertion failed") - } - offset, err := op.ExecuteStackProgram(0, append(parentinstr, instructions...)) - if err != nil { - return 0, err - } - - return uint64(offset), nil -} - // Returns the value of the named symbol. func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) { pc, err := thread.PC() @@ -852,11 +842,19 @@ func (thread *ThreadContext) readArrayValues(addr uintptr, count int64, stride i } func (thread *ThreadContext) readInt(addr uintptr, size int64) (string, error) { + n, err := thread.readIntRaw(addr, size) + if err != nil { + return "", err + } + return strconv.FormatInt(n, 10), nil +} + +func (thread *ThreadContext) readIntRaw(addr uintptr, size int64) (int64, error) { var n int64 val, err := thread.readMemory(addr, int(size)) if err != nil { - return "", err + return 0, err } switch size { @@ -870,15 +868,23 @@ func (thread *ThreadContext) readInt(addr uintptr, size int64) (string, error) { n = int64(binary.LittleEndian.Uint64(val)) } - return strconv.FormatInt(n, 10), nil + return n, nil } func (thread *ThreadContext) readUint(addr uintptr, size int64) (string, error) { + n, err := thread.readUintRaw(addr, size) + if err != nil { + return "", err + } + return strconv.FormatUint(n, 10), nil +} + +func (thread *ThreadContext) readUintRaw(addr uintptr, size int64) (uint64, error) { var n uint64 val, err := thread.readMemory(addr, int(size)) if err != nil { - return "", err + return 0, err } switch size { @@ -892,7 +898,7 @@ func (thread *ThreadContext) readUint(addr uintptr, size int64) (string, error) n = uint64(binary.LittleEndian.Uint64(val)) } - return strconv.FormatUint(n, 10), nil + return n, nil } func (thread *ThreadContext) readFloat(addr uintptr, size int64) (string, error) {