variables.go 21.0 KB
Newer Older
D
Derek Parker 已提交
1
package proc
2 3 4

import (
	"bytes"
D
Derek Parker 已提交
5
	"debug/dwarf"
6
	"debug/gosym"
7 8 9 10 11 12 13
	"encoding/binary"
	"fmt"
	"strconv"
	"strings"
	"unsafe"

	"github.com/derekparker/delve/dwarf/op"
14
	"github.com/derekparker/delve/dwarf/reader"
15 16
)

17
const (
18 19
	maxVariableRecurse = 1  // How far to recurse when evaluating nested types.
	maxArrayValues     = 64 // Max value for reading large arrays.
20 21 22

	ChanRecv = "chan receive"
	ChanSend = "chan send"
23 24
)

25
// Represents an evaluated variable.
26 27 28 29 30 31
type Variable struct {
	Name  string
	Value string
	Type  string
}

32
// Represents a runtime M (OS thread) structure.
33
type M struct {
34 35 36 37
	procid   int     // Thread ID or port.
	spinning uint8   // Busy looping.
	blocked  uint8   // Waiting on futex / semaphore.
	curg     uintptr // Current G running on this thread.
38 39
}

40 41
// Represents a runtime G (goroutine) structure (at least the
// fields that Delve is interested in).
D
Derek Parker 已提交
42
type G struct {
43 44 45 46 47 48 49 50 51 52
	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.

	// Information on goroutine location.
	File string
	Line int
	Func *gosym.Func
53 54 55

	// PC of entry to top-most deferred function.
	DeferPC uint64
A
aarzilli 已提交
56 57 58

	// Thread that this goroutine is currently allocated to
	thread *Thread
59 60
}

61 62
// Returns whether the goroutine is blocked on
// a channel read operation.
63 64 65 66 67
func (g *G) ChanRecvBlocked() bool {
	return g.WaitReason == ChanRecv
}

// chanRecvReturnAddr returns the address of the return from a channel read.
D
Derek Parker 已提交
68
func (g *G) chanRecvReturnAddr(dbp *Process) (uint64, error) {
69 70 71 72 73
	locs, err := dbp.stacktrace(g.PC, g.SP, 4)
	if err != nil {
		return 0, err
	}
	topLoc := locs[len(locs)-1]
A
aarzilli 已提交
74
	return topLoc.PC, nil
D
Derek Parker 已提交
75 76
}

D
Derek Parker 已提交
77 78
// NoGError returned when a G could not be found
// for a specific thread.
79 80 81 82 83 84 85 86
type NoGError struct {
	tid int
}

func (ng NoGError) Error() string {
	return fmt.Sprintf("no G executing on thread %d", ng.tid)
}

D
Derek Parker 已提交
87
func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
88 89 90 91 92 93 94 95 96 97 98 99 100
	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}
		}
101
	}
102

103
	rdr := thread.dbp.DwarfReader()
D
Derek Parker 已提交
104
	rdr.Seek(0)
105
	entry, err := rdr.SeekToTypeNamed("runtime.g")
106 107 108
	if err != nil {
		return nil, err
	}
D
Derek Parker 已提交
109

110 111 112 113 114 115 116
	// Parse defer
	deferAddr, err := rdr.AddrForMember("_defer", initialInstructions)
	if err != nil {
		return nil, err
	}
	var deferPC uint64
	// Dereference *defer pointer
117
	deferAddrBytes, err := thread.readMemory(uintptr(deferAddr), thread.dbp.arch.PtrSize())
118
	if err != nil {
119
		return nil, fmt.Errorf("error derefing defer %s", err)
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
	}
	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
		}
	}
137 138 139 140 141 142 143 144 145

	// 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
	}

D
Derek Parker 已提交
146 147
	// Parse sched
	schedAddr, err := rdr.AddrForMember("sched", initialInstructions)
148
	if err != nil {
D
Derek Parker 已提交
149
		return nil, err
150
	}
D
Derek Parker 已提交
151 152
	// From sched, let's parse PC and SP.
	sp, err := thread.readUintRaw(uintptr(schedAddr), 8)
153
	if err != nil {
D
Derek Parker 已提交
154
		return nil, err
155
	}
156
	pc, err := thread.readUintRaw(uintptr(schedAddr+uint64(thread.dbp.arch.PtrSize())), 8)
157
	if err != nil {
D
Derek Parker 已提交
158
		return nil, err
159
	}
