delve/proc/proc.go

779 lines
19 KiB
Go
Raw Normal View History

2015-06-12 19:49:23 +00:00
package proc
import (
2014-11-24 23:27:56 +00:00
"debug/dwarf"
"debug/gosym"
"encoding/binary"
"fmt"
"go/constant"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
"github.com/derekparker/delve/dwarf/reader"
)
2015-06-20 22:54:52 +00:00
// Process represents all of the information the debugger
// is holding onto regarding the process we are debugging.
2015-06-20 22:54:52 +00:00
type Process struct {
Pid int // Process Pid
Process *os.Process // Pointer to process struct for the actual process we are debugging
// Breakpoint table, holds information on breakpoints.
// Maps instruction address to Breakpoint struct.
2015-06-12 19:32:32 +00:00
Breakpoints map[uint64]*Breakpoint
2015-06-12 19:51:23 +00:00
// List of threads mapped as such: pid -> *Thread
Threads map[int]*Thread
// Active thread
2015-06-12 19:51:23 +00:00
CurrentThread *Thread
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
SelectedGoroutine *G
// Maps package names to package paths, needed to lookup types inside DWARF info
packageMap map[string]string
allGCache []*G
dwarf *dwarf.Data
goSymTable *gosym.Table
frameEntries frame.FrameDescriptionEntries
lineInfo line.DebugLines
firstStart bool
os *OSProcessDetails
arch Arch
breakpointIDCounter int
tempBreakpointIDCounter int
halt bool
exited bool
ptraceChan chan func()
ptraceDoneChan chan interface{}
}
2016-01-10 08:57:52 +00:00
// New returns an initialized Process struct. Before returning,
// it will also launch a goroutine in order to handle ptrace(2)
// functions. For more information, see the documentation on
// `handlePtraceFuncs`.
2015-06-20 22:54:52 +00:00
func New(pid int) *Process {
dbp := &Process{
Pid: pid,
Threads: make(map[int]*Thread),
Breakpoints: make(map[uint64]*Breakpoint),
firstStart: true,
os: new(OSProcessDetails),
ptraceChan: make(chan func()),
ptraceDoneChan: make(chan interface{}),
}
go dbp.handlePtraceFuncs()
return dbp
2014-12-08 23:15:52 +00:00
}
// 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 {
2015-06-21 18:08:14 +00:00
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
}
2015-07-10 20:57:32 +00:00
// Detach from the process being debugged, optionally killing it.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Detach(kill bool) (err error) {
2015-07-13 21:34:51 +00:00
if dbp.Running() {
if err = dbp.Halt(); err != nil {
return
}
}
if !kill {
// Clean up any breakpoints we've set.
for _, bp := range dbp.Breakpoints {
if bp != nil {
_, err := dbp.ClearBreakpoint(bp.Addr)
if err != nil {
return err
}
2015-06-21 03:47:44 +00:00
}
}
}
dbp.execPtraceFunc(func() {
err = PtraceDetach(dbp.Pid, 0)
if err != nil {
return
}
if kill {
err = sys.Kill(dbp.Pid, sys.SIGINT)
}
})
return
}
2016-01-10 08:57:52 +00:00
// Exited returns whether the debugged
// process has exited.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Exited() bool {
return dbp.exited
}
2016-01-10 08:57:52 +00:00
// Running returns whether the debugged
2015-02-02 21:09:56 +00:00
// process is currently executing.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Running() bool {
for _, th := range dbp.Threads {
if th.running {
return true
}
}
return false
}
2016-01-10 08:57:52 +00:00
// LoadInformation finds the executable and then uses it
// to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
2015-06-20 22:54:52 +00:00
func (dbp *Process) LoadInformation(path string) error {
var wg sync.WaitGroup
exe, err := dbp.findExecutable(path)
if err != nil {
return err
}
wg.Add(4)
go dbp.loadProcessInformation(&wg)
go dbp.parseDebugFrame(exe, &wg)
go dbp.obtainGoSymbols(exe, &wg)
go dbp.parseDebugLineInfo(exe, &wg)
wg.Wait()
return nil
}
2016-01-10 08:57:52 +00:00
// FindFileLocation returns the PC for a given file:line.
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
pc, _, err := dbp.goSymTable.LineToPC(fileName, lineno)
if err != nil {
return 0, err
}
return pc, nil
}
2016-01-10 08:57:52 +00:00
// FindFunctionLocation finds address of a function's line
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
// If lineOffset is passed FindFunctionLocation will return the address of that line
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
// Note that setting breakpoints at that address will cause surprising behavior:
// https://github.com/derekparker/delve/issues/170
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
origfn := dbp.goSymTable.LookupFunc(funcName)
if origfn == nil {
return 0, fmt.Errorf("Could not find function %s\n", funcName)
}
if firstLine {
return dbp.FunctionEntryToFirstLine(origfn.Entry)
} else if lineOffset > 0 {
filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry)
breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset)
return breakAddr, err
2015-03-24 13:31:56 +00:00
}
return origfn.Entry, nil
}
func (dbp *Process) FunctionEntryToFirstLine(entry uint64) (uint64, error) {
filename, lineno, startfn := dbp.goSymTable.PCToLine(entry)
if filepath.Ext(filename) != ".go" {
return entry, nil
}
if startfn == nil {
return entry, nil
}
funcName := startfn.Name
for {
lineno++
pc, fn, _ := dbp.goSymTable.LineToPC(filename, lineno)
if fn != nil {
if fn.Name != funcName {
if strings.Contains(fn.Name, funcName) {
continue
}
break
}
if fn.Name == funcName {
return pc, nil
}
}
}
return entry, nil
}
2016-01-10 08:57:52 +00:00
// RequestManualStop sets the `halt` flag and
// sends SIGSTOP to all threads.
2015-06-20 22:54:52 +00:00
func (dbp *Process) RequestManualStop() error {
dbp.halt = true
return dbp.requestManualStop()
}
2016-01-10 08:57:52 +00:00
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
2015-01-14 02:37:10 +00:00
// 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.
func (dbp *Process) SetBreakpoint(addr uint64) (*Breakpoint, error) {
2016-01-10 08:57:52 +00:00
return dbp.setBreakpoint(dbp.CurrentThread.ID, addr, false)
}
2016-01-10 08:57:52 +00:00
// SetTempBreakpoint sets a temp breakpoint. Used during 'next' operations.
func (dbp *Process) SetTempBreakpoint(addr uint64) (*Breakpoint, error) {
2016-01-10 08:57:52 +00:00
return dbp.setBreakpoint(dbp.CurrentThread.ID, addr, true)
}
2016-01-10 08:57:52 +00:00
// ClearBreakpoint clears the breakpoint at addr.
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
bp, ok := dbp.FindBreakpoint(addr)
if !ok {
return nil, NoBreakpointError{addr: addr}
}
if _, err := bp.Clear(dbp.CurrentThread); err != nil {
return nil, err
}
delete(dbp.Breakpoints, addr)
return bp, nil
}
2016-01-10 08:57:52 +00:00
// Status returns the status of the current main thread context.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Status() *sys.WaitStatus {
return dbp.CurrentThread.Status
}
2016-01-10 08:57:52 +00:00
// Next continues execution until the next source line.
func (dbp *Process) Next() (err error) {
for i := range dbp.Breakpoints {
if dbp.Breakpoints[i].Temp {
return fmt.Errorf("next while nexting")
2015-08-28 13:17:12 +00:00
}
2015-04-25 02:56:57 +00:00
}
// Get the goroutine for the current thread. We will
// use it later in order to ensure we are on the same
// goroutine.
2015-06-28 15:00:56 +00:00
g, err := dbp.CurrentThread.GetG()
if err != nil {
return err
}
// Set breakpoints for any goroutine that is currently
// blocked trying to read from a channel. This is so that
// if control flow switches to that goroutine, we end up
// somewhere useful instead of in runtime code.
if _, err = dbp.setChanRecvBreakpoints(); err != nil {
return
}
var goroutineExiting bool
if err = dbp.CurrentThread.setNextBreakpoints(); err != nil {
switch t := err.(type) {
case ThreadBlockedError, NoReturnAddr: // Noop
case GoroutineExitingError:
2016-01-10 08:57:52 +00:00
goroutineExiting = t.goid == g.ID
default:
dbp.clearTempBreakpoints()
2015-08-28 13:17:12 +00:00
return
}
}
if !goroutineExiting {
for i := range dbp.Breakpoints {
if dbp.Breakpoints[i].Temp {
2016-01-10 08:57:52 +00:00
dbp.Breakpoints[i].Cond = g.ID
}
}
}
return dbp.Continue()
}
2015-06-20 22:54:52 +00:00
func (dbp *Process) setChanRecvBreakpoints() (int, error) {
2015-04-25 02:56:57 +00:00
var count int
2015-04-20 21:58:49 +00:00
allg, err := dbp.GoroutinesInfo()
if err != nil {
2015-04-25 02:56:57 +00:00
return 0, err
2015-04-20 21:58:49 +00:00
}
2015-04-20 21:58:49 +00:00
for _, g := range allg {
if g.ChanRecvBlocked() {
ret, err := g.chanRecvReturnAddr(dbp)
if err != nil {
2015-05-07 21:55:06 +00:00
if _, ok := err.(NullAddrError); ok {
continue
}
2015-04-25 02:56:57 +00:00
return 0, err
2015-04-20 21:58:49 +00:00
}
if _, err = dbp.SetTempBreakpoint(ret); err != nil {
if _, ok := err.(BreakpointExistsError); ok {
// Ignore duplicate breakpoints in case if multiple
// goroutines wait on the same channel
continue
}
2015-04-25 02:56:57 +00:00
return 0, err
2015-04-20 21:58:49 +00:00
}
2015-04-25 02:56:57 +00:00
count++
2015-04-20 21:58:49 +00:00
}
}
2015-04-25 02:56:57 +00:00
return count, nil
2015-04-20 21:58:49 +00:00
}
2016-01-10 08:57:52 +00:00
// Continue continues execution of the debugged
// process. It will continue until it hits a breakpoint
// or is otherwise stopped.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Continue() error {
for {
2015-11-24 21:48:28 +00:00
if err := dbp.resume(); err != nil {
return err
}
2015-11-24 21:48:28 +00:00
var trapthread *Thread
var err error
2015-11-24 21:48:28 +00:00
dbp.run(func() error {
trapthread, err = dbp.trapWait(-1)
return nil
})
2015-07-10 19:48:45 +00:00
if err != nil {
return err
}
if err := dbp.Halt(); err != nil {
return dbp.exitGuard(err)
2015-07-10 19:48:45 +00:00
}
2015-11-24 21:48:28 +00:00
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
return err
}
2015-11-24 21:48:28 +00:00
if err := dbp.pickCurrentThread(trapthread); err != nil {
2015-07-10 19:48:45 +00:00
return err
}
2015-11-24 21:48:28 +00:00
switch {
case dbp.CurrentThread.CurrentBreakpoint == nil:
// runtime.Breakpoint or manual stop
if dbp.CurrentThread.onRuntimeBreakpoint() {
for i := 0; i < 2; i++ {
if err = dbp.CurrentThread.Step(); err != nil {
return err
}
2015-07-10 19:48:45 +00:00
}
2015-03-01 04:03:26 +00:00
}
2015-11-24 21:48:28 +00:00
return nil
case dbp.CurrentThread.onTriggeredTempBreakpoint():
return dbp.clearTempBreakpoints()
case dbp.CurrentThread.onTriggeredBreakpoint():
if dbp.CurrentThread.onNextGoroutine() {
return dbp.clearTempBreakpoints()
}
2015-11-24 21:48:28 +00:00
return nil
default:
// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
2015-03-01 04:03:26 +00:00
}
2015-11-24 21:48:28 +00:00
}
}
// pick a new dbp.CurrentThread, with the following priority:
// - a thread with onTriggeredTempBreakpoint() == true
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
// - trapthread
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
for _, th := range dbp.Threads {
if th.onTriggeredTempBreakpoint() {
2016-01-10 08:57:52 +00:00
return dbp.SwitchThread(th.ID)
2015-11-24 21:48:28 +00:00
}
}
if trapthread.onTriggeredBreakpoint() {
2016-01-10 08:57:52 +00:00
return dbp.SwitchThread(trapthread.ID)
2015-11-24 21:48:28 +00:00
}
for _, th := range dbp.Threads {
if th.onTriggeredBreakpoint() {
2016-01-10 08:57:52 +00:00
return dbp.SwitchThread(th.ID)
2015-11-24 21:48:28 +00:00
}
}
2016-01-10 08:57:52 +00:00
return dbp.SwitchThread(trapthread.ID)
}
2016-01-10 08:57:52 +00:00
// Step will continue the debugged process for exactly
// one instruction.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Step() (err error) {
2015-01-14 02:37:10 +00:00
fn := func() error {
for _, th := range dbp.Threads {
if th.blocked() {
continue
}
if err := th.Step(); err != nil {
2015-01-14 02:37:10 +00:00
return err
}
}
return nil
}
return dbp.run(fn)
}
2016-01-10 08:57:52 +00:00
// SwitchThread changes from current thread to the thread specified by `tid`.
2015-06-20 22:54:52 +00:00
func (dbp *Process) SwitchThread(tid int) error {
if th, ok := dbp.Threads[tid]; ok {
dbp.CurrentThread = th
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
return nil
}
return fmt.Errorf("thread %d does not exist", tid)
}
2016-01-10 08:57:52 +00:00
// SwitchGoroutine changes from current thread to the thread
// running the specified goroutine.
func (dbp *Process) SwitchGoroutine(gid int) error {
g, err := dbp.FindGoroutine(gid)
if err != nil {
return err
}
if g == nil {
// user specified -1 and SelectedGoroutine is nil
return nil
}
if g.thread != nil {
2016-01-10 08:57:52 +00:00
return dbp.SwitchThread(g.thread.ID)
}
dbp.SelectedGoroutine = g
return nil
}
2016-01-10 08:57:52 +00:00
// GoroutinesInfo returns an array of G structures representing the information
2015-04-23 01:00:42 +00:00
// Delve cares about from the internal runtime G structure.
2015-06-20 22:54:52 +00:00
func (dbp *Process) GoroutinesInfo() ([]*G, error) {
if dbp.allGCache != nil {
return dbp.allGCache, nil
}
var (
threadg = map[int]*Thread{}
allg []*G
rdr = dbp.DwarfReader()
)
for i := range dbp.Threads {
if dbp.Threads[i].blocked() {
continue
}
2015-06-28 15:00:56 +00:00
g, _ := dbp.Threads[i].GetG()
if g != nil {
2016-01-10 08:57:52 +00:00
threadg[g.ID] = dbp.Threads[i]
}
}
addr, err := rdr.AddrFor("runtime.allglen")
if err != nil {
return nil, err
}
allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
if err != nil {
return nil, err
}
allglen := binary.LittleEndian.Uint64(allglenBytes)
rdr.Seek(0)
allgentryaddr, err := rdr.AddrFor("runtime.allgs")
if err != nil {
// try old name (pre Go 1.6)
allgentryaddr, err = rdr.AddrFor("runtime.allg")
if err != nil {
return nil, err
}
}
faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
allgptr := binary.LittleEndian.Uint64(faddr)
for i := uint64(0); i < allglen; i++ {
g, err := parseG(dbp.CurrentThread, allgptr+(i*uint64(dbp.arch.PtrSize())), true)
if err != nil {
return nil, err
}
2016-01-10 08:57:52 +00:00
if thread, allocated := threadg[g.ID]; allocated {
loc, err := thread.Location()
if err != nil {
return nil, err
}
g.thread = thread
// Prefer actual thread location information.
g.CurrentLoc = *loc
}
if g.Status != Gdead {
allg = append(allg, g)
}
}
dbp.allGCache = allg
return allg, nil
}
2016-01-10 08:57:52 +00:00
// Halt stops all threads.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Halt() (err error) {
2015-04-13 22:17:06 +00:00
for _, th := range dbp.Threads {
if err := th.Halt(); err != nil {
return err
}
}
return nil
}
2016-01-10 08:57:52 +00:00
// Registers obtains register values from the
// "current" thread of the traced process.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Registers() (Registers, error) {
return dbp.CurrentThread.Registers()
}
2016-01-10 08:57:52 +00:00
// PC returns the PC of the current thread.
2015-06-20 22:54:52 +00:00
func (dbp *Process) PC() (uint64, error) {
2015-04-23 15:40:33 +00:00
return dbp.CurrentThread.PC()
}
2016-01-10 08:57:52 +00:00
// CurrentBreakpoint returns the breakpoint the current thread
// is stopped at.
2015-06-20 22:54:52 +00:00
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
return dbp.CurrentThread.CurrentBreakpoint
}
2016-01-10 08:57:52 +00:00
// DwarfReader returns a reader for the dwarf data
2015-06-20 22:54:52 +00:00
func (dbp *Process) DwarfReader() *reader.Reader {
return reader.New(dbp.dwarf)
}
2016-01-10 08:57:52 +00:00
// Sources returns list of source files that comprise the debugged binary.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Sources() map[string]*gosym.Obj {
return dbp.goSymTable.Files
}
2016-01-10 08:57:52 +00:00
// Funcs returns list of functions present in the debugged program.
2015-06-20 22:54:52 +00:00
func (dbp *Process) Funcs() []gosym.Func {
return dbp.goSymTable.Funcs
}
2016-01-10 08:57:52 +00:00
// PCToLine converts an instruction address to a file/line/function.
2015-06-20 22:54:52 +00:00
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
return dbp.goSymTable.PCToLine(pc)
}
2016-01-10 08:57:52 +00:00
// FindBreakpointByID finds the breakpoint for the given ID.
2015-06-20 22:54:52 +00:00
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
for _, bp := range dbp.Breakpoints {
if bp.ID == id {
return bp, true
}
}
return nil, false
}
2016-01-10 08:57:52 +00:00
// FindBreakpoint finds the breakpoint for the given pc.
2015-06-20 22:54:52 +00:00
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
if bp, ok := dbp.Breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok {
return bp, true
}
// Directly use addr to lookup breakpoint.
2015-06-12 19:32:32 +00:00
if bp, ok := dbp.Breakpoints[pc]; ok {
return bp, true
}
return nil, false
}
2015-06-20 22:54:52 +00:00
// Returns a new Process struct.
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
2015-02-27 23:11:13 +00:00
if attach {
var err error
dbp.execPtraceFunc(func() { err = sys.PtraceAttach(dbp.Pid) })
2015-02-27 23:11:13 +00:00
if err != nil {
return nil, err
}
_, _, err = dbp.wait(dbp.Pid, 0)
2015-02-27 23:11:13 +00:00
if err != nil {
return nil, err
}
}
proc, err := os.FindProcess(dbp.Pid)
2015-02-27 23:11:13 +00:00
if err != nil {
return nil, err
}
dbp.Process = proc
err = dbp.LoadInformation(path)
2015-02-27 23:11:13 +00:00
if err != nil {
return nil, err
}
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
}
if err := dbp.updateThreadList(); err != nil {
return nil, err
}
ver, isextld, err := dbp.getGoInformation()
if err != nil {
return nil, err
}
dbp.arch.SetGStructOffset(ver, isextld)
// SelectedGoroutine can not be set correctly by the call to updateThreadList
// because without calling SetGStructOffset we can not read the G struct of CurrentThread
// but without calling updateThreadList we can not examine memory to determine
// the offset of g struct inside TLS
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
return dbp, nil
2015-02-27 23:11:13 +00:00
}
2015-06-20 22:54:52 +00:00
func (dbp *Process) clearTempBreakpoints() error {
2015-06-12 19:32:32 +00:00
for _, bp := range dbp.Breakpoints {
if !bp.Temp {
continue
}
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
return err
}
}
for i := range dbp.Threads {
if dbp.Threads[i].CurrentBreakpoint != nil && dbp.Threads[i].CurrentBreakpoint.Temp {
dbp.Threads[i].CurrentBreakpoint = nil
}
}
return nil
}
2015-06-20 22:54:52 +00:00
func (dbp *Process) run(fn func() error) error {
dbp.allGCache = nil
if dbp.exited {
2015-05-04 22:31:13 +00:00
return fmt.Errorf("process has already exited")
}
for _, th := range dbp.Threads {
th.CurrentBreakpoint = nil
}
if err := fn(); err != nil {
return err
}
return nil
}
2015-06-20 22:54:52 +00:00
func (dbp *Process) handlePtraceFuncs() {
// We must ensure here that we are running on the same thread during
2015-10-06 17:45:36 +00:00
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
// all commands after PTRACE_ATTACH to come from the same thread.
runtime.LockOSThread()
for fn := range dbp.ptraceChan {
fn()
dbp.ptraceDoneChan <- nil
}
}
2015-06-20 22:54:52 +00:00
func (dbp *Process) execPtraceFunc(fn func()) {
dbp.ptraceChan <- fn
<-dbp.ptraceDoneChan
}
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
vv, err := dbp.EvalPackageVariable("runtime.buildVersion")
if err != nil {
err = fmt.Errorf("Could not determine version number: %v\n", err)
return
}
2015-11-18 12:08:53 +00:00
if vv.Unreadable != nil {
err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable)
return
}
ver, ok := parseVersionString(constant.StringVal(vv.Value))
if !ok {
err = fmt.Errorf("Could not parse version number: %v\n", vv.Value)
return
}
rdr := dbp.DwarfReader()
rdr.Seek(0)
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
if err != nil {
return ver, isextld, err
}
if prod, ok := entry.Val(dwarf.AttrProducer).(string); ok && (strings.HasPrefix(prod, "GNU AS")) {
isextld = true
break
}
}
return
}
2016-01-10 08:57:52 +00:00
// FindGoroutine returns a G struct representing the goroutine
// specified by `gid`.
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
if gid == -1 {
return dbp.SelectedGoroutine, nil
}
gs, err := dbp.GoroutinesInfo()
if err != nil {
return nil, err
}
for i := range gs {
2016-01-10 08:57:52 +00:00
if gs[i].ID == gid {
return gs[i], nil
}
}
return nil, fmt.Errorf("Unknown goroutine %d", gid)
}
2016-01-10 08:57:52 +00:00
// ConvertEvalScope returns a new EvalScope in the context of the
// specified goroutine ID and stack frame.
func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
g, err := dbp.FindGoroutine(gid)
if err != nil {
return nil, err
}
if g == nil {
return dbp.CurrentThread.Scope()
}
var out EvalScope
if g.thread == nil {
out.Thread = dbp.CurrentThread
} else {
out.Thread = g.thread
}
locs, err := dbp.GoroutineStacktrace(g, frame)
if err != nil {
return nil, err
}
if frame >= len(locs) {
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
}
out.PC, out.CFA = locs[frame].Current.PC, locs[frame].CFA
return &out, nil
}
func (dbp *Process) postExit() {
dbp.exited = true
close(dbp.ptraceChan)
close(dbp.ptraceDoneChan)
}