debugger.go 9.7 KB
Newer Older
D
Dan Mace 已提交
1 2 3
package debugger

import (
4
	"errors"
D
Dan Mace 已提交
5 6 7 8
	"fmt"
	"log"
	"regexp"

D
Derek Parker 已提交
9
	"github.com/derekparker/delve/proc"
D
Dan Mace 已提交
10 11 12
	"github.com/derekparker/delve/service/api"
)

D
Derek Parker 已提交
13 14 15
// Debugger service.
//
// Debugger provides a higher level of
D
Derek Parker 已提交
16
// abstraction over proc.Process.
D
Derek Parker 已提交
17 18 19 20
// It handles converting from internal types to
// the types expected by clients. It also handles
// functionality needed by clients, but not needed in
// lower lever packages such as proc.
D
Dan Mace 已提交
21
type Debugger struct {
22
	config  *Config
D
Derek Parker 已提交
23
	process *proc.Process
D
Dan Mace 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
}

// Config provides the configuration to start a Debugger.
//
// Only one of ProcessArgs or AttachPid should be specified. If ProcessArgs is
// provided, a new process will be launched. Otherwise, the debugger will try
// to attach to an existing process with AttachPid.
type Config struct {
	// ProcessArgs are the arguments to launch a new process.
	ProcessArgs []string
	// AttachPid is the PID of an existing process to which the debugger should
	// attach.
	AttachPid int
}

// New creates a new Debugger.
40 41 42
func New(config *Config) (*Debugger, error) {
	d := &Debugger{
		config: config,
D
Dan Mace 已提交
43 44 45 46 47
	}

	// Create the process by either attaching or launching.
	if d.config.AttachPid > 0 {
		log.Printf("attaching to pid %d", d.config.AttachPid)
D
Derek Parker 已提交
48
		p, err := proc.Attach(d.config.AttachPid)
D
Dan Mace 已提交
49
		if err != nil {
50
			return nil, attachErrorMessage(d.config.AttachPid, err)
D
Dan Mace 已提交
51 52 53 54
		}
		d.process = p
	} else {
		log.Printf("launching process with args: %v", d.config.ProcessArgs)
D
Derek Parker 已提交
55
		p, err := proc.Launch(d.config.ProcessArgs)
D
Dan Mace 已提交
56
		if err != nil {
57
			return nil, fmt.Errorf("could not launch process: %s", err)
D
Dan Mace 已提交
58 59 60
		}
		d.process = p
	}
61
	return d, nil
D
Dan Mace 已提交
62 63 64
}

func (d *Debugger) Detach(kill bool) error {
65
	return d.process.Detach(kill)
D
Dan Mace 已提交
66 67 68
}

func (d *Debugger) State() (*api.DebuggerState, error) {
69 70 71 72 73 74 75 76
	var (
		state  *api.DebuggerState
		thread *api.Thread
	)
	th := d.process.CurrentThread
	if th != nil {
		thread = api.ConvertThread(th)
	}
D
Dan Mace 已提交
77

78 79 80 81 82
	var breakpoint *api.Breakpoint
	bp := d.process.CurrentBreakpoint()
	if bp != nil {
		breakpoint = api.ConvertBreakpoint(bp)
	}
D
Dan Mace 已提交
83

84 85 86 87 88
	state = &api.DebuggerState{
		Breakpoint:    breakpoint,
		CurrentThread: thread,
		Exited:        d.process.Exited(),
	}
D
Dan Mace 已提交
89

90
	return state, nil
D
Dan Mace 已提交
91 92
}

D
Derek Parker 已提交
93 94
func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
	var createdBp *api.Breakpoint
95 96 97 98 99 100 101 102 103
	var loc string
	switch {
	case len(requestedBp.File) > 0:
		loc = fmt.Sprintf("%s:%d", requestedBp.File, requestedBp.Line)
	case len(requestedBp.FunctionName) > 0:
		loc = requestedBp.FunctionName
	default:
		return nil, fmt.Errorf("no file or function name specified")
	}
D
Dan Mace 已提交
104

105
	bp, err := d.process.SetBreakpointByLocation(loc)
106 107 108
	if err != nil {
		return nil, err
	}
A
aarzilli 已提交
109 110 111
	bp.Tracepoint = requestedBp.Tracepoint
	bp.Goroutine = requestedBp.Goroutine
	bp.Stacktrace = requestedBp.Stacktrace
D
Derek Parker 已提交
112
	bp.Variables = requestedBp.Variables
113 114 115
	createdBp = api.ConvertBreakpoint(bp)
	log.Printf("created breakpoint: %#v", createdBp)
	return createdBp, nil
D
Dan Mace 已提交
116 117
}

D
Derek Parker 已提交
118 119
func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
	var clearedBp *api.Breakpoint
120
	bp, err := d.process.ClearBreakpoint(requestedBp.Addr)
121 122 123 124 125
	if err != nil {
		return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
	}
	clearedBp = api.ConvertBreakpoint(bp)
	log.Printf("cleared breakpoint: %#v", clearedBp)
D
Dan Mace 已提交
126 127 128
	return clearedBp, err
}

D
Derek Parker 已提交
129 130
func (d *Debugger) Breakpoints() []*api.Breakpoint {
	bps := []*api.Breakpoint{}
131 132 133
	for _, bp := range d.process.Breakpoints {
		if bp.Temp {
			continue
D
Dan Mace 已提交
134
		}
135 136
		bps = append(bps, api.ConvertBreakpoint(bp))
	}
D
Dan Mace 已提交
137 138 139
	return bps
}

D
Derek Parker 已提交
140 141
func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
	for _, bp := range d.Breakpoints() {
D
Dan Mace 已提交
142 143 144 145 146 147 148 149 150
		if bp.ID == id {
			return bp
		}
	}
	return nil
}

func (d *Debugger) Threads() []*api.Thread {
	threads := []*api.Thread{}
151 152 153
	for _, th := range d.process.Threads {
		threads = append(threads, api.ConvertThread(th))
	}
D
Dan Mace 已提交
154 155 156 157 158 159 160 161 162 163 164 165
	return threads
}

func (d *Debugger) FindThread(id int) *api.Thread {
	for _, thread := range d.Threads() {
		if thread.ID == id {
			return thread
		}
	}
	return nil
}

D
Derek Parker 已提交
166
// Command handles commands which control the debugger lifecycle
D
Dan Mace 已提交
167 168 169 170
func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, error) {
	var err error
	switch command.Name {
	case api.Continue:
171 172
		log.Print("continuing")
		err = d.process.Continue()
173 174 175 176
		state, stateErr := d.State()
		if stateErr != nil {
			return state, stateErr
		}
A
aarzilli 已提交
177
		if err != nil {
178
			if exitedErr, exited := err.(proc.ProcessExitedError); exited {
179 180 181 182
				state.Exited = true
				state.ExitStatus = exitedErr.Status
				state.Err = errors.New(exitedErr.Error())
				return state, nil
A
aarzilli 已提交
183 184 185 186 187 188
			}
			return nil, err
		}
		err = d.collectBreakpointInformation(state)
		return state, err

D
Dan Mace 已提交
189
	case api.Next:
190 191
		log.Print("nexting")
		err = d.process.Next()
D
Dan Mace 已提交
192
	case api.Step:
193 194
		log.Print("stepping")
		err = d.process.Step()
D
Dan Mace 已提交
195
	case api.SwitchThread:
196 197
		log.Printf("switching to thread %d", command.ThreadID)
		err = d.process.SwitchThread(command.ThreadID)
D
Dan Mace 已提交
198 199 200 201 202 203 204
	case api.Halt:
		// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
		// access the process directly.
		log.Print("halting")
		err = d.process.RequestManualStop()
	}
	if err != nil {
D
Derek Parker 已提交
205
		return nil, err
D
Dan Mace 已提交
206 207 208 209
	}
	return d.State()
}

A
aarzilli 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error {
	if state == nil || state.Breakpoint == nil {
		return nil
	}

	bp := state.Breakpoint
	bpi := &api.BreakpointInfo{}
	state.BreakpointInfo = bpi

	if bp.Goroutine {
		g, err := d.process.CurrentThread.GetG()
		if err != nil {
			return err
		}
		bpi.Goroutine = api.ConvertGoroutine(g)
	}

	if bp.Stacktrace > 0 {
D
Derek Parker 已提交
228
		rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace)
A
aarzilli 已提交
229 230 231
		if err != nil {
			return err
		}
D
Derek Parker 已提交
232
		bpi.Stacktrace = convertStacktrace(rawlocs)
A
aarzilli 已提交
233 234
	}

D
Derek Parker 已提交
235 236
	if len(bp.Variables) > 0 {
		bpi.Variables = make([]api.Variable, len(bp.Variables))
A
aarzilli 已提交
237
	}
D
Derek Parker 已提交
238 239
	for i := range bp.Variables {
		v, err := d.process.CurrentThread.EvalVariable(bp.Variables[i])
A
aarzilli 已提交
240 241 242 243 244 245 246 247 248
		if err != nil {
			return err
		}
		bpi.Variables[i] = api.ConvertVar(v)
	}

	return nil
}

D
Dan Mace 已提交
249 250 251 252 253 254 255
func (d *Debugger) Sources(filter string) ([]string, error) {
	regex, err := regexp.Compile(filter)
	if err != nil {
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
	}

	files := []string{}
256 257 258
	for f := range d.process.Sources() {
		if regex.Match([]byte(f)) {
			files = append(files, f)
D
Dan Mace 已提交
259
		}
260
	}
D
Dan Mace 已提交
261 262 263 264 265 266 267 268 269 270
	return files, nil
}

