From b18e90f549f21f6a8cff7039d932754c4cfe2b46 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Thu, 31 Oct 2013 21:00:23 +0000 Subject: [PATCH] infrun.c: use GDB_SIGNAL_0 when hidding signals, not GDB_SIGNAL_TRAP. IMO, it doesn't make sense to map random syscall, fork, etc. events to GDB_SIGNAL_TRAP, and possible have the debuggee see that trap. This just seems conceptually wrong to me - these aren't real signals a debuggee would ever see. In fact, when stopped for those events, on Linux, the debuggee isn't in a signal-stop -- there's no way to resume-and-deliver-signal at that point, for example. E.g., when stopped at a fork event: (gdb) catch fork Catchpoint 2 (fork) (gdb) c Continuing. Catchpoint 2 (forked process 4570), 0x000000323d4ba7c4 in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:131 131 pid = ARCH_FORK (); (gdb) set debug infrun 1 (gdb) signal SIGTRAP Continuing with signal SIGTRAP. infrun: clear_proceed_status_thread (process 4566) infrun: proceed (addr=0xffffffffffffffff, signal=5, step=0) infrun: resume (step=0, signal=5), trap_expected=0, current thread [process 4566] at 0x323d4ba7c4 infrun: wait_for_inferior () infrun: target_wait (-1, status) = infrun: 4566 [process 4566], infrun: status->kind = exited, status = 0 infrun: infwait_normal_state infrun: TARGET_WAITKIND_EXITED [Inferior 1 (process 4566) exited normally] infrun: stop_stepping (gdb) Note the signal went nowhere. It was swallowed. Resuming with a SIGTRAP from a syscall event does queue the signal, but doesn't deliver it immediately, like "signal SIGTRAP" from a real signal would. It's still an artificial SIGTRAP: (gdb) catch syscall Catchpoint 2 (any syscall) (gdb) c Continuing. Catchpoint 2 (call to syscall clone), 0x000000323d4ba7c4 in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:131 131 pid = ARCH_FORK (); (gdb) set debug infrun 1 (gdb) signal SIGTRAP Continuing with signal SIGTRAP. infrun: clear_proceed_status_thread (process 4622) infrun: proceed (addr=0xffffffffffffffff, signal=5, step=0) infrun: resume (step=0, signal=5), trap_expected=0, current thread [process 4622] at 0x323d4ba7c4 infrun: wait_for_inferior () infrun: target_wait (-1, status) = infrun: 4622 [process 4622], infrun: status->kind = exited syscall infrun: infwait_normal_state infrun: TARGET_WAITKIND_SYSCALL_RETURN infrun: syscall number = '56' infrun: BPSTAT_WHAT_STOP_NOISY infrun: stop_stepping Catchpoint 2 (returned from syscall clone), 0x000000323d4ba7c4 in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:131 131 pid = ARCH_FORK (); (gdb) c Continuing. infrun: clear_proceed_status_thread (process 4622) infrun: proceed (addr=0xffffffffffffffff, signal=144, step=0) infrun: resume (step=0, signal=0), trap_expected=0, current thread [process 4622] at 0x323d4ba7c4 infrun: wait_for_inferior () infrun: target_wait (-1, status) = infrun: 4622 [process 4622], infrun: status->kind = stopped, signal = SIGTRAP infrun: infwait_normal_state infrun: TARGET_WAITKIND_STOPPED infrun: stop_pc = 0x323d4ba7c4 infrun: random signal 5 Program received signal SIGTRAP, Trace/breakpoint trap. infrun: stop_stepping 0x000000323d4ba7c4 in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:131 131 pid = ARCH_FORK (); (gdb) In all the above, I used 'signal SIGTRAP' to emulate 'handle SIGTRAP pass'. As described in "keep_going", 'handle SIGTRAP pass' does have its place: /* Do not deliver GDB_SIGNAL_TRAP (except when the user explicitly specifies that such a signal should be delivered to the target program). Typically, that would occur when a user is debugging a target monitor on a simulator: the target monitor sets a breakpoint; the simulator encounters this breakpoint and halts the simulation handing control to GDB; GDB, noting that the stop address doesn't map to any known breakpoint, returns control back to the simulator; the simulator then delivers the hardware equivalent of a GDB_SIGNAL_TRAP to the program being debugged. */ ... and I've made use of that myself when implementing/debugging stubs/monitors. But in these cases, treating these events as SIGTRAP possibly injects signals in the debuggee they'd never see otherwise, because you need to use ptrace to enable these special events, which aren't real signals. There's more. Take this bit of handle_inferior_event, where we determine whether a real signal (TARGET_WAITKIND_STOPPED) was random or not: if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) ecs->random_signal = !((bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, GDB_SIGNAL_TRAP) != BPSTAT_SIGNAL_NO) || stopped_by_watchpoint || ecs->event_thread->control.trap_expected || (ecs->event_thread->control.step_range_end && (ecs->event_thread->control.step_resume_breakpoint == NULL))); else { enum bpstat_signal_value sval; sval = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, ecs->event_thread->suspend.stop_signal); ecs->random_signal = (sval == BPSTAT_SIGNAL_NO); if (sval == BPSTAT_SIGNAL_HIDE) ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; } Note that the if (sval == BPSTAT_SIGNAL_HIDE) ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; bit is only reacheable for signals != GDB_SIGNAL_TRAP. AFAICS, sval can only be BPSTAT_SIGNAL_HIDE if nothing in the bpstat returns BPSTAT_SIGNAL_PASS. So that excludes a "catch signal" for the signal in question in the bpstat. All other catchpoints that aren't based on breakpoints behind the scenes call process_event_stop_test directly (don't pass through here) (well, almost all: TARGET_WAITKIND_LOADED does have a fall through, but only for STOP_QUIETLY or STOP_QUIETLY_NO_SIGSTOP, which still return before this code is reached). Catchpoints that are implemented as breakpoints behind the scenes can only appear in the bpstat if the signal was GDB_SIGNAL_TRAP (bkpt_breakpoint_hit returns false otherwise). So that leaves a target reporting a hardware watchpoint hit with a signal other than GDB_SIGNAL_TRAP. And even then it looks quite wrong to me to magically convert the signal into a GDB_SIGNAL_TRAP here too -- if the user has set SIGTRAP to "handle pass", the program will see a trap that gdb invented, not one the program would ever see without gdb in the picture. Tested on x86_64 Fedora 17. gdb/ 2013-10-31 Pedro Alves * infrun.c (handle_syscall_event): Don't set or clear stop_signal. (handle_inferior_event) : Don't set stop_signal to GDB_SIGNAL_TRAP, or clear it. Pass GDB_SIGNAL_0 to bpstat_explains signal, instead of GDB_SIGNAL_TRAP. : If the bpstat chain wants the signal to be hidden, then set stop_signal to GDB_SIGNAL_0 instead of GDB_SIGNAL_TRAP. --- gdb/ChangeLog | 11 +++++++++++ gdb/infrun.c | 13 ++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cd2e55b..60a2bd9 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2013-10-31 Pedro Alves + + * infrun.c (handle_syscall_event): Don't set or clear stop_signal. + (handle_inferior_event) : Don't set stop_signal to + GDB_SIGNAL_TRAP, or clear it. Pass GDB_SIGNAL_0 to + bpstat_explains signal, instead of GDB_SIGNAL_TRAP. + : If the bpstat chain wants the signal to be + hidden, then set stop_signal to GDB_SIGNAL_0 instead of + GDB_SIGNAL_TRAP. + 2013-10-31 Andrew Burgess * breakpoint.c (update_watchpoint): Update error message and add diff --git a/gdb/infrun.c b/gdb/infrun.c index 17612d7..c9e2fe2 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3105,19 +3105,17 @@ handle_syscall_event (struct execution_control_state *ecs) stop_pc, ecs->ptid, &ecs->ws); sval = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, - GDB_SIGNAL_TRAP); + GDB_SIGNAL_0); ecs->random_signal = sval == BPSTAT_SIGNAL_NO; if (!ecs->random_signal) { /* Catchpoint hit. */ - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; return 0; } } /* If no catchpoint triggered for this, then keep going. */ - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; keep_going (ecs); return 1; } @@ -3344,13 +3342,12 @@ handle_inferior_event (struct execution_control_state *ecs) sval = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, - GDB_SIGNAL_TRAP); + GDB_SIGNAL_0); ecs->random_signal = sval == BPSTAT_SIGNAL_NO; if (!ecs->random_signal) { /* A catchpoint triggered. */ - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; process_event_stop_test (ecs); return; } @@ -3629,7 +3626,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); stop_stepping (ecs); return; } - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; process_event_stop_test (ecs); return; @@ -3676,7 +3672,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); stop_pc, ecs->ptid, &ecs->ws); ecs->random_signal = (bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, - GDB_SIGNAL_TRAP) + GDB_SIGNAL_0) == BPSTAT_SIGNAL_NO); /* Note that this may be referenced from inside @@ -3691,7 +3687,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); keep_going (ecs); return; } - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; process_event_stop_test (ecs); return; @@ -4288,7 +4283,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); ecs->random_signal = (sval == BPSTAT_SIGNAL_NO); if (sval == BPSTAT_SIGNAL_HIDE) - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; } /* For the program's own signals, act according to -- 2.7.4