D
Derek Parker 已提交
160 161
	// Parse goid
	goidAddr, err := rdr.AddrForMember("goid", initialInstructions)
162 163 164
	if err != nil {
		return nil, err
	}
D
Derek Parker 已提交
165
	goid, err := thread.readIntRaw(uintptr(goidAddr), 8)
166 167 168
	if err != nil {
		return nil, err
	}
D
Derek Parker 已提交
169 170
	// Parse waitreason
	waitReasonAddr, err := rdr.AddrForMember("waitreason", initialInstructions)
171
	if err != nil {
D
Derek Parker 已提交
172
		return nil, err
173
	}
D
Derek Parker 已提交
174
	waitreason, err := thread.readString(uintptr(waitReasonAddr))
175
	if err != nil {
D
Derek Parker 已提交
176
		return nil, err
177
	}
D
Derek Parker 已提交
178 179
	// Parse gopc
	gopcAddr, err := rdr.AddrForMember("gopc", initialInstructions)
180
	if err != nil {
D
Derek Parker 已提交
181 182 183 184 185
		return nil, err
	}
	gopc, err := thread.readUintRaw(uintptr(gopcAddr), 8)
	if err != nil {
		return nil, err
186
	}
187

188
	f, l, fn := thread.dbp.goSymTable.PCToLine(gopc)
189
	g := &G{
D
Derek Parker 已提交
190 191 192 193
		Id:         int(goid),
		GoPC:       gopc,
		PC:         pc,
		SP:         sp,
194 195 196 197
		File:       f,
		Line:       l,
		Func:       fn,
		WaitReason: waitreason,
198
		DeferPC:    deferPC,
199 200
	}
	return g, nil
201 202
}

203
// Returns the value of the named variable.
D
Derek Parker 已提交
204
func (thread *Thread) EvalVariable(name string) (*Variable, error) {
D
Derek Parker 已提交
205
	pc, err := thread.PC()
D
Derek Parker 已提交
206 207 208
	if err != nil {
		return nil, err
	}
209

210
	reader := thread.dbp.DwarfReader()
211

212 213
	_, err = reader.SeekToFunction(pc)
	if err != nil {
D
Derek Parker 已提交
214 215
		return nil, err
	}
D
Derek Parker 已提交
216

217 218
	varName := name
	memberName := ""
219 220
	if strings.Contains(name, ".") {
		idx := strings.Index(name, ".")
221 222
		varName = name[:idx]
		memberName = name[idx+1:]
223 224
	}

225
	for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
D
Derek Parker 已提交
226
		if err != nil {
227
			return nil, err
D
Derek Parker 已提交
228 229 230 231 232 233 234
		}

		n, ok := entry.Val(dwarf.AttrName).(string)
		if !ok {
			continue
		}

235 236 237 238 239
		if n == varName {
			if len(memberName) == 0 {
				return thread.extractVariableFromEntry(entry)
			}
			return thread.evaluateStructMember(entry, reader, memberName)
D
Derek Parker 已提交
240 241 242
		}
	}

243
	return nil, fmt.Errorf("could not find symbol value for %s", name)
D
Derek Parker 已提交
244
}
245

246
// LocalVariables returns all local variables from the current function scope.
D
Derek Parker 已提交
247
func (thread *Thread) LocalVariables() ([]*Variable, error) {
248 249 250 251
	return thread.variablesByTag(dwarf.TagVariable)
}

// FunctionArguments returns the name, value, and type of all current function arguments.
D
Derek Parker 已提交
252
func (thread *Thread) FunctionArguments() ([]*Variable, error) {
253 254 255 256
	return thread.variablesByTag(dwarf.TagFormalParameter)
}

// PackageVariables returns the name, value, and type of all package variables in the application.
D
Derek Parker 已提交
257
func (thread *Thread) PackageVariables() ([]*Variable, error) {
258
	reader := thread.dbp.DwarfReader()
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

	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
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
func (thread *Thread) EvalPackageVariable(name string) (*Variable, error) {
	reader := thread.dbp.DwarfReader()

	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 {
			return thread.extractVariableFromEntry(entry)
		}
	}

	return nil, fmt.Errorf("could not find symbol value for %s", name)
}

D
Derek Parker 已提交
299
func (thread *Thread) evaluateStructMember(parentEntry *dwarf.Entry, rdr *reader.Reader, memberName string) (*Variable, error) {
300
	parentAddr, err := thread.extractVariableDataAddress(parentEntry, rdr)
301 302 303
	if err != nil {
		return nil, err
	}
304

D
Derek Parker 已提交
305
	// Get parent variable name
306
	parentName, ok := parentEntry.Val(dwarf.AttrName).(string)
307
	if !ok {
D
Derek Parker 已提交
308
		return nil, fmt.Errorf("unable to retrive variable name")
309
	}
310 311

	// Seek reader to the type information so members can be iterated
312
	_, err = rdr.SeekToType(parentEntry, true, true)
313 314 315
	if err != nil {
		return nil, err
	}
316 317

	// Iterate to find member by name
318
	for memberEntry, err := rdr.NextMemberVariable(); memberEntry != nil; memberEntry, err = rdr.NextMemberVariable() {
319 320 321 322 323 324 325 326 327 328
		if err != nil {
			return nil, err
		}

		name, ok := memberEntry.Val(dwarf.AttrName).(string)
		if !ok {
			continue
		}

		if name == memberName {
D
Derek Parker 已提交
329
			// Nil ptr, wait until here to throw a nil pointer error to prioritize no such member error
330 331 332 333
			if parentAddr == 0 {
				return nil, fmt.Errorf("%s is nil", parentName)
			}

334
			memberInstr, err := rdr.InstructionsForEntry(memberEntry)
335 336 337 338 339 340
			if err != nil {
				return nil, err
			}

			offset, ok := memberEntry.Val(dwarf.AttrType).(dwarf.Offset)
			if !ok {
D
Derek Parker 已提交
341
				return nil, fmt.Errorf("type assertion failed")
342 343
			}

344
			data := thread.dbp.dwarf
345 346 347 348 349
			t, err := data.Type(offset)
			if err != nil {
				return nil, err
			}

350 351 352 353
			baseAddr := make([]byte, 8)
			binary.LittleEndian.PutUint64(baseAddr, uint64(parentAddr))

			parentInstructions := append([]byte{op.DW_OP_addr}, baseAddr...)
E
epipho 已提交
354
			val, err := thread.extractValue(append(parentInstructions, memberInstr...), 0, t, true)
355 356 357 358 359 360 361
			if err != nil {
				return nil, err
			}
			return &Variable{Name: strings.Join([]string{parentName, memberName}, "."), Type: t.String(), Value: val}, nil
		}
	}

362
	return nil, fmt.Errorf("%s has no member %s", parentName, memberName)
363 364
}

E
epipho 已提交
365
// Extracts the name, type, and value of a variable from a dwarf entry
D
Derek Parker 已提交
366
func (thread *Thread) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
E
epipho 已提交
367
	if entry == nil {
D
Derek Parker 已提交
368
		return nil, fmt.Errorf("invalid entry")
E
epipho 已提交
369 370 371 372 373 374 375 376
	}

	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 {
D
Derek Parker 已提交
377
		return nil, fmt.Errorf("type assertion failed")
E
epipho 已提交
378 379 380 381
	}

	offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
	if !ok {
D
Derek Parker 已提交
382
		return nil, fmt.Errorf("type assertion failed")
E
epipho 已提交
383 384
	}

385
	data := thread.dbp.dwarf
E
epipho 已提交
386 387 388 389 390 391 392
	t, err := data.Type(offset)
	if err != nil {
		return nil, err
	}

	instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
	if !ok {
D
Derek Parker 已提交
393
		return nil, fmt.Errorf("type assertion failed")
E
epipho 已提交
394 395
	}

E
epipho 已提交
396
	val, err := thread.extractValue(instructions, 0, t, true)
E
epipho 已提交
397 398 399 400 401 402 403
	if err != nil {
		return nil, err
	}

	return &Variable{Name: n, Type: t.String(), Value: val}, nil
}

404
// Execute the stack program taking into account the current stack frame
D
Derek Parker 已提交
405
func (thread *Thread) executeStackProgram(instructions []byte) (int64, error) {
406 407 408 409
	regs, err := thread.Registers()
	if err != nil {
		return 0, err
	}
410

411 412
	var cfa int64 = 0

413
	fde, err := thread.dbp.frameEntries.FDEForPC(regs.PC())
414 415 416
	if err == nil {
		fctx := fde.EstablishFrame(regs.PC())
		cfa = fctx.CFAOffset() + int64(regs.SP())
417
	}
D
Derek Parker 已提交
418
	address, err := op.ExecuteStackProgram(cfa, instructions)
419 420 421 422 423 424 425
	if err != nil {
		return 0, err
	}
	return address, nil
}