func (d *Debugger) Functions(filter string) ([]string, error) {
	regex, err := regexp.Compile(filter)
	if err != nil {
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
	}

	funcs := []string{}
271 272 273
	for _, f := range d.process.Funcs() {
		if f.Sym != nil && regex.Match([]byte(f.Name)) {
			funcs = append(funcs, f.Name)
D
Dan Mace 已提交
274
		}
275
	}
D
Dan Mace 已提交
276 277 278 279 280 281 282 283 284 285
	return funcs, nil
}

func (d *Debugger) PackageVariables(threadID int, filter string) ([]api.Variable, error) {
	regex, err := regexp.Compile(filter)
	if err != nil {
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
	}

	vars := []api.Variable{}
286 287 288 289 290 291 292 293 294 295 296
	thread, found := d.process.Threads[threadID]
	if !found {
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
	}
	pv, err := thread.PackageVariables()
	if err != nil {
		return nil, err
	}
	for _, v := range pv {
		if regex.Match([]byte(v.Name)) {
			vars = append(vars, api.ConvertVar(v))
D
Dan Mace 已提交
297
		}
298
	}
D
Dan Mace 已提交
299 300 301
	return vars, err
}

302 303 304 305 306 307 308 309 310 311 312 313
func (d *Debugger) Registers(threadID int) (string, error) {
	thread, found := d.process.Threads[threadID]
	if !found {
		return "", fmt.Errorf("couldn't find thread %d", threadID)
	}
	regs, err := thread.Registers()
	if err != nil {
		return "", err
	}
	return regs.String(), err
}

314 315
func (d *Debugger) LocalVariables(threadID int) ([]api.Variable, error) {
	vars := []api.Variable{}
316 317 318 319 320 321 322 323 324 325 326
	thread, found := d.process.Threads[threadID]
	if !found {
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
	}
	pv, err := thread.LocalVariables()
	if err != nil {
		return nil, err
	}
	for _, v := range pv {
		vars = append(vars, api.ConvertVar(v))
	}
327 328 329 330 331
	return vars, err
}

func (d *Debugger) FunctionArguments(threadID int) ([]api.Variable, error) {
	vars := []api.Variable{}
332 333 334
	thread, found := d.process.Threads[threadID]
	if !found {
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
D
Dan Mace 已提交
335
	}
336 337 338
	pv, err := thread.FunctionArguments()
	if err != nil {
		return nil, err
D
Dan Mace 已提交
339
	}
340 341
	for _, v := range pv {
		vars = append(vars, api.ConvertVar(v))
D
Dan Mace 已提交
342
	}
343
	return vars, err
D
Dan Mace 已提交
344 345
}

346 347 348 349 350 351 352 353
func (d *Debugger) EvalVariableInThread(threadID int, symbol string) (*api.Variable, error) {
	thread, found := d.process.Threads[threadID]
	if !found {
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
	}
	v, err := thread.EvalVariable(symbol)
	if err != nil {
		return nil, err
D
Dan Mace 已提交
354
	}
355 356
	converted := api.ConvertVar(v)
	return &converted, err
D
Dan Mace 已提交
357 358
}

359 360 361 362 363
func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
	goroutines := []*api.Goroutine{}
	gs, err := d.process.GoroutinesInfo()
	if err != nil {
		return nil, err
D
Dan Mace 已提交
364
	}
365 366
	for _, g := range gs {
		goroutines = append(goroutines, api.ConvertGoroutine(g))
D
Dan Mace 已提交
367
	}
368
	return goroutines, err
D
Dan Mace 已提交
369
}
A
aarzilli 已提交
370 371 372 373 374 375

func (d *Debugger) Stacktrace(goroutineId, depth int) ([]api.Location, error) {
	var rawlocs []proc.Location
	var err error

	if goroutineId < 0 {
D
Derek Parker 已提交
376
		rawlocs, err = d.process.CurrentThread.Stacktrace(depth)
A
aarzilli 已提交
377 378 379 380 381 382 383 384 385 386
		if err != nil {
			return nil, err
		}
	} else {
		gs, err := d.process.GoroutinesInfo()
		if err != nil {
			return nil, err
		}
		for _, g := range gs {
			if g.Id == goroutineId {
D
Derek Parker 已提交
387
				rawlocs, err = d.process.GoroutineStacktrace(g, depth)
A
aarzilli 已提交
388 389 390 391 392 393 394 395 396 397 398 399
				if err != nil {
					return nil, err
				}
				break
			}
		}

		if rawlocs == nil {
			return nil, fmt.Errorf("Unknown goroutine id %d\n", goroutineId)
		}
	}

D
Derek Parker 已提交
400
	return convertStacktrace(rawlocs), nil
A
aarzilli 已提交
401 402
}

D
Derek Parker 已提交
403 404
func convertStacktrace(rawlocs []proc.Location) []api.Location {
	locations := make([]api.Location, 0, len(rawlocs))
A
aarzilli 已提交
405 406 407 408 409
	for i := range rawlocs {
		rawlocs[i].Line--
		locations = append(locations, api.ConvertLocation(rawlocs[i]))
	}

A
aarzilli 已提交
410
	return locations
A
aarzilli 已提交
411
}