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.
This commit is contained in:
parent
22fe6da0f9
commit
a96d9b2e9a
27 changed files with 1358 additions and 99 deletions
114
gdb/ChangeLog
114
gdb/ChangeLog
|
@ -1,3 +1,117 @@
|
|||
2009-09-14 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
|
||||
|
||||
* 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.
|
||||
|
||||
2009-09-14 Doug Evans <dje@google.com>
|
||||
|
||||
* target.c (memory_xfer_partial): Only update dcache after we know
|
||||
|
|
|
@ -166,6 +166,9 @@ INTL_CFLAGS = @INCINTL@
|
|||
TARGET_SYSTEM_ROOT = @TARGET_SYSTEM_ROOT@
|
||||
TARGET_SYSTEM_ROOT_DEFINE = @TARGET_SYSTEM_ROOT_DEFINE@
|
||||
|
||||
# Did the user give us a --with-gdb-datadir option?
|
||||
GDB_DATADIR_PATH = @GDB_DATADIR_PATH@
|
||||
|
||||
# Helper code from gnulib.
|
||||
LIBGNU = gnulib/libgnu.a
|
||||
INCGNU = -I$(srcdir)/gnulib -Ignulib
|
||||
|
@ -677,7 +680,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
|
|||
xml-tdesc.c xml-support.c \
|
||||
inferior.c gdb_usleep.c \
|
||||
record.c \
|
||||
jit.c
|
||||
jit.c \
|
||||
xml-syscall.c \
|
||||
|
||||
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
|
||||
|
||||
|
@ -746,7 +750,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.h gdb-stabs.h reggroups.h \
|
|||
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
|
||||
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
|
||||
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
|
||||
gdb_usleep.h jit.h
|
||||
gdb_usleep.h jit.h xml-syscall.h
|
||||
|
||||
# Header files that already have srcdir in them, or which are in objdir.
|
||||
|
||||
|
@ -826,11 +830,17 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
|||
trad-frame.o \
|
||||
tramp-frame.o \
|
||||
solib.o solib-null.o \
|
||||
prologue-value.o memory-map.o xml-support.o \
|
||||
prologue-value.o memory-map.o xml-support.o xml-syscall.o \
|
||||
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
|
||||
inferior.o osdata.o gdb_usleep.o record.o \
|
||||
jit.o
|
||||
|
||||
# Definitions for the syscall's XML files and dir
|
||||
XML_SYSCALLS_DIR = syscalls/
|
||||
XML_SYSCALLS_FILES = gdb-syscalls.dtd \
|
||||
ppc-linux.xml ppc64-linux.xml \
|
||||
i386-linux.xml amd64-linux.xml
|
||||
|
||||
TSOBS = inflow.o
|
||||
|
||||
SUBDIRS = @subdirs@
|
||||
|
@ -864,11 +874,41 @@ generated_files = config.h observer.h observer.inc ada-lex.c \
|
|||
$(COMPILE) $<
|
||||
$(POSTCOMPILE)
|
||||
|
||||
all: gdb$(EXEEXT) $(CONFIG_ALL)
|
||||
all: gdb$(EXEEXT) $(CONFIG_ALL) xml-syscall-copy
|
||||
@$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=`echo $(SUBDIRS) | sed 's/testsuite//'`" subdir_do
|
||||
.PHONY: all-tui
|
||||
all-tui: $(TUI)$(EXEEXT)
|
||||
|
||||
# This is needed for running GDB from the build directory
|
||||
.PHONY: xml-syscall-copy
|
||||
xml-syscall-copy:
|
||||
if [ "`cd $(srcdir) && pwd`" != "`pwd`" ] ; then \
|
||||
mkdir -p ./$(XML_SYSCALLS_DIR) ; \
|
||||
list='$(XML_SYSCALLS_FILES)' ; \
|
||||
for file in $$list ; do \
|
||||
f=$(srcdir)/$(XML_SYSCALLS_DIR)/$$file ; \
|
||||
if test -f $$f ; then \
|
||||
$(INSTALL_DATA) $$f \
|
||||
./$(XML_SYSCALLS_DIR) ; \
|
||||
fi ; \
|
||||
done ; \
|
||||
fi ;
|
||||
|
||||
# This target is responsible for properly installing the syscalls'
|
||||
# XML files in the system.
|
||||
.PHONY: xml-syscall-install
|
||||
xml-syscall-install:
|
||||
$(SHELL) $(srcdir)/../mkinstalldirs \
|
||||
$(DESTDIR)$(GDB_DATADIR_PATH)/$(XML_SYSCALLS_DIR) ; \
|
||||
list='$(XML_SYSCALLS_FILES)' ; \
|
||||
for file in $$list ; do \
|
||||
f=$(srcdir)/$(XML_SYSCALLS_DIR)/$$file ; \
|
||||
if test -f $$f ; then \
|
||||
$(INSTALL_DATA) $$f \
|
||||
$(DESTDIR)$(GDB_DATADIR_PATH)/$(XML_SYSCALLS_DIR) ; \
|
||||
fi ; \
|
||||
done ;
|
||||
|
||||
installcheck:
|
||||
|
||||
# The check target can not use subdir_do, because subdir_do does not
|
||||
|
@ -925,8 +965,11 @@ gdb.z:gdb.1
|
|||
# source file and doesn't care about rebuilding or just wants to save the
|
||||
# time it takes for make to check that all is up to date.
|
||||
# install-only is intended to address that need.
|
||||
install: all install-only
|
||||
install-only: $(CONFIG_INSTALL)
|
||||
install: all install-only
|
||||
|
||||
# The "install-only" target also installs the syscalls' XML files in
|
||||
# the system.
|
||||
install-only: $(CONFIG_INSTALL) xml-syscall-install
|
||||
transformed_name=`t='$(program_transform_name)'; \
|
||||
echo gdb | sed -e "$$t"` ; \
|
||||
if test "x$$transformed_name" = x; then \
|
||||
|
|
10
gdb/NEWS
10
gdb/NEWS
|
@ -234,6 +234,16 @@ powerpc-linux or powerpc64-linux and the spu-elf targets, using the
|
|||
|
||||
* New commands (for set/show, see "New options" below)
|
||||
|
||||
catch syscall [NAME(S) | NUMBER(S)]
|
||||
Catch system calls. Arguments, which should be names of system
|
||||
calls or their numbers, mean catch only those syscalls. Without
|
||||
arguments, every syscall will be caught. When the inferior issues
|
||||
any of the specified syscalls, GDB will stop and announce the system
|
||||
call, both when it is called and when its call returns. This
|
||||
feature is currently available with a native GDB running on the
|
||||
Linux Kernel, under the following architectures: x86, x86_64,
|
||||
PowerPC and PowerPC64.
|
||||
|
||||
find [/size-char] [/max-count] start-address, end-address|+search-space-size,
|
||||
val1 [, val2, ...]
|
||||
Search memory for a sequence of bytes.
|
||||
|
|
|
@ -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);
|
||||
|
|
489
gdb/breakpoint.c
489
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; i < 127 && 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, 0);
|
||||
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);
|
||||
|
||||
|
|
|
@ -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 *);
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2009-09-14 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
|
||||
|
||||
* gdb.texinfo (Set Catchpoints): Documentation about the catch syscall
|
||||
feature.
|
||||
|
||||
2009-09-13 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* gdbint.texinfo (Unwinding the Frame ID): Reference outer_frame_id.
|
||||
|
|
|
@ -3685,6 +3685,137 @@ A failed Ada assertion.
|
|||
A call to @code{exec}. This is currently only available for HP-UX
|
||||
and @sc{gnu}/Linux.
|
||||
|
||||
@item syscall
|
||||
@itemx syscall @r{[}@var{name} @r{|} @var{number}@r{]} @r{...}
|
||||
@cindex break on a system call.
|
||||
A call to or return from a system call, a.k.a.@: @dfn{syscall}. A
|
||||
syscall is a mechanism for application programs to request a service
|
||||
from the operating system (OS) or one of the OS system services.
|
||||
@value{GDBN} can catch some or all of the syscalls issued by the
|
||||
debuggee, and show the related information for each syscall. If no
|
||||
argument is specified, calls to and returns from all system calls
|
||||
will be caught.
|
||||
|
||||
@var{name} can be any system call name that is valid for the
|
||||
underlying OS. Just what syscalls are valid depends on the OS. On
|
||||
GNU and Unix systems, you can find the full list of valid syscall
|
||||
names on @file{/usr/include/asm/unistd.h}.
|
||||
|
||||
@c For MS-Windows, the syscall names and the corresponding numbers
|
||||
@c can be found, e.g., on this URL:
|
||||
@c http://www.metasploit.com/users/opcode/syscalls.html
|
||||
@c but we don't support Windows syscalls yet.
|
||||
|
||||
Normally, @value{GDBN} knows in advance which syscalls are valid for
|
||||
each OS, so you can use the @value{GDBN} command-line completion
|
||||
facilities (@pxref{Completion,, command completion}) to list the
|
||||
available choices.
|
||||
|
||||
You may also specify the system call numerically. A syscall's
|
||||
number is the value passed to the OS's syscall dispatcher to
|
||||
identify the requested service. When you specify the syscall by its
|
||||
name, @value{GDBN} uses its database of syscalls to convert the name
|
||||
into the corresponding numeric code, but using the number directly
|
||||
may be useful if @value{GDBN}'s database does not have the complete
|
||||
list of syscalls on your system (e.g., because @value{GDBN} lags
|
||||
behind the OS upgrades).
|
||||
|
||||
The example below illustrates how this command works if you don't provide
|
||||
arguments to it:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) catch syscall
|
||||
Catchpoint 1 (syscall)
|
||||
(@value{GDBP}) r
|
||||
Starting program: /tmp/catch-syscall
|
||||
|
||||
Catchpoint 1 (call to syscall 'close'), \
|
||||
0xffffe424 in __kernel_vsyscall ()
|
||||
(@value{GDBP}) c
|
||||
Continuing.
|
||||
|
||||
Catchpoint 1 (returned from syscall 'close'), \
|
||||
0xffffe424 in __kernel_vsyscall ()
|
||||
(@value{GDBP})
|
||||
@end smallexample
|
||||
|
||||
Here is an example of catching a system call by name:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) catch syscall chroot
|
||||
Catchpoint 1 (syscall 'chroot' [61])
|
||||
(@value{GDBP}) r
|
||||
Starting program: /tmp/catch-syscall
|
||||
|
||||
Catchpoint 1 (call to syscall 'chroot'), \
|
||||
0xffffe424 in __kernel_vsyscall ()
|
||||
(@value{GDBP}) c
|
||||
Continuing.
|
||||
|
||||
Catchpoint 1 (returned from syscall 'chroot'), \
|
||||
0xffffe424 in __kernel_vsyscall ()
|
||||
(@value{GDBP})
|
||||
@end smallexample
|
||||
|
||||
An example of specifying a system call numerically. In the case
|
||||
below, the syscall number has a corresponding entry in the XML
|
||||
file, so @value{GDBN} finds its name and prints it:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) catch syscall 252
|
||||
Catchpoint 1 (syscall(s) 'exit_group')
|
||||
(@value{GDBP}) r
|
||||
Starting program: /tmp/catch-syscall
|
||||
|
||||
Catchpoint 1 (call to syscall 'exit_group'), \
|
||||
0xffffe424 in __kernel_vsyscall ()
|
||||
(@value{GDBP}) c
|
||||
Continuing.
|
||||
|
||||
Program exited normally.
|
||||
(@value{GDBP})
|
||||
@end smallexample
|
||||
|
||||
However, there can be situations when there is no corresponding name
|
||||
in XML file for that syscall number. In this case, @value{GDBN} prints
|
||||
a warning message saying that it was not able to find the syscall name,
|
||||
but the catchpoint will be set anyway. See the example below:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) catch syscall 764
|
||||
warning: The number '764' does not represent a known syscall.
|
||||
Catchpoint 2 (syscall 764)
|
||||
(@value{GDBP})
|
||||
@end smallexample
|
||||
|
||||
If you configure @value{GDBN} using the @samp{--without-expat} option,
|
||||
it will not be able to display syscall names. Also, if your
|
||||
architecture does not have an XML file describing its system calls,
|
||||
you will not be able to see the syscall names. It is important to
|
||||
notice that these two features are used for accessing the syscall
|
||||
name database. In either case, you will see a warning like this:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) catch syscall
|
||||
warning: Could not open "syscalls/i386-linux.xml"
|
||||
warning: Could not load the syscall XML file 'syscalls/i386-linux.xml'.
|
||||
GDB will not be able to display syscall names.
|
||||
Catchpoint 1 (syscall)
|
||||
(@value{GDBP})
|
||||
@end smallexample
|
||||
|
||||
Of course, the file name will change depending on your architecture and system.
|
||||
|
||||
Still using the example above, you can also try to catch a syscall by its
|
||||
number. In this case, you would see something like:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) catch syscall 252
|
||||
Catchpoint 1 (syscall(s) 252)
|
||||
@end smallexample
|
||||
|
||||
Again, in this case @value{GDBN} would not be able to display syscall's names.
|
||||
|
||||
@item fork
|
||||
A call to @code{fork}. This is currently only available for HP-UX
|
||||
and @sc{gnu}/Linux.
|
||||
|
|
|
@ -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);
|
||||
|
@ -865,6 +868,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
|||
fprintf_unfiltered (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));
|
||||
|
@ -3380,6 +3389,30 @@ set_gdbarch_record_special_symbol (struct gdbarch *gdbarch,
|
|||
gdbarch->record_special_symbol = record_special_symbol;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,15 @@ inf_child_remove_exec_catchpoint (int pid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
88
gdb/infrun.c
88
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)
|
||||
|
@ -5626,6 +5683,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;
|
||||
|
|
144
gdb/linux-nat.c
144
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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -2866,9 +2870,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:
|
||||
|
|
44
gdb/target.h
44
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. */
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2009-09-14 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
|
||||
|
||||
* Makefile.in: Inclusion of catch-syscall object.
|
||||
* gdb.base/catch-syscall.c: New file.
|
||||
* gdb.base/catch-syscall.exp: New file.
|
||||
|
||||
2009-09-12 Michael Snyder <msnyder@vmware.com>
|
||||
|
||||
* gdb.reverse/step-reverse.exp: Explicitly check for targets
|
||||
|
|
|
@ -12,7 +12,7 @@ EXECUTABLES = all-types annota1 bitfields break \
|
|||
scope section_command setshow setvar shmain sigall signals \
|
||||
solib solib_sl so-impl-ld so-indr-cl \
|
||||
step-line step-test structs structs2 \
|
||||
twice-tmp varargs vforked-prog watchpoint whatis
|
||||
twice-tmp varargs vforked-prog watchpoint whatis catch-syscall
|
||||
|
||||
MISCELLANEOUS = coremmap.data ../foobar.baz \
|
||||
shr1.sl shr2.sl solib_sl.sl solib1.sl solib2.sl
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue