Fix: Improve handling of soft signals on darwin
Fixes a bug on OSX where, if the debugged process spawned a child, when that process received a SIGCHLD it would cause Delve to hang. Fixes #197
This commit is contained in:
parent
3f4476da02
commit
a336c92a8b
25
_fixtures/sigchldprog.go
Normal file
25
_fixtures/sigchldprog.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := exec.Command("date")
|
||||
reader, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
scanner := bufio.NewScanner(reader)
|
||||
go func() {
|
||||
for scanner.Scan() {
|
||||
fmt.Println(scanner.Text())
|
||||
}
|
||||
reader.Close()
|
||||
}()
|
||||
cmd.Start()
|
||||
cmd.Wait()
|
||||
}
|
@ -113,6 +113,8 @@ mach_port_t
|
||||
mach_port_wait(mach_port_t port_set) {
|
||||
kern_return_t kret;
|
||||
thread_act_t thread;
|
||||
NDR_record_t *ndr;
|
||||
integer_t *data;
|
||||
union
|
||||
{
|
||||
mach_msg_header_t hdr;
|
||||
@ -128,28 +130,21 @@ mach_port_wait(mach_port_t port_set) {
|
||||
mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
|
||||
mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
|
||||
thread = desc[0].name;
|
||||
ndr = (NDR_record_t *)(desc + 2);
|
||||
data = (integer_t *)(ndr + 1);
|
||||
|
||||
switch (msg.hdr.msgh_id) {
|
||||
case 2401: // Exception
|
||||
kret = thread_suspend(thread);
|
||||
if (kret != KERN_SUCCESS) return 0;
|
||||
|
||||
if (thread_suspend(thread) != KERN_SUCCESS) return 0;
|
||||
// Send our reply back so the kernel knows this exception has been handled.
|
||||
mig_reply_error_t reply;
|
||||
mach_msg_header_t *rh = &reply.Head;
|
||||
rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg.hdr.msgh_bits), 0);
|
||||
rh->msgh_remote_port = msg.hdr.msgh_remote_port;
|
||||
rh->msgh_size = (mach_msg_size_t) sizeof(mig_reply_error_t);
|
||||
rh->msgh_local_port = MACH_PORT_NULL;
|
||||
rh->msgh_id = msg.hdr.msgh_id + 100;
|
||||
|
||||
reply.NDR = NDR_record;
|
||||
reply.RetCode = KERN_SUCCESS;
|
||||
|
||||
kret = mach_msg(&reply.Head, MACH_SEND_MSG|MACH_SEND_INTERRUPT, rh->msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
|
||||
kret = mach_send_reply(msg.hdr);
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
if (data[2] == EXC_SOFT_SIGNAL) {
|
||||
if (data[3] != SIGTRAP) {
|
||||
if (thread_resume(thread) != KERN_SUCCESS) return 0;
|
||||
return mach_port_wait(port_set);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 72: // Death
|
||||
@ -159,6 +154,23 @@ mach_port_wait(mach_port_t port_set) {
|
||||
return thread;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t hdr) {
|
||||
mig_reply_error_t reply;
|
||||
mach_msg_header_t *rh = &reply.Head;
|
||||
rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr.msgh_bits), 0);
|
||||
rh->msgh_remote_port = hdr.msgh_remote_port;
|
||||
rh->msgh_size = (mach_msg_size_t) sizeof(mig_reply_error_t);
|
||||
rh->msgh_local_port = MACH_PORT_NULL;
|
||||
rh->msgh_id = hdr.msgh_id + 100;
|
||||
|
||||
reply.NDR = NDR_record;
|
||||
reply.RetCode = KERN_SUCCESS;
|
||||
|
||||
return mach_msg(&reply.Head, MACH_SEND_MSG|MACH_SEND_INTERRUPT, rh->msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t task, mach_port_t thread, mach_port_t exception_port, exception_type_t exception) {
|
||||
return exception_raise(exception_port, thread, task, exception, 0, 0);
|
||||
|
@ -301,26 +301,25 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
dbp.updateThreadList()
|
||||
th, err = dbp.handleBreakpointOnThread(int(port))
|
||||
if err != nil {
|
||||
if _, ok := err.(NoBreakpointError); ok {
|
||||
if dbp.halt {
|
||||
dbp.halt = false
|
||||
return dbp.Threads[int(port)], nil
|
||||
}
|
||||
th := dbp.Threads[int(port)]
|
||||
if dbp.firstStart || th.singleStepping {
|
||||
dbp.firstStart = false
|
||||
return dbp.Threads[int(port)], nil
|
||||
}
|
||||
if err := th.Continue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
if _, ok := err.(NoBreakpointError); !ok {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
thread := dbp.Threads[int(port)]
|
||||
if dbp.halt {
|
||||
dbp.halt = false
|
||||
return thread, nil
|
||||
}
|
||||
if dbp.firstStart || thread.singleStepping {
|
||||
dbp.firstStart = false
|
||||
return thread, nil
|
||||
}
|
||||
if err := thread.Continue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return th, nil
|
||||
}
|
||||
return th, nil
|
||||
}
|
||||
|
||||
func wait(pid, tgid, options int) (int, *sys.WaitStatus, error) {
|
||||
|
@ -38,6 +38,9 @@ thread_count(task_t task);
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t);
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t);
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t, mach_port_t, mach_port_t, exception_type_t);
|
||||
|
||||
|
@ -694,3 +694,13 @@ func TestBreakpointOnFunctionEntry(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestProcessReceivesSIGCHLD(t *testing.T) {
|
||||
withTestProcess("sigchldprog", t, func(p *Process, fixture protest.Fixture) {
|
||||
err := p.Continue()
|
||||
_, ok := err.(ProcessExitedError)
|
||||
if !ok {
|
||||
t.Fatalf("Continue() returned unexpected error type %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user