Re: [PATCH 1/3] catch syscall -- try 5 -- Source code modifications

Sérgio Durigan Júnior
2009-04-26T21:31:07Z

[-- Attachment #1: Type: text/plain, Size: 4551 bytes --]

Hi Eli,

On Sat, 2009-04-25 at 12:26 +0300, Eli Zaretskii wrote:
> > From: =?ISO-8859-1?Q?S=E9rgio?= Durigan =?ISO-8859-1?Q?J=FAnior?= <sergiodj@linux.vnet.ibm.com>
> > Date: Wed, 22 Apr 2009 21:33:03 -0300

Thanks for the comments. Here goes the new version.

Regards,

--
SÃ©rgio Durigan JÃºnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil

gdb/ChangeLog:

2009-04-26 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>

* breakpoint.c: New include: xml-syscall.h.
(set_raw_breakpoint_without_location): Setting the parameters
for the catch syscall feature.
(any_syscall_count, syscalls_counts)
(total_syscalls_count): New variables to keep track of requested
syscall catchpoints.
(insert_catch_syscall): New.
(remove_catch_syscall): New.
(breakpoint_hit_catch_syscall): New.
(print_it_catch_syscall): New.
(print_one_catch_syscall): New.
(print_mention_catch_syscall): New.
(catch_syscall_breakpoint_ops): New.
(syscall_catchpoint_p): New.
(create_catchpoint_without_mention): New.
(create_catchpoint): Modified in order to use
create_catchpoint_without_mention.
(create_syscall_event_catchpoint): New.
(clean_up_filters): New.
(catch_syscall_split_args): New.
(catch_syscall_command_1): New.
(delete_breakpoint): Add cleanup for catch syscall.
(is_syscall_catchpoint_enabled): New.
(catch_syscall_enabled): New.
(catching_syscall_number): New.
(catch_syscall_completer): New completer function.
(add_catch_command): Add the completer function for catchpoints.
* breakpoint.h (syscalls_to_be_caught): New vector.
(catch_syscall_enabled): New.
(catching_syscall_number): New.
* gdbarch.c: Regenerated.
* gdbarch.h: Regenerated.
* gdbarch.sh: Add syscall catchpoint functions and structures.
(get_syscall_number): New.
(UNKNOWN_SYSCALL): New definition.
* i386-linux-nat.c (i386_linux_resume): Select the proper request
to be made for ptrace() considering if we are catching syscalls
or not.
* i386-linux-tdep.c: Include xml-syscall.h header, define the XML
syscall name for the architecture.
(i386_linux_get_syscall_number): New.
(i386_linux_init_abi): Register the correct functions for syscall
catchpoint; set the correct syscall file name.
* inf-child.c (inf_child_set_syscall_catchpoint): New.
(inf_child_target): Assign default values to target_ops.
* inf-ptrace.c (inf_ptrace_resume): Select the proper request
to be made for ptrace() considering if we are catching syscalls
or not.
* infrun.c (resume): Add syscall catchpoint.
(deal_with_syscall_event): New.
(handle_inferior_event): Add syscall entry/return events.
(inferior_has_called_syscall): New.
* linux-nat.c: Define some helpful variables to track wether we have
support for the needed ptrace option.
(linux_target_install_ops): Setting the default methods.
(linux_test_for_tracesysgood): New.
(linux_supports_tracesysgood): New.
(linux_enable_tracesysgood): New.
(linux_enable_event_reporting): Save the current used ptrace
options.
(linux_child_post_attach): Calling linux_enable_tracesysgood.
(linux_child_post_startup_inferior): Likewise.
(linux_child_set_syscall_catchpoint): New function.
(linux_handle_extended_wait): Handle the case which the inferior stops
because it has called or returned from a syscall.
(linux_target_install_ops): Install the necessary functions to handle
syscall catchpoints.
* linux-nat.h (struct lwp_info): Include syscall_state into the
structure, which indicates if we are in a syscall entry or return.
* ppc-linux-tdep.c: Include xml-syscall.h header, define the XML
syscall filename for the arch.
(ppc_linux_get_syscall_number): New.
(ppc_linux_init_abi): Register the correct functions for syscall
catchpoint; setting the correct name for the XML syscall file.
* target.c (update_current_target): Update/copy functions related to
syscall catchpoint.
(target_waitstatus_to_string): Add syscall catchpoint entry/return
events.
* target.h (struct target_waitstatus): Add syscall number.
(struct syscall): New struct to hold information about syscalls
in the system.
(struct target_ops): Add ops for syscall catchpoint.
(inferior_has_called_syscall): New.
(target_set_syscall_catchpoint): New.
* xml-support.c (xml_fetch_content_from_file): New function,
transferred from xml-tdesc.c.
* xml-support.h (xml_fetch_content_from_file): New.
* xml-tdesc.c (fetch_xml_from_file): Function removed;
transferred to xml-support.c.
(file_read_description_xml): Updated to use the new
xml_fetch_content_from_file function.

[-- Attachment #2: catch-syscall-source-code-mod.patch --]
[-- Type: text/x-patch, Size: 56317 bytes --]

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 330a53a..8c4a496 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -58,6 +58,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "xml-syscall.h"

/* readline include files */
#include "readline/readline.h"
@@ -199,6 +200,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);
@@ -4274,6 +4277,7 @@ set_raw_breakpoint_without_location (enum bptype bptype)
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;

@@ -4797,7 +4801,276 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
print_mention_catch_vfork
};

