proctl.go 14.3 KB
Newer Older
1 2 3
package proctl

import (
D
Derek Parker 已提交
4
	"debug/dwarf"
5
	"debug/gosym"
6
	"encoding/binary"
7 8
	"fmt"
	"os"
9
	"path/filepath"
10
	"runtime"
11 12
	"strconv"
	"strings"
D
Derek Parker 已提交
13
	"sync"
14

15 16
	sys "golang.org/x/sys/unix"

17
	"github.com/derekparker/delve/dwarf/frame"
D
Derek Parker 已提交
18
	"github.com/derekparker/delve/dwarf/line"
19
	"github.com/derekparker/delve/dwarf/reader"
D
Derek Parker 已提交
20
	"github.com/derekparker/delve/source"
21 22
)

D
Derek Parker 已提交
23 24 25
// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
type DebuggedProcess struct {
26 27 28 29 30 31 32 33 34 35 36 37 38
	Pid                     int
	Process                 *os.Process
	HWBreakPoints           [4]*BreakPoint
	BreakPoints             map[uint64]*BreakPoint
	Threads                 map[int]*ThreadContext
	CurrentThread           *ThreadContext
	dwarf                   *dwarf.Data
	goSymTable              *gosym.Table
	frameEntries            frame.FrameDescriptionEntries
	lineInfo                *line.DebugLineInfo
	firstStart              bool
	singleStepping          bool
	os                      *OSProcessDetails
39
	arch                    Arch
40 41 42 43 44 45
	ast                     *source.Searcher
	breakpointIDCounter     int
	tempBreakpointIDCounter int
	running                 bool
	halt                    bool
	exited                  bool
D
Derek Parker 已提交
46 47
}

D
Derek Parker 已提交
48 49
// A ManualStopError happens when the user triggers a
// manual stop via SIGERM.
D
Derek Parker 已提交
50 51 52 53 54 55
type ManualStopError struct{}

func (mse ManualStopError) Error() string {
	return "Manual stop requested"
}

56 57 58 59 60 61 62 63 64 65 66
// ProcessExitedError indicates that the process has exited and contains both
// process id and exit status.
type ProcessExitedError struct {
	Pid    int
	Status int
}

func (pe ProcessExitedError) Error() string {
	return fmt.Sprintf("process %d has exited with status %d", pe.Pid, pe.Status)
}

D
Derek Parker 已提交
67
// Attach to an existing process with the given PID.
68
func Attach(pid int) (*DebuggedProcess, error) {
69 70 71 72 73 74 75 76
	dbp := &DebuggedProcess{
		Pid:         pid,
		Threads:     make(map[int]*ThreadContext),
		BreakPoints: make(map[uint64]*BreakPoint),
		os:          new(OSProcessDetails),
		ast:         source.New(),
	}
	dbp, err := initializeDebugProcess(dbp, "", true)
77 78 79 80 81 82
	if err != nil {
		return nil, err
	}
	return dbp, nil
}

D
Derek Parker 已提交
83 84 85 86
func (dbp *DebuggedProcess) Detach() error {
	return PtraceDetach(dbp.Pid, int(sys.SIGINT))
}

87 88 89 90 91 92
// Returns whether or not Delve thinks the debugged
// process has exited.
func (dbp *DebuggedProcess) Exited() bool {
	return dbp.exited
}

D
Derek Parker 已提交
93 94
// Returns whether or not Delve thinks the debugged
// process is currently executing.
D
Derek Parker 已提交
95 96 97 98
func (dbp *DebuggedProcess) Running() bool {
	return dbp.running
}

