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]

Re: [RFC 1/3] catch syscall -- try 6 -- Source-code modifications


Hi Eli,

On Saturday 05 September 2009, Eli Zaretskii wrote:
> > +/* Syscall catch.  NEEDED is nonzero if any syscall catch (of any
> > +   kind) is requested.  ANY_COUNT is nonzero if a generic
> > +   (filter-less) syscall catch is being requested.  TABLE is an array
> > +   of ints, indexed by syscall number.  An element in this array is
> > +   nonzero if that syscall should be caught.  TABLE_SIZE is the number
> > +   of elements in TABLE.  */
> > +
> > +#define target_set_syscall_catchpoint(pid, needed, any_count,
> > table_size, table) \ +     (*current_target.to_set_syscall_catchpoint)
> > (pid, needed, any_count, \ +						  table_size, table)
> > +
> 
> Could you perhaps expand the comment a bit more?  For example, what
> should the target do if NEEDED is zero?  Also, I understand that
> ANY_COUNT nonzero means TABLE should be ignored, is that right?
> 
> IOW, imagine that someone is to implement this method, and try to give
> any information necessary to write the code.

I tried to be more descriptive about this piece of code.  I also addressed 
your other message, fixing some wrong comments.  What do you think now?

Thank you for reviewing this,

-- 
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 791e2c4..f527b7e 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -35,6 +35,10 @@
 
 #include "amd64-tdep.h"
 #include "solib-svr4.h"
+#include "xml-syscall.h"
+
+/* The syscall's XML filename for i386.  */
+#define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
 
 #include "record.h"
 #include "linux-record.h"
@@ -174,6 +178,28 @@ amd64_linux_sigcontext_addr (struct frame_info *this_frame)
 }
 
 
+static LONGEST
+amd64_linux_get_syscall_number (struct gdbarch *gdbarch,
+                                ptid_t ptid)
+{
+  struct regcache *regcache = get_thread_regcache (ptid);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  /* The content of a register.  */
+  gdb_byte buf[8];
+  /* The result.  */
+  LONGEST ret;
+
+  /* Getting the system call number from the register.
+     When dealing with x86_64 architecture, this information
+     is stored at %rax register.  */
+  regcache_cooked_read (regcache, AMD64_LINUX_ORIG_RAX_REGNUM, buf);
+
+  ret = extract_signed_integer (buf, 8, byte_order);
+
+  return ret;
+}
+
+
 /* From <asm/sigcontext.h>.  */
 static int amd64_linux_sc_reg_offset[] =
 {
@@ -1168,6 +1194,11 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_register_type (gdbarch, amd64_linux_register_type);
   set_gdbarch_register_reggroup_p (gdbarch, amd64_linux_register_reggroup_p);
 
+  /* Functions for 'catch syscall'.  */
+  set_xml_syscall_file_name (XML_SYSCALL_FILENAME_AMD64);
+  set_gdbarch_get_syscall_number (gdbarch,
+                                  amd64_linux_get_syscall_number);
+
   /* Enable TLS support.  */
   set_gdbarch_fetch_tls_load_module_address (gdbarch,
                                              svr4_fetch_objfile_link_map);
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 9f50872..5799935 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -60,6 +60,7 @@
 #include "wrapper.h"
 #include "valprint.h"
 #include "jit.h"
+#include "xml-syscall.h"
 
 /* readline include files */
 #include "readline/readline.h"
@@ -204,6 +205,8 @@ static int is_hardware_watchpoint (struct breakpoint *bpt);
 
 static void insert_breakpoint_locations (void);
 
+static int syscall_catchpoint_p (struct breakpoint *b);
+
 static void tracepoints_info (char *, int);
 
 static void delete_trace_command (char *, int);
@@ -4445,6 +4448,7 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch,
   b->frame_id = null_frame_id;
   b->forked_inferior_pid = null_ptid;
   b->exec_pathname = NULL;
+  b->syscalls_to_be_caught = NULL;
   b->ops = NULL;
   b->condition_not_parsed = 0;
 
@@ -4939,7 +4943,266 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
   print_mention_catch_vfork
 };
 
