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

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

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

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

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

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

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

53 54 55 56 57 58 59 60 61 62 63
// 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 已提交
64
// Attach to an existing process with the given PID.
65 66 67 68 69 70 71 72
func Attach(pid int) (*DebuggedProcess, error) {
	dbp, err := newDebugProcess(pid, true)
	if err != nil {
		return nil, err
	}
	return dbp, nil
}

D
Derek Parker 已提交
73 74 75
// Create and begin debugging a new process. First entry in
// `cmd` is the program to run, and then rest are the arguments
// to be supplied to that process.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
func Launch(cmd []string) (*DebuggedProcess, error) {
	proc := exec.Command(cmd[0])
	proc.Args = cmd
	proc.Stdout = os.Stdout
	proc.Stderr = os.Stderr
	proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}

	if err := proc.Start(); err != nil {
		return nil, err
	}

	_, _, err := wait(proc.Process.Pid, 0)
	if err != nil {
		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
	}

	return newDebugProcess(proc.Process.Pid, false)
}

95 96 97 98 99 100
// Returns whether or not Delve thinks the debugged
// process has exited.
func (dbp *DebuggedProcess) Exited() bool {
	return dbp.exited
}

D
Derek Parker 已提交
101 102
// Returns whether or not Delve thinks the debugged
// process is currently executing.
D
Derek Parker 已提交
103 104 105 106
func (dbp *DebuggedProcess) Running() bool {
	return dbp.running
}

D
Derek Parker 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
// Finds the executable and then uses it
// to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
func (dbp *DebuggedProcess) LoadInformation() error {
	var wg sync.WaitGroup

	exe, err := dbp.findExecutable()
	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
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
// 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
		}

145
		pc, _, err := dbp.goSymTable.LineToPC(fileName, line)
146 147 148 149
		if err != nil {
			return 0, err
		}
		return pc, nil
D
Derek Parker 已提交
150
	}
151

D
Derek Parker 已提交
152
	// Try to lookup by function name
153
	fn := dbp.goSymTable.LookupFunc(str)
D
Derek Parker 已提交
154 155 156 157 158 159 160 161 162
	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)
	}
163

D
Derek Parker 已提交
164 165 166 167
	// Use as breakpoint id
	for _, bp := range dbp.HWBreakPoints {
		if bp == nil {
			continue
168
		}
D
Derek Parker 已提交
169 170
		if uint64(bp.ID) == id {
			return bp.Addr, nil
171 172
		}
	}
D
Derek Parker 已提交
173 174 175 176 177 178 179 180
	for _, bp := range dbp.BreakPoints {
		if uint64(bp.ID) == id {
			return bp.Addr, nil
		}
	}

	// Last resort, use as raw address
	return id, nil
181 182
}

D
Derek Parker 已提交
183 184
// Sends out a request that the debugged process halt
// execution. Sends SIGSTOP to all threads.
D
Derek Parker 已提交
185
func (dbp *DebuggedProcess) RequestManualStop() error {
D
Derek Parker 已提交
186
	dbp.halt = true
D
Derek Parker 已提交
187 188 189
	err := dbp.requestManualStop()
	if err != nil {
		return err
D
Derek Parker 已提交
190 191
	}
	dbp.running = false
D
Derek Parker 已提交
192
	return nil
D
Derek Parker 已提交
193 194
}

D
Derek Parker 已提交
195 196 197 198 199 200 201 202 203
// 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.
204
func (dbp *DebuggedProcess) Break(addr uint64) (*BreakPoint, error) {
205 206 207
	return dbp.setBreakpoint(dbp.CurrentThread.Id, addr, false)
}

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

// 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 已提交
219
	return dbp.Break(addr)
220 221 222
}

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

// 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 已提交
233
	return dbp.Clear(addr)
234 235 236
}

// Returns the status of the current main thread context.
P
Paul Sbarra 已提交
237
func (dbp *DebuggedProcess) Status() *sys.WaitStatus {
238 239 240 241 242
	return dbp.CurrentThread.Status
}

