debugger.go 7.3 KB
Newer Older
D
Dan Mace 已提交
1 2 3 4 5 6 7
package debugger

import (
	"fmt"
	"log"
	"regexp"

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

12 13
// Debugger provides a conveniant way to convert from internal
// structure representations to API representations.
D
Dan Mace 已提交
14
type Debugger struct {
15 16
	config  *Config
	process *proc.DebuggedProcess
D
Dan Mace 已提交
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
}

// 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.
33 34 35
func New(config *Config) (*Debugger, error) {
	d := &Debugger{
		config: config,
D
Dan Mace 已提交
36 37 38 39 40
	}

	// 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 已提交
41
		p, err := proc.Attach(d.config.AttachPid)
D
Dan Mace 已提交
42
		if err != nil {
43
			return nil, fmt.Errorf("couldn't attach to pid %d: %s", d.config.AttachPid, err)
D
Dan Mace 已提交
44 45 46 47
		}
		d.process = p
	} else {
		log.Printf("launching process with args: %v", d.config.ProcessArgs)
D
Derek Parker 已提交
48
		p, err := proc.Launch(d.config.ProcessArgs)
D
Dan Mace 已提交
49
		if err != nil {
50
			return nil, fmt.Errorf("couldn't launch process: %s", err)
D
Dan Mace 已提交
51 52 53
		}
		d.process = p
	}
54
	return d, nil
D
Dan Mace 已提交
55 56 57
}

func (d *Debugger) Detach(kill bool) error {
58
	return d.process.Detach(kill)
D
Dan Mace 已提交
59 60 61
}

func (d *Debugger) State() (*api.DebuggerState, error) {
62 63 64 65 66 67 68 69
	var (
		state  *api.DebuggerState
		thread *api.Thread
	)
	th := d.process.CurrentThread
	if th != nil {
		thread = api.ConvertThread(th)
	}
D
Dan Mace 已提交
70

71 72 73 74 75
	var breakpoint *api.Breakpoint
	bp := d.process.CurrentBreakpoint()
	if bp != nil {
		breakpoint = api.ConvertBreakpoint(bp)
	}
D
Dan Mace 已提交
76

77 78 79 80 81
	state = &api.DebuggerState{
		Breakpoint:    breakpoint,
		CurrentThread: thread,
		Exited:        d.process.Exited(),
	}
D
Dan Mace 已提交
82

83
	return state, nil
D
Dan Mace 已提交
84 85
}

D
Derek Parker 已提交
86 87
func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
	var createdBp *api.Breakpoint
88 89 90 91 92 93 94 95 96
	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 已提交
97

98 99 100 101 102 103 104
	bp, err := d.process.BreakByLocation(loc)
	if err != nil {
		return nil, err
	}
	createdBp = api.ConvertBreakpoint(bp)
	log.Printf("created breakpoint: %#v", createdBp)
	return createdBp, nil
D
Dan Mace 已提交
105 106
}

D
Derek Parker 已提交
107 108
func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
	var clearedBp *api.Breakpoint
109 110 111 112 113 114
	bp, err := d.process.Clear(requestedBp.Addr)
	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 已提交
115 116 117
	return clearedBp, err
}

D
Derek Parker 已提交
118 119
func (d *Debugger) Breakpoints() []*api.Breakpoint {
	bps := []*api.Breakpoint{}
120 121 122
	for _, bp := range d.process.Breakpoints {
		if bp.Temp {
			continue
D
Dan Mace 已提交
123
		}
124 125
		bps = append(bps, api.ConvertBreakpoint(bp))
	}
D
Dan Mace 已提交
126 127 128
	return bps
}

D
Derek Parker 已提交
129 130
func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
	for _, bp := range d.Breakpoints() {
D
Dan Mace 已提交
131 132 133 134 135 136 137 138 139
		if bp.ID == id {
			return bp
		}
	}
	return nil
}

func (d *Debugger) Threads() []*api.Thread {
	threads := []*api.Thread{}
140 141 142
	for _, th := range d.process.Threads {
		threads = append(threads, api.ConvertThread(th))
	}
D
Dan Mace 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	return threads
}

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

// Command handles commands which control the debugger lifecycle. Like other
// debugger operations, these are executed one at a time as part of the
// process operation pipeline.
//
// The one exception is the Halt command, which can be executed concurrently
// with any operation.
func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, error) {
	var err error
	switch command.Name {
	case api.Continue:
165 166
		log.Print("continuing")
		err = d.process.Continue()
D
Dan Mace 已提交
167
	case api.Next:
168 169
		log.Print("nexting")
		err = d.process.Next()
D
Dan Mace 已提交
170
	case api.Step:
171 172
		log.Print("stepping")
		err = d.process.Step()
D
Dan Mace 已提交
173
	case api.SwitchThread:
174 175
		log.Printf("switching to thread %d", command.ThreadID)
		err = d.process.SwitchThread(command.ThreadID)
D
Dan Mace 已提交
176 177 178 179 180 181 182 183
	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 {
		// Only report the error if it's not a process exit.
D
Derek Parker 已提交
184
		if _, exited := err.(proc.ProcessExitedError); !exited {
D
Dan Mace 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197
			return nil, err
		}
	}
	return d.State()
}

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{}
198 199 200
	for f := range d.process.Sources() {
		if regex.Match([]byte(f)) {
			files = append(files, f)
D
Dan Mace 已提交
201
		}
202
	}
D
Dan Mace 已提交
203 204 205 206 207 208 209 210 211 212
	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{}
213 214 215
	for _, f := range d.process.Funcs() {
		if f.Sym != nil && regex.Match([]byte(f.Name)) {
			funcs = append(funcs, f.Name)
D
Dan Mace 已提交
216
		}
217
	}
D
Dan Mace 已提交
218 219 220 221 222 223 224 225 226 227
	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{}
228 229 230 231 232 233 234 235 236 237 238
	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 已提交
239
		}
240
	}
D
Dan Mace 已提交
241 242 243
	return vars, err
}

244 245
func (d *Debugger) LocalVariables(threadID int) ([]api.Variable, error) {
	vars := []api.Variable{}
246 247 248 249 250 251 252 253 254 255 256
	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))
	}
257 258 259 260 261
	return vars, err
}

func (d *Debugger) FunctionArguments(threadID int) ([]api.Variable, error) {
	vars := []api.Variable{}
262 263 264
	thread, found := d.process.Threads[threadID]
	if !found {
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
D
Dan Mace 已提交
265
	}
266 267 268
	pv, err := thread.FunctionArguments()
	if err != nil {
		return nil, err
D
Dan Mace 已提交
269
	}
270 271
	for _, v := range pv {
		vars = append(vars, api.ConvertVar(v))
D
Dan Mace 已提交
272
	}
273
	return vars, err
D
Dan Mace 已提交
274 275
}

276 277 278 279 280 281 282 283
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 已提交
284
	}
285 286
	converted := api.ConvertVar(v)
	return &converted, err
D
Dan Mace 已提交
287 288
}

289 290 291 292 293
func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
	goroutines := []*api.Goroutine{}
	gs, err := d.process.GoroutinesInfo()
	if err != nil {
		return nil, err
D
Dan Mace 已提交
294
	}
295 296
	for _, g := range gs {
		goroutines = append(goroutines, api.ConvertGoroutine(g))
D
Dan Mace 已提交
297
	}
298
	return goroutines, err
D
Dan Mace 已提交
299
}