* [rfc] catch syscall
@ 2007-06-09 20:18 Alan Curry
2007-07-03 17:52 ` Daniel Jacobowitz
0 siblings, 1 reply; 6+ messages in thread
From: Alan Curry @ 2007-06-09 20:18 UTC (permalink / raw)
To: gdb-patches
For a long time I've wanted to be able to mix strace-like syscall tracing
with the awesome power of gdb. Other people have mentioned the idea a few
times over the years, but nothing ever seems to get done. So I did something.
The patch below is far from ready for inclusion. I only started looking at
gdb's internals a few days ago and there are still parts I don't understand.
But it does work, at least in a few simple tests. This seems like a good time
to have someone look it over and tell me what should be done differently.
The added feature is a new command, "catch syscall", which is much like
"catch fork" and "catch exec" (in fact a lot of the new code is made up of
lightly-modified copies of the code blocks implementing those). By saying
"catch syscall" the user requests that the program stop on entry to or exit
from a syscall.
Implementation outline:
1. PTRACE_O_TRACESYSGOOD is enabled to make syscall stops distinguishable
from random SIGTRAPs
2. When the inferior is resumed, if there is an enabled syscall catchpoint,
PTRACE_SYSCALL is used instead of PTRACE_CONT.
3. When the inferior stops, the special signal value 0x80|SIGTRAP is
recognized and the syscall catchpoint claims responsibility for the
stop.
Here's a sample of a program being traced. The program is hello world written
in assembly without linking to libc, so it only makes 2 syscalls: write and
_exit. I've added extra annotations starting with '#'
GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "powerpc-unknown-linux-gnu"...
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) catch syscall
Catchpoint 1 (syscall)
(gdb) r
(no debugging symbols found)
# we've stopped at entry to the first syscall.
Catchpoint 1 (syscall), 0x1000008c in _start ()
# on linuxppc where I'm testing, register r0 holds the syscall number
(gdb) p $r0
# 4 is __NR_write
$1 = 4
# The arguments to the syscall are in registers r3, r4, r5, etc.
(gdb) p $r3
# writing to fd 1, stdout
$2 = 1
(gdb) x/s $r4
0x10000098: "hello world\n"
(gdb) p $r5
# strlen("hello world\n") is 12
$3 = 12
# Continuing from here allows the syscall to complete, then stops again
(gdb) c
hello world
# Notice the PC didn't change, we're still at 0x1000008c
Catchpoint 1 (syscall), 0x1000008c in _start ()
# Now continue again, until the next syscall
(gdb) c
Catchpoint 1 (syscall), 0x10000098 in ?? ()
(gdb) p $r0
# 1 is __NR_exit
$4 = 1
(gdb) p $r3
$5 = 0
(gdb) c
Program exited normally.
diff -ru gdb-6.6/gdb/breakpoint.c gdb-6.6/gdb/breakpoint.c
--- gdb-6.6/gdb/breakpoint.c 2006-10-19 10:58:25.000000000 -0500
+++ gdb-6.6/gdb/breakpoint.c 2007-06-07 12:43:24.000000000 -0500
@@ -741,6 +741,9 @@
case bp_catch_exec:
target_insert_exec_catchpoint (PIDGET (inferior_ptid));
break;
+ case bp_catch_syscall:
+ target_insert_syscall_catchpoint (PIDGET (inferior_ptid));
+ break;
default:
internal_error (__FILE__, __LINE__, _("unknown breakpoint type"));
break;
@@ -1089,7 +1092,8 @@
else if (bpt->owner->type == bp_catch_fork
|| bpt->owner->type == bp_catch_vfork
- || bpt->owner->type == bp_catch_exec)
+ || bpt->owner->type == bp_catch_exec
+ || bpt->owner->type == bp_catch_syscall)
{
struct gdb_exception e = catch_exception (uiout, insert_catchpoint,
bpt->owner, RETURN_MASK_ERROR);
@@ -1532,7 +1536,8 @@
}
else if ((b->owner->type == bp_catch_fork ||
b->owner->type == bp_catch_vfork ||
- b->owner->type == bp_catch_exec)
+ b->owner->type == bp_catch_exec ||
+ b->owner->type == bp_catch_syscall)
&& breakpoint_enabled (b->owner)
&& !b->duplicate)
{
@@ -1548,6 +1553,9 @@
case bp_catch_exec:
val = target_remove_exec_catchpoint (PIDGET (inferior_ptid));
break;
+ case bp_catch_syscall:
+ val = target_remove_syscall_catchpoint (PIDGET (inferior_ptid));
+ break;
default:
warning (_("Internal error, %s line %d."), __FILE__, __LINE__);
break;
@@ -1820,6 +1828,7 @@
|| (ep->type == bp_catch_fork)
|| (ep->type == bp_catch_vfork)
|| (ep->type == bp_catch_exec)
+ || (ep->type == bp_catch_syscall)
|| (ep->type == bp_catch_catch)
|| (ep->type == bp_catch_throw);
@@ -2178,6 +2187,14 @@
return PRINT_SRC_AND_LOC;
break;
+ case bp_catch_syscall:
+ annotate_catchpoint (bs->breakpoint_at->number);
+ /* Ideally, print strace-style output here. */
+ printf_filtered (_("\nCatchpoint %d (syscall), "),
+ bs->breakpoint_at->number);
+ return PRINT_SRC_AND_LOC;
+ break;
+
case bp_catch_catch:
if (current_exception_event &&
(CURRENT_EXCEPTION_KIND == EX_EVENT_CATCH))
@@ -2613,6 +2630,7 @@
&& b->type != bp_catch_fork
&& b->type != bp_catch_vfork
&& b->type != bp_catch_exec
+ && b->type != bp_catch_syscall
&& b->type != bp_catch_catch
&& b->type != bp_catch_throw) /* a non-watchpoint bp */
{
@@ -2687,6 +2705,10 @@
&& !inferior_has_execd (PIDGET (inferior_ptid), &b->exec_pathname))
continue;
+ if ((b->type == bp_catch_syscall)
+ && !inferior_has_syscalled (PIDGET (inferior_ptid)))
+ continue;
+
if (ep_is_exception_catchpoint (b) &&
!(current_exception_event = target_get_current_exception_event ()))
continue;
@@ -3152,6 +3174,7 @@
case bp_catch_fork:
case bp_catch_vfork:
case bp_catch_exec:
+ case bp_catch_syscall:
if (bs->stop)
{
if (bs->print)
@@ -3317,6 +3340,7 @@
{bp_catch_fork, "catch fork"},
{bp_catch_vfork, "catch vfork"},
{bp_catch_exec, "catch exec"},
+ {bp_catch_syscall, "catch syscall"},
{bp_catch_catch, "catch catch"},
{bp_catch_throw, "catch throw"}
};
@@ -3440,6 +3464,25 @@
}
break;
+ case bp_catch_syscall:
+ /* Field 4, the address, is omitted (which makes the columns
+ not line up too nicely with the headers, but the effect
+ is relatively readable). */
+ if (addressprint)
+ ui_out_field_skip (uiout, "addr");
+ annotate_field (5);
+#if 0
+ /* Ideally, print the syscall's __NR here, and maybe its name.
+ Getting the __NR and mapping it to a name are both arch-specific
+ so I'm saving it for later. It'll go something like this: */
+ if (b->syscall_name != NULL)
+ {
+ ui_out_field_string (uiout, "what", b->syscall_name);
+ }
+#endif
+ break;
+
+
case bp_catch_catch:
/* Field 4, the address, is omitted (which makes the columns
not line up too nicely with the headers, but the effect
@@ -3654,6 +3697,7 @@
|| b->type == bp_catch_fork
|| b->type == bp_catch_vfork
|| b->type == bp_catch_exec
+ || b->type == bp_catch_syscall
|| b->type == bp_catch_catch
|| b->type == bp_catch_throw
|| b->type == bp_hardware_breakpoint
@@ -3854,7 +3898,8 @@
bp_catch_exec
bp_longjmp_resume
bp_catch_fork
- bp_catch_vork */
+ bp_catch_vfork
+ bp_catch_syscall */
static int
breakpoint_address_is_meaningful (struct breakpoint *bpt)
@@ -3868,7 +3913,8 @@
&& type != bp_catch_exec
&& type != bp_longjmp_resume
&& type != bp_catch_fork
- && type != bp_catch_vfork);
+ && type != bp_catch_vfork
+ && type != bp_catch_syscall);
}
/* Rescan breakpoints at the same address and section as BPT,
@@ -3979,7 +4025,8 @@
|| bptype == bp_access_watchpoint
|| bptype == bp_catch_fork
|| bptype == bp_catch_vfork
- || bptype == bp_catch_exec)
+ || bptype == bp_catch_exec
+ || bptype == bp_catch_syscall)
{
/* Watchpoints and the various bp_catch_* eventpoints should not
have their addresses modified. */
@@ -4046,6 +4093,7 @@
case bp_catch_fork:
case bp_catch_vfork:
case bp_catch_exec:
+ case bp_catch_syscall:
case bp_catch_catch:
case bp_catch_throw:
loc->loc_type = bp_loc_other;
@@ -4646,6 +4694,32 @@
mention (b);
}
+void
+create_syscall_event_catchpoint (int tempflag, char *cond_string)
+{
+ struct symtab_and_line sal;
+ struct breakpoint *b;
+ int thread = -1; /* All threads. */
+
+ init_sal (&sal);
+ sal.pc = 0;
+ sal.symtab = NULL;
+ sal.line = 0;
+
+ b = set_raw_breakpoint (sal, bp_catch_syscall);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->cond = NULL;
+ b->cond_string = (cond_string == NULL) ?
+ NULL : savestring (cond_string, strlen (cond_string));
+ b->thread = thread;
+ b->addr_string = NULL;
+ b->enable_state = bp_enabled;
+ b->disposition = tempflag ? disp_del : disp_donttouch;
+
+ mention (b);
+}
+
static int
hw_breakpoint_used_count (void)
{
@@ -4873,6 +4947,10 @@
printf_filtered (_("Catchpoint %d (exec)"),
b->number);
break;
+ case bp_catch_syscall:
+ printf_filtered (_("Catchpoint %d (syscall)"),
+ b->number);
+ break;
case bp_catch_catch:
case bp_catch_throw:
printf_filtered (_("Catchpoint %d (%s)"),
@@ -6192,6 +6270,28 @@
}
static void
+catch_syscall_command_1 (char *arg, int tempflag, int from_tty)
+{
+ char *cond_string = NULL;
+
+ ep_skip_leading_whitespace (&arg);
+
+ /* The allowed syntax is:
+ catch syscall
+ catch syscall if <cond>
+
+ First, check if there's an if clause. */
+ cond_string = ep_parse_optional_if_clause (&arg);
+
+ if ((*arg != '\0') && !isspace (*arg))
+ error (_("Junk at end of arguments."));
+
+ /* If this target supports it, create a syscall catchpoint
+ and enable reporting of such events. */
+ create_syscall_event_catchpoint (tempflag, cond_string);
+}
+
+static void
catch_load_command_1 (char *arg, int tempflag, int from_tty)
{
char *dll_pathname = NULL;
@@ -6532,6 +6632,10 @@
{
catch_exec_command_1 (arg1_end + 1, tempflag, from_tty);
}
+ else if (strncmp (arg1_start, "syscall", arg1_length) == 0)
+ {
+ catch_syscall_command_1 (arg1_end + 1, tempflag, from_tty);
+ }
else if (strncmp (arg1_start, "load", arg1_length) == 0)
{
catch_load_command_1 (arg1_end + 1, tempflag, from_tty);
@@ -6825,7 +6929,8 @@
&& bpt->type != bp_access_watchpoint
&& bpt->type != bp_catch_fork
&& bpt->type != bp_catch_vfork
- && bpt->type != bp_catch_exec)
+ && bpt->type != bp_catch_exec
+ && bpt->type != bp_catch_syscall)
{
ALL_BREAKPOINTS (b)
if (b->loc->address == bpt->loc->address
@@ -7193,6 +7298,7 @@
case bp_catch_fork:
case bp_catch_vfork:
case bp_catch_exec:
+ case bp_catch_syscall:
break;
default:
@@ -7435,6 +7541,7 @@
case bp_catch_fork:
case bp_catch_vfork:
case bp_catch_exec:
+ case bp_catch_syscall:
case bp_catch_catch:
case bp_catch_throw:
case bp_hardware_breakpoint:
@@ -7586,6 +7693,7 @@
case bp_catch_fork:
case bp_catch_vfork:
case bp_catch_exec:
+ case bp_catch_syscall:
case bp_catch_catch:
case bp_catch_throw:
case bp_hardware_breakpoint:
diff -ru gdb-6.6/gdb/breakpoint.h gdb-6.6/gdb/breakpoint.h
--- gdb-6.6/gdb/breakpoint.h 2006-04-18 14:20:06.000000000 -0500
+++ gdb-6.6/gdb/breakpoint.h 2007-06-03 21:45:24.000000000 -0500
@@ -126,14 +126,15 @@
bp_catch_unload,
/* These are not really breakpoints, but are catchpoints that
- implement the "catch fork", "catch vfork" and "catch exec" commands
- on platforms whose kernel support such functionality. (I.e.,
- kernels which can raise an event when a fork or exec occurs, as
- opposed to the debugger setting breakpoints on functions named
- "fork" or "exec".) */
+ implement the "catch fork", "catch vfork", "catch exec", and
+ "catch syscall" commands on platforms whose kernel support such
+ functionality. (I.e., kernels which can raise an event when a fork
+ or exec occurs, as opposed to the debugger setting breakpoints on
+ functions named "fork" or "exec".) */
bp_catch_fork,
bp_catch_vfork,
bp_catch_exec,
+ bp_catch_syscall,
/* These are catchpoints to implement "catch catch" and "catch throw"
commands for C++ exception handling. */
diff -ru gdb-6.6/gdb/inf-child.c gdb-6.6/gdb/inf-child.c
--- gdb-6.6/gdb/inf-child.c 2005-12-17 17:34:01.000000000 -0500
+++ gdb-6.6/gdb/inf-child.c 2007-06-04 20:56:15.000000000 -0500
@@ -151,6 +151,21 @@
return 1;
}
+static void
+inf_child_insert_syscall_catchpoint (int pid)
+{
+ /* This version of Unix doesn't support notification of syscall
+ events. */
+}
+
+static int
+inf_child_remove_syscall_catchpoint (int pid)
+{
+ /* This version of Unix doesn't support notification of syscall
+ events. */
+ return 0;
+}
+
static int
inf_child_can_run (void)
{
@@ -209,6 +224,8 @@
t->to_remove_exec_catchpoint = inf_child_remove_exec_catchpoint;
t->to_reported_exec_events_per_exec_call =
inf_child_reported_exec_events_per_exec_call;
+ t->to_insert_syscall_catchpoint = inf_child_insert_syscall_catchpoint;
+ t->to_remove_syscall_catchpoint = inf_child_remove_syscall_catchpoint;
t->to_can_run = inf_child_can_run;
t->to_enable_exception_callback = inf_child_enable_exception_callback;
t->to_get_current_exception_event = inf_child_get_current_exception_event;
diff -ru gdb-6.6/gdb/inf-ptrace.c gdb-6.6/gdb/inf-ptrace.c
--- gdb-6.6/gdb/inf-ptrace.c 2006-01-24 17:34:34.000000000 -0500
+++ gdb-6.6/gdb/inf-ptrace.c 2007-06-06 20:43:04.000000000 -0500
@@ -317,7 +317,7 @@
inf_ptrace_resume (ptid_t ptid, int step, enum target_signal signal)
{
pid_t pid = ptid_get_pid (ptid);
- int request = PT_CONTINUE;
+ int request = catching_syscalls ? PT_SYSCALL : PT_CONTINUE;
if (pid == -1)
/* Resume all threads. Traditionally ptrace() only supports
diff -ru gdb-6.6/gdb/inferior.h gdb-6.6/gdb/inferior.h
--- gdb-6.6/gdb/inferior.h 2006-10-18 11:56:13.000000000 -0500
+++ gdb-6.6/gdb/inferior.h 2007-06-06 21:54:37.000000000 -0500
@@ -417,6 +417,12 @@
extern int proceed_to_finish;
+/* Nonzero if a syscall catchpoint is active. There must be a better place
+ for this, where it can be associated with a particular process if multiple
+ forks are being traced. Where should it be really? */
+
+extern int catching_syscalls;
+
/* Save register contents here when about to pop a stack dummy frame,
if-and-only-if proceed_to_finish is set.
Thus this contains the return value from the called function (assuming
diff -ru gdb-6.6/gdb/infrun.c gdb-6.6/gdb/infrun.c
--- gdb-6.6/gdb/infrun.c 2006-10-18 11:56:13.000000000 -0500
+++ gdb-6.6/gdb/infrun.c 2007-06-07 14:49:58.000000000 -0500
@@ -282,6 +282,12 @@
int proceed_to_finish;
+/* Nonzero if a syscall catchpoint is active. There must be a better place
+ for this, where it can be associated with a particular process if multiple
+ forks are being traced. Where should it be really? */
+
+int catching_syscalls;
+
/* Save register contents here when about to pop a stack dummy frame,
if-and-only-if proceed_to_finish is set.
Thus this contains the return value from the called function (assuming
@@ -582,6 +588,10 @@
pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
break;
+ case TARGET_WAITKIND_SYSCALL:
+ /* Anything to do here? Toggle an entry/exit flag? */
+ break;
+
default:
break;
}
@@ -1497,6 +1507,33 @@
}
goto process_event_stop_test;
+ case TARGET_WAITKIND_SYSCALL:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL\n");
+ stop_signal = TARGET_SIGNAL_TRAP;
+ pending_follow.kind = ecs->ws.kind;
+
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ {
+ context_switch (ecs);
+ flush_cached_frames ();
+ }
+
+ stop_pc = read_pc ();
+
+ stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid, 0);
+
+ ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
+
+ /* If no catchpoint triggered for this, then keep going. */
+ if (ecs->random_signal)
+ {
+ stop_signal = TARGET_SIGNAL_0;
+ keep_going (ecs);
+ return;
+ }
+ goto process_event_stop_test;
+
/* Be careful not to try to gather much state about a thread
that's in a syscall. It's frequently a losing proposition. */
case TARGET_WAITKIND_SYSCALL_ENTRY:
@@ -3809,6 +3846,23 @@
return 1;
}
+int
+inferior_has_syscalled (int pid)
+{
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ get_last_target_status (&last_ptid, &last);
+
+ if (last.kind != TARGET_WAITKIND_SYSCALL)
+ return 0;
+
+ if (ptid_get_pid (last_ptid) != pid)
+ return 0;
+
+ return 1;
+}
+
/* Oft used ptids */
ptid_t null_ptid;
ptid_t minus_one_ptid;
diff -ru gdb-6.6/gdb/linux-nat.c gdb-6.6/gdb/linux-nat.c
--- gdb-6.6/gdb/linux-nat.c 2006-11-20 16:58:51.000000000 -0500
+++ gdb-6.6/gdb/linux-nat.c 2007-06-07 15:42:21.000000000 -0500
@@ -117,8 +117,13 @@
};
struct simple_pid_list *stopped_pids;
-/* This variable is a tri-state flag: -1 for unknown, 0 if PTRACE_O_TRACEFORK
- can not be used, 1 if it can. */
+/* This variable is a tri-state flag: -1 for unknown, 0 if
+ PTRACE_O_TRACESYSGOOD can not be used, 1 if it can. */
+
+static int linux_supports_tracesysgood_flag = -1;
+
+/* If we have PTRACE_O_TRACESYSGOOD, this flag indicates whether we also have
+ PTRACE_O_TRACEFORK. */
static int linux_supports_tracefork_flag = -1;
@@ -162,7 +167,7 @@
}
\f
-/* A helper function for linux_test_for_tracefork, called after fork (). */
+/* A helper function for linux_test_for_traceflags, called after fork (). */
static void
linux_tracefork_child (void)
@@ -190,9 +195,27 @@
return ret;
}
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.
+/* The extended result code for the PTRACE_O_TRACESYSGOOD option is returned
+ as part of WSTOPSIG(), unlike the other PTRACE_EVENT_* codes. This is
+ inconvenient for most callers, which would like to first check that
+ WSTOPSIG() == SIGTRAP and then look at the extended flags. This wrapper
+ lets them do that. */
+static int my_WSTOPSIG (int status)
+{
+ int sig = WSTOPSIG (status);
+ /* return sig&0x80 would work, but if the kernel says something totally
+ unexpected like SIGSTOP|0x80, GDB should report the weirdness. */
+ if (sig == (SIGTRAP|0x80))
+ sig = SIGTRAP;
+ return sig;
+}
+
+/* Determine which PTRACE_O_* flags are usable with the running kernel.
- First, we try to enable fork tracing on ORIGINAL_PID. If this fails,
+ First, we try to enable TRACESYSGOOD on ORIGINAL_PID. If this fails, the
+ kernel is too old to support any of the flags.
+
+ Next, we try to enable fork tracing on ORIGINAL_PID. If this fails,
we know that the feature is not available. This may change the tracing
options for ORIGINAL_PID, but we'll be setting them shortly anyway.
@@ -201,17 +224,28 @@
create a child process, attach to it, use PTRACE_SETOPTIONS to enable
fork tracing, and let it fork. If the process exits, we assume that we
can't use TRACEFORK; if we get the fork notification, and we can extract
- the new child's PID, then we assume that we can. */
+ the new child's PID, then we assume that we can.
+
+ The results are stored in the linux_supports_trace* flags, and the
+ supported subset of {TRACESYSGOOD,TRACEFORK,TRACEVFORKDONE} is left
+ enabled on ORIGINAL_PID. */
static void
-linux_test_for_tracefork (int original_pid)
+linux_test_for_traceflags (int original_pid)
{
int child_pid, ret, status;
long second_pid;
+ linux_supports_tracesysgood_flag = 0;
linux_supports_tracefork_flag = 0;
linux_supports_tracevforkdone_flag = 0;
+ ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD);
+ if (ret != 0)
+ return;
+
+ linux_supports_tracesysgood_flag = 1;
+
ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK);
if (ret != 0)
return;
@@ -227,9 +261,9 @@
if (ret == -1)
perror_with_name (("waitpid"));
else if (ret != child_pid)
- error (_("linux_test_for_tracefork: waitpid: unexpected result %d."), ret);
+ error (_("linux_test_for_traceflags: waitpid: unexpected result %d."), ret);
if (! WIFSTOPPED (status))
- error (_("linux_test_for_tracefork: waitpid: unexpected status %d."), status);
+ error (_("linux_test_for_traceflags: waitpid: unexpected status %d."), status);
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
if (ret != 0)
@@ -237,15 +271,15 @@
ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
if (ret != 0)
{
- warning (_("linux_test_for_tracefork: failed to kill child"));
+ warning (_("linux_test_for_traceflags: failed to kill child"));
return;
}
ret = my_waitpid (child_pid, &status, 0);
if (ret != child_pid)
- warning (_("linux_test_for_tracefork: failed to wait for killed child"));
+ warning (_("linux_test_for_traceflags: failed to wait for killed child"));
else if (!WIFSIGNALED (status))
- warning (_("linux_test_for_tracefork: unexpected wait status 0x%x from "
+ warning (_("linux_test_for_traceflags: unexpected wait status 0x%x from "
"killed child"), status);
return;
@@ -258,7 +292,7 @@
ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
if (ret != 0)
- warning (_("linux_test_for_tracefork: failed to resume child"));
+ warning (_("linux_test_for_traceflags: failed to resume child"));
ret = my_waitpid (child_pid, &status, 0);
@@ -275,20 +309,28 @@
my_waitpid (second_pid, &second_status, 0);
ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
if (ret != 0)
- warning (_("linux_test_for_tracefork: failed to kill second child"));
+ warning (_("linux_test_for_traceflags: failed to kill second child"));
my_waitpid (second_pid, &status, 0);
}
}
else
- warning (_("linux_test_for_tracefork: unexpected result from waitpid "
+ warning (_("linux_test_for_traceflags: unexpected result from waitpid "
"(%d, status 0x%x)"), ret, status);
ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
if (ret != 0)
- warning (_("linux_test_for_tracefork: failed to kill child"));
+ warning (_("linux_test_for_traceflags: failed to kill child"));
my_waitpid (child_pid, &status, 0);
}
+static int
+linux_supports_tracesysgood (int pid)
+{
+ if (linux_supports_tracesysgood_flag == -1)
+ linux_test_for_traceflags (pid);
+ return linux_supports_tracesysgood_flag;
+}
+
/* Return non-zero iff we have tracefork functionality available.
This function also sets linux_supports_tracefork_flag. */
@@ -296,7 +338,7 @@
linux_supports_tracefork (int pid)
{
if (linux_supports_tracefork_flag == -1)
- linux_test_for_tracefork (pid);
+ linux_test_for_traceflags (pid);
return linux_supports_tracefork_flag;
}
@@ -304,7 +346,7 @@
linux_supports_tracevforkdone (int pid)
{
if (linux_supports_tracefork_flag == -1)
- linux_test_for_tracefork (pid);
+ linux_test_for_traceflags (pid);
return linux_supports_tracevforkdone_flag;
}
@@ -318,11 +360,15 @@
if (pid == 0)
pid = ptid_get_pid (ptid);
- if (! linux_supports_tracefork (pid))
+ if (! linux_supports_tracesysgood (pid))
return;
- options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
- | PTRACE_O_TRACECLONE;
+ options = PTRACE_O_TRACESYSGOOD;
+
+ if (linux_supports_tracefork (pid))
+ options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
+ | PTRACE_O_TRACECLONE;
+
if (linux_supports_tracevforkdone (pid))
options |= PTRACE_O_TRACEVFORKDONE;
@@ -522,6 +568,12 @@
{
int event = status >> 16;
+ if (WSTOPSIG (status) == (SIGTRAP|0x80))
+ {
+ ourstatus->kind = TARGET_WAITKIND_SYSCALL;
+ return inferior_ptid;
+ }
+
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
|| event == PTRACE_EVENT_CLONE)
{
@@ -601,6 +653,21 @@
error (_("Your system does not support exec catchpoints."));
}
+void
+child_insert_syscall_catchpoint (int pid)
+{
+ if (!linux_supports_tracesysgood (pid))
+ error (_("Your system does not support syscall catchpoints."));
+ catching_syscalls=1;
+}
+
+int
+child_remove_syscall_catchpoint (int pid)
+{
+ catching_syscalls=0;
+ return 0;
+}
+
/* On GNU/Linux there are no real LWP's. The closest thing to LWP's
are processes sharing the same VM space. A multi-threaded process
is basically a group of such processes. However, such a grouping
@@ -687,10 +754,10 @@
if (WIFSTOPPED (status))
snprintf (buf, sizeof (buf), "%s (stopped)",
- strsignal (WSTOPSIG (status)));
+ strsignal (my_WSTOPSIG (status)));
else if (WIFSIGNALED (status))
snprintf (buf, sizeof (buf), "%s (terminated)",
- strsignal (WSTOPSIG (status)));
+ strsignal (my_WSTOPSIG (status)));
else
snprintf (buf, sizeof (buf), "%d (exited)", WEXITSTATUS (status));
@@ -1044,14 +1111,14 @@
if (debug_linux_nat && lp->status)
fprintf_unfiltered (gdb_stdlog, "DC: Pending %s for %s on detach.\n",
- strsignal (WSTOPSIG (lp->status)),
+ strsignal (my_WSTOPSIG (lp->status)),
target_pid_to_str (lp->ptid));
while (lp->signalled && lp->stopped)
{
errno = 0;
if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
- WSTOPSIG (lp->status)) < 0)
+ my_WSTOPSIG (lp->status)) < 0)
error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
@@ -1080,7 +1147,7 @@
{
errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
- WSTOPSIG (lp->status)) < 0)
+ my_WSTOPSIG (lp->status)) < 0)
error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
@@ -1088,7 +1155,7 @@
fprintf_unfiltered (gdb_stdlog,
"PTRACE_DETACH (%s, %s, 0) (OK)\n",
target_pid_to_str (lp->ptid),
- strsignal (WSTOPSIG (lp->status)));
+ strsignal (my_WSTOPSIG (lp->status)));
delete_lwp (lp->ptid);
}
@@ -1203,7 +1270,7 @@
if (lp->status && WIFSTOPPED (lp->status))
{
- int saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+ int saved_signo = target_signal_from_host (my_WSTOPSIG (lp->status));
if (signal_stop_state (saved_signo) == 0
&& signal_print_state (saved_signo) == 0
@@ -1381,7 +1448,9 @@
gdb_assert (WIFSTOPPED (status));
/* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WIFSTOPPED (status)
+ && ((WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ || WSTOPSIG (status) == (SIGTRAP|0x80)))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
@@ -1443,7 +1512,7 @@
return 0;
/* Ignore any signals in FLUSH_MASK. */
- if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
+ if (flush_mask && sigismember (flush_mask, my_WSTOPSIG (status)))
{
if (!lp->signalled)
{
@@ -1464,7 +1533,7 @@
if (WSTOPSIG (status) != SIGSTOP)
{
- if (WSTOPSIG (status) == SIGTRAP)
+ if (my_WSTOPSIG (status) == SIGTRAP)
{
/* If a LWP other than the LWP that we're reporting an
event for has hit a GDB breakpoint (as opposed to
@@ -1505,7 +1574,7 @@
target_pid_to_str (lp->ptid),
status_to_str ((int) status));
}
- kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
+ kill_lwp (GET_LWP (lp->ptid), my_WSTOPSIG (lp->status));
}
/* Save the sigtrap event. */
lp->status = status;
@@ -1549,7 +1618,7 @@
target_pid_to_str (lp->ptid),
status_to_str ((int) status));
}
- kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
+ kill_lwp (GET_LWP (lp->ptid), my_WSTOPSIG (status));
}
return 0;
}
@@ -1623,7 +1692,7 @@
{
if (debug_linux_nat)
printf_unfiltered (_("FC: LP has pending status %06x\n"), lp->status);
- if (WIFSTOPPED (lp->status) && sigismember (flush_mask, WSTOPSIG (lp->status)))
+ if (WIFSTOPPED (lp->status) && sigismember (flush_mask, my_WSTOPSIG (lp->status)))
lp->status = 0;
}
@@ -1677,7 +1746,7 @@
/* Count only LWPs that have a SIGTRAP event pending. */
if (lp->status != 0
- && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP)
+ && WIFSTOPPED (lp->status) && my_WSTOPSIG (lp->status) == SIGTRAP)
(*count)++;
return 0;
@@ -1705,7 +1774,7 @@
/* Select only LWPs that have a SIGTRAP event pending. */
if (lp->status != 0
- && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP)
+ && WIFSTOPPED (lp->status) && my_WSTOPSIG (lp->status) == SIGTRAP)
if ((*selector)-- == 0)
return 1;
@@ -1733,7 +1802,7 @@
tripped on it. */
if (lp->status != 0
- && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
+ && WIFSTOPPED (lp->status) && my_WSTOPSIG (lp->status) == SIGTRAP
&& breakpoint_inserted_here_p (read_pc_pid (lp->ptid) -
DECR_PC_AFTER_BREAK))
{
@@ -2026,7 +2095,9 @@
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WIFSTOPPED (status)
+ && ((WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ || WSTOPSIG (status) == (SIGTRAP|0x80)))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
@@ -2162,7 +2233,7 @@
if (WIFSTOPPED (status))
{
- int signo = target_signal_from_host (WSTOPSIG (status));
+ int signo = target_signal_from_host (my_WSTOPSIG (status));
/* If we get a signal while single-stepping, we may need special
care, e.g. to skip the signal handler. Defer to common code. */
@@ -2227,7 +2298,7 @@
the comment in cancel_breakpoints_callback to find out why. */
iterate_over_lwps (cancel_breakpoints_callback, lp);
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
+ if (WIFSTOPPED (status) && my_WSTOPSIG (status) == SIGTRAP)
{
trap_ptid = lp->ptid;
if (debug_linux_nat)
@@ -3165,6 +3236,8 @@
t->to_insert_fork_catchpoint = child_insert_fork_catchpoint;
t->to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
t->to_insert_exec_catchpoint = child_insert_exec_catchpoint;
+ t->to_insert_syscall_catchpoint = child_insert_syscall_catchpoint;
+ t->to_remove_syscall_catchpoint = child_remove_syscall_catchpoint;
t->to_pid_to_exec_file = child_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
t->to_post_attach = child_post_attach;
diff -ru gdb-6.6/gdb/target.c gdb-6.6/gdb/target.c
--- gdb-6.6/gdb/target.c 2006-10-17 16:55:23.000000000 -0500
+++ gdb-6.6/gdb/target.c 2007-06-05 00:58:39.000000000 -0500
@@ -434,6 +434,8 @@
INHERIT (to_insert_exec_catchpoint, t);
INHERIT (to_remove_exec_catchpoint, t);
INHERIT (to_reported_exec_events_per_exec_call, t);
+ INHERIT (to_insert_syscall_catchpoint, t);
+ INHERIT (to_remove_syscall_catchpoint, t);
INHERIT (to_has_exited, t);
INHERIT (to_mourn_inferior, t);
INHERIT (to_can_run, t);
@@ -596,6 +598,12 @@
de_fault (to_reported_exec_events_per_exec_call,
(int (*) (void))
return_one);
+ de_fault (to_insert_syscall_catchpoint,
+ (void (*) (int))
+ tcomplain);
+ de_fault (to_remove_syscall_catchpoint,
+ (int (*) (int))
+ tcomplain);
de_fault (to_has_exited,
(int (*) (int, int, int *))
return_zero);
@@ -2112,6 +2120,9 @@
case TARGET_WAITKIND_EXECD:
fprintf_unfiltered (gdb_stdlog, "execd\n");
break;
+ case TARGET_WAITKIND_SYSCALL:
+ fprintf_unfiltered (gdb_stdlog, "syscalled\n");
+ break;
case TARGET_WAITKIND_SPURIOUS:
fprintf_unfiltered (gdb_stdlog, "spurious\n");
break;
@@ -2549,6 +2560,28 @@
return reported_exec_events;
}
+static void
+debug_to_insert_syscall_catchpoint (int pid)
+{
+ debug_target.to_insert_syscall_catchpoint (pid);
+
+ fprintf_unfiltered (gdb_stdlog, "target_insert_syscall_catchpoint (%d)\n",
+ pid);
+}
+
+static int
+debug_to_remove_syscall_catchpoint (int pid)
+{
+ int retval;
+
+ retval = debug_target.to_remove_syscall_catchpoint (pid);
+
+ fprintf_unfiltered (gdb_stdlog, "target_remove_syscall_catchpoint (%d) = %d\n",
+ pid, retval);
+
+ return retval;
+}
+
static int
debug_to_has_exited (int pid, int wait_status, int *exit_status)
{
@@ -2707,6 +2740,8 @@
current_target.to_insert_exec_catchpoint = debug_to_insert_exec_catchpoint;
current_target.to_remove_exec_catchpoint = debug_to_remove_exec_catchpoint;
current_target.to_reported_exec_events_per_exec_call = debug_to_reported_exec_events_per_exec_call;
+ current_target.to_insert_syscall_catchpoint = debug_to_insert_syscall_catchpoint;
+ current_target.to_remove_syscall_catchpoint = debug_to_remove_syscall_catchpoint;
current_target.to_has_exited = debug_to_has_exited;
current_target.to_mourn_inferior = debug_to_mourn_inferior;
current_target.to_can_run = debug_to_can_run;
diff -ru gdb-6.6/gdb/target.h gdb-6.6/gdb/target.h
--- gdb-6.6/gdb/target.h 2006-10-17 16:55:23.000000000 -0500
+++ gdb-6.6/gdb/target.h 2007-06-06 16:22:27.000000000 -0500
@@ -117,6 +117,12 @@
TARGET_WAITKIND_SYSCALL_ENTRY,
TARGET_WAITKIND_SYSCALL_RETURN,
+ /* The program has entered or returned from a system call, but the
+ kernel (e.g. Linux) doesn't provide enough information to tell
+ which. */
+
+ TARGET_WAITKIND_SYSCALL,
+
/* Nothing happened, but we stopped anyway. This perhaps should be handled
within target_wait, but I'm not sure target_wait should be resuming the
inferior. */
@@ -383,6 +389,8 @@
void (*to_insert_exec_catchpoint) (int);
int (*to_remove_exec_catchpoint) (int);
int (*to_reported_exec_events_per_exec_call) (void);
+ void (*to_insert_syscall_catchpoint) (int);
+ int (*to_remove_syscall_catchpoint) (int);
int (*to_has_exited) (int, int, int *);
void (*to_mourn_inferior) (void);
int (*to_can_run) (void);
@@ -712,6 +720,8 @@
extern int inferior_has_execd (int pid, char **execd_pathname);
+extern int inferior_has_syscalled (int pid);
+
/* From exec.c */
extern void print_section_info (struct target_ops *, bfd *);
@@ -878,6 +888,16 @@
#define target_reported_exec_events_per_exec_call() \
(*current_target.to_reported_exec_events_per_exec_call) ()
+/* On some targets, we can catch an inferior syscall event when it
+ occurs. These functions insert/remove an already-created
+ catchpoint for such events. */
+
+#define target_insert_syscall_catchpoint(pid) \
+ (*current_target.to_insert_syscall_catchpoint) (pid)
+
+#define target_remove_syscall_catchpoint(pid) \
+ (*current_target.to_remove_syscall_catchpoint) (pid)
+
/* Returns TRUE if PID has exited. And, also sets EXIT_STATUS to the
exit code of PID, if any. */
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [rfc] catch syscall
2007-06-09 20:18 [rfc] catch syscall Alan Curry
@ 2007-07-03 17:52 ` Daniel Jacobowitz
2007-07-04 21:36 ` Alan Curry
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Jacobowitz @ 2007-07-03 17:52 UTC (permalink / raw)
To: Alan Curry; +Cc: gdb-patches
On Sat, Jun 09, 2007 at 04:17:03PM -0400, Alan Curry wrote:
> For a long time I've wanted to be able to mix strace-like syscall tracing
> with the awesome power of gdb. Other people have mentioned the idea a few
> times over the years, but nothing ever seems to get done. So I did something.
Thank you! I've always wanted this. Every time I thought about it, I
got hung up on all the argument decoding that strace knows how to do.
But just because we don't know how to decode arguments doesn't mean
this isn't useful anyway.
For a patch of this size, we will need an FSF copyright assignment.
Unless you or your employer have one in place already, you'll need to
contact the FSF for this. Let me know if you need one, and I'll send
you the request form.
> 1. PTRACE_O_TRACESYSGOOD is enabled to make syscall stops distinguishable
> from random SIGTRAPs
> 2. When the inferior is resumed, if there is an enabled syscall catchpoint,
> PTRACE_SYSCALL is used instead of PTRACE_CONT.
> 3. When the inferior stops, the special signal value 0x80|SIGTRAP is
> recognized and the syscall catchpoint claims responsibility for the
> stop.
Sounds right to me. If we can distinguish between entry and exit, it
would be nice to do so; though as I recall from hacking on strace,
this is tricky.
On a minor note, be careful about tabs please; our convention is to
always use a tab in place of eight leading spaces, and I can tell from
some parts of your diff that line up funny that you haven't. Well,
either that our your mail client ate them...
I don't realy like the duplication in breakpoint.c. I don't see a
great way around it, either. Maybe there should be a single
target_insert_catchpoint method.
There's already TARGET_WAITKIND_SYSCALL_ENTRY and
TARGET_WAITKIND_SYSCALL_EXIT. Their default behavior is only useful
for HP/UX, but maybe we could combine them with this new thing. It's
a pity ttrace makes it easy to separate entry/exit and ptrace doesn't.
> +#if 0
> + /* Ideally, print the syscall's __NR here, and maybe its name.
> + Getting the __NR and mapping it to a name are both arch-specific
> + so I'm saving it for later. It'll go something like this: */
> + if (b->syscall_name != NULL)
> + {
> + ui_out_field_string (uiout, "what", b->syscall_name);
> + }
> +#endif
Yes, good choice to wait :-) If we want to do this later, we can add
gdbarch methods as needed.
> diff -ru gdb-6.6/gdb/inferior.h gdb-6.6/gdb/inferior.h
> --- gdb-6.6/gdb/inferior.h 2006-10-18 11:56:13.000000000 -0500
> +++ gdb-6.6/gdb/inferior.h 2007-06-06 21:54:37.000000000 -0500
> @@ -417,6 +417,12 @@
>
> extern int proceed_to_finish;
>
> +/* Nonzero if a syscall catchpoint is active. There must be a better place
> + for this, where it can be associated with a particular process if multiple
> + forks are being traced. Where should it be really? */
> +
> +extern int catching_syscalls;
> +
> /* Save register contents here when about to pop a stack dummy frame,
> if-and-only-if proceed_to_finish is set.
> Thus this contains the return value from the called function (assuming
I guess this is as good as anywhere. I don't see how to do it without
the global while still reusing inf_ptrace_resume.
> @@ -1497,6 +1507,33 @@
> }
> goto process_event_stop_test;
>
> + case TARGET_WAITKIND_SYSCALL:
> + if (debug_infrun)
> + fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL\n");
> + stop_signal = TARGET_SIGNAL_TRAP;
> + pending_follow.kind = ecs->ws.kind;
I don't think you need to mess with pending_follow. It's an event;
_LOADED may be a better model than _FORKED for the event handling
side.
> +/* Determine which PTRACE_O_* flags are usable with the running kernel.
>
> - First, we try to enable fork tracing on ORIGINAL_PID. If this fails,
> + First, we try to enable TRACESYSGOOD on ORIGINAL_PID. If this fails, the
> + kernel is too old to support any of the flags.
> +
> + Next, we try to enable fork tracing on ORIGINAL_PID. If this fails,
> we know that the feature is not available. This may change the tracing
> options for ORIGINAL_PID, but we'll be setting them shortly anyway.
>
This won't actually work, I don't think. Old kernels used to always
allow TRACESYSGOOD or else silently ignore attempts to set it. I
might be wrong though - you have to go back quite a ways to find a
kernel where this doesn't work.
Probably we can just assume TRACESYSGOOD works OK.
> + catching_syscalls=0;
Spaces around operators please.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [rfc] catch syscall
2007-07-03 17:52 ` Daniel Jacobowitz
@ 2007-07-04 21:36 ` Alan Curry
2007-07-04 22:04 ` Daniel Jacobowitz
0 siblings, 1 reply; 6+ messages in thread
From: Alan Curry @ 2007-07-04 21:36 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
Just when I had given up on getting any replies...
Daniel Jacobowitz writes the following:
>
>On Sat, Jun 09, 2007 at 04:17:03PM -0400, Alan Curry wrote:
>> For a long time I've wanted to be able to mix strace-like syscall tracing
>> with the awesome power of gdb. Other people have mentioned the idea a few
>> times over the years, but nothing ever seems to get done. So I did something.
>
>Thank you! I've always wanted this. Every time I thought about it, I
>got hung up on all the argument decoding that strace knows how to do.
That is definitely the hard part.
>But just because we don't know how to decode arguments doesn't mean
>this isn't useful anyway.
A lot of syscalls can be printed nicely using existing gdb commands, like
this:
if $r0 == 4
output "write("
output $r3
output ", "
output *(char *)$r4@$r5
output ", "
output $r5
output ")\n"
end
>
>For a patch of this size, we will need an FSF copyright assignment.
>Unless you or your employer have one in place already, you'll need to
>contact the FSF for this. Let me know if you need one, and I'll send
>you the request form.
I haven't got one of those yet. And I've got no employer right now[1]
>
>> 1. PTRACE_O_TRACESYSGOOD is enabled to make syscall stops distinguishable
>> from random SIGTRAPs
>> 2. When the inferior is resumed, if there is an enabled syscall catchpoint,
>> PTRACE_SYSCALL is used instead of PTRACE_CONT.
>> 3. When the inferior stops, the special signal value 0x80|SIGTRAP is
>> recognized and the syscall catchpoint claims responsibility for the
>> stop.
>
>Sounds right to me. If we can distinguish between entry and exit, it
>would be nice to do so; though as I recall from hacking on strace,
>this is tricky.
Actually it doesn't look too difficult in theory. As far as I can tell, a
syscall event is an exit if and only if the previous event was a syscall
entry and the program was continued with PTRACE_SYSCALL. (Interesting note
from my tests: if you're currently stopped at a syscall entry, stepi actually
steps further than cont. The PTRACE_SINGLESTEP completes the syscall and then
executes one userspace instruction, whereas cont maps to PTRACE_SYSCALL which
just finishes the syscall. This could be hacked around if it's a problem.)
We just need to keep track of a single bit of extra state for each inferior
thread, to know what type of syscall event is expected next. I'm just having
a hard time finding where per-inferior-thread information is supposed to be
stored.
>
>On a minor note, be careful about tabs please; our convention is to
>always use a tab in place of eight leading spaces, and I can tell from
>some parts of your diff that line up funny that you haven't. Well,
>either that our your mail client ate them...
That's the ':set expandtab' that I usually use.
>
>I don't realy like the duplication in breakpoint.c. I don't see a
>great way around it, either. Maybe there should be a single
>target_insert_catchpoint method.
Well, I think I only added one of what there were already several of...
>
>> +#if 0
>> + /* Ideally, print the syscall's __NR here, and maybe its name.
>> + Getting the __NR and mapping it to a name are both arch-specific
>> + so I'm saving it for later. It'll go something like this: */
>> + if (b->syscall_name != NULL)
>> + {
>> + ui_out_field_string (uiout, "what", b->syscall_name);
>> + }
>> +#endif
>
>Yes, good choice to wait :-) If we want to do this later, we can add
>gdbarch methods as needed.
It's just a matter of "which register do we peek at to get syscall number"
(one integer of per-arch information), and converting asm/unistd.h into a
static string table. But the hypothetical libstrace would do that and a lot
more.
>> +/* Nonzero if a syscall catchpoint is active. There must be a better place
>> + for this, where it can be associated with a particular process if multiple
>> + forks are being traced. Where should it be really? */
>> +
>> +extern int catching_syscalls;
>> +
>
>I guess this is as good as anywhere. I don't see how to do it without
>the global while still reusing inf_ptrace_resume.
I was confused here because I created to_insert_syscall_catchpoint as a copy
of to_insert_{fork,vfork,exec}_catchpoint, and they all take a pid argument,
but don't use it. So I expected to find a struct full of tracking information
I could store a flag in, like this:
get_task_struct(pid)->catching_syscalls = 1;
but couldn't find it. Then I realized the user interface of the "catch"
command doesn't really provide a way to request catching on a specific PID.
So why do those insert_*_catchpoint methods take an argument, if enabling a
catchpoint always applies to all the tasks being debugged?
>> + stop_signal = TARGET_SIGNAL_TRAP;
>> + pending_follow.kind = ecs->ws.kind;
>
>I don't think you need to mess with pending_follow. It's an event;
>_LOADED may be a better model than _FORKED for the event handling
>side.
That was pure cut-and-paste. I haven't figured out what rules apply to the
use of pending_follow. Maybe it's the right place for that new
"next_syscall_event_will_be_exit" flag I was talking about.
>
>> +/* Determine which PTRACE_O_* flags are usable with the running kernel.
>>
>> - First, we try to enable fork tracing on ORIGINAL_PID. If this fails,
>> + First, we try to enable TRACESYSGOOD on ORIGINAL_PID. If this fails, the
>> + kernel is too old to support any of the flags.
>> +
>> + Next, we try to enable fork tracing on ORIGINAL_PID. If this fails,
>> we know that the feature is not available. This may change the tracing
>> options for ORIGINAL_PID, but we'll be setting them shortly anyway.
>>
>
>This won't actually work, I don't think. Old kernels used to always
>allow TRACESYSGOOD or else silently ignore attempts to set it. I
>might be wrong though - you have to go back quite a ways to find a
>kernel where this doesn't work.
I did go back and look at those old kernels. PTRACE_SETOPTIONS didn't exist
before TRACESYSGOOD, so it looks like you should get an EIO for invalid
ptrace command. The ignoring of unrecognized options only applies to the
kernels new enough to have PTRACE_SETOPTIONS (and therefore at least
TRACESYSGOOD). But I didn't actually try compiling and booting one of those
old kernels to confirm it.
>
>> + catching_syscalls=0;
>
>Spaces around operators please.
If that's the worst thing I did, this has gone better than expected.
[1] Hint
--
Alan Curry
pacman@world.std.com
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [rfc] catch syscall
2007-07-04 21:36 ` Alan Curry
@ 2007-07-04 22:04 ` Daniel Jacobowitz
2007-07-06 23:48 ` Alan Curry
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Jacobowitz @ 2007-07-04 22:04 UTC (permalink / raw)
To: Alan Curry; +Cc: gdb-patches
On Wed, Jul 04, 2007 at 04:53:58PM -0400, Alan Curry wrote:
> Just when I had given up on getting any replies...
Sorry. GDB development does move slowly sometimes.
> A lot of syscalls can be printed nicely using existing gdb commands, like
> this:
Right. Or, we could eventually build up a library in either C or
Python to handle it.
> >Sounds right to me. If we can distinguish between entry and exit, it
> >would be nice to do so; though as I recall from hacking on strace,
> >this is tricky.
>
> Actually it doesn't look too difficult in theory. As far as I can tell, a
> syscall event is an exit if and only if the previous event was a syscall
> entry and the program was continued with PTRACE_SYSCALL. (Interesting note
> from my tests: if you're currently stopped at a syscall entry, stepi actually
> steps further than cont. The PTRACE_SINGLESTEP completes the syscall and then
> executes one userspace instruction, whereas cont maps to PTRACE_SYSCALL which
> just finishes the syscall. This could be hacked around if it's a problem.)
We'd probably want to use PTRACE_SYSCALL for single step, if we knew
we were inside a syscall.
If you didn't use PTRACE_SYSCALL previously, are you always past the
point of syscall tracing by the time the process stops? I was worried
that the first PTRACE_SYSCALL might sometimes take you to an exit
rather than an entrance. But maybe that never happens.
> We just need to keep track of a single bit of extra state for each inferior
> thread, to know what type of syscall event is expected next. I'm just having
> a hard time finding where per-inferior-thread information is supposed to be
> stored.
This would probably be Linux-specific data, at least for now. Take a
look at the LWP list in linux-nat.c.
> >I don't realy like the duplication in breakpoint.c. I don't see a
> >great way around it, either. Maybe there should be a single
> >target_insert_catchpoint method.
>
> Well, I think I only added one of what there were already several of...
Yeah. A lot of bits of GDB just accreted the way they are. This also
applies to your question about the ptid_t argument; it's no longer
useful.
> >> + stop_signal = TARGET_SIGNAL_TRAP;
> >> + pending_follow.kind = ecs->ws.kind;
> >
> >I don't think you need to mess with pending_follow. It's an event;
> >_LOADED may be a better model than _FORKED for the event handling
> >side.
>
> That was pure cut-and-paste. I haven't figured out what rules apply to the
> use of pending_follow. Maybe it's the right place for that new
> "next_syscall_event_will_be_exit" flag I was talking about.
Maybe, but I don't think you'll need it. We can handle this entirely
within the GNU/Linux specific support.
> I did go back and look at those old kernels. PTRACE_SETOPTIONS didn't exist
> before TRACESYSGOOD, so it looks like you should get an EIO for invalid
> ptrace command. The ignoring of unrecognized options only applies to the
> kernels new enough to have PTRACE_SETOPTIONS (and therefore at least
> TRACESYSGOOD). But I didn't actually try compiling and booting one of those
> old kernels to confirm it.
Maybe we can skip some of the checking code, then.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [rfc] catch syscall
2007-07-04 22:04 ` Daniel Jacobowitz
@ 2007-07-06 23:48 ` Alan Curry
2007-07-07 0:51 ` Daniel Jacobowitz
0 siblings, 1 reply; 6+ messages in thread
From: Alan Curry @ 2007-07-06 23:48 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
Daniel Jacobowitz writes the following:
>
>If you didn't use PTRACE_SYSCALL previously, are you always past the
>point of syscall tracing by the time the process stops? I was worried
>that the first PTRACE_SYSCALL might sometimes take you to an exit
>rather than an entrance. But maybe that never happens.
I've tried to make that happen and I can't.
>
>> We just need to keep track of a single bit of extra state for each inferior
>> thread, to know what type of syscall event is expected next. I'm just having
>> a hard time finding where per-inferior-thread information is supposed to be
>> stored.
>
>This would probably be Linux-specific data, at least for now. Take a
>look at the LWP list in linux-nat.c.
If we want the generic code in inf-ptrace.c to behave differently (for
example using PTRACE_SYSCALL instead of PTRACE_SINGLESTEP) depending on the
value of a flag in that Linux-specific data, how do we get at it? Add another
target method to return the flag? I noticed when doing this patch that adding
a target method involves changing code in several different places.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [rfc] catch syscall
2007-07-06 23:48 ` Alan Curry
@ 2007-07-07 0:51 ` Daniel Jacobowitz
0 siblings, 0 replies; 6+ messages in thread
From: Daniel Jacobowitz @ 2007-07-07 0:51 UTC (permalink / raw)
To: Alan Curry; +Cc: gdb-patches
On Fri, Jul 06, 2007 at 07:48:06PM -0400, Alan Curry wrote:
> >> We just need to keep track of a single bit of extra state for each inferior
> >> thread, to know what type of syscall event is expected next. I'm just having
> >> a hard time finding where per-inferior-thread information is supposed to be
> >> stored.
> >
> >This would probably be Linux-specific data, at least for now. Take a
> >look at the LWP list in linux-nat.c.
>
> If we want the generic code in inf-ptrace.c to behave differently (for
> example using PTRACE_SYSCALL instead of PTRACE_SINGLESTEP) depending on the
> value of a flag in that Linux-specific data, how do we get at it? Add another
> target method to return the flag? I noticed when doing this patch that adding
> a target method involves changing code in several different places.
I don't know. The best answer may be to not use the generic routine
any more. There is not much of one, and some of it (the ptid check)
is not required with the linux-nat usage.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2007-07-07 0:51 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-09 20:18 [rfc] catch syscall Alan Curry
2007-07-03 17:52 ` Daniel Jacobowitz
2007-07-04 21:36 ` Alan Curry
2007-07-04 22:04 ` Daniel Jacobowitz
2007-07-06 23:48 ` Alan Curry
2007-07-07 0:51 ` Daniel Jacobowitz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox