2014-10-25 15:48:14 +00:00
|
|
|
package proctl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2014-11-24 23:27:56 +00:00
|
|
|
"debug/dwarf"
|
2014-10-25 15:48:14 +00:00
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/derekparker/delve/dwarf/op"
|
2014-12-31 21:13:28 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/reader"
|
2014-10-25 15:48:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Variable struct {
|
|
|
|
Name string
|
|
|
|
Value string
|
|
|
|
Type string
|
|
|
|
}
|
|
|
|
|
2014-11-27 02:35:53 +00:00
|
|
|
type M struct {
|
|
|
|
procid int
|
|
|
|
spinning uint8
|
|
|
|
blocked uint8
|
|
|
|
curg uintptr
|
|
|
|
}
|
|
|
|
|
2014-12-20 05:10:32 +00:00
|
|
|
const ptrsize uintptr = unsafe.Sizeof(int(1))
|
|
|
|
|
2014-11-27 02:35:53 +00:00
|
|
|
// Parses and returns select info on the internal M
|
|
|
|
// data structures used by the Go scheduler.
|
|
|
|
func (thread *ThreadContext) AllM() ([]*M, error) {
|
2014-12-08 23:15:52 +00:00
|
|
|
reader := thread.Process.Dwarf.Reader()
|
2014-11-27 02:35:53 +00:00
|
|
|
|
|
|
|
allmaddr, err := parseAllMPtr(thread.Process, reader)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-20 05:10:32 +00:00
|
|
|
mptr, err := thread.readMemory(uintptr(allmaddr), ptrsize)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
m := binary.LittleEndian.Uint64(mptr)
|
|
|
|
if m == 0 {
|
|
|
|
return nil, fmt.Errorf("allm contains no M pointers")
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse addresses
|
2014-12-03 05:30:39 +00:00
|
|
|
procidInstructions, err := instructionsFor("procid", thread.Process, reader, true)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
spinningInstructions, err := instructionsFor("spinning", thread.Process, reader, true)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
alllinkInstructions, err := instructionsFor("alllink", thread.Process, reader, true)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
blockedInstructions, err := instructionsFor("blocked", thread.Process, reader, true)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
curgInstructions, err := instructionsFor("curg", thread.Process, reader, true)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var allm []*M
|
|
|
|
for {
|
|
|
|
// curg
|
|
|
|
curgAddr, err := executeMemberStackProgram(mptr, curgInstructions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-20 05:10:32 +00:00
|
|
|
curgBytes, err := thread.readMemory(uintptr(curgAddr), ptrsize)
|
2014-11-27 02:35:53 +00:00
|
|
|
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
|
|
|
|
}
|
2014-12-20 05:10:32 +00:00
|
|
|
procidBytes, err := thread.readMemory(uintptr(procidAddr), ptrsize)
|
2014-11-27 02:35:53 +00:00
|
|
|
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 {
|
2014-12-02 18:42:17 +00:00
|
|
|
return nil, fmt.Errorf("could not read spinning %#v %s", spinningAddr, err)
|
2014-11-27 02:35:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2014-12-20 05:10:32 +00:00
|
|
|
mptr, err = thread.readMemory(uintptr(alllinkAddr), ptrsize)
|
2014-11-27 02:35:53 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-12-03 05:30:39 +00:00
|
|
|
func instructionsFor(name string, dbp *DebuggedProcess, reader *dwarf.Reader, member bool) ([]byte, error) {
|
2014-11-27 02:35:53 +00:00
|
|
|
reader.Seek(0)
|
2014-12-03 05:30:39 +00:00
|
|
|
entry, err := findDwarfEntry(name, reader, member)
|
2014-11-27 02:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
return instructionsForEntry(entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
func instructionsForEntry(entry *dwarf.Entry) ([]byte, error) {
|
|
|
|
if entry.Tag == dwarf.TagMember {
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
2014-12-03 05:30:39 +00:00
|
|
|
if !ok {
|
2014-12-31 21:13:28 +00:00
|
|
|
return nil, fmt.Errorf("member data has no data member location attribute")
|
2014-12-03 05:30:39 +00:00
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
// clone slice to prevent stomping on the dwarf data
|
|
|
|
return append([]byte{}, instructions...), nil
|
2014-11-27 02:35:53 +00:00
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
|
|
|
|
// 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
|
2014-11-27 02:35:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-11-08 14:02:31 +00:00
|
|
|
func (dbp *DebuggedProcess) PrintGoroutinesInfo() error {
|
2014-12-08 23:15:52 +00:00
|
|
|
reader := dbp.Dwarf.Reader()
|
2014-11-08 14:02:31 +00:00
|
|
|
|
2014-11-11 03:26:13 +00:00
|
|
|
allglen, err := allglenval(dbp, reader)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-11-13 14:36:20 +00:00
|
|
|
reader.Seek(0)
|
2014-11-26 02:37:43 +00:00
|
|
|
allgentryaddr, err := addressFor(dbp, "runtime.allg", reader)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Printf("[%d goroutines]\n", allglen)
|
2014-12-20 05:10:32 +00:00
|
|
|
faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), ptrsize)
|
2014-11-08 14:02:31 +00:00
|
|
|
allg := binary.LittleEndian.Uint64(faddr)
|
|
|
|
|
|
|
|
for i := uint64(0); i < allglen; i++ {
|
2014-12-20 05:10:32 +00:00
|
|
|
err = printGoroutineInfo(dbp, allg+(i*uint64(ptrsize)), reader)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-11-26 02:37:43 +00:00
|
|
|
func printGoroutineInfo(dbp *DebuggedProcess, addr uint64, reader *dwarf.Reader) error {
|
2014-12-20 05:10:32 +00:00
|
|
|
gaddrbytes, err := dbp.CurrentThread.readMemory(uintptr(addr), ptrsize)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error derefing *G %s", err)
|
|
|
|
}
|
2014-11-26 02:37:43 +00:00
|
|
|
initialInstructions := append([]byte{op.DW_OP_addr}, gaddrbytes...)
|
2014-11-08 14:02:31 +00:00
|
|
|
|
2014-11-26 02:37:43 +00:00
|
|
|
reader.Seek(0)
|
|
|
|
goidaddr, err := offsetFor(dbp, "goid", reader, initialInstructions)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
2014-11-26 02:37:43 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
reader.Seek(0)
|
|
|
|
schedaddr, err := offsetFor(dbp, "sched", reader, initialInstructions)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-11-08 14:02:31 +00:00
|
|
|
}
|
2014-11-26 02:37:43 +00:00
|
|
|
|
2014-12-20 05:10:32 +00:00
|
|
|
goidbytes, err := dbp.CurrentThread.readMemory(uintptr(goidaddr), ptrsize)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error reading goid %s", err)
|
|
|
|
}
|
2014-12-20 05:10:32 +00:00
|
|
|
schedbytes, err := dbp.CurrentThread.readMemory(uintptr(schedaddr+uint64(ptrsize)), ptrsize)
|
2014-11-26 02:37:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error reading sched %s", err)
|
|
|
|
}
|
2014-11-08 14:02:31 +00:00
|
|
|
gopc := binary.LittleEndian.Uint64(schedbytes)
|
2014-11-26 02:37:43 +00:00
|
|
|
f, l, fn := dbp.GoSymTable.PCToLine(gopc)
|
|
|
|
fname := ""
|
|
|
|
if fn != nil {
|
|
|
|
fname = fn.Name
|
|
|
|
}
|
|
|
|
fmt.Printf("Goroutine %d - %s:%d %s\n", binary.LittleEndian.Uint64(goidbytes), f, l, fname)
|
2014-11-08 14:02:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-11-11 03:26:13 +00:00
|
|
|
func allglenval(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) {
|
2014-11-26 02:37:43 +00:00
|
|
|
entry, err := findDwarfEntry("runtime.allglen", reader, false)
|
2014-11-08 14:02:31 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-11-26 02:37:43 +00:00
|
|
|
func addressFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader) (uint64, error) {
|
|
|
|
entry, err := findDwarfEntry(name, reader, false)
|
2014-11-08 14:02:31 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-11-26 02:37:43 +00:00
|
|
|
func offsetFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader, parentinstr []byte) (uint64, error) {
|
|
|
|
entry, err := findDwarfEntry(name, reader, true)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
|
|
|
if !ok {
|
|
|
|
return 0, fmt.Errorf("type assertion failed")
|
|
|
|
}
|
2014-11-26 02:37:43 +00:00
|
|
|
offset, err := op.ExecuteStackProgram(0, append(parentinstr, instructions...))
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return uint64(offset), nil
|
|
|
|
}
|
|
|
|
|
2014-10-25 15:48:14 +00:00
|
|
|
// Returns the value of the named symbol.
|
|
|
|
func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
|
2014-11-11 03:26:13 +00:00
|
|
|
pc, err := thread.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
reader := thread.Process.DwarfReader()
|
2014-12-03 05:30:39 +00:00
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
_, err = reader.SeekToFunction(pc)
|
|
|
|
if err != nil {
|
2014-12-02 22:42:15 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-11-11 03:26:13 +00:00
|
|
|
|
2014-12-31 21:13:28 +00:00
|
|
|
varName := name
|
|
|
|
memberName := ""
|
2014-12-03 05:30:39 +00:00
|
|
|
if strings.Contains(name, ".") {
|
|
|
|
idx := strings.Index(name, ".")
|
2014-12-31 21:13:28 +00:00
|
|
|
varName = name[:idx]
|
|
|
|
memberName = name[idx+1:]
|
2014-12-03 05:30:39 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
|
2014-11-11 03:26:13 +00:00
|
|
|
if err != nil {
|
2014-12-31 13:34:41 +00:00
|
|
|
return nil, err
|
2014-11-11 03:26:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-12-31 21:13:28 +00:00
|
|
|
if n == varName {
|
|
|
|
if len(memberName) == 0 {
|
|
|
|
return thread.extractVariableFromEntry(entry)
|
|
|
|
}
|
|
|
|
return thread.evaluateStructMember(entry, reader, memberName)
|
2014-11-11 03:26:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
2014-11-11 03:26:13 +00:00
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2014-11-26 02:37:43 +00:00
|
|
|
func findDwarfEntry(name string, reader *dwarf.Reader, member bool) (*dwarf.Entry, error) {
|
2014-12-30 17:57:31 +00:00
|
|
|
depth := 1
|
2014-10-25 15:48:14 +00:00
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:57:31 +00:00
|
|
|
if entry.Children {
|
|
|
|
depth++
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == 0 {
|
|
|
|
depth--
|
|
|
|
if depth <= 0 {
|
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-26 02:37:43 +00:00
|
|
|
if member {
|
|
|
|
if entry.Tag != dwarf.TagMember {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
2014-11-27 02:35:53 +00:00
|
|
|
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType {
|
2014-11-26 02:37:43 +00:00
|
|
|
continue
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok || n != name {
|
|
|
|
continue
|
|
|
|
}
|
2014-11-08 14:02:31 +00:00
|
|
|
return entry, nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
|
|
|
}
|
|
|
|
|
2014-12-31 21:13:28 +00:00
|
|
|
func (thread *ThreadContext) evaluateStructMember(parentEntry *dwarf.Entry, reader *reader.Reader, memberName string) (*Variable, error) {
|
2015-01-01 09:00:44 +00:00
|
|
|
parentAddr, err := thread.extractVariableDataAddress(parentEntry, reader)
|
2014-12-03 05:30:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
|
2015-01-02 16:09:32 +00:00
|
|
|
// Get parent variable name
|
2014-12-31 21:13:28 +00:00
|
|
|
parentName, ok := parentEntry.Val(dwarf.AttrName).(string)
|
2014-12-03 05:30:39 +00:00
|
|
|
if !ok {
|
2014-12-31 21:13:28 +00:00
|
|
|
return nil, fmt.Errorf("unable to retrive variable name")
|
2014-12-03 05:30:39 +00:00
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
|
|
|
|
// Seek reader to the type information so members can be iterated
|
2015-01-01 09:00:44 +00:00
|
|
|
_, err = reader.SeekToType(parentEntry, true, true)
|
2014-12-03 05:30:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-31 21:13:28 +00:00
|
|
|
|
|
|
|
// Iterate to find member by name
|
|
|
|
for memberEntry, err := reader.NextMemberVariable(); memberEntry != nil; memberEntry, err = reader.NextMemberVariable() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
name, ok := memberEntry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if name == memberName {
|
2015-01-02 16:09:32 +00:00
|
|
|
// Nil ptr, wait until here to throw a nil pointer error to prioritize no such member error
|
2015-01-01 09:00:44 +00:00
|
|
|
if parentAddr == 0 {
|
|
|
|
return nil, fmt.Errorf("%s is nil", parentName)
|
|
|
|
}
|
|
|
|
|
2014-12-31 21:13:28 +00:00
|
|
|
memberInstr, err := instructionsForEntry(memberEntry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
offset, ok := memberEntry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
data := thread.Process.Dwarf
|
|
|
|
t, err := data.Type(offset)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
baseAddr := make([]byte, 8)
|
|
|
|
binary.LittleEndian.PutUint64(baseAddr, uint64(parentAddr))
|
|
|
|
|
|
|
|
parentInstructions := append([]byte{op.DW_OP_addr}, baseAddr...)
|
|
|
|
val, err := thread.extractValue(append(parentInstructions, memberInstr...), 0, t)
|
2014-12-31 21:13:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &Variable{Name: strings.Join([]string{parentName, memberName}, "."), Type: t.String(), Value: val}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
return nil, fmt.Errorf("%s has no member %s", parentName, memberName)
|
2014-12-03 05:30:39 +00:00
|
|
|
}
|
|
|
|
|
2014-12-30 17:57:31 +00:00
|
|
|
// Extracts the name, type, and value of a variable from a dwarf entry
|
|
|
|
func (thread *ThreadContext) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
|
|
|
|
if entry == nil {
|
|
|
|
return nil, fmt.Errorf("invalid entry")
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagVariable {
|
|
|
|
return nil, fmt.Errorf("invalid entry tag, only supports FormalParameter and Variable, got %s", entry.Tag.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
data := thread.Process.Dwarf
|
|
|
|
t, err := data.Type(offset)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := thread.extractValue(instructions, 0, t)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Variable{Name: n, Type: t.String(), Value: val}, nil
|
|
|
|
}
|
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
// Execute the stack program taking into account the current stack frame
|
|
|
|
func (thread *ThreadContext) executeStackProgram(instructions []byte) (int64, error) {
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
fde, err := thread.Process.FrameEntries.FDEForPC(regs.PC())
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
fctx := fde.EstablishFrame(regs.PC())
|
2015-01-16 21:30:22 +00:00
|
|
|
cfa := fctx.CFAOffset() + int64(regs.SP())
|
|
|
|
address, err := op.ExecuteStackProgram(cfa, instructions)
|
2015-01-01 09:00:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return address, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
address, err := thread.executeStackProgram(instructions)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2015-01-02 16:09:32 +00:00
|
|
|
// Dereference pointers to get down the concrete type
|
2015-01-01 09:00:44 +00:00
|
|
|
for typeEntry, err := reader.SeekToType(entry, true, false); typeEntry != nil; typeEntry, err = reader.SeekToType(typeEntry, true, false) {
|
2014-12-31 07:37:27 +00:00
|
|
|
if err != nil {
|
2015-01-01 09:00:44 +00:00
|
|
|
return 0, err
|
2014-12-31 07:37:27 +00:00
|
|
|
}
|
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
if typeEntry.Tag != dwarf.TagPointerType {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
ptraddress := uintptr(address)
|
|
|
|
|
|
|
|
ptr, err := thread.readMemory(ptraddress, ptrsize)
|
2014-12-31 07:37:27 +00:00
|
|
|
if err != nil {
|
2015-01-01 09:00:44 +00:00
|
|
|
return 0, err
|
2014-12-31 07:37:27 +00:00
|
|
|
}
|
2015-01-01 09:00:44 +00:00
|
|
|
address = int64(binary.LittleEndian.Uint64(ptr))
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
return address, nil
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
// Extracts the value from the instructions given in the DW_AT_location entry.
|
|
|
|
// We execute the stack program described in the DW_OP_* instruction stream, and
|
|
|
|
// then grab the value from the other processes memory.
|
|
|
|
func (thread *ThreadContext) extractValue(instructions []byte, addr int64, typ interface{}) (string, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if addr == 0 {
|
|
|
|
addr, err = thread.executeStackProgram(instructions)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have a user defined type, find the
|
|
|
|
// underlying concrete type and use that.
|
|
|
|
if tt, ok := typ.(*dwarf.TypedefType); ok {
|
|
|
|
typ = tt.Type
|
|
|
|
}
|
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
ptraddress := uintptr(addr)
|
2014-10-25 15:48:14 +00:00
|
|
|
switch t := typ.(type) {
|
|
|
|
case *dwarf.PtrType:
|
2014-12-31 07:37:27 +00:00
|
|
|
ptr, err := thread.readMemory(ptraddress, ptrsize)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2015-01-01 09:00:44 +00:00
|
|
|
|
|
|
|
intaddr := int64(binary.LittleEndian.Uint64(ptr))
|
|
|
|
if intaddr == 0 {
|
|
|
|
return fmt.Sprintf("%s nil", t.String()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := thread.extractValue(nil, intaddr, t.Type)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2015-01-01 09:00:44 +00:00
|
|
|
return fmt.Sprintf("*%s", val), nil
|
2014-10-25 15:48:14 +00:00
|
|
|
case *dwarf.StructType:
|
|
|
|
switch t.StructName {
|
|
|
|
case "string":
|
2015-01-19 23:25:08 +00:00
|
|
|
return thread.readString(ptraddress)
|
2014-10-25 15:48:14 +00:00
|
|
|
case "[]int":
|
2014-12-31 07:37:27 +00:00
|
|
|
return thread.readIntSlice(ptraddress, t)
|
2014-10-25 15:48:14 +00:00
|
|
|
default:
|
2014-10-25 16:13:02 +00:00
|
|
|
// Recursively call extractValue to grab
|
2014-10-25 15:48:14 +00:00
|
|
|
// the value of all the members of the struct.
|
|
|
|
fields := make([]string, 0, len(t.Field))
|
|
|
|
for _, field := range t.Field {
|
2015-01-01 09:00:44 +00:00
|
|
|
val, err := thread.extractValue(nil, field.ByteOffset+addr, field.Type)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
fields = append(fields, fmt.Sprintf("%s: %s", field.Name, val))
|
|
|
|
}
|
|
|
|
retstr := fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", "))
|
|
|
|
return retstr, nil
|
|
|
|
}
|
|
|
|
case *dwarf.ArrayType:
|
2014-12-31 07:37:27 +00:00
|
|
|
return thread.readIntArray(ptraddress, t)
|
2014-10-25 15:48:14 +00:00
|
|
|
case *dwarf.IntType:
|
2014-12-31 07:37:27 +00:00
|
|
|
return thread.readInt(ptraddress, t.ByteSize)
|
2014-10-25 15:48:14 +00:00
|
|
|
case *dwarf.FloatType:
|
2014-12-31 07:37:27 +00:00
|
|
|
return thread.readFloat(ptraddress, t.ByteSize)
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return "", fmt.Errorf("could not find value for type %s", typ)
|
|
|
|
}
|
|
|
|
|
2015-01-19 23:25:08 +00:00
|
|
|
func (thread *ThreadContext) readString(addr uintptr) (string, error) {
|
|
|
|
// string data structure is always two ptrs in size. Addr, followed by len
|
|
|
|
// http://research.swtch.com/godata
|
|
|
|
|
|
|
|
// read len
|
|
|
|
val, err := thread.readMemory(addr+ptrsize, ptrsize)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
strlen := uintptr(binary.LittleEndian.Uint64(val))
|
|
|
|
|
|
|
|
// read addr
|
|
|
|
val, err = thread.readMemory(addr, ptrsize)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
addr = uintptr(binary.LittleEndian.Uint64(val))
|
2015-01-16 21:30:22 +00:00
|
|
|
|
2015-01-19 23:25:08 +00:00
|
|
|
val, err = thread.readMemory(addr, strlen)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-10-25 17:25:00 +00:00
|
|
|
return *(*string)(unsafe.Pointer(&val)), nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2014-12-30 02:59:52 +00:00
|
|
|
func (thread *ThreadContext) readIntSlice(addr uintptr, t *dwarf.StructType) (string, error) {
|
2014-10-25 15:48:14 +00:00
|
|
|
val, err := thread.readMemory(addr, uintptr(24))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
a := binary.LittleEndian.Uint64(val[:8])
|
|
|
|
l := binary.LittleEndian.Uint64(val[8:16])
|
|
|
|
c := binary.LittleEndian.Uint64(val[16:24])
|
|
|
|
|
2014-12-29 23:04:08 +00:00
|
|
|
val, err = thread.readMemory(uintptr(a), uintptr(uint64(ptrsize)*l))
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-12-30 02:59:52 +00:00
|
|
|
switch t.StructName {
|
|
|
|
case "[]int":
|
|
|
|
members := *(*[]int)(unsafe.Pointer(&val))
|
2014-12-30 03:05:22 +00:00
|
|
|
setSliceLength(unsafe.Pointer(&members), int(l))
|
2014-12-30 02:59:52 +00:00
|
|
|
return fmt.Sprintf("len: %d cap: %d %d", l, c, members), nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
2014-12-30 02:59:52 +00:00
|
|
|
return "", fmt.Errorf("Could not read slice")
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (thread *ThreadContext) readIntArray(addr uintptr, t *dwarf.ArrayType) (string, error) {
|
|
|
|
val, err := thread.readMemory(addr, uintptr(t.ByteSize))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-12-29 04:37:18 +00:00
|
|
|
switch t.Type.Size() {
|
|
|
|
case 4:
|
|
|
|
members := *(*[]uint32)(unsafe.Pointer(&val))
|
2014-12-30 03:05:22 +00:00
|
|
|
setSliceLength(unsafe.Pointer(&members), int(t.Count))
|
2014-12-29 22:59:41 +00:00
|
|
|
return fmt.Sprintf("%s %d", t, members), nil
|
2014-12-29 04:37:18 +00:00
|
|
|
case 8:
|
|
|
|
members := *(*[]uint64)(unsafe.Pointer(&val))
|
2014-12-30 03:05:22 +00:00
|
|
|
setSliceLength(unsafe.Pointer(&members), int(t.Count))
|
2014-12-29 22:59:41 +00:00
|
|
|
return fmt.Sprintf("%s %d", t, members), nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
2014-12-29 04:37:18 +00:00
|
|
|
return "", fmt.Errorf("Could not read array")
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 16:05:03 +00:00
|
|
|
func (thread *ThreadContext) readInt(addr uintptr, size int64) (string, error) {
|
|
|
|
var n int
|
|
|
|
|
|
|
|
val, err := thread.readMemory(addr, uintptr(size))
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-10-25 16:05:03 +00:00
|
|
|
switch size {
|
|
|
|
case 1:
|
|
|
|
n = int(val[0])
|
|
|
|
case 2:
|
|
|
|
n = int(binary.LittleEndian.Uint16(val))
|
|
|
|
case 4:
|
|
|
|
n = int(binary.LittleEndian.Uint32(val))
|
|
|
|
case 8:
|
|
|
|
n = int(binary.LittleEndian.Uint64(val))
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2014-10-25 16:05:03 +00:00
|
|
|
return strconv.Itoa(n), nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2014-10-26 17:44:26 +00:00
|
|
|
func (thread *ThreadContext) readFloat(addr uintptr, size int64) (string, error) {
|
|
|
|
val, err := thread.readMemory(addr, uintptr(size))
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(val)
|
|
|
|
|
2014-10-26 17:44:26 +00:00
|
|
|
switch size {
|
|
|
|
case 4:
|
|
|
|
n := float32(0)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &n)
|
|
|
|
return strconv.FormatFloat(float64(n), 'f', -1, int(size)*8), nil
|
|
|
|
case 8:
|
|
|
|
n := float64(0)
|
|
|
|
binary.Read(buf, binary.LittleEndian, &n)
|
|
|
|
return strconv.FormatFloat(n, 'f', -1, int(size)*8), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", fmt.Errorf("could not read float")
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (thread *ThreadContext) readMemory(addr uintptr, size uintptr) ([]byte, error) {
|
|
|
|
buf := make([]byte, size)
|
|
|
|
|
2014-12-09 16:51:17 +00:00
|
|
|
_, err := readMemory(thread.Id, addr, buf)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf, nil
|
|
|
|
}
|
2014-12-30 03:05:22 +00:00
|
|
|
|
2014-12-30 17:57:31 +00:00
|
|
|
// Fetches all variables of a specific type in the current function scope
|
|
|
|
func (thread *ThreadContext) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
|
|
|
pc, err := thread.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
reader := thread.Process.DwarfReader()
|
2014-12-30 17:57:31 +00:00
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
_, err = reader.SeekToFunction(pc)
|
|
|
|
if err != nil {
|
2014-12-30 17:57:31 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := make([]*Variable, 0)
|
|
|
|
|
2014-12-31 13:34:41 +00:00
|
|
|
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
|
2014-12-30 17:57:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == tag {
|
|
|
|
val, err := thread.extractVariableFromEntry(entry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
vars = append(vars, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return vars, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalVariables returns all local variables from the current function scope
|
|
|
|
func (thread *ThreadContext) LocalVariables() ([]*Variable, error) {
|
|
|
|
return thread.variablesByTag(dwarf.TagVariable)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FunctionArguments returns the name, value, and type of all current function arguments
|
|
|
|
func (thread *ThreadContext) FunctionArguments() ([]*Variable, error) {
|
|
|
|
return thread.variablesByTag(dwarf.TagFormalParameter)
|
|
|
|
}
|
|
|
|
|
2014-12-30 03:05:22 +00:00
|
|
|
// Sets the length of a slice.
|
|
|
|
func setSliceLength(ptr unsafe.Pointer, l int) {
|
|
|
|
lptr := (*int)(unsafe.Pointer(uintptr(ptr) + ptrsize))
|
2014-12-30 15:23:22 +00:00
|
|
|
*lptr = l
|
2014-12-30 03:05:22 +00:00
|
|
|
}
|