D
Derek Parker 已提交
99 100 101 102 103
// Finds the executable and then uses it
// to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
104
func (dbp *DebuggedProcess) LoadInformation(path string) error {
D
Derek Parker 已提交
105 106
	var wg sync.WaitGroup

107
	exe, err := dbp.findExecutable(path)
D
Derek Parker 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120
	if err != nil {
		return err
	}

	wg.Add(3)
	go dbp.parseDebugFrame(exe, &wg)
	go dbp.obtainGoSymbols(exe, &wg)
	go dbp.parseDebugLineInfo(exe, &wg)
	wg.Wait()

	return nil
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
// Find a location by string (file+line, function, breakpoint id, addr)
func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) {
	// File + Line
	if strings.ContainsRune(str, ':') {
		fl := strings.Split(str, ":")

		fileName, err := filepath.Abs(fl[0])
		if err != nil {
			return 0, err
		}

		line, err := strconv.Atoi(fl[1])
		if err != nil {
			return 0, err
		}

137
		pc, _, err := dbp.goSymTable.LineToPC(fileName, line)
138 139 140 141
		if err != nil {
			return 0, err
		}
		return pc, nil
D
Derek Parker 已提交
142
	}
143

D
Derek Parker 已提交
144
	// Try to lookup by function name
145
	fn := dbp.goSymTable.LookupFunc(str)
D
Derek Parker 已提交
146 147 148 149 150 151 152 153 154
	if fn != nil {
		return fn.Entry, nil
	}

	// Attempt to parse as number for breakpoint id or raw address
	id, err := strconv.ParseUint(str, 0, 64)
	if err != nil {
		return 0, fmt.Errorf("unable to find location for %s", str)
	}
155

D
Derek Parker 已提交
156 157 158 159
	// Use as breakpoint id
	for _, bp := range dbp.HWBreakPoints {
		if bp == nil {
			continue
160
		}
D
Derek Parker 已提交
161 162
		if uint64(bp.ID) == id {
			return bp.Addr, nil
163 164
		}
	}
D
Derek Parker 已提交
165 166 167 168 169 170 171 172
	for _, bp := range dbp.BreakPoints {
		if uint64(bp.ID) == id {
			return bp.Addr, nil
		}
	}

	// Last resort, use as raw address
	return id, nil
173 174
}

D
Derek Parker 已提交
175 176
// Sends out a request that the debugged process halt
// execution. Sends SIGSTOP to all threads.
D
Derek Parker 已提交
177
func (dbp *DebuggedProcess) RequestManualStop() error {
D
Derek Parker 已提交
178
	dbp.halt = true
D
Derek Parker 已提交
179 180 181
	err := dbp.requestManualStop()
	if err != nil {
		return err
D
Derek Parker 已提交
182
	}
D
Derek Parker 已提交
183 184 185 186
	err = dbp.Halt()
	if err != nil {
		return err
	}
D
Derek Parker 已提交
187
	dbp.running = false
D
Derek Parker 已提交
188
	return nil
D
Derek Parker 已提交
189 190
}

D
Derek Parker 已提交
191 192 193 194 195 196 197 198 199
// Sets a breakpoint at addr, and stores it in the process wide
// break point table. Setting a break point must be thread specific due to
// ptrace actions needing the thread to be in a signal-delivery-stop.
//
// Depending on hardware support, Delve will choose to either
// set a hardware or software breakpoint. Essentially, if the
// hardware supports it, and there are free debug registers, Delve
// will set a hardware breakpoint. Otherwise we fall back to software
// breakpoints, which are a bit more work for us.
200
func (dbp *DebuggedProcess) Break(addr uint64) (*BreakPoint, error) {
201 202 203
	return dbp.setBreakpoint(dbp.CurrentThread.Id, addr, false)
}

D
Derek Parker 已提交
204
// Sets a temp breakpoint, for the 'next' command.
205 206
func (dbp *DebuggedProcess) TempBreak(addr uint64) (*BreakPoint, error) {
	return dbp.setBreakpoint(dbp.CurrentThread.Id, addr, true)
207 208 209 210 211 212 213 214
}

// Sets a breakpoint by location string (function, file+line, address)
func (dbp *DebuggedProcess) BreakByLocation(loc string) (*BreakPoint, error) {
	addr, err := dbp.FindLocation(loc)
	if err != nil {
		return nil, err
	}
D
Derek Parker 已提交
215
	return dbp.Break(addr)
216 217 218
}

// Clears a breakpoint in the current thread.
219
func (dbp *DebuggedProcess) Clear(addr uint64) (*BreakPoint, error) {
220
	return dbp.clearBreakpoint(dbp.CurrentThread.Id, addr)
221 222 223 224 225 226 227 228
}

// Clears a breakpoint by location (function, file+line, address, breakpoint id)
func (dbp *DebuggedProcess) ClearByLocation(loc string) (*BreakPoint, error) {
	addr, err := dbp.FindLocation(loc)
	if err != nil {
		return nil, err
	}
D
Derek Parker 已提交
229
	return dbp.Clear(addr)
230 231 232
}

// Returns the status of the current main thread context.
P
Paul Sbarra 已提交
233
func (dbp *DebuggedProcess) Status() *sys.WaitStatus {
234 235 236 237 238
	return dbp.CurrentThread.Status
}

// Step over function calls.
func (dbp *DebuggedProcess) Next() error {
D
Derek Parker 已提交
239 240
	return dbp.run(dbp.next)
}
241