-/* Create a new breakpoint of the bp_catchpoint kind and return it.
+/* Implement the "insert" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static void
+insert_catch_syscall (struct breakpoint *b)
+{
+  struct inferior *inf = current_inferior ();
+
+  ++inf->total_syscalls_count;
+  if (!b->syscalls_to_be_caught)
+    ++inf->any_syscall_count;
+  else
+    {
+      int i, iter;
+      for (i = 0;
+           VEC_iterate (int, b->syscalls_to_be_caught, i, iter);
+           i++)
+	{
+          int elem;
+	  if (iter >= VEC_length (int, inf->syscalls_counts))
+	    {
+              int old_size = VEC_length (int, inf->syscalls_counts);
+              uintptr_t vec_addr_offset = old_size * ((uintptr_t) sizeof (int));
+              uintptr_t vec_addr;
+              VEC_safe_grow (int, inf->syscalls_counts, iter + 1);
+              vec_addr = (uintptr_t) VEC_address (int, inf->syscalls_counts) +
+		vec_addr_offset;
+              memset ((void *) vec_addr, 0,
+                      (iter + 1 - old_size) * sizeof (int));
+	    }
+          elem = VEC_index (int, inf->syscalls_counts, iter);
+          VEC_replace (int, inf->syscalls_counts, iter, ++elem);
+	}
+    }
+
+  target_set_syscall_catchpoint (PIDGET (inferior_ptid),
+				 inf->total_syscalls_count != 0,
+				 inf->any_syscall_count,
+				 VEC_length (int, inf->syscalls_counts),
+				 VEC_address (int, inf->syscalls_counts));
+}
+
+/* Implement the "remove" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static int
+remove_catch_syscall (struct breakpoint *b)
+{
+  struct inferior *inf = current_inferior ();
+
+  --inf->total_syscalls_count;
+  if (!b->syscalls_to_be_caught)
+    --inf->any_syscall_count;
+  else
+    {
+      int i, iter;
+      for (i = 0;
+           VEC_iterate (int, b->syscalls_to_be_caught, i, iter);
+           i++)
+	{
+          int elem;
+	  if (iter >= VEC_length (int, inf->syscalls_counts))
+	    /* Shouldn't happen.  */
+	    continue;
+          elem = VEC_index (int, inf->syscalls_counts, iter);
+          VEC_replace (int, inf->syscalls_counts, iter, --elem);
+        }
+    }
+
+  return target_set_syscall_catchpoint (PIDGET (inferior_ptid),
+					inf->total_syscalls_count != 0,
+					inf->any_syscall_count,
+					VEC_length (int, inf->syscalls_counts),
+					VEC_address (int, inf->syscalls_counts));
+}
+
+/* Implement the "breakpoint_hit" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static int
+breakpoint_hit_catch_syscall (struct breakpoint *b)
+{
+  /* We must check if we are catching specific syscalls in this breakpoint.
+     If we are, then we must guarantee that the called syscall is the same
+     syscall we are catching.  */
+  int syscall_number = 0;
+
+  if (!inferior_has_called_syscall (inferior_ptid, &syscall_number))
+    return 0;
+
+  /* Now, checking if the syscall is the same.  */
+  if (b->syscalls_to_be_caught)
+    {
+      int i, iter;
+      for (i = 0;
+           VEC_iterate (int, b->syscalls_to_be_caught, i, iter);
+           i++)
+	if (syscall_number == iter)
+	  break;
+      /* Not the same.  */
+      if (!iter)
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Implement the "print_it" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static enum print_stop_action
+print_it_catch_syscall (struct breakpoint *b)
+{
+  /* These are needed because we want to know in which state a
+     syscall is.  It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
+     or TARGET_WAITKIND_SYSCALL_RETURN, and depending on it we
+     must print "called syscall" or "returned from syscall".  */
+  ptid_t ptid;
+  struct target_waitstatus last;
+  struct syscall s;
+  struct cleanup *old_chain;
+  char *syscall_id;
+
+  get_last_target_status (&ptid, &last);
+
+  get_syscall_by_number (last.value.syscall_number, &s);
+
+  annotate_catchpoint (b->number);
+
+  if (s.name == NULL)
+    syscall_id = xstrprintf ("%d", last.value.syscall_number);
+  else
+    syscall_id = xstrprintf ("'%s'", s.name);
+
+  old_chain = make_cleanup (xfree, syscall_id);
+
+  if (last.kind == TARGET_WAITKIND_SYSCALL_ENTRY)
+    printf_filtered (_("\nCatchpoint %d (call to syscall %s), "),
+                     b->number, syscall_id);
+  else if (last.kind == TARGET_WAITKIND_SYSCALL_RETURN)
+    printf_filtered (_("\nCatchpoint %d (returned from syscall %s), "),
+                     b->number, syscall_id);
+
+  do_cleanups (old_chain);
+
+  return PRINT_SRC_AND_LOC;
+}
+
+/* Implement the "print_one" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static void
+print_one_catch_syscall (struct breakpoint *b,
+                         struct bp_location **last_loc)
+{
+  struct value_print_options opts;
+
+  get_user_print_options (&opts);
+  /* 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 (opts.addressprint)
+    ui_out_field_skip (uiout, "addr");
+  annotate_field (5);
+
+  if (b->syscalls_to_be_caught
+      && VEC_length (int, b->syscalls_to_be_caught) > 1)
+    ui_out_text (uiout, "syscalls \"");
+  else
+    ui_out_text (uiout, "syscall \"");
+
+  if (b->syscalls_to_be_caught)
+    {
+      int i, iter;
+      char *text = xstrprintf ("%s", "");
+      for (i = 0;
+           VEC_iterate (int, b->syscalls_to_be_caught, i, iter);
+           i++)
+        {
+          char *x = text;
+          struct syscall s;
+          get_syscall_by_number (iter, &s);
+
+          if (s.name != NULL)
+            text = xstrprintf ("%s%s, ", text, s.name);
+          else
+            text = xstrprintf ("%s%d, ", text, iter);
+
+          /* We have to xfree the last 'text' (now stored at 'x')
+             because xstrprintf dinamically allocates new space for it
+             on every call.  */
+	  xfree (x);
+        }
+      /* Remove the last comma.  */
+      text[strlen (text) - 2] = '\0';
+      ui_out_field_string (uiout, "what", text);
+    }
+  else
+    ui_out_field_string (uiout, "what", "<any syscall>");
+  ui_out_text (uiout, "\" ");
+}
+
+/* Implement the "print_mention" breakpoint_ops method for syscall
+   catchpoints.  */
+
+static void
+print_mention_catch_syscall (struct breakpoint *b)
+{
+  if (b->syscalls_to_be_caught)
+    {
+      int i, iter;
+
+      if (VEC_length (int, b->syscalls_to_be_caught) > 1)
+        printf_filtered (_("Catchpoint %d (syscalls"), b->number);
+      else
+        printf_filtered (_("Catchpoint %d (syscall"), b->number);
+
+      for (i = 0;
+           VEC_iterate (int, b->syscalls_to_be_caught, i, iter);
+           i++)
+        {
+          struct syscall s;
+          get_syscall_by_number (iter, &s);
+
+          if (s.name)
+            printf_filtered (" '%s' [%d]", s.name, s.number);
+          else
+            printf_filtered (" %d", s.number);
+        }
+      printf_filtered (")");
+    }
+  else
+    printf_filtered (_("Catchpoint %d (any syscall)"),
+                     b->number);
+}
+
+/* The breakpoint_ops structure to be used in syscall catchpoints.  */
+
+static struct breakpoint_ops catch_syscall_breakpoint_ops =
+{
+  insert_catch_syscall,
+  remove_catch_syscall,
+  breakpoint_hit_catch_syscall,
+  print_it_catch_syscall,
+  print_one_catch_syscall,
+  print_mention_catch_syscall
+};
+
+/* Returns non-zero if 'b' is a syscall catchpoint.  */
+
+static int
+syscall_catchpoint_p (struct breakpoint *b)
+{
+  return (b->ops == &catch_syscall_breakpoint_ops);
+}
+
+/* Create a new breakpoint of the bp_catchpoint kind and return it,
+   but does NOT mention it nor update the global location list.
+   This is useful if you need to fill more fields in the
+   struct breakpoint before calling mention.
  
    If TEMPFLAG is non-zero, then make the breakpoint temporary.
    If COND_STRING is not NULL, then store it in the breakpoint.
@@ -4947,16 +5210,14 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
    to the catchpoint.  */
 
 static struct breakpoint *
