variables.go 20.5 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
}

D
Derek Parker 已提交
278
func (thread *Thread) evaluateStructMember(parentEntry *dwarf.Entry, rdr *reader.Reader, memberName string) (*Variable, error) {
279
	parentAddr, err := thread.extractVariableDataAddress(parentEntry, rdr)
280 281 282
	if err != nil {
		return nil, err
	}
283

D
Derek Parker 已提交
284
	// Get parent variable name
285
	parentName, ok := parentEntry.Val(dwarf.AttrName).(string)
286
	if !ok {
D
Derek Parker 已提交
287
		return nil, fmt.Errorf("unable to retrive variable name")
288
	}
289 290

	// Seek reader to the type information so members can be iterated
291
	_, err = rdr.SeekToType(parentEntry, true, true)
292 293 294
	if err != nil {
		return nil, err
	}
295 296

	// Iterate to find member by name
297
	for memberEntry, err := rdr.NextMemberVariable(); memberEntry != nil; memberEntry, err = rdr.NextMemberVariable() {
298 299 300 301 302 303 304 305 306 307
		if err != nil {
			return nil, err
		}

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

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

313
			memberInstr, err := rdr.InstructionsForEntry(memberEntry)
314 315 316 317 318 319
			if err != nil {
				return nil, err
			}

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

323
			data := thread.dbp.dwarf
324 325 326 327 328
			t, err := data.Type(offset)
			if err != nil {
				return nil, err
			}

329 330 331 332
			baseAddr := make([]byte, 8)
			binary.LittleEndian.PutUint64(baseAddr, uint64(parentAddr))

			parentInstructions := append([]byte{op.DW_OP_addr}, baseAddr...)
E
epipho 已提交
333
			val, err := thread.extractValue(append(parentInstructions, memberInstr...), 0, t, true)
334 335 336 337 338 339 340
			if err != nil {
				return nil, err
			}
			return &Variable{Name: strings.Join([]string{parentName, memberName}, "."), Type: t.String(), Value: val}, nil
		}
	}

341
	return nil, fmt.Errorf("%s has no member %s", parentName, memberName)
342 343
}

E
epipho 已提交
344
// Extracts the name, type, and value of a variable from a dwarf entry
D
Derek Parker 已提交
345
func (thread *Thread) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
E
epipho 已提交
346
	if entry == nil {
D
Derek Parker 已提交
347
		return nil, fmt.Errorf("invalid entry")
E
epipho 已提交
348 349 350 351 352 353 354 355
	}

	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 已提交
356
		return nil, fmt.Errorf("type assertion failed")
E
epipho 已提交
357 358 359 360
	}

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

364
	data := thread.dbp.dwarf
E
epipho 已提交
365 366 367 368 369 370 371
	t, err := data.Type(offset)
	if err != nil {
		return nil, err
	}

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

E
epipho 已提交
375
	val, err := thread.extractValue(instructions, 0, t, true)
E
epipho 已提交
376 377 378 379 380 381 382
	if err != nil {
		return nil, err
	}

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

383
// Execute the stack program taking into account the current stack frame
D
Derek Parker 已提交
384
func (thread *Thread) executeStackProgram(instructions []byte) (int64, error) {
385 386 387 388
	regs, err := thread.Registers()
	if err != nil {
		return 0, err
	}
389

390
	fde, err := thread.dbp.frameEntries.FDEForPC(regs.PC())
391 392 393 394 395
	if err != nil {
		return 0, err
	}

	fctx := fde.EstablishFrame(regs.PC())
D
Derek Parker 已提交
396 397
	cfa := fctx.CFAOffset() + int64(regs.SP())
	address, err := op.ExecuteStackProgram(cfa, instructions)
398 399 400 401 402 403 404
	if err != nil {
		return 0, err
	}
	return address, nil
}