-/* Create a new breakpoint of the bp_catchpoint kind and return it.
+/* 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. */
+static 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. */
+static int total_syscalls_count;
+
+/* Implement the "insert" breakpoint_ops method for syscall
+ catchpoints. */
+
+static void
+insert_catch_syscall (struct breakpoint *b)
+{
+ ++total_syscalls_count;
+ if (!b->syscalls_to_be_caught)
+ ++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, syscalls_counts))
+ {
+ int old_size = VEC_length (int, syscalls_counts);
+ uintptr_t vec_addr_offset = old_size * ((uintptr_t) sizeof (int));
+ uintptr_t vec_addr;
+ VEC_safe_grow (int, syscalls_counts, iter + 1);
+ vec_addr = (uintptr_t) VEC_address (int, syscalls_counts) + vec_addr_offset;
+ memset ((void *) vec_addr, 0,
+ (iter + 1 - old_size) * sizeof (int));
+ }
+ elem = VEC_index (int, syscalls_counts, iter);
+ VEC_replace (int, syscalls_counts, iter, ++elem);
+ }
+ }
+
+ target_set_syscall_catchpoint (PIDGET (inferior_ptid),
+ total_syscalls_count != 0,
+ any_syscall_count,
+ VEC_length (int, syscalls_counts),
+ VEC_address (int, syscalls_counts));
+}
+
+/* Implement the "remove" breakpoint_ops method for syscall
+ catchpoints. */
+
+static int
+remove_catch_syscall (struct breakpoint *b)
+{
+ --total_syscalls_count;
+ if (!b->syscalls_to_be_caught)
+ --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, syscalls_counts))
+ {
+ /* Shouldn't happen. */
+ continue;
+ }
+ elem = VEC_index (int, syscalls_counts, iter);
+ VEC_replace (int, syscalls_counts, iter, --elem);
+ }
+ }
+
+ return target_set_syscall_catchpoint (PIDGET (inferior_ptid),
+ total_syscalls_count != 0,
+ any_syscall_count,
+ VEC_length (int, syscalls_counts),
+ VEC_address (int, 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, CORE_ADDR *last_addr)
+{
+ 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.
@@ -4805,16 +5078,13 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
to the catchpoint. */

static struct breakpoint *
-create_catchpoint (int tempflag, char *cond_string,
- struct breakpoint_ops *ops)
+create_catchpoint_without_mention (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 (sal, bp_catchpoint);
set_breakpoint_count (breakpoint_count + 1);
@@ -4828,6 +5098,23 @@ create_catchpoint (int tempflag, char *cond_string,
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 (int tempflag, char *cond_string,
+ struct breakpoint_ops *ops)
+{
+ struct breakpoint *b =
+ create_catchpoint_without_mention (tempflag, cond_string, ops);
+
mention (b);
update_global_location_list (1);

@@ -4912,6 +5199,21 @@ 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 breakpoint *b =
+ create_catchpoint_without_mention (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)
{
@@ -6836,6 +7138,112 @@ 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;
+
+ /* Checking if the feature if supported. */
+ if (gdbarch_get_syscall_number_p (current_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
@@ -7302,6 +7710,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?
@@ -8215,6 +8624,58 @@ 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)
+{
+ return 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. */
@@ -8565,6 +9026,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)
{
@@ -8574,11 +9037,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
@@ -8855,36 +9320,56 @@ 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.\n\
+The program can take one or more arguments (syscalls names\n\
+and/or numbers), in which case it will catch the syscalls\n\
+provided. It can also take no arguments, in which case it\n\
+will catch every syscall.\n\
+The argument(s) should be the syscall name(s) (if your system\n\
+has support for it), or the syscall number(s). If you need\n\
+more information, please refer to GDB manual."),
+ 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 17b2761..42ded0a 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -33,7 +33,8 @@ struct block;

#define BREAKPOINT_MAX 16
\f
-/* Type of breakpoint. */
+
+/* Type of breakpoint. */
/* FIXME In the future, we should fold all other breakpoint-like things into
here. This includes:

@@ -339,6 +340,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);

@@ -447,6 +451,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;

@@ -873,6 +883,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 3273b34..80987d1 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -243,6 +243,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;
};

@@ -378,6 +379,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 */
/* startup_gdbarch() */
};
@@ -634,6 +636,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 */
buf = ui_file_xstrdup (log, &dummy);
make_cleanup (xfree, buf);
@@ -859,6 +862,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_solist = %s\n",
plongest (gdbarch->has_global_solist));
fprintf_unfiltered (file,
@@ -3333,6 +3342,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 04c8920..ffbfe10 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;

extern struct gdbarch *current_gdbarch;
extern struct gdbarch *target_gdbarch;
@@ -839,6 +840,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.
When this property is true, GDB assumes that since shared libraries
@@ -848,6 +858,9 @@ extern void set_gdbarch_record_special_symbol (struct gdbarch *gdbarch, gdbarch_
extern int gdbarch_has_global_solist (struct gdbarch *gdbarch);
extern void set_gdbarch_has_global_solist (struct gdbarch *gdbarch, int has_global_solist);

+/* 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 f93bfc1..55844a6 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.
# When this property is true, GDB assumes that since shared libraries
@@ -842,6 +847,7 @@ struct bp_target_info;
struct target_desc;
struct displaced_step_closure;
struct core_regset_section;
+struct syscall;

extern struct gdbarch *current_gdbarch;
extern struct gdbarch *target_gdbarch;
@@ -911,6 +917,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 2366474..687eccd 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -751,7 +751,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 1a2e4f0..4a421ee 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"

/* Supported register note sections. */
static struct core_regset_section i386_linux_regset_sections[] =
@@ -349,6 +353,26 @@ i386_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
}
\f

+static LONGEST
+i386_linux_get_syscall_number (struct gdbarch *gdbarch,
+ ptid_t ptid)
+{
+ struct regcache *regcache = get_thread_regcache (ptid);
+ /* 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);
+
+ 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
@@ -471,6 +495,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 38311f1..fc968cf 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 f088ffd..186b419 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -356,13 +356,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/infrun.c b/gdb/infrun.c
index b9fba6f..4059bd5 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1058,7 +1058,7 @@ a command like `return' or `jump' to continue execution."));
if (step)
step = maybe_software_singlestep (gdbarch, pc);

- /* If there were any forks/vforks/execs that were caught and are
+ /* If there were any forks/vforks/execs/syscalls that were caught and are
now to be followed, then do so. */
switch (pending_follow.kind)
{
@@ -1081,6 +1081,11 @@ a command like `return' or `jump' to continue execution."));
pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
break;

+ case TARGET_WAITKIND_SYSCALL_ENTRY:
+ case TARGET_WAITKIND_SYSCALL_RETURN:
+ pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+ break;
+
default:
break;
}
@@ -1522,7 +1527,7 @@ init_wait_for_inferior (void)

breakpoint_init_inferior (inf_starting);

- /* The first resume is not following a fork/vfork/exec. */
+ /* The first resume is not following a fork/vfork/exec/syscall. */
pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */

clear_proceed_status ();
@@ -1866,6 +1871,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);

@@ -2177,6 +2186,55 @@ ensure_not_running (void)
error_is_running ();
}

+/* 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)
+{
+ int syscall_number = gdbarch_get_syscall_number (current_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;
+ pending_follow.kind = ecs->ws.kind;
+
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ {
+ context_switch (ecs->ptid);
+ reinit_frame_cache ();
+ }
+
+ stop_pc = read_pc ();
+
+ 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. */
@@ -2471,9 +2529,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
@@ -2483,9 +2543,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)
@@ -5206,6 +5267,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 0679173..ab6b798 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -61,6 +61,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
@@ -281,6 +285,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. */

@@ -292,6 +301,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 };
@@ -636,6 +648,41 @@ linux_test_for_tracefork (int original_pid)
linux_nat_async_events (async_events_original_state);
}

+/* 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;
+ enum sigchld_state async_events_original_state;
+
+ async_events_original_state = linux_nat_async_events (sigchld_sync);
+
+ linux_supports_tracesysgood_flag = 0;
+
+ ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD);
+ if (ret != 0)
+ return;
+
+ linux_supports_tracesysgood_flag = 1;
+ linux_nat_async_events (async_events_original_state);
+}
+
+/* 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. */

@@ -655,12 +702,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);
+}
+
\f
void
linux_enable_event_reporting (ptid_t ptid)
{
int pid = ptid_get_lwp (ptid);
- int options;

if (pid == 0)
pid = ptid_get_pid (ptid);
@@ -668,15 +730,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
@@ -684,6 +747,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
@@ -691,6 +755,7 @@ linux_child_post_startup_inferior (ptid_t ptid)
{
linux_enable_event_reporting (ptid);
check_for_thread_db ();
+ linux_enable_tracesysgood (ptid);
}

static int
@@ -931,6 +996,16 @@ 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."));
+ /* We ignore the arguments. */
+ 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
@@ -1996,6 +2071,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);
}
@@ -2606,11 +2722,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,
@@ -4265,6 +4386,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 fec5139..36d2439 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 d08d4fc..b9d0275 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -38,6 +38,7 @@
#include "trad-frame.h"
#include "frame-unwind.h"
#include "tramp-frame.h"
+#include "xml-syscall.h"

#include "features/rs6000/powerpc-32l.c"
#include "features/rs6000/powerpc-altivec32l.c"
@@ -53,6 +54,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,
@@ -1009,6 +1013,38 @@ 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);
+ 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);
+ do_cleanups (cleanbuf);
+
+ return ret;
+}
+
static void
ppc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
{
@@ -1080,6 +1116,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
@@ -1099,6 +1138,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);
@@ -1116,6 +1158,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 f7366f8..85ff352 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -459,6 +459,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);
@@ -602,6 +603,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);
@@ -2696,9 +2700,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 e7f087b..5044bcf 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -140,18 +140,34 @@ 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;
};

+/* 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 *);
@@ -392,6 +408,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);
@@ -723,6 +740,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);
+
/* From exec.c */

extern void print_section_info (struct target_ops *, bfd *);
@@ -880,6 +899,17 @@ 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. 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)
+
/* 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 1d4e12c..8038dbd 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -421,69 +421,6 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher,
#endif /* HAVE_LIBEXPAT */
\f

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

@@ -495,7 +432,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);
@@ -508,7 +445,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;