Fix: properly handle random signals
* Ignore signals we do not care about * Implement custom fork/exec for Darwin to convert signals to mach exceptions
This commit is contained in:
parent
047a91af5b
commit
173ee20097
49
proctl/exec_darwin.c
Normal file
49
proctl/exec_darwin.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "exec_darwin.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
fork_exec(char *argv0, char **argv,
|
||||||
|
mach_port_name_t *task,
|
||||||
|
mach_port_t *port_set,
|
||||||
|
mach_port_t *exception_port,
|
||||||
|
mach_port_t *notification_port)
|
||||||
|
{
|
||||||
|
int fd[2];
|
||||||
|
if (pipe(fd) < 0) return -1;
|
||||||
|
|
||||||
|
kern_return_t kret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid > 0) {
|
||||||
|
// In parent.
|
||||||
|
close(fd[0]);
|
||||||
|
kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
|
||||||
|
if (kret != KERN_SUCCESS) return -1;
|
||||||
|
|
||||||
|
char msg = 'c';
|
||||||
|
write(fd[1], &msg, 1);
|
||||||
|
close(fd[1]);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fork succeeded, we are in the child.
|
||||||
|
int pret;
|
||||||
|
char sig;
|
||||||
|
|
||||||
|
close(fd[1]);
|
||||||
|
read(fd[0], &sig, 1);
|
||||||
|
close(fd[0]);
|
||||||
|
|
||||||
|
// Set errno to zero before a call to ptrace.
|
||||||
|
// It is documented that ptrace can return -1 even
|
||||||
|
// for successful calls.
|
||||||
|
errno = 0;
|
||||||
|
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
|
||||||
|
if (pret != 0 && errno != 0) return -errno;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pret = ptrace(PT_SIGEXC, 0, 0, 0);
|
||||||
|
if (pret != 0 && errno != 0) return -errno;
|
||||||
|
|
||||||
|
// Create the child process.
|
||||||
|
execve(argv0, argv, NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
9
proctl/exec_darwin.h
Normal file
9
proctl/exec_darwin.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "proctl_darwin.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ptrace.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
fork_exec(char *argv0, char **argv, mach_port_name_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
@ -6,12 +6,10 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
|
|
||||||
@ -34,6 +32,8 @@ type DebuggedProcess struct {
|
|||||||
goSymTable *gosym.Table
|
goSymTable *gosym.Table
|
||||||
frameEntries frame.FrameDescriptionEntries
|
frameEntries frame.FrameDescriptionEntries
|
||||||
lineInfo *line.DebugLineInfo
|
lineInfo *line.DebugLineInfo
|
||||||
|
firstStart bool
|
||||||
|
singleStep bool
|
||||||
os *OSProcessDetails
|
os *OSProcessDetails
|
||||||
ast *source.Searcher
|
ast *source.Searcher
|
||||||
breakpointIDCounter int
|
breakpointIDCounter int
|
||||||
@ -63,35 +63,20 @@ func (pe ProcessExitedError) Error() string {
|
|||||||
|
|
||||||
// Attach to an existing process with the given PID.
|
// Attach to an existing process with the given PID.
|
||||||
func Attach(pid int) (*DebuggedProcess, error) {
|
func Attach(pid int) (*DebuggedProcess, error) {
|
||||||
dbp, err := newDebugProcess(pid, true)
|
dbp := &DebuggedProcess{
|
||||||
|
Pid: pid,
|
||||||
|
Threads: make(map[int]*ThreadContext),
|
||||||
|
BreakPoints: make(map[uint64]*BreakPoint),
|
||||||
|
os: new(OSProcessDetails),
|
||||||
|
ast: source.New(),
|
||||||
|
}
|
||||||
|
dbp, err := initializeDebugProcess(dbp, "", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return dbp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and begin debugging a new process. First entry in
|
|
||||||
// `cmd` is the program to run, and then rest are the arguments
|
|
||||||
// to be supplied to that process.
|
|
||||||
func Launch(cmd []string) (*DebuggedProcess, error) {
|
|
||||||
proc := exec.Command(cmd[0])
|
|
||||||
proc.Args = cmd
|
|
||||||
proc.Stdout = os.Stdout
|
|
||||||
proc.Stderr = os.Stderr
|
|
||||||
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
|
|
||||||
|
|
||||||
if err := proc.Start(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err := wait(proc.Process.Pid, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newDebugProcess(proc.Process.Pid, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns whether or not Delve thinks the debugged
|
// Returns whether or not Delve thinks the debugged
|
||||||
// process has exited.
|
// process has exited.
|
||||||
func (dbp *DebuggedProcess) Exited() bool {
|
func (dbp *DebuggedProcess) Exited() bool {
|
||||||
@ -109,10 +94,10 @@ func (dbp *DebuggedProcess) Running() bool {
|
|||||||
// * Dwarf .debug_frame section
|
// * Dwarf .debug_frame section
|
||||||
// * Dwarf .debug_line section
|
// * Dwarf .debug_line section
|
||||||
// * Go symbol table.
|
// * Go symbol table.
|
||||||
func (dbp *DebuggedProcess) LoadInformation() error {
|
func (dbp *DebuggedProcess) LoadInformation(path string) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
exe, err := dbp.findExecutable()
|
exe, err := dbp.findExecutable(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -367,6 +352,8 @@ func (dbp *DebuggedProcess) resume() error {
|
|||||||
// Single step, will execute a single instruction.
|
// Single step, will execute a single instruction.
|
||||||
func (dbp *DebuggedProcess) Step() (err error) {
|
func (dbp *DebuggedProcess) Step() (err error) {
|
||||||
fn := func() error {
|
fn := func() error {
|
||||||
|
dbp.singleStep = true
|
||||||
|
defer func() { dbp.singleStep = false }()
|
||||||
for _, th := range dbp.Threads {
|
for _, th := range dbp.Threads {
|
||||||
if th.blocked() {
|
if th.blocked() {
|
||||||
continue
|
continue
|
||||||
@ -491,33 +478,25 @@ func (dbp *DebuggedProcess) FindBreakpoint(pc uint64) (*BreakPoint, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new DebuggedProcess struct.
|
// Returns a new DebuggedProcess struct.
|
||||||
func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) {
|
func initializeDebugProcess(dbp *DebuggedProcess, path string, attach bool) (*DebuggedProcess, error) {
|
||||||
dbp := DebuggedProcess{
|
|
||||||
Pid: pid,
|
|
||||||
Threads: make(map[int]*ThreadContext),
|
|
||||||
BreakPoints: make(map[uint64]*BreakPoint),
|
|
||||||
os: new(OSProcessDetails),
|
|
||||||
ast: source.New(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if attach {
|
if attach {
|
||||||
err := sys.PtraceAttach(pid)
|
err := sys.PtraceAttach(dbp.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, _, err = wait(pid, 0)
|
_, _, err = wait(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc, err := os.FindProcess(pid)
|
proc, err := os.FindProcess(dbp.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp.Process = proc
|
dbp.Process = proc
|
||||||
err = dbp.LoadInformation()
|
err = dbp.LoadInformation(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -526,7 +505,7 @@ func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &dbp, nil
|
return dbp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) clearTempBreakpoints() error {
|
func (dbp *DebuggedProcess) clearTempBreakpoints() error {
|
||||||
@ -575,7 +554,7 @@ func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, er
|
|||||||
if dbp.halt {
|
if dbp.halt {
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no breakpoint at %#v", pc)
|
return nil, NoBreakPointError{addr: pc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) run(fn func() error) error {
|
func (dbp *DebuggedProcess) run(fn func() error) error {
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
package proctl
|
package proctl
|
||||||
|
|
||||||
// #include "proctl_darwin.h"
|
// #include "proctl_darwin.h"
|
||||||
|
// #include "exec_darwin.h"
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
|
"github.com/derekparker/delve/source"
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,6 +25,55 @@ type OSProcessDetails struct {
|
|||||||
notificationPort C.mach_port_t
|
notificationPort C.mach_port_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create and begin debugging a new process. Uses a
|
||||||
|
// custom fork/exec process in order to take advantage of
|
||||||
|
// PT_SIGEXC on Darwin.
|
||||||
|
func Launch(cmd []string) (*DebuggedProcess, error) {
|
||||||
|
argv0, err := filepath.Abs(cmd[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
task C.mach_port_name_t
|
||||||
|
portSet C.mach_port_t
|
||||||
|
exceptionPort C.mach_port_t
|
||||||
|
notificationPort C.mach_port_t
|
||||||
|
|
||||||
|
argv = C.CString(cmd[0])
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(cmd) == 1 {
|
||||||
|
argv = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pid := int(C.fork_exec(C.CString(argv0), &argv, &task, &portSet, &exceptionPort, ¬ificationPort))
|
||||||
|
if pid <= 0 {
|
||||||
|
return nil, fmt.Errorf("could not fork/exec")
|
||||||
|
}
|
||||||
|
|
||||||
|
dbp := &DebuggedProcess{
|
||||||
|
Pid: pid,
|
||||||
|
Threads: make(map[int]*ThreadContext),
|
||||||
|
BreakPoints: make(map[uint64]*BreakPoint),
|
||||||
|
firstStart: true,
|
||||||
|
os: new(OSProcessDetails),
|
||||||
|
ast: source.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
dbp.os = &OSProcessDetails{
|
||||||
|
task: task,
|
||||||
|
portSet: portSet,
|
||||||
|
exceptionPort: exceptionPort,
|
||||||
|
notificationPort: notificationPort,
|
||||||
|
}
|
||||||
|
dbp, err = initializeDebugProcess(dbp, argv0, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = dbp.Continue()
|
||||||
|
return dbp, err
|
||||||
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) requestManualStop() (err error) {
|
func (dbp *DebuggedProcess) requestManualStop() (err error) {
|
||||||
var (
|
var (
|
||||||
task = C.mach_port_t(dbp.os.task)
|
task = C.mach_port_t(dbp.os.task)
|
||||||
@ -153,16 +205,11 @@ func (dbp *DebuggedProcess) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) findExecutable() (*macho.File, error) {
|
func (dbp *DebuggedProcess) findExecutable(path string) (*macho.File, error) {
|
||||||
ret := C.acquire_mach_task(C.int(dbp.Pid), &dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort, &dbp.os.notificationPort)
|
if path == "" {
|
||||||
if ret != C.KERN_SUCCESS {
|
path = C.GoString(C.find_executable(C.int(dbp.Pid)))
|
||||||
return nil, fmt.Errorf("could not acquire mach task %d", ret)
|
|
||||||
}
|
}
|
||||||
pathptr, err := C.find_executable(C.int(dbp.Pid))
|
exe, err := macho.Open(path)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
exe, err := macho.Open(C.GoString(pathptr))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -175,32 +222,53 @@ func (dbp *DebuggedProcess) findExecutable() (*macho.File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
|
func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
|
||||||
port := C.mach_port_wait(dbp.os.portSet)
|
var (
|
||||||
|
th *ThreadContext
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
port := C.mach_port_wait(dbp.os.portSet)
|
||||||
|
|
||||||
switch port {
|
switch port {
|
||||||
case dbp.os.notificationPort:
|
case dbp.os.notificationPort:
|
||||||
_, status, err := wait(dbp.Pid, 0)
|
_, status, err := wait(dbp.Pid, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbp.exited = true
|
||||||
|
return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
||||||
|
case C.MACH_RCV_INTERRUPTED:
|
||||||
|
if !dbp.halt {
|
||||||
|
// Call trapWait again, it seems
|
||||||
|
// MACH_RCV_INTERRUPTED is emitted before
|
||||||
|
// process natural death _sometimes_.
|
||||||
|
return dbp.trapWait(pid)
|
||||||
|
}
|
||||||
|
return nil, ManualStopError{}
|
||||||
|
case 0:
|
||||||
|
return nil, fmt.Errorf("error while waiting for task")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we cannot be notified of new threads on OS X
|
||||||
|
// this is as good a time as any to check for them.
|
||||||
|
dbp.updateThreadList()
|
||||||
|
th, err = dbp.handleBreakpointOnThread(int(port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if _, ok := err.(NoBreakPointError); ok {
|
||||||
|
if dbp.firstStart || dbp.singleStep {
|
||||||
|
dbp.firstStart = false
|
||||||
|
return dbp.Threads[int(port)], nil
|
||||||
|
}
|
||||||
|
if th, ok := dbp.Threads[int(port)]; ok {
|
||||||
|
th.Continue()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbp.exited = true
|
return th, nil
|
||||||
return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
|
||||||
case C.MACH_RCV_INTERRUPTED:
|
|
||||||
if !dbp.halt {
|
|
||||||
// Call trapWait again, it seems
|
|
||||||
// MACH_RCV_INTERRUPTED is emitted before
|
|
||||||
// process natural death _sometimes_.
|
|
||||||
return dbp.trapWait(pid)
|
|
||||||
}
|
|
||||||
return nil, ManualStopError{}
|
|
||||||
case 0:
|
|
||||||
return nil, fmt.Errorf("error while waiting for task")
|
|
||||||
}
|
}
|
||||||
|
return th, nil
|
||||||
// Since we cannot be notified of new threads on OS X
|
|
||||||
// this is as good a time as any to check for them.
|
|
||||||
dbp.updateThreadList()
|
|
||||||
return dbp.handleBreakpointOnThread(int(port))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func wait(pid, options int) (int, *sys.WaitStatus, error) {
|
func wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
|
"github.com/derekparker/delve/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -25,6 +27,34 @@ const (
|
|||||||
// Not actually needed for Linux.
|
// Not actually needed for Linux.
|
||||||
type OSProcessDetails interface{}
|
type OSProcessDetails interface{}
|
||||||
|
|
||||||
|
// Create and begin debugging a new process. First entry in
|
||||||
|
// `cmd` is the program to run, and then rest are the arguments
|
||||||
|
// to be supplied to that process.
|
||||||
|
func Launch(cmd []string) (*DebuggedProcess, error) {
|
||||||
|
proc := exec.Command(cmd[0])
|
||||||
|
proc.Args = cmd
|
||||||
|
proc.Stdout = os.Stdout
|
||||||
|
proc.Stderr = os.Stderr
|
||||||
|
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
|
||||||
|
|
||||||
|
if err := proc.Start(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, _, err := wait(proc.Process.Pid, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||||
|
}
|
||||||
|
dbp := &DebuggedProcess{
|
||||||
|
Pid: proc.Process.Pid,
|
||||||
|
Threads: make(map[int]*ThreadContext),
|
||||||
|
BreakPoints: make(map[uint64]*BreakPoint),
|
||||||
|
os: new(OSProcessDetails),
|
||||||
|
ast: source.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return initializeDebugProcess(dbp, proc.Path, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) requestManualStop() (err error) {
|
func (dbp *DebuggedProcess) requestManualStop() (err error) {
|
||||||
return sys.Kill(dbp.Pid, sys.SIGSTOP)
|
return sys.Kill(dbp.Pid, sys.SIGSTOP)
|
||||||
}
|
}
|
||||||
@ -102,10 +132,11 @@ func (dbp *DebuggedProcess) updateThreadList() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) findExecutable() (*elf.File, error) {
|
func (dbp *DebuggedProcess) findExecutable(path string) (*elf.File, error) {
|
||||||
procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)
|
if path == "" {
|
||||||
|
path = fmt.Sprintf("/proc/%d/exe", dbp.Pid)
|
||||||
f, err := os.OpenFile(procpath, 0, os.ModePerm)
|
}
|
||||||
|
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -200,13 +231,17 @@ func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
|
|||||||
if wpid == 0 {
|
if wpid == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if th, ok := dbp.Threads[wpid]; ok {
|
th, ok := dbp.Threads[wpid]
|
||||||
|
if ok {
|
||||||
th.Status = status
|
th.Status = status
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Exited() && wpid == dbp.Pid {
|
if status.Exited() {
|
||||||
dbp.exited = true
|
if wpid == dbp.Pid {
|
||||||
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
dbp.exited = true
|
||||||
|
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||||
// A traced thread has cloned a new thread, grab the pid and
|
// A traced thread has cloned a new thread, grab the pid and
|
||||||
@ -216,7 +251,7 @@ func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
|
|||||||
return nil, fmt.Errorf("could not get event message: %s", err)
|
return nil, fmt.Errorf("could not get event message: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
th, err := dbp.addThread(int(cloned), false)
|
th, err = dbp.addThread(int(cloned), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -238,6 +273,11 @@ func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
|
|||||||
if status.StopSignal() == sys.SIGSTOP && dbp.halt {
|
if status.StopSignal() == sys.SIGSTOP && dbp.halt {
|
||||||
return nil, ManualStopError{}
|
return nil, ManualStopError{}
|
||||||
}
|
}
|
||||||
|
if th != nil {
|
||||||
|
if err := th.Continue(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ func TestExit(t *testing.T) {
|
|||||||
err := p.Continue()
|
err := p.Continue()
|
||||||
pe, ok := err.(ProcessExitedError)
|
pe, ok := err.(ProcessExitedError)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Continue() returned unexpected error type")
|
t.Fatalf("Continue() returned unexpected error type %s", err)
|
||||||
}
|
}
|
||||||
if pe.Status != 0 {
|
if pe.Status != 0 {
|
||||||
t.Errorf("Unexpected error status: %d", pe.Status)
|
t.Errorf("Unexpected error status: %d", pe.Status)
|
||||||
|
@ -112,6 +112,7 @@ func TestVariableFunctionScoping(t *testing.T) {
|
|||||||
|
|
||||||
err = p.Continue()
|
err = p.Continue()
|
||||||
assertNoError(err, t, "Continue() returned an error")
|
assertNoError(err, t, "Continue() returned an error")
|
||||||
|
p.Clear(pc)
|
||||||
|
|
||||||
_, err = p.EvalSymbol("a1")
|
_, err = p.EvalSymbol("a1")
|
||||||
assertNoError(err, t, "Unable to find variable a1")
|
assertNoError(err, t, "Unable to find variable a1")
|
||||||
|
Loading…
Reference in New Issue
Block a user