
Currently there is no need for the other items in the ProcessStatus struct, we really only care if the process is not running, so we can avoid sending signals to it.
155 lines
2.9 KiB
Go
155 lines
2.9 KiB
Go
package proctl
|
|
|
|
import (
|
|
"debug/elf"
|
|
"debug/gosym"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"syscall"
|
|
|
|
"github.com/derekparker/delve/dwarf/frame"
|
|
)
|
|
|
|
const (
|
|
STATUS_SLEEPING = 'S'
|
|
STATUS_RUNNING = 'R'
|
|
STATUS_TRACE_STOP = 't'
|
|
)
|
|
|
|
func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) {
|
|
err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
|
|
if err == syscall.ESRCH {
|
|
_, _, err = wait(tid, 0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
|
|
}
|
|
|
|
err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
|
|
}
|
|
}
|
|
|
|
dbp.Threads[tid] = &ThreadContext{
|
|
Id: tid,
|
|
Process: dbp,
|
|
}
|
|
|
|
return dbp.Threads[tid], nil
|
|
}
|
|
|
|
func stopped(pid int) bool {
|
|
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer f.Close()
|
|
|
|
var (
|
|
p int
|
|
comm string
|
|
state rune
|
|
)
|
|
fmt.Fscanf(f, "%d %s %c", &p, &comm, &state)
|
|
if state == STATUS_TRACE_STOP {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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)
|
|
|
|
f, err := os.OpenFile(procpath, 0, os.ModePerm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
elffile, err := elf.NewFile(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data, err := elffile.DWARF()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbp.Dwarf = data
|
|
|
|
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
|
|
}
|