D
Derek Parker 已提交
242
func (dbp *DebuggedProcess) next() error {
D
Derek Parker 已提交
243
	// Make sure we clean up the temp breakpoints created by thread.Next
244 245
	defer dbp.clearTempBreakpoints()

D
Derek Parker 已提交
246 247 248 249 250
	chanRecvCount, err := dbp.setChanRecvBreakpoints()
	if err != nil {
		return err
	}

251
	g, err := dbp.CurrentThread.getG()
D
Derek Parker 已提交
252 253 254
	if err != nil {
		return err
	}
255

256 257
	if g.DeferPC != 0 {
		_, err = dbp.TempBreak(g.DeferPC)
258 259 260 261 262
		if err != nil {
			return err
		}
	}

263
	var goroutineExiting bool
D
Derek Parker 已提交
264
	var waitCount int
D
Derek Parker 已提交
265
	for _, th := range dbp.Threads {
266 267
		if th.blocked() {
			// Ignore threads that aren't running go code.
D
Derek Parker 已提交
268 269
			continue
		}
D
Derek Parker 已提交
270
		waitCount++
271
		if err = th.SetNextBreakpoints(); err != nil {
272
			if err, ok := err.(GoroutineExitingError); ok {
D
Derek Parker 已提交
273
				waitCount = waitCount - 1 + chanRecvCount
274
				if err.goid == g.Id {
275 276 277 278
					goroutineExiting = true
				}
				continue
			}
D
Derek Parker 已提交
279 280 281
			return err
		}
	}
282 283 284 285 286
	for _, th := range dbp.Threads {
		if err = th.Continue(); err != nil {
			return err
		}
	}
287

D
Derek Parker 已提交
288
	for waitCount > 0 {
289
		thread, err := dbp.trapWait(-1)
D
Derek Parker 已提交
290 291
		if err != nil {
			return err
D
Derek Parker 已提交
292
		}
293
		tg, err := thread.getG()
D
Derek Parker 已提交
294 295 296
		if err != nil {
			return err
		}
D
Derek Parker 已提交
297
		// Make sure we're on the same goroutine, unless it has exited.
298
		if tg.Id == g.Id || goroutineExiting {
299 300
			if dbp.CurrentThread != thread {
				dbp.SwitchThread(thread.Id)
D
Derek Parker 已提交
301
			}
302
		}
D
Derek Parker 已提交
303
		waitCount--
304
	}
D
Derek Parker 已提交
305
	return dbp.Halt()
306 307
}

D
Derek Parker 已提交
308 309
func (dbp *DebuggedProcess) setChanRecvBreakpoints() (int, error) {
	var count int
D
Derek Parker 已提交
310 311
	allg, err := dbp.GoroutinesInfo()
	if err != nil {
D
Derek Parker 已提交
312
		return 0, err
D
Derek Parker 已提交
313 314 315 316 317
	}
	for _, g := range allg {
		if g.ChanRecvBlocked() {
			ret, err := g.chanRecvReturnAddr(dbp)
			if err != nil {
318 319 320
				if _, ok := err.(NullAddrError); ok {
					continue
				}
D
Derek Parker 已提交
321
				return 0, err
D
Derek Parker 已提交
322 323
			}
			if _, err = dbp.TempBreak(ret); err != nil {
D
Derek Parker 已提交
324
				return 0, err
D
Derek Parker 已提交
325
			}
D
Derek Parker 已提交
326
			count++
D
Derek Parker 已提交
327 328
		}
	}
D
Derek Parker 已提交
329
	return count, nil
D
Derek Parker 已提交
330 331
}

332 333 334 335 336 337 338 339
// Resume process.
func (dbp *DebuggedProcess) Continue() error {
	for _, thread := range dbp.Threads {
		err := thread.Continue()
		if err != nil {
			return err
		}
	}
340 341
	return dbp.run(dbp.resume)
}
342

343
func (dbp *DebuggedProcess) resume() error {
344
	thread, err := dbp.trapWait(-1)
345 346 347 348 349 350
	if err != nil {
		return err
	}
	if dbp.CurrentThread != thread {
		dbp.SwitchThread(thread.Id)
	}
D
Derek Parker 已提交
351
	pc, err := thread.PC()
352 353 354
	if err != nil {
		return err
	}
D
Derek Parker 已提交
355 356
	if dbp.CurrentBreakpoint != nil || dbp.halt {
		return dbp.Halt()
357 358
	}
	// Check to see if we hit a runtime.breakpoint
359
	fn := dbp.goSymTable.PCToFunc(pc)
360 361 362 363 364
	if fn != nil && fn.Name == "runtime.breakpoint" {
		// step twice to get back to user code
		for i := 0; i < 2; i++ {
			if err = thread.Step(); err != nil {
				return err
D
Derek Parker 已提交
365 366
			}
		}
367
		return dbp.Halt()
368
	}
369 370

	return fmt.Errorf("unrecognized breakpoint %#v", pc)
371 372
}

