threads.go 12.6 KB
Newer Older
D
Derek Parker 已提交
1
package proc
D
Derek Parker 已提交
2 3

import (
4
	"debug/gosym"
5
	"encoding/binary"
6
	"errors"
7
	"go/ast"
8
	"go/token"
9
	"path/filepath"
10
	"reflect"
A
aarzilli 已提交
11
	"strings"
D
Derek Parker 已提交
12

13
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
D
Derek Parker 已提交
14 15
)

16 17
// Thread represents a thread.
type Thread interface {
18
	MemoryReadWriter
19 20 21 22 23 24 25 26 27 28 29 30
	Location() (*Location, error)
	// Breakpoint will return the breakpoint that this thread is stopped at or
	// nil if the thread is not stopped at any breakpoint.
	// Active will be true if the thread is stopped at a breakpoint and the
	// breakpoint's condition is met.
	// If there was an error evaluating the breakpoint's condition it will be
	// returned as condErr
	Breakpoint() (breakpoint *Breakpoint, active bool, condErr error)
	ThreadID() int
	Registers(floatingPoint bool) (Registers, error)
	Arch() Arch
	BinInfo() *BinaryInfo
31
	StepInstruction() error
32 33
	// Blocked returns true if the thread is blocked
	Blocked() bool
34 35
	// SetCurrentBreakpoint updates the current breakpoint of this thread
	SetCurrentBreakpoint() error
36 37
}

D
Derek Parker 已提交
38
// Location represents the location of a thread.
D
Derek Parker 已提交
39 40
// Holds information on the current instruction
// address, the source file:line, and the function.
41 42 43 44 45 46 47
type Location struct {
	PC   uint64
	File string
	Line int
	Fn   *gosym.Func
}

D
Derek Parker 已提交
48 49
// ThreadBlockedError is returned when the thread
// is blocked in the scheduler.
50 51 52 53 54 55
type ThreadBlockedError struct{}

func (tbe ThreadBlockedError) Error() string {
	return ""
}

56 57
// topframe returns the two topmost frames of g, or thread if g is nil.
func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
58
	var frames []Stackframe
59
	var err error
60

61
	if g == nil {
62
		if thread.Blocked() {
63
			return Stackframe{}, Stackframe{}, ThreadBlockedError{}
64
		}
65
		frames, err = ThreadStacktrace(thread, 1)
66
	} else {
67
		frames, err = g.Stacktrace(1)
68
	}
D
Derek Parker 已提交
69
	if err != nil {
70
		return Stackframe{}, Stackframe{}, err
D
Derek Parker 已提交
71
	}
72 73 74 75 76 77 78
	switch len(frames) {
	case 0:
		return Stackframe{}, Stackframe{}, errors.New("empty stack trace")
	case 1:
		return frames[0], Stackframe{}, nil
	default:
		return frames[0], frames[1], nil
79 80 81
	}
}

