delve/proctl/proctl_linux_amd64.go

175 lines
3.3 KiB
Go
Raw Normal View History

2014-05-21 15:23:14 +00:00
// Package proctl provides functions for attaching to and manipulating
// a process during the debug session.
2014-05-20 18:23:35 +00:00
package proctl
import (
"debug/elf"
"debug/gosym"
2014-05-20 18:23:35 +00:00
"fmt"
"os"
"syscall"
)
2014-05-21 15:23:14 +00:00
// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
2014-05-20 18:23:35 +00:00
type DebuggedProcess struct {
2014-05-20 18:23:36 +00:00
Pid int
Regs *syscall.PtraceRegs
Process *os.Process
ProcessState *os.ProcessState
Executable *elf.File
Symbols []elf.Symbol
GoSymTable *gosym.Table
2014-05-20 18:23:35 +00:00
}
2014-05-21 15:23:14 +00:00
// Returns a new DebuggedProcess struct with sensible defaults.
2014-05-20 18:23:35 +00:00
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
proc, err := os.FindProcess(pid)
2014-05-20 18:23:35 +00:00
if err != nil {
return nil, err
}
err = syscall.PtraceAttach(pid)
2014-05-20 18:23:35 +00:00
if err != nil {
return nil, err
}
2014-05-20 18:23:36 +00:00
ps, err := proc.Wait()
2014-05-20 18:23:35 +00:00
if err != nil {
return nil, err
}
2014-05-20 18:23:36 +00:00
debuggedProc := DebuggedProcess{
Pid: pid,
Regs: &syscall.PtraceRegs{},
Process: proc,
ProcessState: ps,
}
err = debuggedProc.LoadInformation()
if err != nil {
return nil, err
}
2014-05-20 18:23:35 +00:00
return &debuggedProc, nil
}
func (dbp *DebuggedProcess) LoadInformation() error {
err := dbp.findExecutable()
if err != nil {
return err
}
err = dbp.obtainGoSymbols()
if err != nil {
return err
}
return nil
}
2014-05-21 15:23:14 +00:00
// Obtains register values from the debugged process.
2014-05-20 18:23:35 +00:00
func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) {
err := syscall.PtraceGetRegs(dbp.Pid, dbp.Regs)
if err != nil {
return nil, fmt.Errorf("Registers():", err)
}
return dbp.Regs, nil
}
// Sets a breakpoint in the running process.
func (dbp *DebuggedProcess) Break(fname string) error {
var (
breakpoint = []byte{'0', 'x', 'C', 'C'}
fn = dbp.GoSymTable.LookupFunc(fname)
addr = uintptr(fn.LineTable.PC)
)
_, err := syscall.PtracePokeData(dbp.Pid, addr, breakpoint)
if err != nil {
return err
}
return nil
}
2014-05-21 15:23:14 +00:00
// Steps through process.
2014-05-20 18:23:35 +00:00
func (dbp *DebuggedProcess) Step() error {
err := dbp.handleResult(syscall.PtraceSingleStep(dbp.Pid))
if err != nil {
return fmt.Errorf("step failed: ", err.Error())
}
regs, err := dbp.Registers()
if err != nil {
return err
}
f := dbp.GoSymTable.PCToFunc(regs.PC())
l := f.LineTable.PCToLine(regs.PC())
fmt.Printf("Stopped at: %s:%d\n", f.Name, l)
return nil
2014-05-20 18:23:35 +00:00
}
2014-05-20 18:23:36 +00:00
2014-05-21 15:23:14 +00:00
// Continue process until next breakpoint.
2014-05-20 18:23:36 +00:00
func (dbp *DebuggedProcess) Continue() error {
2014-05-21 15:23:45 +00:00
return dbp.handleResult(syscall.PtraceCont(dbp.Pid, 0))
2014-05-20 20:15:52 +00:00
}
2014-05-21 15:23:45 +00:00
func (dbp *DebuggedProcess) handleResult(err error) error {
2014-05-20 18:23:36 +00:00
if err != nil {
return err
}
ps, err := dbp.Process.Wait()
if err != nil {
return err
}
dbp.ProcessState = ps
return nil
}
func (dbp *DebuggedProcess) findExecutable() error {
procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)
f, err := os.Open(procpath)
if err != nil {
return err
}
elffile, err := elf.NewFile(f)
if err != nil {
return err
}
dbp.Executable = elffile
return nil
}
func (dbp *DebuggedProcess) obtainGoSymbols() error {
symdat, err := dbp.Executable.Section(".gosymtab").Data()
if err != nil {
return err
}
pclndat, err := dbp.Executable.Section(".gopclntab").Data()
if err != nil {
return err
}
pcln := gosym.NewLineTable(pclndat, dbp.Executable.Section(".text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
return err
}
dbp.GoSymTable = tab
return nil
}