proctl.go 9.1 KB
Newer Older
1 2 3 4 5
// Package proctl provides functions for attaching to and manipulating
// a process during the debug session.
package proctl

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

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

18
	"github.com/derekparker/delve/dwarf/frame"
19
	"github.com/derekparker/delve/dwarf/reader"
20 21
)

D
Derek Parker 已提交
22 23 24
// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
type DebuggedProcess struct {
25 26 27 28
	Pid                 int
	Process             *os.Process
	Dwarf               *dwarf.Data
	GoSymTable          *gosym.Table
29
	FrameEntries        frame.FrameDescriptionEntries
D
Derek Parker 已提交
30
	HWBreakPoints       [4]*BreakPoint
31 32 33
	BreakPoints         map[uint64]*BreakPoint
	Threads             map[int]*ThreadContext
	CurrentThread       *ThreadContext
D
Derek Parker 已提交
34
	os                  *OSProcessDetails
35 36 37
	breakpointIDCounter int
	running             bool
	halt                bool
D
Derek Parker 已提交
38 39
}

D
Derek Parker 已提交
40 41
// A ManualStopError happens when the user triggers a
// manual stop via SIGERM.
D
Derek Parker 已提交
42 43 44 45 46 47
type ManualStopError struct{}

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

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

D
Derek Parker 已提交
68 69 70
// 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.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
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)
}

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

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
// 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
		}

		pc, _, err := dbp.GoSymTable.LineToPC(fileName, line)
		if err != nil {
			return 0, err
		}
		return pc, nil
	} else {
		// Try to lookup by function name
		fn := dbp.GoSymTable.LookupFunc(str)
		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)
		}

		// Use as breakpoint id
131 132 133 134 135 136 137 138
		for _, bp := range dbp.HWBreakPoints {
			if bp == nil {
				continue
			}
			if uint64(bp.ID) == id {
				return bp.Addr, nil
			}
		}
139 140 141 142 143 144 145 146 147 148 149
		for _, bp := range dbp.BreakPoints {
			if uint64(bp.ID) == id {
				return bp.Addr, nil
			}
		}

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

D
Derek Parker 已提交
150 151
// Sends out a request that the debugged process halt
// execution. Sends SIGSTOP to all threads.
D
Derek Parker 已提交
152 153 154
func (dbp *DebuggedProcess) RequestManualStop() {
	dbp.halt = true
	for _, th := range dbp.Threads {
D
Derek Parker 已提交
155
		th.Halt()
D
Derek Parker 已提交
156 157 158 159
	}
	dbp.running = false
}

D
Derek Parker 已提交
160 161 162 163 164 165 166 167 168
// 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.
169
func (dbp *DebuggedProcess) Break(addr uint64) (*BreakPoint, error) {
D
Derek Parker 已提交
170
	return dbp.setBreakpoint(dbp.CurrentThread.Id, addr)
171 172 173 174 175 176 177 178
}

// 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 已提交
179
	return dbp.Break(addr)
180 181 182
}

// Clears a breakpoint in the current thread.
183
func (dbp *DebuggedProcess) Clear(addr uint64) (*BreakPoint, error) {
D
Derek Parker 已提交
184
	return dbp.clearBreakpoint(dbp.CurrentThread.Id, addr)
185 186 187 188 189 190 191 192
}

// 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 已提交
193
	return dbp.Clear(addr)
194 195 196
}

// Returns the status of the current main thread context.
P
Paul Sbarra 已提交
197
func (dbp *DebuggedProcess) Status() *sys.WaitStatus {
198 199 200 201 202
	return dbp.CurrentThread.Status
}

