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

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

14
	"golang.org/x/debug/dwarf"
D
Derek Parker 已提交
15 16
)

17 18
// Thread represents a thread.
type Thread interface {
19
	MemoryReadWriter
20 21 22 23 24 25 26 27 28 29 30 31
	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
32
	StepInstruction() error
33 34
	// Blocked returns true if the thread is blocked
	Blocked() bool
35 36
	// SetCurrentBreakpoint updates the current breakpoint of this thread
	SetCurrentBreakpoint() error
37 38
}

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

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

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

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

62
	if g == nil {
63
		if thread.Blocked() {
64
			return Stackframe{}, Stackframe{}, ThreadBlockedError{}
65
		}
66
		frames, err = ThreadStacktrace(thread, 1)
67
	} else {
68
		frames, err = g.Stacktrace(1)
69
	}
D
Derek Parker 已提交
70
	if err != nil {
71
		return Stackframe{}, Stackframe{}, err
D
Derek Parker 已提交
72
	}
73 74 75 76 77 78 79
	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
80 81 82
	}
}

A
aarzilli 已提交
83 84 85 86 87 88 89
// 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.
90 91 92 93 94 95 96 97 98 99
//
// 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.
100
func next(dbp Process, stepInto bool) error {
101 102
	selg := dbp.SelectedGoroutine()
	curthread := dbp.CurrentThread()
103
	topframe, retframe, err := topframe(selg, curthread)
104 105
	if err != nil {
		return err
106
	}
D
Derek Parker 已提交
107

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

A
aarzilli 已提交
115
	csource := filepath.Ext(topframe.Current.File) != ".go"
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
	for i := range text {
		if text[i].Inst == nil {
			fmt.Printf("error at instruction %d\n", i)
		}
	}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	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)),
			},
		}
	}
152

A
aarzilli 已提交
153
	if stepInto {
154
		for _, instr := range text {
A
aarzilli 已提交
155 156 157 158 159
			if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
				continue
			}

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

A
aarzilli 已提交
174 175 176 177 178 179 180 181 182
	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)
			}
183
		}
A
aarzilli 已提交
184 185 186

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

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

A
aarzilli 已提交
217 218 219 220 221 222 223
	if !csource {
		var covered bool
		for i := range pcs {
			if topframe.FDE.Cover(pcs[i]) {
				covered = true
				break
			}
D
Derek Parker 已提交
224
		}
225

A
aarzilli 已提交
226
		if !covered {
227 228
			fn := dbp.BinInfo().goSymTable.PCToFunc(topframe.Ret)
			if selg != nil && fn != nil && fn.Name == "runtime.goexit" {
A
aarzilli 已提交
229 230
				return nil
			}
D
Derek Parker 已提交
231
		}
232
	}
233 234

	// Add a breakpoint on the return address for the current frame
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	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
			}
		} else {
			return err
		}
	}
	if bp, _, _ := curthread.Breakpoint(); bp == nil {
		curthread.SetCurrentBreakpoint()
	}
260
	success = true
261
	return nil
262 263
}

264
func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) error {
A
aarzilli 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	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
294
	pc, _ := FirstPCAfterPrologue(dbp, fn, false)
295
	if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
A
aarzilli 已提交
296 297 298 299 300 301
		if _, ok := err.(BreakpointExistsError); !ok {
			return err
		}
	}

	return nil
302
}
303

304
func getGVariable(thread Thread) (*Variable, error) {
305
	arch := thread.Arch()
A
aarzilli 已提交
306
	regs, err := thread.Registers(false)
307 308 309 310
	if err != nil {
		return nil, err
	}

311
	if arch.GStructOffset() == 0 {
312
		// GetG was called through SwitchThread / updateThreadList during initialization
D
Derek Parker 已提交
313
		// thread.dbp.arch isn't setup yet (it needs a current thread to read global variables from)
314 315 316
		return nil, fmt.Errorf("g struct offset not initialized")
	}

317 318
	gaddr, hasgaddr := regs.GAddr()
	if !hasgaddr {
319 320
		gaddrbs := make([]byte, arch.PtrSize())
		_, err := thread.ReadMemory(gaddrbs, uintptr(regs.TLS()+arch.GStructOffset()))
321 322 323 324
		if err != nil {
			return nil, err
		}
		gaddr = binary.LittleEndian.Uint64(gaddrbs)
325
	}
A
go fmt  
aarzilli 已提交
326

327
	return newGVariable(thread, uintptr(gaddr), arch.DerefTLS())
328 329
}

330
func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) {
331
	typ, err := thread.BinInfo().findType("runtime.g")
332 333 334 335 336 337 338
	if err != nil {
		return nil, err
	}

	name := ""

	if deref {
339
		typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.Arch().PtrSize()), "", reflect.Ptr, 0}, typ}
340 341 342 343
	} else {
		name = "runtime.curg"
	}

344
	return newVariableFromThread(thread, name, gaddr, typ), nil
345 346
}

D
Derek Parker 已提交
347
// GetG returns information on the G (goroutine) that is executing on this thread.
348
//
D
Derek Parker 已提交
349 350
// 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.
351 352 353 354 355 356 357 358
//
// 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.
//
359 360
// 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.
361
func GetG(thread Thread) (g *G, err error) {
362
	gaddr, err := getGVariable(thread)
363 364 365
	if err != nil {
		return nil, err
	}
366 367

	g, err = gaddr.parseG()
368
	if err == nil {
369
		g.Thread = thread
370 371 372
		if loc, err := thread.Location(); err == nil {
			g.CurrentLoc = *loc
		}
373
	}
374
	return
D
Derek Parker 已提交
375
}
376

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

// GoroutineScope returns an EvalScope for the goroutine running on this thread.
390
func GoroutineScope(thread Thread) (*EvalScope, error) {
391
	locations, err := ThreadStacktrace(thread, 0)
392 393 394 395 396 397
	if err != nil {
		return nil, err
	}
	if len(locations) < 1 {
		return nil, errors.New("could not decode first frame")
	}
398
	g, err := GetG(thread)
399 400 401
	if err != nil {
		return nil, err
	}
402
	return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil
403
}
404

405
func onRuntimeBreakpoint(thread Thread) bool {
D
Derek Parker 已提交
406
	loc, err := thread.Location()
A
aarzilli 已提交
407 408 409 410 411
	if err != nil {
		return false
	}
	return loc.Fn != nil && loc.Fn.Name == "runtime.breakpoint"
}
412

H
Hubert Krauze 已提交
413
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
414
func onNextGoroutine(thread Thread, breakpoints map[uint64]*Breakpoint) (bool, error) {
415
	var bp *Breakpoint
416 417 418
	for i := range breakpoints {
		if breakpoints[i].Internal() {
			bp = breakpoints[i]
A
aarzilli 已提交
419
			break
420 421 422
		}
	}
	if bp == nil {
423
		return false, nil
424
	}
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
	// 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 已提交
449
	}
450
	return w
451
}