A
aarzilli 已提交
82 83 84 85 86 87 88
// Set breakpoints at every line, and the return address. Also look for
// a deferred function and set a breakpoint there too.
// If stepInto is true it will also set breakpoints inside all
// functions called on the current source line, for non-absolute CALLs
// a breakpoint of kind StepBreakpoint is set on the CALL instruction,
// Continue will take care of setting a breakpoint to the destination
// once the CALL is reached.
89 90 91 92 93 94 95 96 97 98
//
// Regardless of stepInto the following breakpoints will be set:
// - a breakpoint on the first deferred function with NextDeferBreakpoint
//   kind, the list of all the addresses to deferreturn calls in this function
//   and condition checking that we remain on the same goroutine
// - a breakpoint on each line of the function, with a condition checking
//   that we stay on the same stack frame and goroutine.
// - a breakpoint on the return address of the function, with a condition
//   checking that we move to the previous stack frame and stay on the same
//   goroutine.
99
func next(dbp Process, stepInto bool) error {
100 101
	selg := dbp.SelectedGoroutine()
	curthread := dbp.CurrentThread()
102
	topframe, retframe, err := topframe(selg, curthread)
103 104
	if err != nil {
		return err
105
	}
D
Derek Parker 已提交
106

107 108 109 110 111 112 113
	success := false
	defer func() {
		if !success {
			dbp.ClearInternalBreakpoints()
		}
	}()

114 115
	ext := filepath.Ext(topframe.Current.File)
	csource := ext != ".go" && ext != ".s"
116
	var thread MemoryReadWriter = curthread
117
	var regs Registers
118 119 120
	if selg != nil && selg.Thread != nil {
		thread = selg.Thread
		regs, err = selg.Thread.Registers(false)
121 122 123
		if err != nil {
			return err
		}
D
Derek Parker 已提交
124
	}
125

126
	text, err := disassemble(thread, regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.FDE.Begin(), topframe.FDE.End())
A
aarzilli 已提交
127 128 129
	if err != nil && stepInto {
		return err
	}
130

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	sameGCond := SameGoroutineCondition(selg)
	retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi))
	sameFrameCond := andFrameoffCondition(sameGCond, topframe.CFA-int64(topframe.StackHi))
	var sameOrRetFrameCond ast.Expr
	if sameGCond != nil {
		sameOrRetFrameCond = &ast.BinaryExpr{
			Op: token.LAND,
			X:  sameGCond,
			Y: &ast.BinaryExpr{
				Op: token.LOR,
				X:  frameoffCondition(topframe.CFA - int64(topframe.StackHi)),
				Y:  frameoffCondition(retframe.CFA - int64(retframe.StackHi)),
			},
		}
	}
146

A
aarzilli 已提交
147
	if stepInto {
148
		for _, instr := range text {
A
aarzilli 已提交
149 150 151 152 153
			if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
				continue
			}

			if instr.DestLoc != nil && instr.DestLoc.Fn != nil {
154
				if err := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, sameGCond); err != nil {
A
aarzilli 已提交
155 156 157 158
					return err
				}
			} else {
				// Non-absolute call instruction, set a StepBreakpoint here
159
				if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, sameGCond); err != nil {
A
aarzilli 已提交
160 161 162 163
					if _, ok := err.(BreakpointExistsError); !ok {
						return err
					}
				}
164 165 166
			}
		}
	}
167

A
aarzilli 已提交
168 169 170 171 172 173 174 175 176
	if !csource {
		deferreturns := []uint64{}

		// Find all runtime.deferreturn locations in the function
		// See documentation of Breakpoint.DeferCond for why this is necessary
		for _, instr := range text {
			if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && instr.DestLoc.Fn.Name == "runtime.deferreturn" {
				deferreturns = append(deferreturns, instr.Loc.PC)
			}
177
		}
A
aarzilli 已提交
178 179 180

		// Set breakpoint on the most recently deferred function (if any)
		var deferpc uint64 = 0
181 182
		if selg != nil {
			deferPCEntry := selg.DeferPC()
A
Alessandro Arzilli 已提交
183
			if deferPCEntry != 0 {
184
				_, _, deferfn := dbp.BinInfo().PCToLine(deferPCEntry)
A
Alessandro Arzilli 已提交
185
				var err error
186
				deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
A
Alessandro Arzilli 已提交
187 188 189
				if err != nil {
					return err
				}
190 191
			}
		}
A
aarzilli 已提交
192
		if deferpc != 0 && deferpc != topframe.Current.PC {
193
			bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
A
aarzilli 已提交
194 195 196 197 198
			if err != nil {
				if _, ok := err.(BreakpointExistsError); !ok {
					return err
				}
			}
199
			if bp != nil && stepInto {
A
aarzilli 已提交
200 201 202
				bp.DeferReturns = deferreturns
			}
		}
203 204 205
	}

	// Add breakpoints on all the lines in the current function
206
	pcs, err := dbp.BinInfo().lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
207 208 209
	if err != nil {
		return err
	}
D
Derek Parker 已提交
210