// Step over function calls.
func (dbp *DebuggedProcess) Next() error {
243 244 245
	if err := dbp.setChanRecvBreakpoints(); err != nil {
		return err
	}
D
Derek Parker 已提交
246 247
	return dbp.run(dbp.next)
}
248

D
Derek Parker 已提交
249
func (dbp *DebuggedProcess) next() error {
D
Derek Parker 已提交
250
	// Make sure we clean up the temp breakpoints created by thread.Next
251 252
	defer dbp.clearTempBreakpoints()

D
Derek Parker 已提交
253 254 255 256
	curg, err := dbp.CurrentThread.curG()
	if err != nil {
		return err
	}
257 258

	var goroutineExiting bool
D
Derek Parker 已提交
259 260
	for _, th := range dbp.Threads {
		if th.blocked() { // Continue threads that aren't running go code.
D
Derek Parker 已提交
261
			if err = th.Continue(); err != nil {
D
Derek Parker 已提交
262
				return err
263
			}
D
Derek Parker 已提交
264 265
			continue
		}
266 267 268 269 270 271 272 273 274 275
		if err = th.Next(); err != nil {
			if err, ok := err.(GoroutineExitingError); ok {
				if err.goid == curg.Id {
					goroutineExiting = true
				}
				if err := th.Continue(); err != nil {
					return err
				}
				continue
			}
D
Derek Parker 已提交
276 277 278
			return err
		}
	}
279

D
Derek Parker 已提交
280
	for {
281
		thread, err := trapWait(dbp, -1)
D
Derek Parker 已提交
282 283
		if err != nil {
			return err
D
Derek Parker 已提交
284
		}
285 286 287
		if goroutineExiting {
			break
		}
288 289 290
		if thread.CurrentBreakpoint != nil {
			bp := thread.CurrentBreakpoint
			if bp != nil && !bp.hardware {
291 292 293 294
				if err = thread.SetPC(bp.Addr); err != nil {
					return err
				}
			}
D
Derek Parker 已提交
295 296
		}
		// Grab the current goroutine for this thread.
297
		tg, err := thread.curG()
D
Derek Parker 已提交
298 299 300 301
		if err != nil {
			return err
		}
		// Make sure we're on the same goroutine.
302
		// TODO(dp) better take into account goroutine exit.
303
		if tg.Id == curg.Id {
304 305
			if dbp.CurrentThread != thread {
				dbp.SwitchThread(thread.Id)
D
Derek Parker 已提交
306
			}
D
Derek Parker 已提交
307
			break
308 309
		}
	}
D
Derek Parker 已提交
310
	return dbp.Halt()
311 312
}

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

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 := trapWait(dbp, -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 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
func (dbp *DebuggedProcess) Step() (err error) {
	fn := func() error {
		for _, th := range dbp.Threads {
			if th.blocked() {
				continue
			}
			err := th.Step()
			if err != nil {
				return err
			}
		}
		return nil
	}

	return dbp.run(fn)
}

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

D
Derek Parker 已提交
401 402
// Returns an array of G structures representing the information
// Delve cares about from the internal runtime G structure.
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) {
	var (
		allg   []*G
		reader = dbp.dwarf.Reader()
	)

	allglen, err := allglenval(dbp, reader)
	if err != nil {
		return nil, err
	}
	reader.Seek(0)
	allgentryaddr, err := addressFor(dbp, "runtime.allg", reader)
	if err != nil {
		return nil, err
	}
	faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), ptrsize)
	allgptr := binary.LittleEndian.Uint64(faddr)

	for i := uint64(0); i < allglen; i++ {
422
		g, err := parseG(dbp.CurrentThread, allgptr+(i*uint64(ptrsize)), reader)
423 424 425 426 427 428 429 430
		if err != nil {
			return nil, err
		}
		allg = append(allg, g)
	}
	return allg, nil
}

D
Derek Parker 已提交
431
// Stop all threads.
D
Derek Parker 已提交
432 433 434 435 436 437 438 439 440
func (dbp *DebuggedProcess) Halt() (err error) {
	for _, th := range dbp.Threads {
		if err := th.Halt(); err != nil {
			return err
		}
	}
	return nil
}