D
Derek Parker 已提交
373
// Single step, will execute a single instruction.
D
Derek Parker 已提交
374 375
func (dbp *DebuggedProcess) Step() (err error) {
	fn := func() error {
376 377
		dbp.singleStepping = true
		defer func() { dbp.singleStepping = false }()
D
Derek Parker 已提交
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
		for _, th := range dbp.Threads {
			if th.blocked() {
				continue
			}
			err := th.Step()
			if err != nil {
				return err
			}
		}
		return nil
	}

	return dbp.run(fn)
}

393 394 395
// Change from current thread to the thread specified by `tid`.
func (dbp *DebuggedProcess) SwitchThread(tid int) error {
	if th, ok := dbp.Threads[tid]; ok {
D
Derek Parker 已提交
396
		fmt.Printf("thread context changed from %d to %d\n", dbp.CurrentThread.Id, tid)
397
		dbp.CurrentThread = th
398 399 400 401 402
		return nil
	}
	return fmt.Errorf("thread %d does not exist", tid)
}

D
Derek Parker 已提交
403 404
// Returns an array of G structures representing the information
// Delve cares about from the internal runtime G structure.
405 406
func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) {
	var (
407 408
		allg []*G
		rdr  = dbp.DwarfReader()
409 410
	)

411
	addr, err := rdr.AddrFor("runtime.allglen")
412 413 414
	if err != nil {
		return nil, err
	}
415 416 417 418 419 420 421 422
	allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
	if err != nil {
		return nil, err
	}
	allglen := binary.LittleEndian.Uint64(allglenBytes)

	rdr.Seek(0)
	allgentryaddr, err := rdr.AddrFor("runtime.allg")
423 424 425
	if err != nil {
		return nil, err
	}
426
	faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
427 428 429
	allgptr := binary.LittleEndian.Uint64(faddr)

	for i := uint64(0); i < allglen; i++ {
430
		g, err := parseG(dbp.CurrentThread, allgptr+(i*uint64(dbp.arch.PtrSize())), true)
431 432 433 434 435 436 437 438
		if err != nil {
			return nil, err
		}
		allg = append(allg, g)
	}
	return allg, nil
}

D
Derek Parker 已提交
439
// Stop all threads.
D
Derek Parker 已提交
440 441 442 443 444 445 446 447 448
func (dbp *DebuggedProcess) Halt() (err error) {
	for _, th := range dbp.Threads {
		if err := th.Halt(); err != nil {
			return err
		}
	}
	return nil
}

449 450
// Obtains register values from what Delve considers to be the current
// thread of the traced process.
451
func (dbp *DebuggedProcess) Registers() (Registers, error) {
452 453 454
	return dbp.CurrentThread.Registers()
}

455
// Returns the PC of the current thread.
D
Derek Parker 已提交
456 457
func (dbp *DebuggedProcess) PC() (uint64, error) {
	return dbp.CurrentThread.PC()
458 459
}

460 461 462 463 464
// Returns the PC of the current thread.
func (dbp *DebuggedProcess) CurrentBreakpoint() *BreakPoint {
	return dbp.CurrentThread.CurrentBreakpoint
}

465 466 467 468 469
// Returns the value of the named symbol.
func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
	return dbp.CurrentThread.EvalSymbol(name)
}

470 471
// Returns a reader for the dwarf data
func (dbp *DebuggedProcess) DwarfReader() *reader.Reader {
472 473 474
	return reader.New(dbp.dwarf)
}

D
Derek Parker 已提交
475
// Returns list of source files that comprise the debugged binary.
476 477 478 479
func (dbp *DebuggedProcess) Sources() map[string]*gosym.Obj {
	return dbp.goSymTable.Files
}

D
Derek Parker 已提交
480
// Returns list of functions present in the debugged program.
481 482 483 484
func (dbp *DebuggedProcess) Funcs() []gosym.Func {
	return dbp.goSymTable.Funcs
}

D
Derek Parker 已提交
485
// Converts an instruction address to a file/line/function.
486 487
func (dbp *DebuggedProcess) PCToLine(pc uint64) (string, int, *gosym.Func) {
	return dbp.goSymTable.PCToLine(pc)
488 489
}