// Extracts the address of a variable, dereferencing any pointers
D
Derek Parker 已提交
426
func (thread *Thread) extractVariableDataAddress(entry *dwarf.Entry, rdr *reader.Reader) (int64, error) {
427
	instructions, err := rdr.InstructionsForEntry(entry)
428 429 430 431 432 433 434 435 436
	if err != nil {
		return 0, err
	}

	address, err := thread.executeStackProgram(instructions)
	if err != nil {
		return 0, err
	}

D
Derek Parker 已提交
437
	// Dereference pointers to get down the concrete type
438
	for typeEntry, err := rdr.SeekToType(entry, true, false); typeEntry != nil; typeEntry, err = rdr.SeekToType(typeEntry, true, false) {
439
		if err != nil {
440
			return 0, err
441 442
		}

443 444 445 446 447 448
		if typeEntry.Tag != dwarf.TagPointerType {
			break
		}

		ptraddress := uintptr(address)

449
		ptr, err := thread.readMemory(ptraddress, thread.dbp.arch.PtrSize())
450
		if err != nil {
451
			return 0, err
452
		}
453 454
		address = int64(binary.LittleEndian.Uint64(ptr))
	}
455

456 457
	return address, nil
}
458

459 460 461
// 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.
D
Derek Parker 已提交
462
func (thread *Thread) extractValue(instructions []byte, addr int64, typ interface{}, printStructName bool) (string, error) {
463 464 465
	return thread.extractValueInternal(instructions, addr, typ, printStructName, 0)
}

D
Derek Parker 已提交
466
func (thread *Thread) extractValueInternal(instructions []byte, addr int64, typ interface{}, printStructName bool, recurseLevel int) (string, error) {
467 468 469 470
	var err error

	if addr == 0 {
		addr, err = thread.executeStackProgram(instructions)
471 472 473 474 475 476 477
		if err != nil {
			return "", err
		}
	}

	// If we have a user defined type, find the
	// underlying concrete type and use that.
478 479 480 481 482 483
	for {
		if tt, ok := typ.(*dwarf.TypedefType); ok {
			typ = tt.Type
		} else {
			break
		}
484 485
	}

486
	ptraddress := uintptr(addr)
487 488
	switch t := typ.(type) {
	case *dwarf.PtrType:
489
		ptr, err := thread.readMemory(ptraddress, thread.dbp.arch.PtrSize())
490 491 492
		if err != nil {
			return "", err
		}
493 494 495 496 497 498

		intaddr := int64(binary.LittleEndian.Uint64(ptr))
		if intaddr == 0 {
			return fmt.Sprintf("%s nil", t.String()), nil
		}

499 500
		// Don't increase the recursion level when dereferencing pointers
		val, err := thread.extractValueInternal(nil, intaddr, t.Type, printStructName, recurseLevel)
501 502 503 504
		if err != nil {
			return "", err
		}

505
		return fmt.Sprintf("*%s", val), nil
506
	case *dwarf.StructType:
E
epipho 已提交
507 508
		switch {
		case t.StructName == "string":
509
			return thread.readString(ptraddress)
E
epipho 已提交
510 511
		case strings.HasPrefix(t.StructName, "[]"):
			return thread.readSlice(ptraddress, t)
512
		default:
D
Derek Parker 已提交
513
			// Recursively call extractValue to grab
514
			// the value of all the members of the struct.
515 516 517 518 519 520 521 522 523
			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))
524
				}
525 526 527 528
				if printStructName {
					return fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", ")), nil
				}
				return fmt.Sprintf("{%s}", strings.Join(fields, ", ")), nil
529
			}
530
			// no fields
E
epipho 已提交
531
			if printStructName {
532
				return fmt.Sprintf("%s {...}", t.StructName), nil
E
epipho 已提交
533
			}
534
			return "{...}", nil
535 536
		}
	case *dwarf.ArrayType:
E
epipho 已提交
537
		return thread.readArray(ptraddress, t)
538
	case *dwarf.IntType:
539
		return thread.readInt(ptraddress, t.ByteSize)
E
epipho 已提交
540 541
	case *dwarf.UintType:
		return thread.readUint(ptraddress, t.ByteSize)
542
	case *dwarf.FloatType:
543
		return thread.readFloat(ptraddress, t.ByteSize)
E
epipho 已提交
544 545
	case *dwarf.BoolType:
		return thread.readBool(ptraddress)
546 547
	case *dwarf.FuncType:
		return thread.readFunctionPtr(ptraddress)
