diff options
author | Sergio Durigan Junior <sergiodj@redhat.com> | 2009-09-15 03:30:08 +0000 |
---|---|---|
committer | Sergio Durigan Junior <sergiodj@redhat.com> | 2009-09-15 03:30:08 +0000 |
commit | a96d9b2e9a79e6cc7a9da9b4e5bab6fcc35f1eb4 (patch) | |
tree | 1dc43d29797720e241a05bb391872137805a0a44 /gdb/linux-nat.c | |
parent | 22fe6da0f9e70167b759d4fe941beb2cf3f7a702 (diff) | |
download | gdb-a96d9b2e9a79e6cc7a9da9b4e5bab6fcc35f1eb4.zip gdb-a96d9b2e9a79e6cc7a9da9b4e5bab6fcc35f1eb4.tar.gz gdb-a96d9b2e9a79e6cc7a9da9b4e5bab6fcc35f1eb4.tar.bz2 |
Implementing catch syscall.
* amd64-linux-tdep.c: Include xml-syscall.h header, define the XML
syscall name for the architecture.
(amd64_linux_get_syscall_number): New function.
(amd64_linux_init_abi): Register the correct functions for syscall
catchpoint; set the correct syscall file name.
* breakpoint.c: New include: xml-syscall.h.
(set_raw_breakpoint_without_location): Setting the parameters
for the catch syscall feature.
(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.
* inferior.h (struct inferior): Included new variables
any_syscall_count, syscalls_counts and total_syscalls_count,
used to keep track of requested syscall catchpoints.
* 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_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.
* syscalls/gdb-syscalls.dtd: New definition file for syscall's XML
support.
* syscalls/amd64-linux.xml: New file containing information about
syscalls for GNU/Linux systems that use amd64 architecture.
* syscalls/i386-linux.xml: New file containing information about
syscalls for GNU/Linux systems that use i386 architecture.
* syscalls/ppc-linux.xml: New file containing information about
syscalls for GNU/Linux systems that use PPC architecture.
* syscalls/ppc64-linux.xml: New file containing information about
syscalls for GNU/Linux systems that use PPC64 architecture.
* xml-syscall.c: New file containing functions for manipulating
syscall's XML files.
* xml-syscall.h: New file, exporting the functions above mentioned.
* Makefile.in: Support for relocatable GDB datadir and XML
syscall.
* NEWS: Added information about the catch syscall feature.
* doc/gdb.texinfo (Set Catchpoints): Documentation about the new
feature.
* testsuite/Makefile.in: Inclusion of catch-syscall object.
* testsuite/gdb.base/catch-syscall.c: New file.
* testsuite/gdb.base/catch-syscall.exp: New file.
Diffstat (limited to 'gdb/linux-nat.c')
-rw-r--r-- | gdb/linux-nat.c | 144 |
1 files changed, 136 insertions, 8 deletions
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; |