// Extracts the address of a variable, dereferencing any pointers
D
Derek Parker 已提交
405
func (thread *Thread) extractVariableDataAddress(entry *dwarf.Entry, rdr *reader.Reader) (int64, error) {
406
	instructions, err := rdr.InstructionsForEntry(entry)
407 408 409 410 411 412 413 414 415
	if err != nil {
		return 0, err
	}

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

D
Derek Parker 已提交
416
	// Dereference pointers to get down the concrete type
417
	for typeEntry, err := rdr.SeekToType(entry, true, false); typeEntry != nil; typeEntry, err = rdr.SeekToType(typeEntry, true, false) {
418
		if err != nil {
419
			return 0, err
420 421
		}

422 423 424 425 426 427
		if typeEntry.Tag != dwarf.TagPointerType {
			break
		}

		ptraddress := uintptr(address)

428
		ptr, err := thread.readMemory(ptraddress, thread.dbp.arch.PtrSize())
429
		if err != nil {
430
			return 0, err
431
		}
432 433
		address = int64(binary.LittleEndian.Uint64(ptr))
	}
434

435 436
	return address, nil
}
437

438 439 440
// 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 已提交
441
func (thread *Thread) extractValue(instructions []byte, addr int64, typ interface{}, printStructName bool) (string, error) {
442 443 444
	return thread.extractValueInternal(instructions, addr, typ, printStructName, 0)
}

D
Derek Parker 已提交
445
func (thread *Thread) extractValueInternal(instructions []byte, addr int64, typ interface{}, printStructName bool, recurseLevel int) (string, error) {
446 447 448 449
	var err error

	if addr == 0 {
		addr, err = thread.executeStackProgram(instructions)
450 451 452 453 454 455 456
		if err != nil {
			return "", err
		}
	}

	// If we have a user defined type, find the
	// underlying concrete type and use that.
457 458 459 460 461 462
	for {
		if tt, ok := typ.(*dwarf.TypedefType); ok {
			typ = tt.Type
		} else {
			break
		}
463 464
	}

465
	ptraddress := uintptr(addr)
466 467
	switch t := typ.(type) {
	case *dwarf.PtrType:
468
		ptr, err := thread.readMemory(ptraddress, thread.dbp.arch.PtrSize())
469 470 471
		if err != nil {
			return "", err
		}
472 473 474 475 476 477

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

478 479
		// Don't increase the recursion level when dereferencing pointers
		val, err := thread.extractValueInternal(nil, intaddr, t.Type, printStructName, recurseLevel)
480 481 482 483
		if err != nil {
			return "", err
		}

484
		return fmt.Sprintf("*%s", val), nil
485
	case *dwarf.StructType:
E
epipho 已提交
486 487
		switch {
		case t.StructName == "string":
488
			return thread.readString(ptraddress)
E
epipho 已提交
489 490
		case strings.HasPrefix(t.StructName, "[]"):
			return thread.readSlice(ptraddress, t)
491
		default:
D
Derek Parker 已提交
492
			// Recursively call extractValue to grab
493
			// the value of all the members of the struct.
494 495 496 497 498 499 500 501 502
			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))
503
				}
504 505 506 507
				if printStructName {
					return fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", ")), nil
				}
				return fmt.Sprintf("{%s}", strings.Join(fields, ", ")), nil
508
			}
509
			// no fields
E
epipho 已提交
510
			if printStructName {
511
				return fmt.Sprintf("%s {...}", t.StructName), nil
E
epipho 已提交
512
			}
513
			return "{...}", nil
514 515
		}
	case *dwarf.ArrayType:
E
epipho 已提交
516
		return thread.readArray(ptraddress, t)
517
	case *dwarf.IntType:
518
		return thread.readInt(ptraddress, t.ByteSize)
E
epipho 已提交
519 520
	case *dwarf.UintType:
		return thread.readUint(ptraddress, t.ByteSize)
521
	case *dwarf.FloatType:
522
		return thread.readFloat(ptraddress, t.ByteSize)
E
epipho 已提交
523 524
	case *dwarf.BoolType:
		return thread.readBool(ptraddress)
525 526
	case *dwarf.FuncType:
		return thread.readFunctionPtr(ptraddress)