-create_catchpoint (struct gdbarch *gdbarch, int tempflag,
-		   char *cond_string, struct breakpoint_ops *ops)
+create_catchpoint_without_mention (struct gdbarch *gdbarch, int tempflag,
+				   char *cond_string,
+				   struct breakpoint_ops *ops)
 {
   struct symtab_and_line sal;
   struct breakpoint *b;
 
   init_sal (&sal);
-  sal.pc = 0;
-  sal.symtab = NULL;
-  sal.line = 0;
 
   b = set_raw_breakpoint (gdbarch, sal, bp_catchpoint);
   set_breakpoint_count (breakpoint_count + 1);
@@ -4969,6 +5230,23 @@ create_catchpoint (struct gdbarch *gdbarch, int tempflag,
   b->disposition = tempflag ? disp_del : disp_donttouch;
   b->ops = ops;
 
+  return b;
+}
+
+/* Create a new breakpoint of the bp_catchpoint kind and return it.
+ 
+   If TEMPFLAG is non-zero, then make the breakpoint temporary.
+   If COND_STRING is not NULL, then store it in the breakpoint.
+   OPS, if not NULL, is the breakpoint_ops structure associated
+   to the catchpoint.  */
+
+static struct breakpoint *
+create_catchpoint (struct gdbarch *gdbarch, int tempflag,
+		   char *cond_string, struct breakpoint_ops *ops)
+{
+  struct breakpoint *b =
+    create_catchpoint_without_mention (gdbarch, tempflag, cond_string, ops);
+
   mention (b);
   update_global_location_list (1);
 
@@ -5055,6 +5333,22 @@ static struct breakpoint_ops catch_exec_breakpoint_ops =
   print_mention_catch_exec
 };
 
+static void
+create_syscall_event_catchpoint (int tempflag, VEC(int) *filter,
+                                 struct breakpoint_ops *ops)
+{
+  struct gdbarch *gdbarch = get_current_arch ();
+  struct breakpoint *b =
+    create_catchpoint_without_mention (gdbarch, tempflag, NULL, ops);
+
+  b->syscalls_to_be_caught = filter;
+
+  /* Now, we have to mention the breakpoint and update the global
+     location list.  */
+  mention (b);
+  update_global_location_list (1);
+}
+
 static int
 hw_breakpoint_used_count (void)
 {
@@ -7150,6 +7444,113 @@ catch_ada_exception_command (char *arg, int from_tty,
                                    from_tty);
 }
 
+/* Cleanup function for a syscall filter list.  */
+static void
+clean_up_filters (void *arg)
+{
+  VEC(int) *iter = *(VEC(int) **) arg;
+  VEC_free (int, iter);
+}
+
+/* Splits the argument using space as delimiter.  Returns an xmalloc'd
+   filter list, or NULL if no filtering is required.  */
+static VEC(int) *
+catch_syscall_split_args (char *arg)
+{
+  VEC(int) *result = NULL;
+  struct cleanup *cleanup = make_cleanup (clean_up_filters, &result);
+
+  while (*arg != '\0')
+    {
+      int i, syscall_number;
+      char *endptr;
+      char cur_name[128];
+      struct syscall s;
+
+      /* Skip whitespace.  */
+      while (isspace (*arg))
+        arg++;
+
+      for (i = 0; arg[i] && !isspace (arg[i]); ++i)
+	cur_name[i] = arg[i];
+      cur_name[i] = '\0';
+      arg += i;
+
+      /* Check if the user provided a syscall name or a number.  */
+      syscall_number = (int) strtol (cur_name, &endptr, 10);
+      if (*endptr == '\0')
+        {
+          get_syscall_by_number (syscall_number, &s);
+
+          if (s.name == NULL)
+            /* We can issue just a warning, but still create the catchpoint.
+               This is because, even not knowing the syscall name that
+               this number represents, we can still try to catch the syscall
+               number.  */
+            warning (_("The number '%d' does not represent a known syscall."),
+                     syscall_number);
+        }
+      else
+        {
+          /* We have a name.  Let's check if it's valid and convert it
+             to a number.  */
+          get_syscall_by_name (cur_name, &s);
+
+          if (s.number == UNKNOWN_SYSCALL)
+            /* Here we have to issue an error instead of a warning, because
+               GDB cannot do anything useful if there's no syscall number to
+               be caught.  */
+            error (_("Unknown syscall name '%s'."), cur_name);
+        }
+
+      /* Ok, it's valid.  */
+      VEC_safe_push (int, result, s.number);
+    }
+
+  discard_cleanups (cleanup);
+  return result;
+}
+
+/* Implement the "catch syscall" command.  */
+
+static void
+catch_syscall_command_1 (char *arg, int from_tty, struct cmd_list_element *command)
+{
+  int tempflag;
+  VEC(int) *filter;
+  struct syscall s;
+  struct gdbarch *gdbarch = get_current_arch ();
+
+  /* Checking if the feature if supported.  */
+  if (gdbarch_get_syscall_number_p (gdbarch) == 0)
+    error (_("The feature 'catch syscall' is not supported on \
+this architeture yet."));
+
+  tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
+
+  ep_skip_leading_whitespace (&arg);
+
+  /* We need to do this first "dummy" translation in order
+     to get the syscall XML file loaded or, most important,
+     to display a warning to the user if there's no XML file
+     for his/her architecture.  */
+  get_syscall_by_number (0, &s);
+
+  /* The allowed syntax is:
+     catch syscall
+     catch syscall <name | number> [<name | number> ... <name | number>]
+
+     Let's check if there's a syscall name.  */
+
+  if (arg != NULL)
+    filter = catch_syscall_split_args (arg);
+  else
+    filter = NULL;
+
+  create_syscall_event_catchpoint (tempflag, filter,
+				   &catch_syscall_breakpoint_ops);
+}
+
 /* Implement the "catch assert" command.  */
 
 static void
@@ -7616,6 +8017,7 @@ delete_breakpoint (struct breakpoint *bpt)
     xfree (bpt->source_file);
   if (bpt->exec_pathname != NULL)
     xfree (bpt->exec_pathname);
+  clean_up_filters (&bpt->syscalls_to_be_caught);
 
   /* Be sure no bpstat's are pointing at it after it's been freed.  */
   /* FIXME, how can we find all bpstat's?
@@ -8552,6 +8954,60 @@ single_step_breakpoint_inserted_here_p (CORE_ADDR pc)
   return 0;
 }
 
+/* Returns 0 if 'bp' is NOT a syscall catchpoint,
+   non-zero otherwise.  */
+static int
+is_syscall_catchpoint_enabled (struct breakpoint *bp)
+{
+  if (syscall_catchpoint_p (bp)
+      && bp->enable_state != bp_disabled
+      && bp->enable_state != bp_call_disabled)
+    return 1;
+  else
+    return 0;
+}
+
+int
+catch_syscall_enabled (void)
+{
+  struct inferior *inf = current_inferior ();
+
+  return inf->total_syscalls_count != 0;
+}
+
+int
+catching_syscall_number (int syscall_number)
+{
+  struct breakpoint *bp;
+
+  ALL_BREAKPOINTS (bp)
+    if (is_syscall_catchpoint_enabled (bp))
+      {
+	if (bp->syscalls_to_be_caught)
+	  {
+            int i, iter;
+            for (i = 0;
+                 VEC_iterate (int, bp->syscalls_to_be_caught, i, iter);
+                 i++)
+	      if (syscall_number == iter)
+		return 1;
+	  }
+	else
+	  return 1;
+      }
+
+  return 0;
+}
+
+/* Complete syscall names.  Used by "catch syscall".  */
+static char **
+catch_syscall_completer (struct cmd_list_element *cmd,
+                         char *text, char *word)
+{
+  const char **list = get_syscall_names ();
+  return (list == NULL) ? NULL : complete_on_enum (list, text, word);
+}
+
 /* Tracepoint-specific operations.  */
 
 /* Set tracepoint count to NUM.  */
@@ -8903,6 +9359,8 @@ static void
 add_catch_command (char *name, char *docstring,
 		   void (*sfunc) (char *args, int from_tty,
 				  struct cmd_list_element *command),
+                   char **(*completer) (struct cmd_list_element *cmd,
+                                         char *text, char *word),
 		   void *user_data_catch,
 		   void *user_data_tcatch)
 {
@@ -8912,11 +9370,13 @@ add_catch_command (char *name, char *docstring,
 		     &catch_cmdlist);
   set_cmd_sfunc (command, sfunc);
   set_cmd_context (command, user_data_catch);
+  set_cmd_completer (command, completer);
 
   command = add_cmd (name, class_breakpoint, NULL, docstring,
 		     &tcatch_cmdlist);
   set_cmd_sfunc (command, sfunc);
   set_cmd_context (command, user_data_tcatch);
+  set_cmd_completer (command, completer);
 }
 
 void
@@ -9190,36 +9650,53 @@ Set temporary catchpoints to catch events."),
 Catch an exception, when caught.\n\
 With an argument, catch only exceptions with the given name."),
 		     catch_catch_command,
+                     NULL,
 		     CATCH_PERMANENT,
 		     CATCH_TEMPORARY);
   add_catch_command ("throw", _("\
 Catch an exception, when thrown.\n\
 With an argument, catch only exceptions with the given name."),
 		     catch_throw_command,
+                     NULL,
 		     CATCH_PERMANENT,
 		     CATCH_TEMPORARY);
   add_catch_command ("fork", _("Catch calls to fork."),
 		     catch_fork_command_1,
+                     NULL,
 		     (void *) (uintptr_t) catch_fork_permanent,
 		     (void *) (uintptr_t) catch_fork_temporary);
   add_catch_command ("vfork", _("Catch calls to vfork."),
 		     catch_fork_command_1,
+                     NULL,
 		     (void *) (uintptr_t) catch_vfork_permanent,
 		     (void *) (uintptr_t) catch_vfork_temporary);
   add_catch_command ("exec", _("Catch calls to exec."),
 		     catch_exec_command_1,
+                     NULL,
+		     CATCH_PERMANENT,
+		     CATCH_TEMPORARY);
+  add_catch_command ("syscall", _("\
+Catch system calls by their names and/or numbers.\n\
+Arguments say which system calls to catch.  If no arguments\n\
+are given, every system call will be caught.\n\
+Arguments, if given, should be one or more system call names\n\
+(if your system supports that), or system call numbers."),
+		     catch_syscall_command_1,
+		     catch_syscall_completer,
 		     CATCH_PERMANENT,
 		     CATCH_TEMPORARY);
   add_catch_command ("exception", _("\
 Catch Ada exceptions, when raised.\n\
 With an argument, catch only exceptions with the given name."),
 		     catch_ada_exception_command,
+                     NULL,
 		     CATCH_PERMANENT,
 		     CATCH_TEMPORARY);
   add_catch_command ("assert", _("\
 Catch failed Ada assertions, when raised.\n\
 With an argument, catch only exceptions with the given name."),
 		     catch_assert_command,
+                     NULL,
 		     CATCH_PERMANENT,
 		     CATCH_TEMPORARY);
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 70b1398..ba499c6 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,8 @@ struct block;
 
 #define	BREAKPOINT_MAX	16
 