441 442
// Obtains register values from what Delve considers to be the current
// thread of the traced process.
443
func (dbp *DebuggedProcess) Registers() (Registers, error) {
444 445 446
	return dbp.CurrentThread.Registers()
}

447
// Returns the PC of the current thread.
D
Derek Parker 已提交
448 449
func (dbp *DebuggedProcess) PC() (uint64, error) {
	return dbp.CurrentThread.PC()
450 451
}

452 453 454 455 456
// Returns the PC of the current thread.
func (dbp *DebuggedProcess) CurrentBreakpoint() *BreakPoint {
	return dbp.CurrentThread.CurrentBreakpoint
}

457 458 459 460 461
// Returns the value of the named symbol.
func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
	return dbp.CurrentThread.EvalSymbol(name)
}

462
func (dbp *DebuggedProcess) CallFn(name string, fn func() error) error {
D
Derek Parker 已提交
463 464 465
	return dbp.CurrentThread.CallFn(name, fn)
}

466 467
// Returns a reader for the dwarf data
func (dbp *DebuggedProcess) DwarfReader() *reader.Reader {
468 469 470
	return reader.New(dbp.dwarf)
}

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

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

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

D
Derek Parker 已提交
486 487 488 489 490 491 492 493 494 495 496 497 498
// 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 已提交
499 500 501 502 503 504 505
// Returns a new DebuggedProcess struct.
func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) {
	dbp := DebuggedProcess{
		Pid:         pid,
		Threads:     make(map[int]*ThreadContext),
		BreakPoints: make(map[uint64]*BreakPoint),
		os:          new(OSProcessDetails),
D
Derek Parker 已提交
506
		ast:         source.New(),
D
Derek Parker 已提交
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	}

	if attach {
		err := sys.PtraceAttach(pid)
		if err != nil {
			return nil, err
		}
		_, _, err = wait(pid, 0)
		if err != nil {
			return nil, err
		}
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return nil, err
	}

	dbp.Process = proc
	err = dbp.LoadInformation()
	if err != nil {
		return nil, err
	}

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

	return &dbp, nil
}
537

D
Derek Parker 已提交
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
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
}
556

557
func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, error) {
D
Derek Parker 已提交
558 559
	thread, ok := dbp.Threads[id]
	if !ok {
560
		return nil, fmt.Errorf("could not find thread for %d", id)
D
Derek Parker 已提交
561
	}
D
Derek Parker 已提交
562
	pc, err := thread.PC()
D
Derek Parker 已提交
563
	if err != nil {
564
		return nil, err
D
Derek Parker 已提交
565 566 567 568
	}
	// Check for hardware breakpoint
	for _, bp := range dbp.HWBreakPoints {
		if bp != nil && bp.Addr == pc {
569
			thread.CurrentBreakpoint = bp
570
			return thread, nil
D
Derek Parker 已提交
571 572 573 574
		}
	}
	// Check to see if we have hit a software breakpoint.
	if bp, ok := dbp.BreakPoints[pc-1]; ok {
575
		thread.CurrentBreakpoint = bp
576
		return thread, nil
D
Derek Parker 已提交
577
	}
578
	return thread, nil
D
Derek Parker 已提交
579
}
D
Derek Parker 已提交
580

D
Derek Parker 已提交
581
func (dbp *DebuggedProcess) run(fn func() error) error {
582 583 584
	if dbp.exited {
		return fmt.Errorf("process has already exited")
	}
D
Derek Parker 已提交
585 586
	dbp.running = true
	dbp.halt = false
587 588 589
	for _, th := range dbp.Threads {
		th.CurrentBreakpoint = nil
	}
D
Derek Parker 已提交
590 591 592 593 594 595 596 597
	defer func() { dbp.running = false }()
	if err := fn(); err != nil {
		if _, ok := err.(ManualStopError); !ok {
			return err
		}
	}
	return nil
}