This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfc] catch syscall


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 @@
 }
 
 
-/* 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.  */
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]