delve/proctl/variables.go

967 lines
24 KiB
Go
Raw Normal View History

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"
"github.com/derekparker/delve/dwarf/reader"
2014-10-25 15:48:14 +00:00
)
const (
maxVariableRecurse = 1
maxArrayValues = 64
)
2014-10-25 15:48:14 +00:00
type Variable struct {
Name string
Value string
Type string
}
type M struct {
procid int
spinning uint8
blocked uint8
curg uintptr
}
type G struct {
id int
pc uint64
}
2014-12-20 05:10:32 +00:00
const ptrsize uintptr = unsafe.Sizeof(int(1))
// 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
}
2014-12-20 05:10:32 +00:00
mptr, err := thread.readMemory(uintptr(allmaddr), ptrsize)
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
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
}
2014-12-20 05:10:32 +00:00
curgBytes, err := thread.readMemory(uintptr(curgAddr), 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
}
2014-12-20 05:10:32 +00:00
procidBytes, err := thread.readMemory(uintptr(procidAddr), 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 {
2014-12-02 18:42:17 +00:00
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
}
2014-12-20 05:10:32 +00:00
mptr, err = thread.readMemory(uintptr(alllinkAddr), 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
}
func (dbp *DebuggedProcess) PrintGoroutinesInfo() error {
reader := dbp.dwarf.Reader()
allglen, err := allglenval(dbp, reader)
if err != nil {
return err
}
2014-11-13 14:36:20 +00:00
reader.Seek(0)
allgentryaddr, err := addressFor(dbp, "runtime.allg", reader)
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)
allg := binary.LittleEndian.Uint64(faddr)
for i := uint64(0); i < allglen; i++ {
g, err := parseG(dbp, allg+(i*uint64(ptrsize)), reader)
if err != nil {
return err
}
f, l, fn := dbp.goSymTable.PCToLine(g.pc)
fname := ""
if fn != nil {
fname = fn.Name
}
fmt.Printf("Goroutine %d - %s:%d %s\n", g.id, f, l, fname)
}
return nil
}
func parseG(dbp *DebuggedProcess, addr uint64, reader *dwarf.Reader) (*G, error) {
2014-12-20 05:10:32 +00:00
gaddrbytes, err := dbp.CurrentThread.readMemory(uintptr(addr), ptrsize)
if err != nil {
return nil, fmt.Errorf("error derefing *G %s", err)
}
initialInstructions := append([]byte{op.DW_OP_addr}, gaddrbytes...)
reader.Seek(0)
goidaddr, err := offsetFor(dbp, "goid", reader, initialInstructions)
if err != nil {
return nil, err
}
reader.Seek(0)
schedaddr, err := offsetFor(dbp, "sched", reader, initialInstructions)
if err != nil {
return nil, err
}
2014-12-20 05:10:32 +00:00
goidbytes, err := dbp.CurrentThread.readMemory(uintptr(goidaddr), ptrsize)
if err != nil {
return nil, 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)
if err != nil {
return nil, fmt.Errorf("error reading sched %s", err)
}
gopc := binary.LittleEndian.Uint64(schedbytes)
return &G{id: int(binary.LittleEndian.Uint64(goidbytes)), pc: gopc}, 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
}
func offsetFor(dbp *DebuggedProcess, 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
}
2014-10-25 15:48:14 +00:00
// Returns the value of the named symbol.
func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
pc, err := thread.CurrentPC()
if err != nil {
return nil, err
}
reader := thread.Process.DwarfReader()
_, err = reader.SeekToFunction(pc)
if err != nil {
2014-12-02 22:42:15 +00:00
return nil, err
}
varName := name
memberName := ""
if strings.Contains(name, ".") {
idx := strings.Index(name, ".")
varName = name[:idx]
memberName = name[idx+1:]
}
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
if err != nil {
return nil, err
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok {
continue
}
if n == varName {
if len(memberName) == 0 {
return thread.extractVariableFromEntry(entry)
}
return thread.evaluateStructMember(entry, reader, memberName)
}
}
return nil, fmt.Errorf("could not find symbol value for %s", name)
}
2014-10-25 15:48:14 +00:00
// 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)
}
// PackageVariables returns the name, value, and type of all package variables in the application.
func (thread *ThreadContext) PackageVariables() ([]*Variable, error) {
reader := thread.Process.DwarfReader()
vars := make([]*Variable, 0)
for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
if err != nil {
return nil, err
}
// Ignore errors trying to extract values
val, err := thread.extractVariableFromEntry(entry)
if err != nil {
continue
}
vars = append(vars, val)
}
return vars, nil
}
func findDwarfEntry(name string, reader *dwarf.Reader, member bool) (*dwarf.Entry, error) {
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
}
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
}
2014-10-25 15:48:14 +00:00
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok || n != name {
continue
}
return entry, nil
2014-10-25 15:48:14 +00:00
}
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)
if err != nil {
return nil, err
}
2015-01-02 16:09:32 +00:00
// Get parent variable name
parentName, ok := parentEntry.Val(dwarf.AttrName).(string)
if !ok {
return nil, fmt.Errorf("unable to retrive variable name")
}
// Seek reader to the type information so members can be iterated
_, err = reader.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() {
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
if parentAddr == 0 {
return nil, fmt.Errorf("%s is nil", parentName)
}
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
}
baseAddr := make([]byte, 8)
binary.LittleEndian.PutUint64(baseAddr, uint64(parentAddr))
parentInstructions := append([]byte{op.DW_OP_addr}, baseAddr...)
2015-01-20 05:32:08 +00:00
val, err := thread.extractValue(append(parentInstructions, memberInstr...), 0, t, true)
if err != nil {
return nil, err
}
return &Variable{Name: strings.Join([]string{parentName, memberName}, "."), Type: t.String(), Value: val}, nil
}
}
return nil, fmt.Errorf("%s has no member %s", parentName, memberName)
}
// 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")
}
2015-01-20 05:32:08 +00:00
val, err := thread.extractValue(instructions, 0, t, true)
if err != nil {
return nil, err
}
return &Variable{Name: n, Type: t.String(), Value: val}, nil
}
// 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
fde, err := thread.Process.frameEntries.FDEForPC(regs.PC())
if err != nil {
return 0, err
}
fctx := fde.EstablishFrame(regs.PC())
cfa := fctx.CFAOffset() + int64(regs.SP())
address, err := op.ExecuteStackProgram(cfa, instructions)
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
for typeEntry, err := reader.SeekToType(entry, true, false); typeEntry != nil; typeEntry, err = reader.SeekToType(typeEntry, true, false) {
if err != nil {
return 0, err
}
if typeEntry.Tag != dwarf.TagPointerType {
break
}
ptraddress := uintptr(address)
ptr, err := thread.readMemory(ptraddress, ptrsize)
if err != nil {
return 0, err
}
address = int64(binary.LittleEndian.Uint64(ptr))
}
2014-10-25 15:48:14 +00:00
return address, nil
}
2014-10-25 15:48:14 +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.
2015-01-20 05:32:08 +00:00
func (thread *ThreadContext) extractValue(instructions []byte, addr int64, typ interface{}, printStructName bool) (string, error) {
return thread.extractValueInternal(instructions, addr, typ, printStructName, 0)
}
func (thread *ThreadContext) extractValueInternal(instructions []byte, addr int64, typ interface{}, printStructName bool, recurseLevel int) (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.
for {
if tt, ok := typ.(*dwarf.TypedefType); ok {
typ = tt.Type
} else {
break
}
2014-10-25 15:48:14 +00:00
}
ptraddress := uintptr(addr)
2014-10-25 15:48:14 +00:00
switch t := typ.(type) {
case *dwarf.PtrType:
ptr, err := thread.readMemory(ptraddress, ptrsize)
2014-10-25 15:48:14 +00:00
if err != nil {
return "", err
}
intaddr := int64(binary.LittleEndian.Uint64(ptr))
if intaddr == 0 {
return fmt.Sprintf("%s nil", t.String()), nil
}
// Don't increase the recursion level when dereferencing pointers
val, err := thread.extractValueInternal(nil, intaddr, t.Type, printStructName, recurseLevel)
2014-10-25 15:48:14 +00:00
if err != nil {
return "", err
}
return fmt.Sprintf("*%s", val), nil
2014-10-25 15:48:14 +00:00
case *dwarf.StructType:
2015-01-20 06:37:52 +00:00
switch {
case t.StructName == "string":
return thread.readString(ptraddress)
2015-01-20 06:37:52 +00:00
case strings.HasPrefix(t.StructName, "[]"):
return thread.readSlice(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.
if recurseLevel <= maxVariableRecurse {
fields := make([]string, 0, len(t.Field))
for _, field := range t.Field {
val, err := thread.extractValueInternal(nil, field.ByteOffset+addr, field.Type, printStructName, recurseLevel+1)
if err != nil {
return "", err
}
fields = append(fields, fmt.Sprintf("%s: %s", field.Name, val))
2014-10-25 15:48:14 +00:00
}
if printStructName {
return fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", ")), nil
}
return fmt.Sprintf("{%s}", strings.Join(fields, ", ")), nil
2014-10-25 15:48:14 +00:00
}
// no fields
2015-01-20 05:32:08 +00:00
if printStructName {
return fmt.Sprintf("%s {...}", t.StructName), nil
2015-01-20 05:32:08 +00:00
}
return "{...}", nil
2014-10-25 15:48:14 +00:00
}
case *dwarf.ArrayType:
2015-01-20 05:32:08 +00:00
return thread.readArray(ptraddress, t)
2014-10-25 15:48:14 +00:00
case *dwarf.IntType:
return thread.readInt(ptraddress, t.ByteSize)
2015-01-20 03:18:17 +00:00
case *dwarf.UintType:
return thread.readUint(ptraddress, t.ByteSize)
2014-10-25 15:48:14 +00:00
case *dwarf.FloatType:
return thread.readFloat(ptraddress, t.ByteSize)
2015-01-20 03:18:17 +00:00
case *dwarf.BoolType:
return thread.readBool(ptraddress)
case *dwarf.FuncType:
return thread.readFunctionPtr(ptraddress)
case *dwarf.VoidType:
return "(void)", nil
case *dwarf.UnspecifiedType:
return "(unknown)", nil
default:
fmt.Printf("Unknown type: %T\n", t)
2014-10-25 15:48:14 +00:00
}
return "", fmt.Errorf("could not find value for type %s", typ)
}
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))
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
}
2015-01-20 06:37:52 +00:00
func (thread *ThreadContext) readSlice(addr uintptr, t *dwarf.StructType) (string, error) {
var sliceLen, sliceCap int64
var arrayAddr uintptr
var arrayType dwarf.Type
for _, f := range t.Field {
switch f.Name {
case "array":
val, err := thread.readMemory(addr+uintptr(f.ByteOffset), ptrsize)
if err != nil {
return "", err
}
arrayAddr = uintptr(binary.LittleEndian.Uint64(val))
// Dereference array type to get value type
ptrType, ok := f.Type.(*dwarf.PtrType)
if !ok {
return "", fmt.Errorf("Invalid type %s in slice array", f.Type)
}
arrayType = ptrType.Type
case "len":
lstr, err := thread.extractValue(nil, int64(addr+uintptr(f.ByteOffset)), f.Type, true)
if err != nil {
return "", err
}
sliceLen, err = strconv.ParseInt(lstr, 10, 64)
if err != nil {
return "", err
}
case "cap":
cstr, err := thread.extractValue(nil, int64(addr+uintptr(f.ByteOffset)), f.Type, true)
if err != nil {
return "", err
}
sliceCap, err = strconv.ParseInt(cstr, 10, 64)
if err != nil {
return "", err
}
}
}
stride := arrayType.Size()
if _, ok := arrayType.(*dwarf.PtrType); ok {
stride = int64(ptrsize)
}
vals, err := thread.readArrayValues(arrayAddr, sliceLen, stride, arrayType)
2014-10-25 15:48:14 +00:00
if err != nil {
return "", err
}
2015-01-20 06:37:52 +00:00
return fmt.Sprintf("[]%s len: %d, cap: %d, [%s]", arrayType, sliceLen, sliceCap, strings.Join(vals, ",")), nil
}
2014-10-25 15:48:14 +00:00
2015-01-20 06:37:52 +00:00
func (thread *ThreadContext) readArray(addr uintptr, t *dwarf.ArrayType) (string, error) {
if t.Count > 0 {
vals, err := thread.readArrayValues(addr, t.Count, t.ByteSize/t.Count, t.Type)
if err != nil {
return "", err
}
return fmt.Sprintf("%s [%s]", t, strings.Join(vals, ",")), nil
2014-10-25 15:48:14 +00:00
}
// because you can declare a zero-size array
return fmt.Sprintf("%s []", t), nil
2014-10-25 15:48:14 +00:00
}
2015-01-20 06:37:52 +00:00
func (thread *ThreadContext) readArrayValues(addr uintptr, count int64, stride int64, t dwarf.Type) ([]string, error) {
2015-01-20 05:32:08 +00:00
vals := make([]string, 0)
2014-10-25 15:48:14 +00:00
2015-01-20 06:37:52 +00:00
for i := int64(0); i < count; i++ {
// Cap number of elements
if i >= maxArrayValues {
vals = append(vals, fmt.Sprintf("...+%d more", count-maxArrayValues))
break
}
2015-01-20 06:37:52 +00:00
val, err := thread.extractValue(nil, int64(addr+uintptr(i*stride)), t, false)
2015-01-20 05:32:08 +00:00
if err != nil {
2015-01-20 06:37:52 +00:00
return nil, err
2015-01-20 05:32:08 +00:00
}
vals = append(vals, val)
2014-10-25 15:48:14 +00:00
}
2015-01-20 06:37:52 +00:00
return vals, nil
2014-10-25 15:48:14 +00:00
}
func (thread *ThreadContext) readInt(addr uintptr, size int64) (string, error) {
2015-01-20 03:18:17 +00:00
var n int64
val, err := thread.readMemory(addr, uintptr(size))
2014-10-25 15:48:14 +00:00
if err != nil {
return "", err
}
switch size {
case 1:
2015-01-20 03:18:17 +00:00
n = int64(val[0])
case 2:
2015-01-20 03:18:17 +00:00
n = int64(binary.LittleEndian.Uint16(val))
case 4:
2015-01-20 03:18:17 +00:00
n = int64(binary.LittleEndian.Uint32(val))
case 8:
2015-01-20 03:18:17 +00:00
n = int64(binary.LittleEndian.Uint64(val))
}
2014-10-25 15:48:14 +00:00
2015-01-20 03:18:17 +00:00
return strconv.FormatInt(n, 10), nil
}
func (thread *ThreadContext) readUint(addr uintptr, size int64) (string, error) {
var n uint64
val, err := thread.readMemory(addr, uintptr(size))
if err != nil {
return "", err
}
switch size {
case 1:
n = uint64(val[0])
case 2:
n = uint64(binary.LittleEndian.Uint16(val))
case 4:
n = uint64(binary.LittleEndian.Uint32(val))
case 8:
n = uint64(binary.LittleEndian.Uint64(val))
}
return strconv.FormatUint(n, 10), 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
}
2015-01-20 03:18:17 +00:00
func (thread *ThreadContext) readBool(addr uintptr) (string, error) {
val, err := thread.readMemory(addr, uintptr(1))
if err != nil {
return "", err
}
if val[0] == 0 {
return "false", nil
}
return "true", nil
}
func (thread *ThreadContext) readFunctionPtr(addr uintptr) (string, error) {
val, err := thread.readMemory(addr, ptrsize)
if err != nil {
return "", err
}
// dereference pointer to find function pc
addr = uintptr(binary.LittleEndian.Uint64(val))
if addr == 0 {
return "nil", nil
}
val, err = thread.readMemory(addr, ptrsize)
if err != nil {
return "", err
}
funcAddr := binary.LittleEndian.Uint64(val)
reader := thread.Process.DwarfReader()
entry, err := reader.SeekToFunction(funcAddr)
if err != nil {
return "", err
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok {
return "", fmt.Errorf("Unable to retrieve function name")
}
return n, nil
}
2014-10-25 15:48:14 +00:00
func (thread *ThreadContext) readMemory(addr uintptr, size uintptr) ([]byte, error) {
buf := make([]byte, size)
2015-01-14 02:37:10 +00:00
_, err := readMemory(thread, 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
// 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
}
reader := thread.Process.DwarfReader()
_, err = reader.SeekToFunction(pc)
if err != nil {
return nil, err
}
vars := make([]*Variable, 0)
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
if err != nil {
return nil, err
}
if entry.Tag == tag {
val, err := thread.extractVariableFromEntry(entry)
if err != nil {
2015-01-20 03:18:17 +00:00
// skip variables that we can't parse yet
continue
}
vars = append(vars, val)
}
}
return vars, nil
}
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
}