2014-05-20 18:23:35 +00:00
|
|
|
package proctl
|
|
|
|
|
|
|
|
import (
|
2014-05-23 14:42:06 +00:00
|
|
|
"debug/gosym"
|
2014-05-20 18:23:35 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2014-12-08 23:15:52 +00:00
|
|
|
"sync"
|
2014-05-20 18:23:35 +00:00
|
|
|
"syscall"
|
2014-06-29 16:52:21 +00:00
|
|
|
|
2014-10-15 14:21:46 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/frame"
|
|
|
|
"github.com/derekparker/delve/vendor/elf"
|
2014-05-20 18:23:35 +00:00
|
|
|
)
|
|
|
|
|
2014-10-18 01:34:58 +00:00
|
|
|
func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) {
|
|
|
|
err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
|
2014-10-27 23:10:45 +00:00
|
|
|
if err == syscall.ESRCH {
|
2014-11-23 00:57:26 +00:00
|
|
|
_, _, err = wait(tid, 0)
|
2014-10-27 23:10:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
|
2014-05-27 15:51:38 +00:00
|
|
|
}
|
|
|
|
|
2014-10-27 23:10:45 +00:00
|
|
|
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)
|
2014-05-29 15:27:03 +00:00
|
|
|
}
|
2014-10-18 01:34:58 +00:00
|
|
|
}
|
2014-05-29 16:18:28 +00:00
|
|
|
|
2014-10-27 23:10:45 +00:00
|
|
|
dbp.Threads[tid] = &ThreadContext{
|
2014-10-25 14:17:05 +00:00
|
|
|
Id: tid,
|
|
|
|
Process: dbp,
|
2014-05-27 15:43:47 +00:00
|
|
|
}
|
|
|
|
|
2014-10-27 23:10:45 +00:00
|
|
|
return dbp.Threads[tid], nil
|
2014-05-20 18:23:35 +00:00
|
|
|
}
|
2014-05-20 18:23:36 +00:00
|
|
|
|
2014-12-05 22:17:10 +00:00
|
|
|
func parseProcessStatus(pid int) (*ProcessStatus, error) {
|
|
|
|
var ps ProcessStatus
|
2014-07-30 00:00:24 +00:00
|
|
|
|
2014-12-05 22:17:10 +00:00
|
|
|
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
2014-07-30 00:00:24 +00:00
|
|
|
if err != nil {
|
2014-12-05 22:17:10 +00:00
|
|
|
return nil, err
|
2014-08-04 18:25:00 +00:00
|
|
|
}
|
2014-12-05 22:17:10 +00:00
|
|
|
defer f.Close()
|
2014-08-04 20:21:35 +00:00
|
|
|
|
2014-12-05 22:17:10 +00:00
|
|
|
fmt.Fscanf(f, "%d %s %c %d", &ps.pid, &ps.comm, &ps.state, &ps.ppid)
|
2014-07-30 00:00:24 +00:00
|
|
|
|
2014-12-05 22:17:10 +00:00
|
|
|
return &ps, nil
|
2014-05-20 18:23:36 +00:00
|
|
|
}
|
2014-05-23 14:42:06 +00:00
|
|
|
|
2014-12-08 23:15:52 +00:00
|
|
|
// 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) {
|
2014-05-23 14:42:06 +00:00
|
|
|
procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)
|
|
|
|
|
2014-09-13 17:28:46 +00:00
|
|
|
f, err := os.OpenFile(procpath, 0, os.ModePerm)
|
2014-05-23 14:42:06 +00:00
|
|
|
if err != nil {
|
2014-12-08 23:15:52 +00:00
|
|
|
return nil, err
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
elffile, err := elf.NewFile(f)
|
|
|
|
if err != nil {
|
2014-12-08 23:15:52 +00:00
|
|
|
return nil, err
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 23:15:52 +00:00
|
|
|
data, err := elffile.DWARF()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dbp.Dwarf = data
|
2014-05-23 14:42:06 +00:00
|
|
|
|
2014-12-08 23:15:52 +00:00
|
|
|
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
|
2014-05-23 14:42:06 +00:00
|
|
|
}
|