548 549 550 551
	case *dwarf.VoidType:
		return "(void)", nil
	case *dwarf.UnspecifiedType:
		return "(unknown)", nil
552 553
	default:
		fmt.Printf("Unknown type: %T\n", t)
554 555 556 557 558
	}

	return "", fmt.Errorf("could not find value for type %s", typ)
}

D
Derek Parker 已提交
559
func (thread *Thread) readString(addr uintptr) (string, error) {
560 561 562 563
	// string data structure is always two ptrs in size. Addr, followed by len
	// http://research.swtch.com/godata

	// read len
564
	val, err := thread.readMemory(addr+uintptr(thread.dbp.arch.PtrSize()), thread.dbp.arch.PtrSize())
565
	if err != nil {
566
		return "", fmt.Errorf("could not read string len %s", err)
567
	}
568
	strlen := int(binary.LittleEndian.Uint64(val))
569 570

	// read addr
571
	val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize())
572
	if err != nil {
573
		return "", fmt.Errorf("could not read string pointer %s", err)
574 575
	}
	addr = uintptr(binary.LittleEndian.Uint64(val))
D
Derek Parker 已提交
576 577 578
	if addr == 0 {
		return "", nil
	}
D
Derek Parker 已提交
579

580
	val, err = thread.readMemory(addr, strlen)
581
	if err != nil {
582
		return "", fmt.Errorf("could not read string at %#v due to %s", addr, err)
583 584
	}

D
Derek Parker 已提交
585
	return *(*string)(unsafe.Pointer(&val)), nil
586 587
}

D
Derek Parker 已提交
588
func (thread *Thread) readSlice(addr uintptr, t *dwarf.StructType) (string, error) {
E
epipho 已提交
589 590 591 592 593 594
	var sliceLen, sliceCap int64
	var arrayAddr uintptr
	var arrayType dwarf.Type
	for _, f := range t.Field {
		switch f.Name {
		case "array":
595
			val, err := thread.readMemory(addr+uintptr(f.ByteOffset), thread.dbp.arch.PtrSize())
E
epipho 已提交
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
			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 {
629
		stride = int64(thread.dbp.arch.PtrSize())
E
epipho 已提交
630 631
	}
	vals, err := thread.readArrayValues(arrayAddr, sliceLen, stride, arrayType)
632 633 634 635
	if err != nil {
		return "", err
	}

E
epipho 已提交
636 637
	return fmt.Sprintf("[]%s len: %d, cap: %d, [%s]", arrayType, sliceLen, sliceCap, strings.Join(vals, ",")), nil
}
638

D
Derek Parker 已提交
639
func (thread *Thread) readArray(addr uintptr, t *dwarf.ArrayType) (string, error) {
640 641 642 643 644 645
	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
646
	}
647 648
	// because you can declare a zero-size array
	return fmt.Sprintf("%s []", t), nil
649 650
}

D
Derek Parker 已提交
651
func (thread *Thread) readArrayValues(addr uintptr, count int64, stride int64, t dwarf.Type) ([]string, error) {
E
epipho 已提交
652
	vals := make([]string, 0)
653

E
epipho 已提交
654
	for i := int64(0); i < count; i++ {
655 656 657 658 659 660
		// Cap number of elements
		if i >= maxArrayValues {
			vals = append(vals, fmt.Sprintf("...+%d more", count-maxArrayValues))
			break
		}

E
epipho 已提交
661
		val, err := thread.extractValue(nil, int64(addr+uintptr(i*stride)), t, false)
E
epipho 已提交
662
		if err != nil {
E
epipho 已提交
663
			return nil, err
E
epipho 已提交
664 665
		}
		vals = append(vals, val)
666
	}
E
epipho 已提交
667
	return vals, nil
668 669
}

D
Derek Parker 已提交
670
func (thread *Thread) readInt(addr uintptr, size int64) (string, error) {
D
Derek Parker 已提交
671 672 673 674 675 676 677
	n, err := thread.readIntRaw(addr, size)
	if err != nil {
		return "", err
	}
	return strconv.FormatInt(n, 10), nil
}

D
Derek Parker 已提交
678
func (thread *Thread) readIntRaw(addr uintptr, size int64) (int64, error) {
E
epipho 已提交
679
	var n int64
680

681
	val, err := thread.readMemory(addr, int(size))
682
	if err != nil {
D
Derek Parker 已提交
683
		return 0, err
684 685
	}

686 687
	switch size {
	case 1:
E
epipho 已提交
688
		n = int64(val[0])
689
	case 2:
E
epipho 已提交
690
		n = int64(binary.LittleEndian.Uint16(val))
691
	case 4:
E
epipho 已提交
692
		n = int64(binary.LittleEndian.Uint32(val))
693
	case 8:
E
epipho 已提交
694
		n = int64(binary.LittleEndian.Uint64(val))
695
	}
696

D
Derek Parker 已提交
697
	return n, nil
E
epipho 已提交
698 699
}

D
Derek Parker 已提交
700
func (thread *Thread) readUint(addr uintptr, size int64) (string, error) {
D
Derek Parker 已提交
701 702 703 704 705 706 707
	n, err := thread.readUintRaw(addr, size)
	if err != nil {
		return "", err
	}
	return strconv.FormatUint(n, 10), nil
}

D
Derek Parker 已提交
708
func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) {
E
epipho 已提交
709 710
	var n uint64

711
	val, err := thread.readMemory(addr, int(size))
E
epipho 已提交
712
	if err != nil {
D
Derek Parker 已提交
713
		return 0, err
E
epipho 已提交
714 715 716 717 718 719 720 721 722 723 724 725 726
	}

	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))
	}