A
aarzilli 已提交
211 212 213 214 215 216 217
	if !csource {
		var covered bool
		for i := range pcs {
			if topframe.FDE.Cover(pcs[i]) {
				covered = true
				break
			}
D
Derek Parker 已提交
218
		}
219

A
aarzilli 已提交
220
		if !covered {
221 222
			fn := dbp.BinInfo().goSymTable.PCToFunc(topframe.Ret)
			if selg != nil && fn != nil && fn.Name == "runtime.goexit" {
A
aarzilli 已提交
223 224
				return nil
			}
D
Derek Parker 已提交
225
		}
226
	}
227 228

	// Add a breakpoint on the return address for the current frame
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	for _, pc := range pcs {
		if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, sameFrameCond); err != nil {
			if _, ok := err.(BreakpointExistsError); !ok {
				dbp.ClearInternalBreakpoints()
				return err
			}
		}

	}
	if bp, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond); err != nil {
		if _, isexists := err.(BreakpointExistsError); isexists {
			if bp.Kind == NextBreakpoint {
				// If the return address shares the same address with one of the lines
				// of the function (because we are stepping through a recursive
				// function) then the corresponding breakpoint should be active both on
				// this frame and on the return frame.
				bp.Cond = sameOrRetFrameCond
			}
		}
248 249
		// Return address could be wrong, if we are unable to set a breakpoint
		// there it's ok.
250
	}
251

252 253 254
	if bp, _, _ := curthread.Breakpoint(); bp == nil {
		curthread.SetCurrentBreakpoint()
	}
255
	success = true
256
	return nil
257 258
}

259
func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) error {
A
aarzilli 已提交
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
	if len(text) <= 0 {
		return nil
	}

	instr := text[0]

	if instr.DestLoc == nil || instr.DestLoc.Fn == nil {
		return nil
	}

	fn := instr.DestLoc.Fn

	// Ensure PC and Entry match, otherwise StepInto is likely to set
	// its breakpoint before DestLoc.PC and hence run too far ahead.
	// Calls to runtime.duffzero and duffcopy have this problem.
	if fn.Entry != instr.DestLoc.PC {
		return nil
	}

	// Skip unexported runtime functions
	if strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) {
		return nil
	}

	//TODO(aarzilli): if we want to let users hide functions
	// or entire packages from being stepped into with 'step'
	// those extra checks should be done here.

	// Set a breakpoint after the function's prologue
289
	pc, _ := FirstPCAfterPrologue(dbp, fn, false)
290
	if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
A
aarzilli 已提交
291 292 293 294 295 296
		if _, ok := err.(BreakpointExistsError); !ok {
			return err
		}
	}

	return nil
297
}
298

299
func getGVariable(thread Thread) (*Variable, error) {
A
aarzilli 已提交
300
	regs, err := thread.Registers(false)
301 302 303 304
	if err != nil {
		return nil, err
	}

305 306
	gaddr, hasgaddr := regs.GAddr()
	if !hasgaddr {
307 308
		gaddrbs := make([]byte, thread.Arch().PtrSize())
		_, err := thread.ReadMemory(gaddrbs, uintptr(regs.TLS()+thread.BinInfo().GStructOffset()))
309 310 311 312
		if err != nil {
			return nil, err
		}
		gaddr = binary.LittleEndian.Uint64(gaddrbs)
313
	}
A
go fmt  
aarzilli 已提交
314

315
	return newGVariable(thread, uintptr(gaddr), thread.Arch().DerefTLS())
316 317
}

318
func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) {
319
	typ, err := thread.BinInfo().findType("runtime.g")
320 321 322 323 324 325 326
	if err != nil {
		return nil, err
	}

	name := ""

	if deref {
327
		typ = &godwarf.PtrType{godwarf.CommonType{int64(thread.Arch().PtrSize()), "", reflect.Ptr, 0}, typ}
328 329 330 331
	} else {
		name = "runtime.curg"
	}

332
	return newVariableFromThread(thread, name, gaddr, typ), nil
333 334
}

D
Derek Parker 已提交
335
// GetG returns information on the G (goroutine) that is executing on this thread.
336
//
D
Derek Parker 已提交
337 338
// The G structure for a thread is stored in thread local storage. Here we simply
// calculate the address and read and parse the G struct.
339 340 341 342 343 344 345 346
//
// We cannot simply use the allg linked list in order to find the M that represents
// the given OS thread and follow its G pointer because on Darwin mach ports are not
// universal, so our port for this thread would not map to the `id` attribute of the M
// structure. Also, when linked against libc, Go prefers the libc version of clone as
// opposed to the runtime version. This has the consequence of not setting M.id for
// any thread, regardless of OS.
//
347 348
// In order to get around all this craziness, we read the address of the G structure for
// the current thread from the thread local storage area.
349
func GetG(thread Thread) (g *G, err error) {
350
	gaddr, err := getGVariable(thread)
351 352 353
	if err != nil {
		return nil, err
	}
354 355

	g, err = gaddr.parseG()
356
	if err == nil {
357
		g.Thread = thread
358 359 360
		if loc, err := thread.Location(); err == nil {
			g.CurrentLoc = *loc
		}
361
	}
362
	return
D
Derek Parker 已提交
363
}
364

365
// ThreadScope returns an EvalScope for this thread.
366
func ThreadScope(thread Thread) (*EvalScope, error) {
367
	locations, err := ThreadStacktrace(thread, 0)
368 369 370
	if err != nil {
		return nil, err
	}
371 372 373
	if len(locations) < 1 {
		return nil, errors.New("could not decode first frame")
	}
374
	return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.BinInfo(), 0}, nil
375 376 377
}

// GoroutineScope returns an EvalScope for the goroutine running on this thread.
378
func GoroutineScope(thread Thread) (*EvalScope, error) {
379
	locations, err := ThreadStacktrace(thread, 0)
380 381 382 383 384 385
	if err != nil {
		return nil, err
	}
	if len(locations) < 1 {
		return nil, errors.New("could not decode first frame")
	}
386
	g, err := GetG(thread)
387 388 389
	if err != nil {
		return nil, err
	}
390
	return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil
391
}
392

393
func onRuntimeBreakpoint(thread Thread) bool {
D
Derek Parker 已提交
394
	loc, err := thread.Location()
A
aarzilli 已提交
395 396 397 398 399
	if err != nil {
		return false
	}
	return loc.Fn != nil && loc.Fn.Name == "runtime.breakpoint"
}
400

H
Hubert Krauze 已提交
401
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
402
func onNextGoroutine(thread Thread, breakpoints map[uint64]*Breakpoint) (bool, error) {
403
	var bp *Breakpoint
404
	for i := range breakpoints {
405
		if breakpoints[i].Internal() && breakpoints[i].Cond != nil {
406
			bp = breakpoints[i]
A
aarzilli 已提交
407
			break
408 409 410
		}
	}
	if bp == nil {
411
		return false, nil
412
	}
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	// Internal breakpoint conditions can take multiple different forms:
	// Step into breakpoints:
	//   runtime.curg.goid == X
	// Next or StepOut breakpoints:
	//   runtime.curg.goid == X && runtime.frameoff == Y
	// Breakpoints that can be hit either by stepping on a line in the same
	// function or by returning from the function:
	//   runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z)
	// Here we are only interested in testing the runtime.curg.goid clause.
	w := onNextGoroutineWalker{thread: thread}
	ast.Walk(&w, bp.Cond)
	return w.ret, w.err
}

type onNextGoroutineWalker struct {
	thread Thread
	ret    bool
	err    error
}

func (w *onNextGoroutineWalker) Visit(n ast.Node) ast.Visitor {
	if binx, isbin := n.(*ast.BinaryExpr); isbin && binx.Op == token.EQL && exprToString(binx.X) == "runtime.curg.goid" {
		w.ret, w.err = evalBreakpointCondition(w.thread, n.(ast.Expr))
		return nil
A
aarzilli 已提交
437
	}
438
	return w
439
}