-/* Type of breakpoint. */
+
+/* Type of breakpoint.  */
 /* FIXME In the future, we should fold all other breakpoint-like things into
    here.  This includes:
 
@@ -359,6 +360,9 @@ enum watchpoint_triggered
   watch_triggered_yes  
 };
 
+/* This is used to declare the VEC syscalls_to_be_caught.  */
+DEF_VEC_I(int);
+
 typedef struct bp_location *bp_location_p;
 DEF_VEC_P(bp_location_p);
 
@@ -469,6 +473,12 @@ struct breakpoint
        triggered.  */
     char *exec_pathname;
 
+    /* Syscall numbers used for the 'catch syscall' feature.
+       If no syscall has been specified for filtering, its value is NULL.
+       Otherwise, it holds a list of all syscalls to be caught.
+       The list elements are allocated with xmalloc.  */
+    VEC(int) *syscalls_to_be_caught;
+
     /* Methods associated with this breakpoint.  */
     struct breakpoint_ops *ops;
 
@@ -924,6 +934,15 @@ extern int breakpoints_always_inserted_mode (void);
    in our opinion won't ever trigger.  */
 extern void breakpoint_retire_moribund (void);
 
+/* Checks if we are catching syscalls or not.
+   Returns 0 if not, greater than 0 if we are.  */
+extern int catch_syscall_enabled (void);
+
+/* Checks if we are catching syscalls with the specific
+   syscall_number.  Used for "filtering" the catchpoints.
+   Returns 0 if not, greater than 0 if we are.  */
+extern int catching_syscall_number (int syscall_number);
+
 /* Tell a breakpoint to be quiet.  */
 extern void make_breakpoint_silent (struct breakpoint *);
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index a20df00..e1b9d0d 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -244,6 +244,7 @@ struct gdbarch
   gdbarch_target_signal_to_host_ftype *target_signal_to_host;
   gdbarch_get_siginfo_type_ftype *get_siginfo_type;
   gdbarch_record_special_symbol_ftype *record_special_symbol;
+  gdbarch_get_syscall_number_ftype *get_syscall_number;
   int has_global_solist;
   int has_global_breakpoints;
 };
@@ -381,6 +382,7 @@ struct gdbarch startup_gdbarch =
   default_target_signal_to_host,  /* target_signal_to_host */
   0,  /* get_siginfo_type */
   0,  /* record_special_symbol */
+  0,  /* get_syscall_number */
   0,  /* has_global_solist */
   0,  /* has_global_breakpoints */
   /* startup_gdbarch() */
@@ -637,6 +639,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of target_signal_to_host, invalid_p == 0 */
   /* Skip verify of get_siginfo_type, has predicate */
   /* Skip verify of record_special_symbol, has predicate */
+  /* Skip verify of get_syscall_number, has predicate */
   /* Skip verify of has_global_solist, invalid_p == 0 */
   /* Skip verify of has_global_breakpoints, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &length);
@@ -866,6 +869,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: get_siginfo_type = <%s>\n",
                       host_address_to_string (gdbarch->get_siginfo_type));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_syscall_number_p() = %d\n",
+                      gdbarch_get_syscall_number_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_syscall_number = <%s>\n",
+                      host_address_to_string (gdbarch->get_syscall_number));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: has_global_breakpoints = %s\n",
                       plongest (gdbarch->has_global_breakpoints));
   fprintf_unfiltered (file,
@@ -3381,6 +3390,30 @@ set_gdbarch_record_special_symbol (struct gdbarch *gdbarch,
 }
 
 int
+gdbarch_get_syscall_number_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_syscall_number != NULL;
+}
+
+LONGEST
+gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_syscall_number != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_syscall_number called\n");
+  return gdbarch->get_syscall_number (gdbarch, ptid);
+}
+
+void
+set_gdbarch_get_syscall_number (struct gdbarch *gdbarch,
+                                gdbarch_get_syscall_number_ftype get_syscall_number)
+{
+  gdbarch->get_syscall_number = get_syscall_number;
+}
+
+int
 gdbarch_has_global_solist (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 9ffef7e..027541d 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -52,6 +52,7 @@ struct bp_target_info;
 struct target_desc;
 struct displaced_step_closure;
 struct core_regset_section;
+struct syscall;
 
 /* The architecture associated with the connection to the target.
  
@@ -853,6 +854,15 @@ typedef void (gdbarch_record_special_symbol_ftype) (struct gdbarch *gdbarch, str
 extern void gdbarch_record_special_symbol (struct gdbarch *gdbarch, struct objfile *objfile, asymbol *sym);
 extern void set_gdbarch_record_special_symbol (struct gdbarch *gdbarch, gdbarch_record_special_symbol_ftype *record_special_symbol);
 
+/* Function for the 'catch syscall' feature.
+   Get architecture-specific system calls information from registers. */
+
+extern int gdbarch_get_syscall_number_p (struct gdbarch *gdbarch);
+
+typedef LONGEST (gdbarch_get_syscall_number_ftype) (struct gdbarch *gdbarch, ptid_t ptid);
+extern LONGEST gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid);
+extern void set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, gdbarch_get_syscall_number_ftype *get_syscall_number);
+
 /* True if the list of shared libraries is one and only for all
    processes, as opposed to a list of shared libraries per inferior.
    This usually means that all processes, although may or may not share
@@ -870,6 +880,9 @@ extern void set_gdbarch_has_global_solist (struct gdbarch *gdbarch, int has_glob
 extern int gdbarch_has_global_breakpoints (struct gdbarch *gdbarch);
 extern void set_gdbarch_has_global_breakpoints (struct gdbarch *gdbarch, int has_global_breakpoints);
 
+/* Definition for an unknown syscall, used basically in error-cases.  */
+#define UNKNOWN_SYSCALL (-1)
+
 extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch);
 
 
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 491efce..3a6e483 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -724,6 +724,11 @@ M:struct type *:get_siginfo_type:void:
 # Record architecture-specific information from the symbol table.
 M:void:record_special_symbol:struct objfile *objfile, asymbol *sym:objfile, sym
 
+# Function for the 'catch syscall' feature.
+
+# Get architecture-specific system calls information from registers.
+M:LONGEST:get_syscall_number:ptid_t ptid:ptid
+
 # True if the list of shared libraries is one and only for all
 # processes, as opposed to a list of shared libraries per inferior.
 # This usually means that all processes, although may or may not share
@@ -848,6 +853,7 @@ struct bp_target_info;
 struct target_desc;
 struct displaced_step_closure;
 struct core_regset_section;
