Further isolate Linux specific code

This commit is contained in:
Derek Parker
2014-12-08 17:15:52 -06:00
parent 2ecf625c5b
commit d41bbbf5c3
3 changed files with 100 additions and 105 deletions

View File

@ -7,12 +7,25 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"sync"
"syscall" "syscall"
"github.com/derekparker/delve/dwarf/frame" "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 // Represents a single breakpoint. Stores information on the break
// point including the byte of data that originally was stored at that // point including the byte of data that originally was stored at that
// address. // address.
@ -179,31 +192,6 @@ func (dbp *DebuggedProcess) PrintThreadInfo() error {
return nil return nil
} }
// Finds the executable from /proc/<pid>/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. // Steps through process.
func (dbp *DebuggedProcess) Step() (err error) { func (dbp *DebuggedProcess) Step() (err error) {
var ( var (
@ -310,53 +298,6 @@ func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
return dbp.CurrentThread.EvalSymbol(name) 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 { type ProcessExitedError struct {
pid int pid int
} }

View File

@ -4,25 +4,13 @@ import (
"debug/gosym" "debug/gosym"
"fmt" "fmt"
"os" "os"
"sync"
"syscall" "syscall"
"github.com/derekparker/delve/dwarf/frame" "github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/vendor/elf" "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) { func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) {
err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
if err == syscall.ESRCH { if err == syscall.ESRCH {
@ -60,20 +48,97 @@ func parseProcessStatus(pid int) (*ProcessStatus, error) {
return &ps, nil return &ps, nil
} }
func (dbp *DebuggedProcess) findExecutable() error { // Finds the executable from /proc/<pid>/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) procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)
f, err := os.OpenFile(procpath, 0, os.ModePerm) f, err := os.OpenFile(procpath, 0, os.ModePerm)
if err != nil { if err != nil {
return err return nil, err
} }
elffile, err := elf.NewFile(f) elffile, err := elf.NewFile(f)
if err != nil { 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
} }

View File

@ -29,11 +29,7 @@ type M struct {
// Parses and returns select info on the internal M // Parses and returns select info on the internal M
// data structures used by the Go scheduler. // data structures used by the Go scheduler.
func (thread *ThreadContext) AllM() ([]*M, error) { func (thread *ThreadContext) AllM() ([]*M, error) {
data, err := thread.Process.Executable.DWARF() reader := thread.Process.Dwarf.Reader()
if err != nil {
return nil, err
}
reader := data.Reader()
allmaddr, err := parseAllMPtr(thread.Process, reader) allmaddr, err := parseAllMPtr(thread.Process, reader)
if err != nil { if err != nil {
@ -186,11 +182,7 @@ func parseAllMPtr(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) {
} }
func (dbp *DebuggedProcess) PrintGoroutinesInfo() error { func (dbp *DebuggedProcess) PrintGoroutinesInfo() error {
data, err := dbp.Executable.DWARF() reader := dbp.Dwarf.Reader()
if err != nil {
return err
}
reader := data.Reader()
allglen, err := allglenval(dbp, reader) allglen, err := allglenval(dbp, reader)
if err != nil { if err != nil {
@ -309,10 +301,7 @@ func offsetFor(dbp *DebuggedProcess, name string, reader *dwarf.Reader, parentin
// Returns the value of the named symbol. // Returns the value of the named symbol.
func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) { func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
data, err := thread.Process.Executable.DWARF() data := thread.Process.Dwarf
if err != nil {
return nil, err
}
pc, err := thread.CurrentPC() pc, err := thread.CurrentPC()
if err != nil { if err != nil {