delve/proctl/proctl_darwin.go

195 lines
4.2 KiB
Go
Raw Normal View History

2015-01-14 02:37:10 +00:00
package proctl
// #include "proctl_darwin.h"
import "C"
import (
"debug/gosym"
"debug/macho"
"fmt"
"os"
"sync"
"unsafe"
"github.com/derekparker/delve/dwarf/frame"
sys "golang.org/x/sys/unix"
)
type OSProcessDetails struct {
task C.mach_port_name_t
exceptionPort C.mach_port_t
}
func (dbp *DebuggedProcess) Halt() (err error) {
for _, th := range dbp.Threads {
err := th.Halt()
if err != nil {
return err
}
}
return nil
}
// Finds the executable and then uses it
// 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 *macho.File
err error
)
if err := dbp.acquireMachTask(); err != nil {
return fmt.Errorf("could not acquire mach task")
}
exe, err = dbp.findExecutable()
if err != nil {
return err
}
data, err := exe.DWARF()
if err != nil {
return err
}
dbp.Dwarf = data
wg.Add(2)
go dbp.parseDebugFrame(exe, &wg)
go dbp.obtainGoSymbols(exe, &wg)
wg.Wait()
return nil
}
func (dbp *DebuggedProcess) updateThreadList() error {
var (
err error
kret C.kern_return_t
count = C.thread_count(C.task_t(dbp.os.task))
)
2015-02-27 21:27:48 +00:00
if count == -1 {
return fmt.Errorf("could not get thread count")
}
list := make([]uint32, count)
2015-01-14 02:37:10 +00:00
2015-03-01 03:34:55 +00:00
// TODO(dp) might be better to malloc mem in C and then free it here
2015-01-14 02:37:10 +00:00
// instead of getting count above and passing in a slice
kret = C.get_threads(C.task_t(dbp.os.task), unsafe.Pointer(&list[0]))
if kret != C.KERN_SUCCESS {
return fmt.Errorf("could not get thread list")
}
if count < 0 {
return fmt.Errorf("could not get thread list")
}
for _, port := range list {
2015-03-01 03:34:55 +00:00
if _, ok := dbp.Threads[int(port)]; !ok {
fmt.Println("new thread spawned", port)
_, err = dbp.addThread(int(port), false)
if err != nil {
return err
}
2015-01-14 02:37:10 +00:00
}
}
return nil
}
func (dbp *DebuggedProcess) acquireMachTask() error {
if ret := C.acquire_mach_task(C.int(dbp.Pid), &dbp.os.task, &dbp.os.exceptionPort); ret != C.KERN_SUCCESS {
return fmt.Errorf("could not acquire mach task %d", ret)
}
return nil
}
// export addThread
func (dbp *DebuggedProcess) addThread(port int, attach bool) (*ThreadContext, error) {
thread := &ThreadContext{
Id: port,
Process: dbp,
os: new(OSSpecificDetails),
}
dbp.Threads[port] = thread
thread.os.thread_act = C.thread_act_t(port)
2015-03-01 03:34:55 +00:00
if dbp.CurrentThread == nil {
dbp.CurrentThread = thread
}
2015-01-14 02:37:10 +00:00
return thread, nil
}
func (dbp *DebuggedProcess) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
defer wg.Done()
if sec := exe.Section("__debug_frame"); sec != nil {
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)
} else {
fmt.Println("could not find __debug_frame section in binary")
os.Exit(1)
}
}
func (dbp *DebuggedProcess) obtainGoSymbols(exe *macho.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
}
func (dbp *DebuggedProcess) findExecutable() (*macho.File, error) {
pathptr, err := C.find_executable(C.int(dbp.Pid))
if err != nil {
return nil, err
}
return macho.Open(C.GoString(pathptr))
}
func trapWait(dbp *DebuggedProcess, pid int) (int, *sys.WaitStatus, error) {
port := C.mach_port_wait(dbp.os.exceptionPort)
if port == 0 {
2015-02-27 21:27:48 +00:00
return -1, nil, ProcessExitedError{}
2015-01-14 02:37:10 +00:00
}
dbp.updateThreadList()
return int(port), nil, nil
}
func wait(pid, options int) (int, *sys.WaitStatus, error) {
var status sys.WaitStatus
wpid, err := sys.Wait4(pid, &status, options, nil)
return wpid, &status, err
}