527 528 529 530
	case *dwarf.VoidType:
		return "(void)", nil
	case *dwarf.UnspecifiedType:
		return "(unknown)", nil
531 532
	default:
		fmt.Printf("Unknown type: %T\n", t)
533 534 535 536 537
	}

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

D
Derek Parker 已提交
538
func (thread *Thread) readString(addr uintptr) (string, error) {
539 540 541 542
	// string data structure is always two ptrs in size. Addr, followed by len
	// http://research.swtch.com/godata

	// read len
543
	val, err := thread.readMemory(addr+uintptr(thread.dbp.arch.PtrSize()), thread.dbp.arch.PtrSize())
544
	if err != nil {
545
		return "", fmt.Errorf("could not read string len %s", err)
546
	}
547
	strlen := int(binary.LittleEndian.Uint64(val))
548 549

	// read addr
550
	val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize())
551
	if err != nil {
552
		return "", fmt.Errorf("could not read string pointer %s", err)
553 554
	}
	addr = uintptr(binary.LittleEndian.Uint64(val))
D
Derek Parker 已提交
555 556 557
	if addr == 0 {
		return "", nil
	}
D
Derek Parker 已提交
558

559
	val, err = thread.readMemory(addr, strlen)
560
	if err != nil {
561
		return "", fmt.Errorf("could not read string at %#v due to %s", addr, err)
562 563
	}

D
Derek Parker 已提交
564
	return *(*string)(unsafe.Pointer(&val)), nil
565 566
}

D
Derek Parker 已提交
567
func (thread *Thread) readSlice(addr uintptr, t *dwarf.StructType) (string, error) {
E
epipho 已提交
568 569 570 571 572 573
	var sliceLen, sliceCap int64
	var arrayAddr uintptr
	var arrayType dwarf.Type
	for _, f := range t.Field {
		switch f.Name {
		case "array":
574
			val, err := thread.readMemory(addr+uintptr(f.ByteOffset), thread.dbp.arch.PtrSize())
E
epipho 已提交
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
			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 {
608
		stride = int64(thread.dbp.arch.PtrSize())
E
epipho 已提交
609 610
	}
	vals, err := thread.readArrayValues(arrayAddr, sliceLen, stride, arrayType)
611 612 613 614
	if err != nil {
		return "", err
	}

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

D
Derek Parker 已提交
618
func (thread *Thread) readArray(addr uintptr, t *dwarf.ArrayType) (string, error) {
619 620 621 622 623 624
	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
625
	}
626 627
	// because you can declare a zero-size array
	return fmt.Sprintf("%s []", t), nil
628 629
}

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

E
epipho 已提交
633
	for i := int64(0); i < count; i++ {
634 635 636 637 638 639
		// Cap number of elements
		if i >= maxArrayValues {
			vals = append(vals, fmt.Sprintf("...+%d more", count-maxArrayValues))
			break
		}

E
epipho 已提交
640
		val, err := thread.extractValue(nil, int64(addr+uintptr(i*stride)), t, false)
E
epipho 已提交
641
		if err != nil {
E
epipho 已提交
642
			return nil, err
E
epipho 已提交
643 644
		}
		vals = append(vals, val)
645
	}
E
epipho 已提交
646
	return vals, nil
647 648
}

D
Derek Parker 已提交
649
func (thread *Thread) readInt(addr uintptr, size int64) (string, error) {
D
Derek Parker 已提交
650 651 652 653 654 655 656
	n, err := thread.readIntRaw(addr, size)
	if err != nil {
		return "", err
	}
	return strconv.FormatInt(n, 10), nil
}

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

660
	val, err := thread.readMemory(addr, int(size))
661
	if err != nil {
D
Derek Parker 已提交
662
		return 0, err
663 664
	}

665 666
	switch size {
	case 1:
E
epipho 已提交
667
		n = int64(val[0])
668
	case 2:
E
epipho 已提交
669
		n = int64(binary.LittleEndian.Uint16(val))
670
	case 4:
E
epipho 已提交
671
		n = int64(binary.LittleEndian.Uint32(val))
672
	case 8:
E
epipho 已提交
673
		n = int64(binary.LittleEndian.Uint64(val))
674
	}
675

D
Derek Parker 已提交
676
	return n, nil
E
epipho 已提交
677 678
}

