diff --git a/proctl/proctl.go b/proctl/proctl.go index 827d1db4..0bf41e51 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -7,12 +7,25 @@ import ( "fmt" "os" "os/exec" - "sync" "syscall" "github.com/derekparker/delve/dwarf/frame" + "github.com/derekparker/delve/vendor/dwarf" ) +// Struct representing a debugged process. Holds onto pid, register values, +// process struct and process state. +type DebuggedProcess struct { + Pid int + Process *os.Process + Dwarf *dwarf.Data + GoSymTable *gosym.Table + FrameEntries *frame.FrameDescriptionEntries + BreakPoints map[uint64]*BreakPoint + Threads map[int]*ThreadContext + CurrentThread *ThreadContext +} + // Represents a single breakpoint. Stores information on the break // point including the byte of data that originally was stored at that // address. @@ -179,31 +192,6 @@ func (dbp *DebuggedProcess) PrintThreadInfo() error { return nil } -// Finds the executable from /proc//exe and then -// uses that to parse the following information: -// * Dwarf .debug_frame section -// * Dwarf .debug_line section -// * Go symbol table. -func (dbp *DebuggedProcess) LoadInformation() error { - var ( - wg sync.WaitGroup - err error - ) - - err = dbp.findExecutable() - if err != nil { - return err - } - - wg.Add(2) - go dbp.parseDebugFrame(&wg) - go dbp.obtainGoSymbols(&wg) - - wg.Wait() - - return nil -} - // Steps through process. func (dbp *DebuggedProcess) Step() (err error) { var ( @@ -310,53 +298,6 @@ func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) { return dbp.CurrentThread.EvalSymbol(name) } -func (dbp *DebuggedProcess) parseDebugFrame(wg *sync.WaitGroup) { - defer wg.Done() - - debugFrame, err := dbp.Executable.Section(".debug_frame").Data() - if err != nil { - fmt.Println("could not get .debug_frame section", err) - os.Exit(1) - } - - dbp.FrameEntries = frame.Parse(debugFrame) -} - -func (dbp *DebuggedProcess) obtainGoSymbols(wg *sync.WaitGroup) { - defer wg.Done() - - var ( - symdat []byte - pclndat []byte - err error - ) - - if sec := dbp.Executable.Section(".gosymtab"); sec != nil { - symdat, err = sec.Data() - if err != nil { - fmt.Println("could not get .gosymtab section", err) - os.Exit(1) - } - } - - if sec := dbp.Executable.Section(".gopclntab"); sec != nil { - pclndat, err = sec.Data() - if err != nil { - fmt.Println("could not get .gopclntab section", err) - os.Exit(1) - } - } - - pcln := gosym.NewLineTable(pclndat, dbp.Executable.Section(".text").Addr) - tab, err := gosym.NewTable(symdat, pcln) - if err != nil { - fmt.Println("could not get initialize line table", err) - os.Exit(1) - } - - dbp.GoSymTable = tab -} - type ProcessExitedError struct { pid int } diff --git a/proctl/proctl_linux_amd64.go b/proctl/proctl_linux_amd64.go index 6a8d15fc..6b9b101a 100644 --- a/proctl/proctl_linux_amd64.go +++ b/proctl/proctl_linux_amd64.go @@ -4,25 +4,13 @@ import ( "debug/gosym" "fmt" "os" + "sync" "syscall" "github.com/derekparker/delve/dwarf/frame" "github.com/derekparker/delve/vendor/elf" ) -// Struct representing a debugged process. Holds onto pid, register values, -// process struct and process state. -type DebuggedProcess struct { - Pid int - Process *os.Process - Executable *elf.File - GoSymTable *gosym.Table - FrameEntries *frame.FrameDescriptionEntries - BreakPoints map[uint64]*BreakPoint - Threads map[int]*ThreadContext - CurrentThread *ThreadContext -} - func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) { err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) if err == syscall.ESRCH { @@ -60,20 +48,97 @@ func parseProcessStatus(pid int) (*ProcessStatus, error) { return &ps, nil } -func (dbp *DebuggedProcess) findExecutable() error { +// Finds the executable from /proc//exe and then +// uses that to parse the following information: +// * Dwarf .debug_frame section +// * Dwarf .debug_line section +// * Go symbol table. +func (dbp *DebuggedProcess) LoadInformation() error { + var ( + wg sync.WaitGroup + exe *elf.File + err error + ) + + exe, err = dbp.findExecutable() + if err != nil { + return err + } + + wg.Add(2) + go dbp.parseDebugFrame(exe, &wg) + go dbp.obtainGoSymbols(exe, &wg) + + wg.Wait() + + return nil +} + +func (dbp *DebuggedProcess) findExecutable() (*elf.File, error) { procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid) f, err := os.OpenFile(procpath, 0, os.ModePerm) if err != nil { - return err + return nil, err } elffile, err := elf.NewFile(f) if err != nil { - return err + return nil, err } - dbp.Executable = elffile + data, err := elffile.DWARF() + if err != nil { + return nil, err + } + dbp.Dwarf = data - return nil + return elffile, nil +} + +func (dbp *DebuggedProcess) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) { + defer wg.Done() + + debugFrame, err := exe.Section(".debug_frame").Data() + if err != nil { + fmt.Println("could not get .debug_frame section", err) + os.Exit(1) + } + + dbp.FrameEntries = frame.Parse(debugFrame) +} + +func (dbp *DebuggedProcess) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) { + defer wg.Done() + + var ( + symdat []byte + pclndat []byte + err error + ) + + if sec := exe.Section(".gosymtab"); sec != nil { + symdat, err = sec.Data() + if err != nil { + fmt.Println("could not get .gosymtab section", err) + os.Exit(1) + } + } + + if sec := exe.Section(".gopclntab"); sec != nil { + pclndat, err = sec.Data() + if err != nil { + fmt.Println("could not get .gopclntab section", err) + os.Exit(1) + } + } + + pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr) + tab, err := gosym.NewTable(symdat, pcln) + if err != nil { + fmt.Println("could not get initialize line table", err) + os.Exit(1) + } + + dbp.GoSymTable = tab } diff --git a/proctl/variables_linux_amd64.go b/proctl/variables_linux_amd64.go index 98fa53df..c9aba7c9 100644 --- a/proctl/variables_linux_amd64.go +++ b/proctl/variables_linux_amd64.go @@ -29,11 +29,7 @@ type M struct { // Parses and returns select info on the internal M // data structures used by the Go scheduler. func (thread *ThreadContext) AllM() ([]*M, error) { - data, err := thread.Process.Executable.DWARF() - if err != nil { - return nil, err - } - reader := data.Reader() + reader := thread.Process.Dwarf.Reader() allmaddr, err := parseAllMPtr(thread.Process, reader) if err != nil { @@ -186,11 +182,7 @@ func parseAllMPtr(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) { } func (dbp *DebuggedProcess) PrintGoroutinesInfo() error { - data, err := dbp.Executable.DWARF() - if err != nil { - return err - } - reader := data.Reader() + reader := dbp.Dwarf.Reader() allglen, err := allglenval(dbp, reader) if err != nil { @@ -309,10 +301,7 @@ func offsetFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader, parentin // Returns the value of the named symbol. func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) { - data, err := thread.Process.Executable.DWARF() - if err != nil { - return nil, err - } + data := thread.Process.Dwarf pc, err := thread.CurrentPC() if err != nil {