+struct syscall;
 
 /* The architecture associated with the connection to the target.
  
@@ -926,6 +932,9 @@ done
 # close it off
 cat <<EOF
 
+/* Definition for an unknown syscall, used basically in error-cases.  */
+#define UNKNOWN_SYSCALL (-1)
+
 extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch);
 
 
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 991b27f..fe848ff 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -752,7 +752,12 @@ i386_linux_resume (struct target_ops *ops,
 {
   int pid = PIDGET (ptid);
 
-  int request = PTRACE_CONT;
+  int request;
+
+  if (catch_syscall_enabled () > 0)
+   request = PTRACE_SYSCALL;
+  else
+    request = PTRACE_CONT;
 
   if (step)
     {
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 748fda0..78fe88e 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -37,6 +37,10 @@
 #include "symtab.h"
 #include "arch-utils.h"
 #include "regset.h"
+#include "xml-syscall.h"
+
+/* The syscall's XML filename for i386.  */
+#define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
 
 #include "record.h"
 #include "linux-record.h"
@@ -411,6 +415,27 @@ i386_linux_intx80_sysenter_record (struct regcache *regcache)
 }
 
 
+static LONGEST
+i386_linux_get_syscall_number (struct gdbarch *gdbarch,
+                               ptid_t ptid)
+{
+  struct regcache *regcache = get_thread_regcache (ptid);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  /* The content of a register.  */
+  gdb_byte buf[4];
+  /* The result.  */
+  LONGEST ret;
+
+  /* Getting the system call number from the register.
+     When dealing with x86 architecture, this information
+     is stored at %eax register.  */
+  regcache_cooked_read (regcache, I386_LINUX_ORIG_EAX_REGNUM, buf);
+
+  ret = extract_signed_integer (buf, 4, byte_order);
+
+  return ret;
+}
+
 /* The register sets used in GNU/Linux ELF core-dumps are identical to
    the register sets in `struct user' that are used for a.out
    core-dumps.  These are also used by ptrace(2).  The corresponding
@@ -697,6 +722,11 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_displaced_step_location (gdbarch,
                                        displaced_step_at_entry_point);
 
+  /* Functions for 'catch syscall'.  */
+  set_xml_syscall_file_name (XML_SYSCALL_FILENAME_I386);
+  set_gdbarch_get_syscall_number (gdbarch,
+                                  i386_linux_get_syscall_number);
+
   set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
 }
 
diff --git a/gdb/inf-child.c b/gdb/inf-child.c
index 2ac7027..07ca814 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -148,6 +148,15 @@ inf_child_remove_exec_catchpoint (int pid)
 }
 
 static int
+inf_child_set_syscall_catchpoint (int pid, int needed, int any_count,
+				  int table_size, int *table)
+{
+  /* This version of Unix doesn't support notification of syscall
+     events.  */
+  return 0;
+}
+
+static int
 inf_child_can_run (void)
 {
   return 1;
@@ -190,6 +199,7 @@ inf_child_target (void)
   t->to_follow_fork = inf_child_follow_fork;
   t->to_insert_exec_catchpoint = inf_child_insert_exec_catchpoint;
   t->to_remove_exec_catchpoint = inf_child_remove_exec_catchpoint;
+  t->to_set_syscall_catchpoint = inf_child_set_syscall_catchpoint;
   t->to_can_run = inf_child_can_run;
   t->to_pid_to_exec_file = inf_child_pid_to_exec_file;
   t->to_stratum = process_stratum;
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 89a37a6..736154a 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -332,13 +332,18 @@ inf_ptrace_resume (struct target_ops *ops,
 		   ptid_t ptid, int step, enum target_signal signal)
 {
   pid_t pid = ptid_get_pid (ptid);
-  int request = PT_CONTINUE;
+  int request;
 
   if (pid == -1)
     /* Resume all threads.  Traditionally ptrace() only supports
        single-threaded processes, so simply resume the inferior.  */
     pid = ptid_get_pid (inferior_ptid);
 
+  if (catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
   if (step)
     {
       /* If this system does not support PT_STEP, a higher level
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 7312e51..f1b5d17 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -425,6 +425,20 @@ struct inferior
 
   /* Private data used by the target vector implementation.  */
   struct private_inferior *private;
+
+  /* We keep a count of the number of times the user has requested a
+     particular syscall to be tracked, and pass this information to the
+     target.  This lets capable targets implement filtering directly.  */
+
+  /* Number of times that "any" syscall is requested.  */
+  int any_syscall_count;
+
+  /* Count of each system call.  */
+  VEC(int) *syscalls_counts;
+
+  /* This counts all syscall catch requests, so we can readily determine
+     if any catching is necessary.  */
+  int total_syscalls_count;
 };
 
 /* Create an empty inferior list, or empty the existing one.  */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c907635..713810b 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2042,6 +2042,10 @@ wait_for_inferior (int treat_exec_as_sigtrap)
 	 state.  */
       old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
 
+      if (ecs->ws.kind == TARGET_WAITKIND_SYSCALL_ENTRY
+          || ecs->ws.kind == TARGET_WAITKIND_SYSCALL_RETURN)
+        ecs->ws.value.syscall_number = UNKNOWN_SYSCALL;
+
       /* Now figure out what to do with the result of the result.  */
       handle_inferior_event (ecs);
 
@@ -2378,6 +2382,56 @@ stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
   return 0;
 }
 
+/* Auxiliary function that handles syscall entry/return events.
+   It returns 1 if the inferior should keep going (and GDB
+   should ignore the event), or 0 if the event deserves to be
+   processed.  */
+static int
+deal_with_syscall_event (struct execution_control_state *ecs)
+{
+  struct regcache *regcache = get_thread_regcache (ecs->ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int syscall_number = gdbarch_get_syscall_number (gdbarch,
+                                                   ecs->ptid);
+  target_last_waitstatus.value.syscall_number = syscall_number;
+
+  if (catch_syscall_enabled () > 0
+      && catching_syscall_number (syscall_number) > 0)
+    {
+      if (debug_infrun)
+        fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n",
+                            syscall_number);
+      ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
+
+      if (!ptid_equal (ecs->ptid, inferior_ptid))
+        {
+          context_switch (ecs->ptid);
+          reinit_frame_cache ();
+        }
+
+      stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
+
+      ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+
+      ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
+
+      /* If no catchpoint triggered for this, then keep going.  */
+      if (ecs->random_signal)
+        {
+          ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
+          keep_going (ecs);
+          return 1;
+        }
+      return 0;
+    }
+  else
+    {
+      resume (0, TARGET_SIGNAL_0);
+      prepare_to_wait (ecs);
+      return 1;
+    }
+}
+
 /* Given an execution control state that has been freshly filled in
    by an event from the inferior, figure out what it means and take
    appropriate action.  */
@@ -2698,9 +2752,11 @@ handle_inferior_event (struct execution_control_state *ecs)
     case TARGET_WAITKIND_SYSCALL_ENTRY:
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n");
-      resume (0, TARGET_SIGNAL_0);
-      prepare_to_wait (ecs);
-      return;
+      /* Getting the current syscall number */
+      if (deal_with_syscall_event (ecs) != 0)
+        return;
+      goto process_event_stop_test;
+      break;
 
       /* Before examining the threads further, step this thread to
          get it entirely out of the syscall.  (We get notice of the
@@ -2710,9 +2766,10 @@ handle_inferior_event (struct execution_control_state *ecs)
     case TARGET_WAITKIND_SYSCALL_RETURN:
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_RETURN\n");
-      target_resume (ecs->ptid, 1, TARGET_SIGNAL_0);
-      prepare_to_wait (ecs);
-      return;
+      if (deal_with_syscall_event (ecs) != 0)
+        return;
+      goto process_event_stop_test;
+      break;
 
     case TARGET_WAITKIND_STOPPED:
       if (debug_infrun)
@@ -5614,6 +5671,25 @@ inferior_has_execd (ptid_t pid, char **execd_pathname)
   return 1;
 }
 
+int
+inferior_has_called_syscall (ptid_t pid, int *syscall_number)
+{
+  struct target_waitstatus last;
+  ptid_t last_ptid;
+
+  get_last_target_status (&last_ptid, &last);
+
+  if (last.kind != TARGET_WAITKIND_SYSCALL_ENTRY &&
+      last.kind != TARGET_WAITKIND_SYSCALL_RETURN)
+    return 0;
+
+  if (!ptid_equal (last_ptid, pid))
+    return 0;
+
+  *syscall_number = last.value.syscall_number;
+  return 1;
+}
+
 /* Oft used ptids */
 ptid_t null_ptid;
 ptid_t minus_one_ptid;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 1308844..98f6347 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -67,6 +67,10 @@
 # endif
 #endif /* HAVE_PERSONALITY */
 
+/* To be used when one needs to know wether a
+   WSTOPSIG (status) is a syscall */
+#define TRAP_IS_SYSCALL (SIGTRAP | 0x80)
+
 /* This comment documents high-level logic of this file. 
 
 Waiting for events in sync mode
@@ -279,6 +283,11 @@ struct simple_pid_list *stopped_pids;
 
 static int linux_supports_tracefork_flag = -1;
 
+/* 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_TRACEFORK, this flag indicates whether we also have
    PTRACE_O_TRACEVFORKDONE.  */
 
@@ -290,6 +299,9 @@ static int linux_supports_tracevforkdone_flag = -1;
    linux_nat_wait should behave as if async mode was off.  */
 static int linux_nat_async_mask_value = 1;
 
+/* Stores the current used ptrace() options.  */
+static int current_ptrace_options = 0;
+
 /* The read/write ends of the pipe registered as waitable file in the
    event loop.  */
 static int linux_nat_event_pipe[2] = { -1, -1 };
@@ -525,6 +537,43 @@ linux_test_for_tracefork (int original_pid)
   restore_child_signals_mask (&prev_mask);
 }
 
+/* Determine if PTRACE_O_TRACESYSGOOD can be used to follow syscalls.
+
+   We try to enable syscall 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.  */
+
+static void
+linux_test_for_tracesysgood (int original_pid)
+{
+  int ret;
+  sigset_t prev_mask;
+
+  /* We don't want those ptrace calls to be interrupted.  */
+  block_child_signals (&prev_mask);
+
+  linux_supports_tracesysgood_flag = 0;
+
+  ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD);
+  if (ret != 0)
+    goto out;
+
+  linux_supports_tracesysgood_flag = 1;
+out:
+  restore_child_signals_mask (&prev_mask);
+}
+
+/* Determine wether we support PTRACE_O_TRACESYSGOOD option available.
+   This function also sets linux_supports_tracesysgood_flag.  */
+
+static int
+linux_supports_tracesysgood (int pid)
+{
+  if (linux_supports_tracesysgood_flag == -1)
+    linux_test_for_tracesysgood (pid);
+  return linux_supports_tracesysgood_flag;
+}
+
 /* Return non-zero iff we have tracefork functionality available.
    This function also sets linux_supports_tracefork_flag.  */
 