D
Derek Parker 已提交
679
func (thread *Thread) readUint(addr uintptr, size int64) (string, error) {
D
Derek Parker 已提交
680 681 682 683 684 685 686
	n, err := thread.readUintRaw(addr, size)
	if err != nil {
		return "", err
	}
	return strconv.FormatUint(n, 10), nil
}

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

690
	val, err := thread.readMemory(addr, int(size))
E
epipho 已提交
691
	if err != nil {
D
Derek Parker 已提交
692
		return 0, err
E
epipho 已提交
693 694 695 696 697 698 699 700 701 702 703 704 705
	}

	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 已提交
706
	return n, nil
707 708
}

D
Derek Parker 已提交
709
func (thread *Thread) readFloat(addr uintptr, size int64) (string, error) {
710
	val, err := thread.readMemory(addr, int(size))
711 712 713 714 715
	if err != nil {
		return "", err
	}
	buf := bytes.NewBuffer(val)

D
Derek Parker 已提交
716 717 718 719 720 721 722 723 724 725 726
	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 已提交
727
	return "", fmt.Errorf("could not read float")
728 729
}

D
Derek Parker 已提交
730
func (thread *Thread) readBool(addr uintptr) (string, error) {
731
	val, err := thread.readMemory(addr, 1)
E
epipho 已提交
732 733 734 735 736 737 738 739 740 741 742
	if err != nil {
		return "", err
	}

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

	return "true", nil
}

D
Derek Parker 已提交
743
func (thread *Thread) readFunctionPtr(addr uintptr) (string, error) {
744
	val, err := thread.readMemory(addr, thread.dbp.arch.PtrSize())
745 746 747 748 749 750
	if err != nil {
		return "", err
	}

	// dereference pointer to find function pc
	addr = uintptr(binary.LittleEndian.Uint64(val))
751 752 753
	if addr == 0 {
		return "nil", nil
	}
754

755
	val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize())
756 757 758 759 760
	if err != nil {
		return "", err
	}

	funcAddr := binary.LittleEndian.Uint64(val)
761
	fn := thread.dbp.goSymTable.PCToFunc(uint64(funcAddr))
762 763
	if fn == nil {
		return "", fmt.Errorf("could not find function for %#v", funcAddr)
764 765
	}

766
	return fn.Name, nil
767 768
}

D
Derek Parker 已提交
769
func (thread *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
770 771 772
	if size == 0 {
		return nil, nil
	}
773

774
	buf := make([]byte, size)
D
Derek Parker 已提交
775
	_, err := readMemory(thread, addr, buf)
776 777 778 779 780
	if err != nil {
		return nil, err
	}
	return buf, nil
}
D
Derek Parker 已提交
781

E
epipho 已提交
782
// Fetches all variables of a specific type in the current function scope
D
Derek Parker 已提交
783
func (thread *Thread) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
D
Derek Parker 已提交
784
	pc, err := thread.PC()
E
epipho 已提交
785 786 787 788
	if err != nil {
		return nil, err
	}

789
	reader := thread.dbp.DwarfReader()
E
epipho 已提交
790

791 792
	_, err = reader.SeekToFunction(pc)
	if err != nil {
E
epipho 已提交
793 794 795 796 797
		return nil, err
	}

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

798
	for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
E
epipho 已提交
799 800 801 802 803 804 805
		if err != nil {
			return nil, err
		}

		if entry.Tag == tag {
			val, err := thread.extractVariableFromEntry(entry)
			if err != nil {
E
epipho 已提交
806 807
				// skip variables that we can't parse yet
				continue
E
epipho 已提交
808 809 810 811 812 813 814 815
			}

			vars = append(vars, val)
		}
	}

	return vars, nil
}