2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2014-10-25 15:48:14 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2014-11-24 23:27:56 +00:00
|
|
|
"debug/dwarf"
|
2015-04-09 14:53:02 +00:00
|
|
|
"debug/gosym"
|
2014-10-25 15:48:14 +00:00
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2015-09-28 10:01:18 +00:00
|
|
|
"go/ast"
|
|
|
|
"go/parser"
|
|
|
|
"go/token"
|
2014-10-25 15:48:14 +00:00
|
|
|
"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
|
|
|
)
|
|
|
|
|
2015-02-02 07:13:42 +00:00
|
|
|
const (
|
2015-04-22 13:18:25 +00:00
|
|
|
maxVariableRecurse = 1 // How far to recurse when evaluating nested types.
|
|
|
|
maxArrayValues = 64 // Max value for reading large arrays.
|
2015-09-20 06:27:25 +00:00
|
|
|
maxErrCount = 3 // Max number of read errors to accept while evaluating slices, arrays and structs
|
2015-04-19 22:11:33 +00:00
|
|
|
|
|
|
|
ChanRecv = "chan receive"
|
|
|
|
ChanSend = "chan send"
|
2015-02-02 07:13:42 +00:00
|
|
|
)
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
// Represents a variable.
|
2014-10-25 15:48:14 +00:00
|
|
|
type Variable struct {
|
2015-09-28 09:17:27 +00:00
|
|
|
Addr uintptr
|
|
|
|
Name string
|
|
|
|
Value string
|
|
|
|
Type string
|
|
|
|
dwarfType dwarf.Type
|
|
|
|
thread *Thread
|
|
|
|
|
|
|
|
Len int64
|
|
|
|
Cap int64
|
|
|
|
base uintptr
|
|
|
|
stride int64
|
|
|
|
fieldType dwarf.Type
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-04-22 13:18:25 +00:00
|
|
|
// Represents a runtime M (OS thread) structure.
|
2014-11-27 02:35:53 +00:00
|
|
|
type M struct {
|
2015-04-22 13:18:25 +00:00
|
|
|
procid int // Thread ID or port.
|
|
|
|
spinning uint8 // Busy looping.
|
|
|
|
blocked uint8 // Waiting on futex / semaphore.
|
|
|
|
curg uintptr // Current G running on this thread.
|
2014-11-27 02:35:53 +00:00
|
|
|
}
|
|
|
|
|
2015-09-17 17:42:05 +00:00
|
|
|
const (
|
|
|
|
// G status, from: src/runtime/runtime2.go
|
|
|
|
Gidle uint64 = iota // 0
|
|
|
|
Grunnable // 1 runnable and on a run queue
|
|
|
|
Grunning // 2
|
|
|
|
Gsyscall // 3
|
|
|
|
Gwaiting // 4
|
|
|
|
Gmoribund_unused // 5 currently unused, but hardcoded in gdb scripts
|
|
|
|
Gdead // 6
|
|
|
|
Genqueue // 7 Only the Gscanenqueue is used.
|
|
|
|
Gcopystack // 8 in this state when newstack is moving the stack
|
|
|
|
)
|
|
|
|
|
2015-04-22 13:18:25 +00:00
|
|
|
// Represents a runtime G (goroutine) structure (at least the
|
|
|
|
// fields that Delve is interested in).
|
2015-03-28 01:12:07 +00:00
|
|
|
type G struct {
|
2015-04-22 13:18:25 +00:00
|
|
|
Id int // Goroutine ID
|
|
|
|
PC uint64 // PC of goroutine when it was parked.
|
|
|
|
SP uint64 // SP of goroutine when it was parked.
|
|
|
|
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
|
|
|
WaitReason string // Reason for goroutine being parked.
|
2015-09-17 17:42:05 +00:00
|
|
|
Status uint64
|
2015-04-22 13:18:25 +00:00
|
|
|
|
|
|
|
// Information on goroutine location.
|
|
|
|
File string
|
|
|
|
Line int
|
|
|
|
Func *gosym.Func
|
2015-05-09 17:44:38 +00:00
|
|
|
|
|
|
|
// PC of entry to top-most deferred function.
|
|
|
|
DeferPC uint64
|
2015-06-17 17:11:57 +00:00
|
|
|
|
|
|
|
// Thread that this goroutine is currently allocated to
|
|
|
|
thread *Thread
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
// Scope for variable evaluation
|
|
|
|
type EvalScope struct {
|
|
|
|
Thread *Thread
|
|
|
|
PC uint64
|
|
|
|
CFA int64
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread) (*Variable, error) {
|
|
|
|
v := &Variable{
|
|
|
|
Name: name,
|
|
|
|
Addr: addr,
|
|
|
|
dwarfType: dwarfType,
|
|
|
|
thread: thread,
|
|
|
|
Type: dwarfType.String(),
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t := dwarfType.(type) {
|
|
|
|
case *dwarf.StructType:
|
|
|
|
if strings.HasPrefix(t.StructName, "[]") {
|
|
|
|
err := v.loadSliceInfo(t)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *dwarf.ArrayType:
|
|
|
|
v.base = v.Addr
|
|
|
|
v.Len = t.Count
|
|
|
|
v.Cap = -1
|
|
|
|
v.fieldType = t.Type
|
|
|
|
v.stride = 0
|
|
|
|
|
|
|
|
if t.Count > 0 {
|
|
|
|
v.stride = t.ByteSize / t.Count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
|
2015-10-07 01:21:28 +00:00
|
|
|
if v.Addr == 0 {
|
|
|
|
return nil, fmt.Errorf("%s is nil", v.Name)
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
name := ""
|
|
|
|
if v.Name != "" {
|
2015-10-01 00:36:36 +00:00
|
|
|
parts := strings.Split(field.Name, ".")
|
|
|
|
if len(parts) > 1 {
|
|
|
|
name = fmt.Sprintf("%s.%s", v.Name, parts[1])
|
|
|
|
} else {
|
2015-10-07 01:21:28 +00:00
|
|
|
name = fmt.Sprintf("%s.%s", v.Name, field.Name)
|
2015-10-01 00:36:36 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
}
|
|
|
|
return newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type, v.thread)
|
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
func (scope *EvalScope) DwarfReader() *reader.Reader {
|
|
|
|
return scope.Thread.dbp.DwarfReader()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) {
|
|
|
|
return scope.Thread.dbp.dwarf.Type(offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *EvalScope) PtrSize() int {
|
|
|
|
return scope.Thread.dbp.arch.PtrSize()
|
|
|
|
}
|
|
|
|
|
2015-04-22 13:18:25 +00:00
|
|
|
// Returns whether the goroutine is blocked on
|
|
|
|
// a channel read operation.
|
2015-04-19 22:11:33 +00:00
|
|
|
func (g *G) ChanRecvBlocked() bool {
|
|
|
|
return g.WaitReason == ChanRecv
|
|
|
|
}
|
|
|
|
|
|
|
|
// chanRecvReturnAddr returns the address of the return from a channel read.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (g *G) chanRecvReturnAddr(dbp *Process) (uint64, error) {
|
2015-10-02 11:09:41 +00:00
|
|
|
locs, err := dbp.GoroutineStacktrace(g, 4)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
topLoc := locs[len(locs)-1]
|
2015-09-17 08:42:34 +00:00
|
|
|
return topLoc.Current.PC, nil
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 18:55:42 +00:00
|
|
|
// NoGError returned when a G could not be found
|
|
|
|
// for a specific thread.
|
2015-04-19 22:11:33 +00:00
|
|
|
type NoGError struct {
|
|
|
|
tid int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ng NoGError) Error() string {
|
|
|
|
return fmt.Sprintf("no G executing on thread %d", ng.tid)
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:51:23 +00:00
|
|
|
func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
2015-06-11 20:17:56 +00:00
|
|
|
initialInstructions := make([]byte, thread.dbp.arch.PtrSize()+1)
|
|
|
|
initialInstructions[0] = op.DW_OP_addr
|
|
|
|
binary.LittleEndian.PutUint64(initialInstructions[1:], gaddr)
|
|
|
|
if deref {
|
|
|
|
gaddrbytes, err := thread.readMemory(uintptr(gaddr), thread.dbp.arch.PtrSize())
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error derefing *G %s", err)
|
|
|
|
}
|
|
|
|
initialInstructions = append([]byte{op.DW_OP_addr}, gaddrbytes...)
|
|
|
|
gaddr = binary.LittleEndian.Uint64(gaddrbytes)
|
|
|
|
if gaddr == 0 {
|
|
|
|
return nil, NoGError{tid: thread.Id}
|
|
|
|
}
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
2014-11-08 14:02:31 +00:00
|
|
|
|
2015-05-27 17:16:45 +00:00
|
|
|
rdr := thread.dbp.DwarfReader()
|
2015-05-09 15:27:06 +00:00
|
|
|
rdr.Seek(0)
|
2015-05-09 17:44:38 +00:00
|
|
|
entry, err := rdr.SeekToTypeNamed("runtime.g")
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-05-09 15:27:06 +00:00
|
|
|
|
2015-05-09 17:44:38 +00:00
|
|
|
// Parse defer
|
|
|
|
deferAddr, err := rdr.AddrForMember("_defer", initialInstructions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var deferPC uint64
|
|
|
|
// Dereference *defer pointer
|
2015-05-27 17:16:45 +00:00
|
|
|
deferAddrBytes, err := thread.readMemory(uintptr(deferAddr), thread.dbp.arch.PtrSize())
|
2015-05-09 17:44:38 +00:00
|
|
|
if err != nil {
|
2015-06-11 20:17:56 +00:00
|
|
|
return nil, fmt.Errorf("error derefing defer %s", err)
|
2015-05-09 17:44:38 +00:00
|
|
|
}
|
|
|
|
if binary.LittleEndian.Uint64(deferAddrBytes) != 0 {
|
|
|
|
initialDeferInstructions := append([]byte{op.DW_OP_addr}, deferAddrBytes...)
|
|
|
|
_, err = rdr.SeekToTypeNamed("runtime._defer")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
deferPCAddr, err := rdr.AddrForMember("fn", initialDeferInstructions)
|
|
|
|
deferPC, err = thread.readUintRaw(uintptr(deferPCAddr), 8)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
deferPC, err = thread.readUintRaw(uintptr(deferPC), 8)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2015-06-11 20:17:56 +00:00
|
|
|
|
|
|
|
// Let's parse all of the members we care about in order so that
|
|
|
|
// we don't have to spend any extra time seeking.
|
|
|
|
|
|
|
|
err = rdr.SeekToEntry(entry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-05-09 15:27:06 +00:00
|
|
|
// Parse sched
|
|
|
|
schedAddr, err := rdr.AddrForMember("sched", initialInstructions)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
2015-05-09 15:27:06 +00:00
|
|
|
return nil, err
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
2015-05-09 15:27:06 +00:00
|
|
|
// From sched, let's parse PC and SP.
|
|
|
|
sp, err := thread.readUintRaw(uintptr(schedAddr), 8)
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
2015-03-28 01:12:07 +00:00
|
|
|
return nil, err
|
2014-11-26 02:37:43 +00:00
|
|
|
}
|
2015-05-27 17:16:45 +00:00
|
|
|
pc, err := thread.readUintRaw(uintptr(schedAddr+uint64(thread.dbp.arch.PtrSize())), 8)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
2015-05-09 15:27:06 +00:00
|
|
|
return nil, err
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
2015-09-17 17:42:05 +00:00
|
|
|
// Parse atomicstatus
|
|
|
|
atomicStatusAddr, err := rdr.AddrForMember("atomicstatus", initialInstructions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
atomicStatus, err := thread.readUintRaw(uintptr(atomicStatusAddr), 4)
|
2015-05-09 15:27:06 +00:00
|
|
|
// Parse goid
|
|
|
|
goidAddr, err := rdr.AddrForMember("goid", initialInstructions)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-05-09 15:27:06 +00:00
|
|
|
goid, err := thread.readIntRaw(uintptr(goidAddr), 8)
|
2015-04-19 22:11:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-05-09 15:27:06 +00:00
|
|
|
// Parse waitreason
|
|
|
|
waitReasonAddr, err := rdr.AddrForMember("waitreason", initialInstructions)
|
2014-11-26 02:37:43 +00:00
|
|
|
if err != nil {
|
2015-03-28 01:12:07 +00:00
|
|
|
return nil, err
|
2014-11-08 14:02:31 +00:00
|
|
|
}
|
2015-05-09 15:27:06 +00:00
|
|
|
waitreason, err := thread.readString(uintptr(waitReasonAddr))
|
2014-11-08 14:02:31 +00:00
|
|
|
if err != nil {
|
2015-05-09 15:27:06 +00:00
|
|
|
return nil, err
|
2014-11-08 14:02:31 +00:00
|
|
|
}
|
2015-05-09 15:27:06 +00:00
|
|
|
// Parse gopc
|
|
|
|
gopcAddr, err := rdr.AddrForMember("gopc", initialInstructions)
|
2014-11-26 02:37:43 +00:00
|
|
|
if err != nil {
|
2015-05-09 15:27:06 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
gopc, err := thread.readUintRaw(uintptr(gopcAddr), 8)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-11-26 02:37:43 +00:00
|
|
|
}
|
2015-04-19 22:11:33 +00:00
|
|
|
|
2015-09-17 03:59:30 +00:00
|
|
|
f, l, fn := thread.dbp.goSymTable.PCToLine(pc)
|
2015-04-09 14:53:02 +00:00
|
|
|
g := &G{
|
2015-05-09 15:27:06 +00:00
|
|
|
Id: int(goid),
|
|
|
|
GoPC: gopc,
|
|
|
|
PC: pc,
|
|
|
|
SP: sp,
|
2015-04-19 22:11:33 +00:00
|
|
|
File: f,
|
|
|
|
Line: l,
|
|
|
|
Func: fn,
|
|
|
|
WaitReason: waitreason,
|
2015-05-09 17:44:38 +00:00
|
|
|
DeferPC: deferPC,
|
2015-09-17 17:42:05 +00:00
|
|
|
Status: atomicStatus,
|
2015-04-09 14:53:02 +00:00
|
|
|
}
|
|
|
|
return g, nil
|
2014-11-08 14:02:31 +00:00
|
|
|
}
|
|
|
|
|
2015-09-29 05:25:40 +00:00
|
|
|
// Returns information for the named variable.
|
2015-09-28 09:17:27 +00:00
|
|
|
func (scope *EvalScope) ExtractVariableInfo(name string) (*Variable, error) {
|
2015-09-29 17:15:31 +00:00
|
|
|
parts := strings.Split(name, ".")
|
|
|
|
varName := parts[0]
|
|
|
|
memberNames := parts[1:]
|
2015-09-28 09:17:27 +00:00
|
|
|
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err := scope.extractVarInfo(varName)
|
2015-09-28 09:17:27 +00:00
|
|
|
if err != nil {
|
|
|
|
origErr := err
|
|
|
|
// Attempt to evaluate name as a package variable.
|
2015-09-29 17:15:31 +00:00
|
|
|
if len(memberNames) > 0 {
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err = scope.packageVarAddr(name)
|
2015-09-28 09:17:27 +00:00
|
|
|
} else {
|
|
|
|
_, _, fn := scope.Thread.dbp.PCToLine(scope.PC)
|
|
|
|
if fn != nil {
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err = scope.packageVarAddr(fn.PackageName() + "." + name)
|
2015-09-28 09:17:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, origErr
|
|
|
|
}
|
2015-09-29 05:25:40 +00:00
|
|
|
v.Name = name
|
2015-09-28 09:17:27 +00:00
|
|
|
} else {
|
2015-09-29 17:15:31 +00:00
|
|
|
for _, memberName := range memberNames {
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err = v.structMember(memberName)
|
2015-09-28 09:17:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-29 05:25:40 +00:00
|
|
|
return v, nil
|
2015-09-28 09:17:27 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 19:04:14 +00:00
|
|
|
// Returns the value of the named variable.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (scope *EvalScope) EvalVariable(name string) (*Variable, error) {
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err := scope.ExtractVariableInfo(name)
|
2015-09-28 09:17:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-29 05:25:40 +00:00
|
|
|
err = v.loadValue(true)
|
|
|
|
return v, err
|
2015-09-28 09:17:27 +00:00
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
|
2015-09-28 10:01:18 +00:00
|
|
|
// Sets the value of the named variable
|
|
|
|
func (scope *EvalScope) SetVariable(name, value string) error {
|
2015-09-29 05:59:39 +00:00
|
|
|
v, err := scope.ExtractVariableInfo(name)
|
2015-09-28 10:01:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-29 05:59:39 +00:00
|
|
|
return v.setValue(value)
|
2015-09-28 10:01:18 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
|
|
|
|
rdr := scope.DwarfReader()
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err := scope.extractVarInfoFromEntry(entry, rdr)
|
2014-12-31 13:34:41 +00:00
|
|
|
if err != nil {
|
2014-12-02 22:42:15 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-29 05:25:40 +00:00
|
|
|
err = v.loadValue(true)
|
|
|
|
return v, err
|
2015-09-28 09:17:27 +00:00
|
|
|
}
|
2014-11-11 03:26:13 +00:00
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
|
|
|
|
reader := scope.DwarfReader()
|
|
|
|
|
|
|
|
_, err := reader.SeekToFunction(scope.PC)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
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 {
|
2015-09-28 09:17:27 +00:00
|
|
|
return scope.extractVarInfoFromEntry(entry, reader)
|
2014-11-11 03:26:13 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", varName)
|
2014-11-11 03:26:13 +00:00
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-02-04 04:20:25 +00:00
|
|
|
// LocalVariables returns all local variables from the current function scope.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (scope *EvalScope) LocalVariables() ([]*Variable, error) {
|
|
|
|
return scope.variablesByTag(dwarf.TagVariable)
|
2015-02-04 04:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FunctionArguments returns the name, value, and type of all current function arguments.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (scope *EvalScope) FunctionArguments() ([]*Variable, error) {
|
|
|
|
return scope.variablesByTag(dwarf.TagFormalParameter)
|
2015-02-04 04:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PackageVariables returns the name, value, and type of all package variables in the application.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (scope *EvalScope) PackageVariables() ([]*Variable, error) {
|
|
|
|
reader := scope.DwarfReader()
|
2015-02-04 04:20:25 +00:00
|
|
|
|
|
|
|
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
|
2015-08-28 20:06:29 +00:00
|
|
|
val, err := scope.extractVariableFromEntry(entry)
|
2015-02-04 04:20:25 +00:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
vars = append(vars, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
return vars, nil
|
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
func (dbp *Process) EvalPackageVariable(name string) (*Variable, error) {
|
|
|
|
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
2015-07-23 17:08:28 +00:00
|
|
|
|
2015-09-29 05:25:40 +00:00
|
|
|
v, err := scope.packageVarAddr(name)
|
2015-09-28 09:17:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-29 05:25:40 +00:00
|
|
|
err = v.loadValue(true)
|
|
|
|
return v, err
|
2015-09-28 09:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *EvalScope) packageVarAddr(name string) (*Variable, error) {
|
|
|
|
reader := scope.DwarfReader()
|
2015-07-23 17:08:28 +00:00
|
|
|
for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if n == name {
|
2015-09-28 09:17:27 +00:00
|
|
|
return scope.extractVarInfoFromEntry(entry, reader)
|
2015-07-23 17:08:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) structMember(memberName string) (*Variable, error) {
|
|
|
|
structVar, err := v.maybeDereference()
|
|
|
|
structVar.Name = v.Name
|
2014-12-03 05:30:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
structVar = structVar.resolveTypedefs()
|
|
|
|
switch t := structVar.dwarfType.(type) {
|
|
|
|
case *dwarf.StructType:
|
|
|
|
for _, field := range t.Field {
|
|
|
|
if field.Name != memberName {
|
|
|
|
continue
|
2014-12-31 21:13:28 +00:00
|
|
|
}
|
2015-10-06 23:34:18 +00:00
|
|
|
return structVar.toField(field)
|
2015-10-01 00:36:36 +00:00
|
|
|
}
|
|
|
|
// Check for embedded field only if field was
|
|
|
|
// not a regular struct member
|
|
|
|
for _, field := range t.Field {
|
|
|
|
isEmbeddedStructMember :=
|
|
|
|
(field.Type.String() == ("struct " + field.Name)) ||
|
2015-10-07 01:21:28 +00:00
|
|
|
(len(field.Name) > 1 &&
|
|
|
|
field.Name[0] == '*' &&
|
|
|
|
field.Type.String()[1:] == ("struct "+field.Name[1:]))
|
2015-10-01 00:36:36 +00:00
|
|
|
if !isEmbeddedStructMember {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Check for embedded field referenced by type name
|
|
|
|
parts := strings.Split(field.Name, ".")
|
|
|
|
if len(parts) > 1 && parts[1] == memberName {
|
2015-10-06 23:34:18 +00:00
|
|
|
embeddedVar, err := structVar.toField(field)
|
2015-10-01 00:36:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return embeddedVar, nil
|
|
|
|
}
|
|
|
|
// Recursively check for promoted fields on the embedded field
|
2015-10-06 23:34:18 +00:00
|
|
|
embeddedVar, err := structVar.toField(field)
|
2015-10-01 00:36:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
embeddedVar.Name = structVar.Name
|
|
|
|
embeddedField, err := embeddedVar.structMember(memberName)
|
|
|
|
if embeddedField != nil {
|
|
|
|
return embeddedField, nil
|
2014-12-31 21:13:28 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
return nil, fmt.Errorf("%s has no member %s", v.Name, memberName)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("%s type %s is not a struct", v.Name, structVar.dwarfType)
|
2014-12-31 21:13:28 +00:00
|
|
|
}
|
2014-12-03 05:30:39 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
// Extracts the name and type of a variable from a dwarf entry
|
|
|
|
// then executes the instructions given in the DW_AT_location attribute to grab the variable's address
|
|
|
|
func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry, rdr *reader.Reader) (*Variable, error) {
|
2014-12-30 17:57:31 +00:00
|
|
|
if entry == nil {
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("invalid entry")
|
2014-12-30 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
2014-12-30 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
2014-12-30 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
t, err := scope.Type(offset)
|
2014-12-30 17:57:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
|
|
|
if !ok {
|
2015-05-04 22:31:13 +00:00
|
|
|
return nil, fmt.Errorf("type assertion failed")
|
2014-12-30 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
addr, err := op.ExecuteStackProgram(scope.CFA, instructions)
|
2014-12-30 17:57:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
return newVariable(n, uintptr(addr), t, scope.Thread)
|
2014-12-30 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
2015-09-29 05:25:40 +00:00
|
|
|
// If v is a pointer a new variable is returned containing the value pointed by v.
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) maybeDereference() (*Variable, error) {
|
|
|
|
v = v.resolveTypedefs()
|
2015-01-01 09:00:44 +00:00
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
switch t := v.dwarfType.(type) {
|
|
|
|
case *dwarf.PtrType:
|
|
|
|
ptrval, err := v.thread.readUintRaw(uintptr(v.Addr), int64(v.thread.dbp.arch.PtrSize()))
|
2014-12-31 07:37:27 +00:00
|
|
|
if err != nil {
|
2015-09-28 09:17:27 +00:00
|
|
|
return nil, err
|
2015-01-01 09:00:44 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
return newVariable("", uintptr(ptrval), t.Type, v.thread)
|
|
|
|
default:
|
|
|
|
return v, nil
|
2015-01-01 09:00:44 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-09-29 05:25:40 +00:00
|
|
|
// Returns a Variable with the same address but a concrete dwarfType.
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) resolveTypedefs() *Variable {
|
|
|
|
typ := v.dwarfType
|
2015-01-21 05:47:59 +00:00
|
|
|
for {
|
|
|
|
if tt, ok := typ.(*dwarf.TypedefType); ok {
|
|
|
|
typ = tt.Type
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
r := *v
|
|
|
|
r.dwarfType = typ
|
|
|
|
return &r
|
|
|
|
}
|
|
|
|
|
2015-09-29 05:25:40 +00:00
|
|
|
// Extracts the value of the variable at the given address.
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) loadValue(printStructName bool) (err error) {
|
|
|
|
v.Value, err = v.loadValueInternal(printStructName, 0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Variable) loadValueInternal(printStructName bool, recurseLevel int) (string, error) {
|
|
|
|
v = v.resolveTypedefs()
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
switch t := v.dwarfType.(type) {
|
2014-10-25 15:48:14 +00:00
|
|
|
case *dwarf.PtrType:
|
2015-09-28 09:17:27 +00:00
|
|
|
ptrv, err := v.maybeDereference()
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2015-01-01 09:00:44 +00:00
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
if ptrv.Addr == 0 {
|
2015-01-01 09:00:44 +00:00
|
|
|
return fmt.Sprintf("%s nil", t.String()), nil
|
|
|
|
}
|
|
|
|
|
2015-02-02 07:13:42 +00:00
|
|
|
// Don't increase the recursion level when dereferencing pointers
|
2015-09-28 09:17:27 +00:00
|
|
|
val, err := ptrv.loadValueInternal(printStructName, recurseLevel)
|
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:
|
2015-01-20 06:37:52 +00:00
|
|
|
switch {
|
|
|
|
case t.StructName == "string":
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.thread.readString(uintptr(v.Addr))
|
2015-01-20 06:37:52 +00:00
|
|
|
case strings.HasPrefix(t.StructName, "[]"):
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.loadArrayValues(recurseLevel)
|
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.
|
2015-02-02 07:13:42 +00:00
|
|
|
if recurseLevel <= maxVariableRecurse {
|
2015-09-20 06:27:25 +00:00
|
|
|
errcount := 0
|
2015-02-02 07:13:42 +00:00
|
|
|
fields := make([]string, 0, len(t.Field))
|
2015-09-20 06:27:25 +00:00
|
|
|
for i, field := range t.Field {
|
2015-09-29 05:25:40 +00:00
|
|
|
var (
|
|
|
|
err error
|
|
|
|
val string
|
|
|
|
fieldvar *Variable
|
|
|
|
)
|
2015-09-28 09:17:27 +00:00
|
|
|
|
|
|
|
fieldvar, err = v.toField(field)
|
|
|
|
if err == nil {
|
|
|
|
val, err = fieldvar.loadValueInternal(printStructName, recurseLevel+1)
|
|
|
|
}
|
2015-02-02 07:13:42 +00:00
|
|
|
if err != nil {
|
2015-09-20 06:27:25 +00:00
|
|
|
errcount++
|
|
|
|
val = fmt.Sprintf("<unreadable: %s>", err.Error())
|
2015-02-02 07:13:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fields = append(fields, fmt.Sprintf("%s: %s", field.Name, val))
|
2015-09-20 06:27:25 +00:00
|
|
|
|
|
|
|
if errcount > maxErrCount {
|
|
|
|
fields = append(fields, fmt.Sprintf("...+%d more", len(t.Field)-i))
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
2015-02-02 07:13:42 +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
|
|
|
}
|
2015-02-02 07:13:42 +00:00
|
|
|
// no fields
|
2015-01-20 05:32:08 +00:00
|
|
|
if printStructName {
|
2015-02-02 07:13:42 +00:00
|
|
|
return fmt.Sprintf("%s {...}", t.StructName), nil
|
2015-01-20 05:32:08 +00:00
|
|
|
}
|
2015-02-02 07:13:42 +00:00
|
|
|
return "{...}", nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
case *dwarf.ArrayType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.loadArrayValues(recurseLevel)
|
2015-07-30 15:07:08 +00:00
|
|
|
case *dwarf.ComplexType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.readComplex(t.ByteSize)
|
2014-10-25 15:48:14 +00:00
|
|
|
case *dwarf.IntType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.readInt(t.ByteSize)
|
2015-01-20 03:18:17 +00:00
|
|
|
case *dwarf.UintType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.readUint(t.ByteSize)
|
2014-10-25 15:48:14 +00:00
|
|
|
case *dwarf.FloatType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.readFloat(t.ByteSize)
|
2015-01-20 03:18:17 +00:00
|
|
|
case *dwarf.BoolType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.readBool()
|
2015-01-20 04:15:40 +00:00
|
|
|
case *dwarf.FuncType:
|
2015-09-28 09:17:27 +00:00
|
|
|
return v.readFunctionPtr()
|
2015-01-21 05:47:59 +00:00
|
|
|
case *dwarf.VoidType:
|
|
|
|
return "(void)", nil
|
|
|
|
case *dwarf.UnspecifiedType:
|
|
|
|
return "(unknown)", nil
|
2015-01-20 04:15:40 +00:00
|
|
|
default:
|
|
|
|
fmt.Printf("Unknown type: %T\n", t)
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
return "", fmt.Errorf("could not find value for type %s", v.dwarfType)
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 10:01:18 +00:00
|
|
|
func (v *Variable) setValue(value string) error {
|
|
|
|
v = v.resolveTypedefs()
|
|
|
|
|
|
|
|
switch t := v.dwarfType.(type) {
|
|
|
|
case *dwarf.PtrType:
|
|
|
|
return v.writeUint(false, value, int64(v.thread.dbp.arch.PtrSize()))
|
|
|
|
case *dwarf.ComplexType:
|
|
|
|
return v.writeComplex(value, t.ByteSize)
|
|
|
|
case *dwarf.IntType:
|
|
|
|
return v.writeUint(true, value, t.ByteSize)
|
|
|
|
case *dwarf.UintType:
|
|
|
|
return v.writeUint(false, value, t.ByteSize)
|
|
|
|
case *dwarf.FloatType:
|
|
|
|
return v.writeFloat(value, t.ByteSize)
|
|
|
|
case *dwarf.BoolType:
|
|
|
|
return v.writeBool(value)
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Can not set value of variables of type: %T\n", t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:51:23 +00:00
|
|
|
func (thread *Thread) readString(addr uintptr) (string, error) {
|
2015-01-19 23:25:08 +00:00
|
|
|
// string data structure is always two ptrs in size. Addr, followed by len
|
|
|
|
// http://research.swtch.com/godata
|
|
|
|
|
|
|
|
// read len
|
2015-05-27 17:16:45 +00:00
|
|
|
val, err := thread.readMemory(addr+uintptr(thread.dbp.arch.PtrSize()), thread.dbp.arch.PtrSize())
|
2015-01-19 23:25:08 +00:00
|
|
|
if err != nil {
|
2015-06-11 20:17:56 +00:00
|
|
|
return "", fmt.Errorf("could not read string len %s", err)
|
2015-01-19 23:25:08 +00:00
|
|
|
}
|
2015-04-29 17:07:27 +00:00
|
|
|
strlen := int(binary.LittleEndian.Uint64(val))
|
2015-09-20 06:27:25 +00:00
|
|
|
if strlen < 0 {
|
|
|
|
return "", fmt.Errorf("invalid length: %d", strlen)
|
|
|
|
}
|
|
|
|
|
|
|
|
count := strlen
|
|
|
|
if count > maxArrayValues {
|
|
|
|
count = maxArrayValues
|
|
|
|
}
|
2015-01-19 23:25:08 +00:00
|
|
|
|
|
|
|
// read addr
|
2015-05-27 17:16:45 +00:00
|
|
|
val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize())
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
2015-06-11 20:17:56 +00:00
|
|
|
return "", fmt.Errorf("could not read string pointer %s", err)
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
addr = uintptr(binary.LittleEndian.Uint64(val))
|
2015-05-07 14:34:34 +00:00
|
|
|
if addr == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
2015-01-16 21:30:22 +00:00
|
|
|
|
2015-09-20 06:27:25 +00:00
|
|
|
val, err = thread.readMemory(addr, count)
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
2015-06-11 20:17:56 +00:00
|
|
|
return "", fmt.Errorf("could not read string at %#v due to %s", addr, err)
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-20 06:27:25 +00:00
|
|
|
retstr := *(*string)(unsafe.Pointer(&val))
|
|
|
|
|
|
|
|
if count != strlen {
|
|
|
|
retstr = retstr + fmt.Sprintf("...+%d more", strlen-count)
|
|
|
|
}
|
|
|
|
|
|
|
|
return retstr, nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) loadSliceInfo(t *dwarf.StructType) error {
|
|
|
|
var err error
|
2015-01-20 06:37:52 +00:00
|
|
|
for _, f := range t.Field {
|
|
|
|
switch f.Name {
|
|
|
|
case "array":
|
2015-09-28 09:17:27 +00:00
|
|
|
var base uint64
|
|
|
|
base, err = v.thread.readUintRaw(uintptr(int64(v.Addr)+f.ByteOffset), int64(v.thread.dbp.arch.PtrSize()))
|
|
|
|
if err == nil {
|
|
|
|
v.base = uintptr(base)
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
v.fieldType = ptrType.Type
|
2015-01-20 06:37:52 +00:00
|
|
|
}
|
|
|
|
case "len":
|
2015-09-28 09:17:27 +00:00
|
|
|
lstrAddr, err := v.toField(f)
|
|
|
|
if err == nil {
|
|
|
|
err = lstrAddr.loadValue(true)
|
2015-01-20 06:37:52 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
if err == nil {
|
|
|
|
v.Len, err = strconv.ParseInt(lstrAddr.Value, 10, 64)
|
2015-01-20 06:37:52 +00:00
|
|
|
}
|
|
|
|
case "cap":
|
2015-09-28 09:17:27 +00:00
|
|
|
cstrAddr, err := v.toField(f)
|
|
|
|
if err == nil {
|
|
|
|
err = cstrAddr.loadValue(true)
|
2015-01-20 06:37:52 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
if err == nil {
|
|
|
|
v.Cap, err = strconv.ParseInt(cstrAddr.Value, 10, 64)
|
2015-01-20 06:37:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
2015-09-28 09:17:27 +00:00
|
|
|
return nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
v.stride = v.fieldType.Size()
|
|
|
|
if _, ok := v.fieldType.(*dwarf.PtrType); ok {
|
|
|
|
v.stride = int64(v.thread.dbp.arch.PtrSize())
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
|
|
|
|
return nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) loadArrayValues(recurseLevel int) (string, error) {
|
2015-01-20 05:32:08 +00:00
|
|
|
vals := make([]string, 0)
|
2015-09-20 06:27:25 +00:00
|
|
|
errcount := 0
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
for i := int64(0); i < v.Len; i++ {
|
2015-02-02 07:13:42 +00:00
|
|
|
// Cap number of elements
|
|
|
|
if i >= maxArrayValues {
|
2015-09-28 09:17:27 +00:00
|
|
|
vals = append(vals, fmt.Sprintf("...+%d more", v.Len-maxArrayValues))
|
2015-02-02 07:13:42 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
var val string
|
|
|
|
fieldvar, err := newVariable("", uintptr(int64(v.base)+(i*v.stride)), v.fieldType, v.thread)
|
|
|
|
if err == nil {
|
|
|
|
val, err = fieldvar.loadValueInternal(false, recurseLevel+1)
|
|
|
|
}
|
2015-01-20 05:32:08 +00:00
|
|
|
if err != nil {
|
2015-09-20 06:27:25 +00:00
|
|
|
errcount++
|
|
|
|
val = fmt.Sprintf("<unreadable: %s>", err.Error())
|
2015-01-20 05:32:08 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
|
2015-01-20 05:32:08 +00:00
|
|
|
vals = append(vals, val)
|
2015-09-20 06:27:25 +00:00
|
|
|
if errcount > maxErrCount {
|
2015-09-28 09:17:27 +00:00
|
|
|
vals = append(vals, fmt.Sprintf("...+%d more", v.Len-i))
|
2015-09-20 06:27:25 +00:00
|
|
|
break
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
|
|
|
|
if v.Cap < 0 {
|
|
|
|
return fmt.Sprintf("%s [%s]", v.dwarfType, strings.Join(vals, ",")), nil
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("[]%s len: %d, cap: %d, [%s]", v.fieldType, v.Len, v.Cap, strings.Join(vals, ",")), nil
|
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) readComplex(size int64) (string, error) {
|
2015-07-30 15:07:08 +00:00
|
|
|
var fs int64
|
|
|
|
switch size {
|
|
|
|
case 8:
|
|
|
|
fs = 4
|
|
|
|
case 16:
|
|
|
|
fs = 8
|
|
|
|
default:
|
|
|
|
return "", fmt.Errorf("invalid size (%d) for complex type", size)
|
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
r, err := v.readFloat(fs)
|
2015-07-30 15:07:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
imagvar := *v
|
|
|
|
imagvar.Addr += uintptr(fs)
|
|
|
|
i, err := imagvar.readFloat(fs)
|
2015-07-30 15:07:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2015-09-28 09:17:27 +00:00
|
|
|
return fmt.Sprintf("(%s + %si)", r, i), nil
|
2015-07-30 15:07:08 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 10:01:18 +00:00
|
|
|
func (v *Variable) writeComplex(value string, size int64) error {
|
|
|
|
var real, imag float64
|
|
|
|
|
|
|
|
expr, err := parser.ParseExpr(value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var lits []*ast.BasicLit
|
|
|
|
|
|
|
|
if e, ok := expr.(*ast.ParenExpr); ok {
|
|
|
|
expr = e.X
|
|
|
|
}
|
|
|
|
|
|
|
|
switch e := expr.(type) {
|
|
|
|
case *ast.BinaryExpr: // "<float> + <float>i" or "<float>i + <float>"
|
|
|
|
x, xok := e.X.(*ast.BasicLit)
|
|
|
|
y, yok := e.Y.(*ast.BasicLit)
|
|
|
|
if e.Op != token.ADD || !xok || !yok {
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
lits = []*ast.BasicLit{x, y}
|
|
|
|
case *ast.CallExpr: // "complex(<float>, <float>)"
|
|
|
|
tname, ok := e.Fun.(*ast.Ident)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
if (tname.Name != "complex64") && (tname.Name != "complex128") {
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
if len(e.Args) != 2 {
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
for i := range e.Args {
|
|
|
|
lit, ok := e.Args[i].(*ast.BasicLit)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
lits = append(lits, lit)
|
|
|
|
}
|
|
|
|
lits[1].Kind = token.IMAG
|
|
|
|
lits[1].Value = lits[1].Value + "i"
|
|
|
|
case *ast.BasicLit: // "<float>" or "<float>i"
|
|
|
|
lits = []*ast.BasicLit{e}
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, lit := range lits {
|
|
|
|
var err error
|
|
|
|
var v float64
|
|
|
|
switch lit.Kind {
|
|
|
|
case token.FLOAT, token.INT:
|
|
|
|
v, err = strconv.ParseFloat(lit.Value, int(size/2))
|
|
|
|
real += v
|
|
|
|
case token.IMAG:
|
|
|
|
v, err = strconv.ParseFloat(lit.Value[:len(lit.Value)-1], int(size/2))
|
|
|
|
imag += v
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Not a complex constant: %s", value)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v.writeFloatRaw(real, int64(size/2))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
imagaddr := *v
|
|
|
|
imagaddr.Addr += uintptr(size / 2)
|
|
|
|
return imagaddr.writeFloatRaw(imag, int64(size/2))
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) readInt(size int64) (string, error) {
|
|
|
|
n, err := v.thread.readIntRaw(v.Addr, size)
|
2015-05-09 15:27:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return strconv.FormatInt(n, 10), nil
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:51:23 +00:00
|
|
|
func (thread *Thread) readIntRaw(addr uintptr, size int64) (int64, error) {
|
2015-01-20 03:18:17 +00:00
|
|
|
var n int64
|
2014-10-25 16:05:03 +00:00
|
|
|
|
2015-04-29 17:07:27 +00:00
|
|
|
val, err := thread.readMemory(addr, int(size))
|
2014-10-25 15:48:14 +00:00
|
|
|
if err != nil {
|
2015-05-09 15:27:06 +00:00
|
|
|
return 0, err
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 16:05:03 +00:00
|
|
|
switch size {
|
|
|
|
case 1:
|
2015-01-20 03:18:17 +00:00
|
|
|
n = int64(val[0])
|
2014-10-25 16:05:03 +00:00
|
|
|
case 2:
|
2015-01-20 03:18:17 +00:00
|
|
|
n = int64(binary.LittleEndian.Uint16(val))
|
2014-10-25 16:05:03 +00:00
|
|
|
case 4:
|
2015-01-20 03:18:17 +00:00
|
|
|
n = int64(binary.LittleEndian.Uint32(val))
|
2014-10-25 16:05:03 +00:00
|
|
|
case 8:
|
2015-01-20 03:18:17 +00:00
|
|
|
n = int64(binary.LittleEndian.Uint64(val))
|
2014-10-25 16:05:03 +00:00
|
|
|
}
|
2014-10-25 15:48:14 +00:00
|
|
|
|
2015-05-09 15:27:06 +00:00
|
|
|
return n, nil
|
2015-01-20 03:18:17 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) readUint(size int64) (string, error) {
|
|
|
|
n, err := v.thread.readUintRaw(v.Addr, size)
|
2015-05-09 15:27:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return strconv.FormatUint(n, 10), nil
|
|
|
|
}
|
|
|
|
|
2015-09-28 10:01:18 +00:00
|
|
|
func (v *Variable) writeUint(signed bool, value string, size int64) error {
|
2015-09-29 05:59:39 +00:00
|
|
|
var (
|
|
|
|
n uint64
|
|
|
|
err error
|
|
|
|
)
|
2015-09-28 10:01:18 +00:00
|
|
|
if signed {
|
|
|
|
var m int64
|
|
|
|
m, err = strconv.ParseInt(value, 0, int(size*8))
|
|
|
|
n = uint64(m)
|
|
|
|
} else {
|
|
|
|
n, err = strconv.ParseUint(value, 0, int(size*8))
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
val := make([]byte, size)
|
|
|
|
|
|
|
|
switch size {
|
|
|
|
case 1:
|
|
|
|
val[0] = byte(n)
|
|
|
|
case 2:
|
|
|
|
binary.LittleEndian.PutUint16(val, uint16(n))
|
|
|
|
case 4:
|
|
|
|
binary.LittleEndian.PutUint32(val, uint32(n))
|
|
|
|
case 8:
|
|
|
|
binary.LittleEndian.PutUint64(val, uint64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = v.thread.writeMemory(v.Addr, val)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:51:23 +00:00
|
|
|
func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) {
|
2015-01-20 03:18:17 +00:00
|
|
|
var n uint64
|
|
|
|
|
2015-04-29 17:07:27 +00:00
|
|
|
val, err := thread.readMemory(addr, int(size))
|
2015-01-20 03:18:17 +00:00
|
|
|
if err != nil {
|
2015-05-09 15:27:06 +00:00
|
|
|
return 0, err
|
2015-01-20 03:18:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2015-05-09 15:27:06 +00:00
|
|
|
return n, nil
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) readFloat(size int64) (string, error) {
|
|
|
|
val, err := v.thread.readMemory(v.Addr, int(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
|
|
|
|
}
|
|
|
|
|
2015-05-04 22:31:13 +00:00
|
|
|
return "", fmt.Errorf("could not read float")
|
2014-10-25 15:48:14 +00:00
|
|
|
}
|
|
|
|
|
2015-09-28 10:01:18 +00:00
|
|
|
func (v *Variable) writeFloat(value string, size int64) error {
|
|
|
|
f, err := strconv.ParseFloat(value, int(size*8))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return v.writeFloatRaw(f, size)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Variable) writeFloatRaw(f float64, size int64) error {
|
|
|
|
buf := bytes.NewBuffer(make([]byte, 0, size))
|
|
|
|
|
|
|
|
switch size {
|
|
|
|
case 4:
|
|
|
|
n := float32(f)
|
|
|
|
binary.Write(buf, binary.LittleEndian, n)
|
|
|
|
case 8:
|
|
|
|
n := float64(f)
|
|
|
|
binary.Write(buf, binary.LittleEndian, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := v.thread.writeMemory(v.Addr, buf.Bytes())
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) readBool() (string, error) {
|
|
|
|
val, err := v.thread.readMemory(v.Addr, 1)
|
2015-01-20 03:18:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if val[0] == 0 {
|
|
|
|
return "false", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "true", nil
|
|
|
|
}
|
|
|
|
|
2015-09-28 10:01:18 +00:00
|
|
|
func (v *Variable) writeBool(value string) error {
|
|
|
|
b, err := strconv.ParseBool(value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
val := []byte{0}
|
|
|
|
if b {
|
|
|
|
val[0] = *(*byte)(unsafe.Pointer(&b))
|
|
|
|
}
|
|
|
|
_, err = v.thread.writeMemory(v.Addr, val)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
func (v *Variable) readFunctionPtr() (string, error) {
|
|
|
|
val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize())
|
2015-01-20 04:15:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// dereference pointer to find function pc
|
2015-09-28 09:17:27 +00:00
|
|
|
fnaddr := uintptr(binary.LittleEndian.Uint64(val))
|
|
|
|
if fnaddr == 0 {
|
2015-01-21 05:47:59 +00:00
|
|
|
return "nil", nil
|
|
|
|
}
|
2015-01-20 04:15:40 +00:00
|
|
|
|
2015-09-28 09:17:27 +00:00
|
|
|
val, err = v.thread.readMemory(fnaddr, v.thread.dbp.arch.PtrSize())
|
2015-01-20 04:15:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
funcAddr := binary.LittleEndian.Uint64(val)
|
2015-09-28 09:17:27 +00:00
|
|
|
fn := v.thread.dbp.goSymTable.PCToFunc(uint64(funcAddr))
|
2015-05-03 19:11:17 +00:00
|
|
|
if fn == nil {
|
|
|
|
return "", fmt.Errorf("could not find function for %#v", funcAddr)
|
2015-01-20 04:15:40 +00:00
|
|
|
}
|
|
|
|
|
2015-05-03 19:11:17 +00:00
|
|
|
return fn.Name, nil
|
2015-01-20 04:15:40 +00:00
|
|
|
}
|
|
|
|
|
2014-12-30 17:57:31 +00:00
|
|
|
// Fetches all variables of a specific type in the current function scope
|
2015-08-28 20:06:29 +00:00
|
|
|
func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
|
|
|
reader := scope.DwarfReader()
|
2014-12-30 17:57:31 +00:00
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
_, err := reader.SeekToFunction(scope.PC)
|
2014-12-31 13:34:41 +00:00
|
|
|
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 {
|
2015-08-28 20:06:29 +00:00
|
|
|
val, err := scope.extractVariableFromEntry(entry)
|
2014-12-30 17:57:31 +00:00
|
|
|
if err != nil {
|
2015-01-20 03:18:17 +00:00
|
|
|
// skip variables that we can't parse yet
|
|
|
|
continue
|
2014-12-30 17:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vars = append(vars, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return vars, nil
|
|
|
|
}
|