D
Derek Parker 已提交
727
	return n, nil
728 729
}

D
Derek Parker 已提交
730
func (thread *Thread) readFloat(addr uintptr, size int64) (string, error) {
731
	val, err := thread.readMemory(addr, int(size))
732 733 734 735 736
	if err != nil {
		return "", err
	}
	buf := bytes.NewBuffer(val)

D
Derek Parker 已提交
737 738 739 740 741 742 743 744 745 746 747
	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
	}

D
Derek Parker 已提交
748
	return "", fmt.Errorf("could not read float")
749 750
}

D
Derek Parker 已提交
751
func (thread *Thread) readBool(addr uintptr) (string, error) {
752
	val, err := thread.readMemory(addr, 1)
E
epipho 已提交
753 754 755 756 757 758 759 760 761 762 763
	if err != nil {
		return "", err
	}

	if val[0] == 0 {
		return "false", nil
	}

	return "true", nil
}

D
Derek Parker 已提交
764
func (thread *Thread) readFunctionPtr(addr uintptr) (string, error) {
765
	val, err := thread.readMemory(addr, thread.dbp.arch.PtrSize())
766 767 768 769 770 771
	if err != nil {
		return "", err
	}

	// dereference pointer to find function pc
	addr = uintptr(binary.LittleEndian.Uint64(val))
772 773 774
	if addr == 0 {
		return "nil", nil
	}
775

776
	val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize())
777 778 779 780 781
	if err != nil {
		return "", err
	}

	funcAddr := binary.LittleEndian.Uint64(val)
782
	fn := thread.dbp.goSymTable.PCToFunc(uint64(funcAddr))
783 784
	if fn == nil {
		return "", fmt.Errorf("could not find function for %#v", funcAddr)
785 786
	}

787
	return fn.Name, nil
788 789
}

D
Derek Parker 已提交
790
func (thread *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
791 792 793 794
	if size == 0 {
		return nil, nil
	}
	buf := make([]byte, size)
D
Derek Parker 已提交
795
	_, err := readMemory(thread, addr, buf)
796 797 798 799 800
	if err != nil {
		return nil, err
	}
	return buf, nil
}
D
Derek Parker 已提交
801

E
epipho 已提交
802
// Fetches all variables of a specific type in the current function scope
D
Derek Parker 已提交
803
func (thread *Thread) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
D
Derek Parker 已提交
804
	pc, err := thread.PC()
E
epipho 已提交
805 806 807 808
	if err != nil {
		return nil, err
	}

809
	reader := thread.dbp.DwarfReader()
E
epipho 已提交
810

811 812
	_, err = reader.SeekToFunction(pc)
	if err != nil {
E
epipho 已提交
813 814 815 816 817
		return nil, err
	}

	vars := make([]*Variable, 0)

818
	for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
E
epipho 已提交
819 820 821 822 823 824 825
		if err != nil {
			return nil, err
		}

		if entry.Tag == tag {
			val, err := thread.extractVariableFromEntry(entry)
			if err != nil {
E
epipho 已提交
826 827
				// skip variables that we can't parse yet
				continue
E
epipho 已提交
828 829 830 831 832 833 834 835
			}

			vars = append(vars, val)
		}
	}

	return vars, nil
}