@@ -544,12 +593,27 @@ linux_supports_tracevforkdone (int pid)
   return linux_supports_tracevforkdone_flag;
 }
 
+static void
+linux_enable_tracesysgood (ptid_t ptid)
+{
+  int pid = ptid_get_lwp (ptid);
+
+  if (pid == 0)
+    pid = ptid_get_pid (ptid);
+
+  if (linux_supports_tracesysgood (pid) == 0)
+    return;
+
+  current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+
+  ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options);
+}
+
 
 void
 linux_enable_event_reporting (ptid_t ptid)
 {
   int pid = ptid_get_lwp (ptid);
-  int options;
 
   if (pid == 0)
     pid = ptid_get_pid (ptid);
@@ -557,15 +621,16 @@ linux_enable_event_reporting (ptid_t ptid)
   if (! linux_supports_tracefork (pid))
     return;
 
-  options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
-    | PTRACE_O_TRACECLONE;
+  current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+    | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE;
+
   if (linux_supports_tracevforkdone (pid))
-    options |= PTRACE_O_TRACEVFORKDONE;
+    current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
      read-only process state.  */
 
-  ptrace (PTRACE_SETOPTIONS, pid, 0, options);
+  ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options);
 }
 
 static void
@@ -573,6 +638,7 @@ linux_child_post_attach (int pid)
 {
   linux_enable_event_reporting (pid_to_ptid (pid));
   check_for_thread_db ();
+  linux_enable_tracesysgood (pid_to_ptid (pid));
 }
 
 static void
@@ -580,6 +646,7 @@ linux_child_post_startup_inferior (ptid_t ptid)
 {
   linux_enable_event_reporting (ptid);
   check_for_thread_db ();
+  linux_enable_tracesysgood (ptid);
 }
 
 static int
@@ -810,6 +877,20 @@ linux_child_insert_exec_catchpoint (int pid)
     error (_("Your system does not support exec catchpoints."));
 }
 
+static int
+linux_child_set_syscall_catchpoint (int pid, int needed, int any_count,
+				    int table_size, int *table)
+{
+  if (! linux_supports_tracesysgood (pid))
+    error (_("Your system does not support syscall catchpoints."));
+  /* On GNU/Linux, we ignore the arguments.  It means that we only
+     enable the syscall catchpoints, but do not disable them.
+     
+     Also, we do not use the `table' information because we do not
+     filter system calls here.  We let GDB do the logic for us.  */
+  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
@@ -1982,6 +2063,47 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
       return 0;
     }
 
+  /* Used for 'catch syscall' feature.  */
+  if (WSTOPSIG (status) == TRAP_IS_SYSCALL)
+    {
+      if (catch_syscall_enabled () == 0)
+	  ourstatus->kind = TARGET_WAITKIND_IGNORE;
+      else
+	{
+	  struct regcache *regcache = get_thread_regcache (lp->ptid);
+	  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+	  ourstatus->value.syscall_number =
+	    (int) gdbarch_get_syscall_number (gdbarch, lp->ptid);
+
+	  /* If we are catching this specific syscall number, then we
+	     should update the target_status to reflect which event
+	     has occurred.  But if this syscall is not to be caught,
+	     then we can safely mark the event as a SYSCALL_RETURN.
+
+	     This is particularly needed if:
+
+	       - We are catching any syscalls, or
+	       - We are catching the syscall "exit"
+
+	     In this case, as the syscall "exit" *doesn't* return,
+	     then GDB would be confused because it would mark the last
+	     syscall event as a SYSCALL_ENTRY.  After that, if we re-ran the
+	     inferior GDB will think that the first syscall event is
+	     the opposite of a SYSCALL_ENTRY, which is the SYSCALL_RETURN.
+	     Therefore, GDB would report inverted syscall events.  */
+	  if (catching_syscall_number (ourstatus->value.syscall_number))
+	    ourstatus->kind = 
+	      (lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY) ?
+	      TARGET_WAITKIND_SYSCALL_RETURN : TARGET_WAITKIND_SYSCALL_ENTRY;
+	  else
+	    ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN;
+
+	  lp->syscall_state = ourstatus->kind;
+	}
+      return 0;
+    }
+
   internal_error (__FILE__, __LINE__,
 		  _("unknown ptrace event %d"), event);
 }
@@ -2580,11 +2702,16 @@ linux_nat_filter_event (int lwpid, int status, int options)
     }
 
   /* Save the trap's siginfo in case we need it later.  */
-  if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
+  if (WIFSTOPPED (status)
+      && (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == TRAP_IS_SYSCALL))
     save_siginfo (lp);
 