// Step over function calls.
func (dbp *DebuggedProcess) Next() error {
D
Derek Parker 已提交
203
	var runnable []*ThreadContext
204

D
Derek Parker 已提交
205
	fn := func() error {
D
Derek Parker 已提交
206 207 208 209 210
		for _, th := range dbp.Threads {
			// Continue any blocked M so that the
			// scheduler can continue to do its'
			// job correctly.
			if th.blocked() {
D
Derek Parker 已提交
211 212 213 214 215
				err := th.Continue()
				if err != nil {
					return err
				}
				continue
216 217
			}

D
Derek Parker 已提交
218 219 220
			runnable = append(runnable, th)
		}
		for _, th := range runnable {
D
Derek Parker 已提交
221
			err := th.Next()
P
Paul Sbarra 已提交
222
			if err != nil && err != sys.ESRCH {
D
Derek Parker 已提交
223 224
				return err
			}
225
		}
D
Derek Parker 已提交
226
		return dbp.Halt()
227
	}
D
Derek Parker 已提交
228
	return dbp.run(fn)
229 230 231 232 233 234 235 236 237 238 239
}

// Resume process.
func (dbp *DebuggedProcess) Continue() error {
	for _, thread := range dbp.Threads {
		err := thread.Continue()
		if err != nil {
			return err
		}
	}

D
Derek Parker 已提交
240
	fn := func() error {
241
		wpid, err := trapWait(dbp, -1)
D
Derek Parker 已提交
242 243 244
		if err != nil {
			return err
		}
D
Derek Parker 已提交
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
		thread, ok := dbp.Threads[wpid]
		if !ok {
			return fmt.Errorf("could not find thread for %d", wpid)
		}

		if wpid != dbp.CurrentThread.Id {
			fmt.Printf("thread context changed from %d to %d\n", dbp.CurrentThread.Id, thread.Id)
			dbp.CurrentThread = thread
		}

		pc, err := thread.CurrentPC()
		if err != nil {
			return err
		}

		// Check to see if we hit a runtime.breakpoint
		fn := dbp.GoSymTable.PCToFunc(pc)
		if fn != nil && fn.Name == "runtime.breakpoint" {
			// step twice to get back to user code
			for i := 0; i < 2; i++ {
				err = thread.Step()
				if err != nil {
					return err
				}
			}
			dbp.Halt()
			return nil
		}

		// Check for hardware breakpoint
		for _, bp := range dbp.HWBreakPoints {
			if bp != nil && bp.Addr == pc {
277
				if !bp.Temp {
D
Derek Parker 已提交
278 279 280 281 282 283 284
					return dbp.Halt()
				}
				return nil
			}
		}
		// Check to see if we have hit a software breakpoint.
		if bp, ok := dbp.BreakPoints[pc-1]; ok {
285
			if !bp.Temp {
D
Derek Parker 已提交
286 287 288 289 290 291
				return dbp.Halt()
			}
			return nil
		}

		return fmt.Errorf("unrecognized breakpoint %#v", pc)
292
	}
D
Derek Parker 已提交
293
	return dbp.run(fn)
294 295
}

D
Derek Parker 已提交
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
// Steps through process.
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)
}

314 315 316 317 318 319 320 321 322
// 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
		return nil
	}
	return fmt.Errorf("thread %d does not exist", tid)
}

323 324
// Obtains register values from what Delve considers to be the current
// thread of the traced process.
325
func (dbp *DebuggedProcess) Registers() (Registers, error) {
326 327 328
	return dbp.CurrentThread.Registers()
}

329
// Returns the PC of the current thread.
330 331 332 333 334 335 336 337 338
func (dbp *DebuggedProcess) CurrentPC() (uint64, error) {
	return dbp.CurrentThread.CurrentPC()
}

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

339 340 341 342 343
// Returns a reader for the dwarf data
func (dbp *DebuggedProcess) DwarfReader() *reader.Reader {
	return reader.New(dbp.Dwarf)
}

D
Derek Parker 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
// 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),
	}

	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
}

D
Derek Parker 已提交
382 383 384 385 386 387 388 389 390 391 392
func (dbp *DebuggedProcess) run(fn func() error) error {
	dbp.running = true
	dbp.halt = false
	defer func() { dbp.running = false }()
	if err := fn(); err != nil {
		if _, ok := err.(ManualStopError); !ok {
			return err
		}
	}
	return nil
}