D
Derek Parker 已提交
490 491 492 493 494 495 496 497 498 499 500 501 502
// Finds the breakpoint for the given pc.
func (dbp *DebuggedProcess) FindBreakpoint(pc uint64) (*BreakPoint, bool) {
	for _, bp := range dbp.HWBreakPoints {
		if bp != nil && bp.Addr == pc {
			return bp, true
		}
	}
	if bp, ok := dbp.BreakPoints[pc]; ok {
		return bp, true
	}
	return nil, false
}

D
Derek Parker 已提交
503
// Returns a new DebuggedProcess struct.
504
func initializeDebugProcess(dbp *DebuggedProcess, path string, attach bool) (*DebuggedProcess, error) {
D
Derek Parker 已提交
505
	if attach {
506
		err := sys.PtraceAttach(dbp.Pid)
D
Derek Parker 已提交
507 508 509
		if err != nil {
			return nil, err
		}
510
		_, _, err = wait(dbp.Pid, 0)
D
Derek Parker 已提交
511 512 513 514 515
		if err != nil {
			return nil, err
		}
	}

516
	proc, err := os.FindProcess(dbp.Pid)
D
Derek Parker 已提交
517 518 519 520 521
	if err != nil {
		return nil, err
	}

	dbp.Process = proc
522
	err = dbp.LoadInformation(path)
D
Derek Parker 已提交
523 524 525 526 527 528 529 530
	if err != nil {
		return nil, err
	}

	if err := dbp.updateThreadList(); err != nil {
		return nil, err
	}

531 532 533 534 535
	switch runtime.GOARCH {
	case "amd64":
		dbp.arch = AMD64Arch()
	}

536
	return dbp, nil
D
Derek Parker 已提交
537
}
538

D
Derek Parker 已提交
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
func (dbp *DebuggedProcess) clearTempBreakpoints() error {
	for _, bp := range dbp.HWBreakPoints {
		if bp != nil && bp.Temp {
			if _, err := dbp.Clear(bp.Addr); err != nil {
				return err
			}
		}
	}
	for _, bp := range dbp.BreakPoints {
		if !bp.Temp {
			continue
		}
		if _, err := dbp.Clear(bp.Addr); err != nil {
			return err
		}
	}
	return nil
}
557

558
func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, error) {
D
Derek Parker 已提交
559 560
	thread, ok := dbp.Threads[id]
	if !ok {
561
		return nil, fmt.Errorf("could not find thread for %d", id)
D
Derek Parker 已提交
562
	}
D
Derek Parker 已提交
563
	pc, err := thread.PC()
D
Derek Parker 已提交
564
	if err != nil {
565
		return nil, err
D
Derek Parker 已提交
566 567 568 569
	}
	// Check for hardware breakpoint
	for _, bp := range dbp.HWBreakPoints {
		if bp != nil && bp.Addr == pc {
570
			thread.CurrentBreakpoint = bp
571
			return thread, nil
D
Derek Parker 已提交
572 573 574 575
		}
	}
	// Check to see if we have hit a software breakpoint.
	if bp, ok := dbp.BreakPoints[pc-1]; ok {
576
		thread.CurrentBreakpoint = bp
D
Derek Parker 已提交
577 578 579 580 581 582
		if err = thread.SetPC(bp.Addr); err != nil {
			return nil, err
		}
		return thread, nil
	}
	if dbp.halt {
583
		return thread, nil
D
Derek Parker 已提交
584
	}
D
Derek Parker 已提交
585 586
	fn := dbp.goSymTable.PCToFunc(pc)
	if fn != nil && fn.Name == "runtime.breakpoint" {
587 588
		thread.singleStepping = true
		defer func() { thread.singleStepping = false }()
D
Derek Parker 已提交
589 590 591 592 593 594 595
		for i := 0; i < 2; i++ {
			if err := thread.Step(); err != nil {
				return nil, err
			}
		}
		return thread, nil
	}
596
	return nil, NoBreakPointError{addr: pc}
D
Derek Parker 已提交
597
}
D
Derek Parker 已提交
598

D
Derek Parker 已提交
599
func (dbp *DebuggedProcess) run(fn func() error) error {
600
	if dbp.exited {
D
Derek Parker 已提交
601
		return fmt.Errorf("process has already exited")
602
	}
D
Derek Parker 已提交
603 604
	dbp.running = true
	dbp.halt = false
605 606 607
	for _, th := range dbp.Threads {
		th.CurrentBreakpoint = nil
	}
D
Derek Parker 已提交
608 609 610 611 612 613 614 615
	defer func() { dbp.running = false }()
	if err := fn(); err != nil {
		if _, ok := err.(ManualStopError); !ok {
			return err
		}
	}
	return nil
}