-  /* Handle GNU/Linux's extended waitstatus for trace events.  */
-  if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+  /* Handle GNU/Linux's extended waitstatus for trace events.
+     It is necessary to check if WSTOPSIG is signaling that
+     the inferior is entering/exiting a system call.  */
+  if (WIFSTOPPED (status)
+      && ((WSTOPSIG (status) == TRAP_IS_SYSCALL)
+          || (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)))
     {
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
@@ -4510,6 +4637,7 @@ linux_target_install_ops (struct target_ops *t)
   t->to_insert_fork_catchpoint = linux_child_insert_fork_catchpoint;
   t->to_insert_vfork_catchpoint = linux_child_insert_vfork_catchpoint;
   t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint;
+  t->to_set_syscall_catchpoint = linux_child_set_syscall_catchpoint;
   t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
   t->to_post_startup_inferior = linux_child_post_startup_inferior;
   t->to_post_attach = linux_child_post_attach;
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index d1ed6fc..eae74f3 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -70,6 +70,13 @@ struct lwp_info
      or to a local variable in lin_lwp_wait.  */
   struct target_waitstatus waitstatus;
 
+  /* Signal wether we are in a SYSCALL_ENTRY or
+     in a SYSCALL_RETURN event.
+     Values:
+     - TARGET_WAITKIND_SYSCALL_ENTRY
+     - TARGET_WAITKIND_SYSCALL_RETURN */
+  int syscall_state;
+
   /* Next LWP in list.  */
   struct lwp_info *next;
 };
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index f0f802c..d8211ed 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -47,6 +47,7 @@
 #include "exceptions.h"
 #include "arch-utils.h"
 #include "spu-tdep.h"
+#include "xml-syscall.h"
 
 #include "features/rs6000/powerpc-32l.c"
 #include "features/rs6000/powerpc-altivec32l.c"
@@ -64,6 +65,9 @@
 #include "features/rs6000/powerpc-isa205-vsx64l.c"
 #include "features/rs6000/powerpc-e500l.c"
 
+/* The syscall's XML filename for PPC and PPC64.  */
+#define XML_SYSCALL_FILENAME_PPC "syscalls/ppc-linux.xml"
+#define XML_SYSCALL_FILENAME_PPC64 "syscalls/ppc64-linux.xml"
 
 /* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint
    in much the same fashion as memory_remove_breakpoint in mem-break.c,
@@ -1066,6 +1070,39 @@ ppc_linux_trap_reg_p (struct gdbarch *gdbarch)
          && register_size (gdbarch, PPC_TRAP_REGNUM) > 0;
 }
 
+/* Return the current system call's number present in the
+   r0 register.  When the function fails, it returns -1.  */
+static LONGEST
+ppc_linux_get_syscall_number (struct gdbarch *gdbarch,
+                              ptid_t ptid)
+{
+  struct regcache *regcache = get_thread_regcache (ptid);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct cleanup *cleanbuf;
+  /* The content of a register */
+  gdb_byte *buf;
+  /* The result */
+  LONGEST ret;
+
+  /* Make sure we're in a 32- or 64-bit machine */
+  gdb_assert (tdep->wordsize == 4 || tdep->wordsize == 8);
+
+  buf = (gdb_byte *) xmalloc (tdep->wordsize * sizeof (gdb_byte));
+
+  cleanbuf = make_cleanup (xfree, buf);
+
+  /* Getting the system call number from the register.
+     When dealing with PowerPC architecture, this information
+     is stored at 0th register.  */
+  regcache_cooked_read (regcache, tdep->ppc_gp0_regnum, buf);
+
+  ret = extract_signed_integer (buf, tdep->wordsize, byte_order);
+  do_cleanups (cleanbuf);
+
+  return ret;
+}
+
 static void
 ppc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
 {
@@ -1435,6 +1472,9 @@ ppc_linux_init_abi (struct gdbarch_info info,
   /* Handle inferior calls during interrupted system calls.  */
   set_gdbarch_write_pc (gdbarch, ppc_linux_write_pc);
 
+  /* Get the syscall number from the arch's register.  */
+  set_gdbarch_get_syscall_number (gdbarch, ppc_linux_get_syscall_number);
+
   if (tdep->wordsize == 4)
     {
       /* Until November 2001, gcc did not comply with the 32 bit SysV
@@ -1454,6 +1494,9 @@ ppc_linux_init_abi (struct gdbarch_info info,
       set_solib_svr4_fetch_link_map_offsets
         (gdbarch, svr4_ilp32_fetch_link_map_offsets);
 
+      /* Setting the correct XML syscall filename.  */
+      set_xml_syscall_file_name (XML_SYSCALL_FILENAME_PPC);
+
       /* Trampolines.  */
       tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sigaction_tramp_frame);
       tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sighandler_tramp_frame);
@@ -1477,6 +1520,9 @@ ppc_linux_init_abi (struct gdbarch_info info,
       set_solib_svr4_fetch_link_map_offsets
         (gdbarch, svr4_lp64_fetch_link_map_offsets);
 
+      /* Setting the correct XML syscall filename.  */
+      set_xml_syscall_file_name (XML_SYSCALL_FILENAME_PPC64);
+
       /* Trampolines.  */
       tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sigaction_tramp_frame);
       tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sighandler_tramp_frame);
diff --git a/gdb/target.c b/gdb/target.c
index a662eca..73226e9 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -647,6 +647,7 @@ update_current_target (void)
       /* Do not inherit to_follow_fork.  */
       INHERIT (to_insert_exec_catchpoint, t);
       INHERIT (to_remove_exec_catchpoint, t);
+      INHERIT (to_set_syscall_catchpoint, t);
       INHERIT (to_has_exited, t);
       /* Do not inherit to_mourn_inferiour.  */
       INHERIT (to_can_run, t);
@@ -789,6 +790,9 @@ update_current_target (void)
   de_fault (to_remove_exec_catchpoint,
 	    (int (*) (int))
 	    tcomplain);
+  de_fault (to_set_syscall_catchpoint,
+	    (int (*) (int, int, int, int, int *))
+	    tcomplain);
   de_fault (to_has_exited,
 	    (int (*) (int, int, int *))
 	    return_zero);
@@ -2865,9 +2869,9 @@ target_waitstatus_to_string (const struct target_waitstatus *ws)
     case TARGET_WAITKIND_EXECD:
       return xstrprintf ("%sexecd", kind_str);
     case TARGET_WAITKIND_SYSCALL_ENTRY:
-      return xstrprintf ("%ssyscall-entry", kind_str);
+      return xstrprintf ("%sentered syscall", kind_str);
     case TARGET_WAITKIND_SYSCALL_RETURN:
-      return xstrprintf ("%ssyscall-return", kind_str);
+      return xstrprintf ("%sexited syscall", kind_str);
     case TARGET_WAITKIND_SPURIOUS:
       return xstrprintf ("%sspurious", kind_str);
     case TARGET_WAITKIND_IGNORE:
diff --git a/gdb/target.h b/gdb/target.h
index c210fea..b1cb852 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -142,14 +142,15 @@ struct target_waitstatus
   {
     enum target_waitkind kind;
 
-    /* Forked child pid, execd pathname, exit status or signal number.  */
+    /* Forked child pid, execd pathname, exit status, signal number or
+       syscall number.  */
     union
       {
 	int integer;
 	enum target_signal sig;
 	ptid_t related_pid;
 	char *execd_pathname;
-	int syscall_id;
+	int syscall_number;
       }
     value;
   };
@@ -161,6 +162,21 @@ struct target_waitstatus
    event.  */
 #define TARGET_WNOHANG 1
 
+/* The structure below stores information about a system call.
+   It is basically used in the "catch syscall" command, and in
+   every function that gives information about a system call.
+   
+   It's also good to mention that its fields represent everything
+   that we currently know about a syscall in GDB.  */
+struct syscall
+  {
+    /* The syscall number.  */
+    int number;
+
+    /* The syscall name.  */
+    const char *name;
+  };
+
 /* Return a pretty printed form of target_waitstatus.
    Space for the result is malloc'd, caller must free.  */
 extern char *target_waitstatus_to_string (const struct target_waitstatus *);
@@ -406,6 +422,7 @@ struct target_ops
     int (*to_follow_fork) (struct target_ops *, int);
     void (*to_insert_exec_catchpoint) (int);
     int (*to_remove_exec_catchpoint) (int);
+    int (*to_set_syscall_catchpoint) (int, int, int, int, int *);
     int (*to_has_exited) (int, int, int *);
     void (*to_mourn_inferior) (struct target_ops *);
     int (*to_can_run) (void);
@@ -748,6 +765,8 @@ extern int inferior_has_vforked (ptid_t pid, ptid_t *child_pid);
 
 extern int inferior_has_execd (ptid_t pid, char **execd_pathname);
 
+extern int inferior_has_called_syscall (ptid_t pid, int *syscall_number);
+
 /* Print a line about the current target.  */
 
 #define	target_files_info()	\
@@ -900,6 +919,27 @@ int target_follow_fork (int follow_child);
 #define target_remove_exec_catchpoint(pid) \
      (*current_target.to_remove_exec_catchpoint) (pid)
 
+/* Syscall catch.
+
+   NEEDED is nonzero if any syscall catch (of any kind) is requested.
+   If NEEDED is zero, it means the target can disable the mechanism to
+   catch system calls because there are no more catchpoints of this type.
+
+   ANY_COUNT is nonzero if a generic (filter-less) syscall catch is
+   being requested.  In this case, both TABLE_SIZE and TABLE should
+   be ignored.
+
+   TABLE_SIZE is the number of elements in TABLE.  It only matters if
+   ANY_COUNT is zero.
+
+   TABLE is an array of ints, indexed by syscall number.  An element in
+   this array is nonzero if that syscall should be caught.  This argument
+   only matters if ANY_COUNT is zero.  */
+
+#define target_set_syscall_catchpoint(pid, needed, any_count, table_size, table) \
+     (*current_target.to_set_syscall_catchpoint) (pid, needed, any_count, \
+						  table_size, table)
+
 /* Returns TRUE if PID has exited.  And, also sets EXIT_STATUS to the
    exit code of PID, if any.  */
 
diff --git a/gdb/xml-support.c b/gdb/xml-support.c
index 937c6c3..ad20d3b 100644
--- a/gdb/xml-support.c
+++ b/gdb/xml-support.c
@@ -1036,6 +1036,66 @@ obstack_xml_printf (struct obstack *obstack, const char *format, ...)
   va_end (ap);
 }
 
+char *
+xml_fetch_content_from_file (const char *filename, void *baton)
+{
+  const char *dirname = baton;
+  FILE *file;
+  struct cleanup *back_to;
+  char *text;
+  size_t len, offset;
+
+  if (dirname && *dirname)
+    {
+      char *fullname = concat (dirname, "/", filename, (char *) NULL);
+      if (fullname == NULL)
+	nomem (0);
+      file = fopen (fullname, FOPEN_RT);
+      xfree (fullname);
+    }
+  else
+    file = fopen (filename, FOPEN_RT);
+
+  if (file == NULL)
+    return NULL;
+
+  back_to = make_cleanup_fclose (file);
+
+  /* Read in the whole file, one chunk at a time.  */
+  len = 4096;
+  offset = 0;
+  text = xmalloc (len);
+  make_cleanup (free_current_contents, &text);
+  while (1)
+    {
+      size_t bytes_read;
+
+      /* Continue reading where the last read left off.  Leave at least
+	 one byte so that we can NUL-terminate the result.  */
+      bytes_read = fread (text + offset, 1, len - offset - 1, file);
+      if (ferror (file))
+	{
+	  warning (_("Read error from \"%s\""), filename);
+	  do_cleanups (back_to);
+	  return NULL;
+	}
+
+      offset += bytes_read;
+
+      if (feof (file))
+	break;
+
+      len = len * 2;
+      text = xrealloc (text, len);
+    }
+
+  fclose (file);
+  discard_cleanups (back_to);
+
+  text[offset] = '\0';
+  return text;
+}
+
 void _initialize_xml_support (void);
 
 void
diff --git a/gdb/xml-support.h b/gdb/xml-support.h
index d6105f7..135263d 100644
--- a/gdb/xml-support.h
+++ b/gdb/xml-support.h
@@ -240,4 +240,10 @@ extern void obstack_xml_printf (struct obstack *obstack,
                                const char *format, ...)
   ATTRIBUTE_PRINTF_2;
 
+/* Open FILENAME, read all its text into memory, close it, and return
+   the text.  If something goes wrong, return NULL and warn.  */
+
+extern char *xml_fetch_content_from_file (const char *filename,
+                                          void *baton);
+
 #endif
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index 4fa7843..102049f 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -426,69 +426,6 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher,
 #endif /* HAVE_LIBEXPAT */
 
 
-/* Open FILENAME, read all its text into memory, close it, and return
-   the text.  If something goes wrong, return NULL and warn.  */
-
-static char *
-fetch_xml_from_file (const char *filename, void *baton)
-{
-  const char *dirname = baton;
-  FILE *file;
-  struct cleanup *back_to;
-  char *text;
-  size_t len, offset;
-
-  if (dirname && *dirname)
-    {
-      char *fullname = concat (dirname, "/", filename, (char *) NULL);
-      if (fullname == NULL)
-	nomem (0);
-      file = fopen (fullname, FOPEN_RT);
-      xfree (fullname);
-    }
-  else
-    file = fopen (filename, FOPEN_RT);
-
-  if (file == NULL)
-    return NULL;
-
-  back_to = make_cleanup_fclose (file);
-
-  /* Read in the whole file, one chunk at a time.  */
-  len = 4096;
-  offset = 0;
-  text = xmalloc (len);
-  make_cleanup (free_current_contents, &text);
-  while (1)
-    {
-      size_t bytes_read;
-
-      /* Continue reading where the last read left off.  Leave at least
-	 one byte so that we can NUL-terminate the result.  */
-      bytes_read = fread (text + offset, 1, len - offset - 1, file);
-      if (ferror (file))
-	{
-	  warning (_("Read error from \"%s\""), filename);
-	  do_cleanups (back_to);
-	  return NULL;
-	}
-
-      offset += bytes_read;
-
-      if (feof (file))
-	break;
-
-      len = len * 2;
-      text = xrealloc (text, len);
-    }
-
-  fclose (file);
-  discard_cleanups (back_to);
-
-  text[offset] = '\0';
-  return text;
-}
-
 /* Read an XML target description from FILENAME.  Parse it, and return
    the parsed description.  */
 
@@ -500,7 +437,7 @@ file_read_description_xml (const char *filename)
   struct cleanup *back_to;
   char *dirname;
 
-  tdesc_str = fetch_xml_from_file (filename, NULL);
+  tdesc_str = xml_fetch_content_from_file (filename, NULL);
   if (tdesc_str == NULL)
     {
       warning (_("Could not open \"%s\""), filename);
@@ -513,7 +450,7 @@ file_read_description_xml (const char *filename)
   if (dirname != NULL)
     make_cleanup (xfree, dirname);
 
-  tdesc = tdesc_parse_xml (tdesc_str, fetch_xml_from_file, dirname);
+  tdesc = tdesc_parse_xml (tdesc_str, xml_fetch_content_from_file, dirname);
   do_cleanups (back_to);
 
   return tdesc;

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