mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-05 15:15:42 +00:00
This commit adds a new option to "info breakpoints", "-hide-locations". It's purpose is to tell GDB to skip printing breakpoint locations, printing only the breakpoint header rows. And then, since only code breakpoint locations print anything in the "Address" column, "-hide-breakpoints" also disables the "Address" column. For example, when debugging GDB, you can use the new options to get this: (top-gdb) i b -h Num Type Disp Enb What 1 breakpoint keep y internal_error 2 breakpoint keep y info_command silent return 3 breakpoint keep y main breakpoint already hit 1 time 4 breakpoint keep y error (top-gdb) instead of: (top-gdb) i b Num Type Disp Enb Address What 1 breakpoint keep y internal_error 1.1 y 0x0000555555f81d12 in internal_error(char const*, int, char const*, ...) at /home/pedro/gdb/binutils-gdb/src/gdbsupport/errors.cc:51 2 breakpoint keep y info_command silent return 2.1 y 0x00005555557b3097 in info_command(char const*, int) at /home/pedro/gdb/binutils-gdb/src/gdb/cli/cli-cmds.c:217 3 breakpoint keep y main breakpoint already hit 1 time 3.1 y 0x000055555564106c in main(int, char**) at /home/pedro/gdb/binutils-gdb/src/gdb/gdb.c:25 3.2 y 0x0000555555dba524 in selftests::string_view::capacity_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/capacity/1.cc:167 3.3 y 0x0000555555dba943 in selftests::string_view::cons_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/cons/char/1.cc:62 3.4 y 0x0000555555dbaa34 in selftests::string_view::cons_2::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/cons/char/2.cc:41 3.5 y 0x0000555555dbaa9a in selftests::string_view::cons_3::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/cons/char/3.cc:34 3.6 y 0x0000555555dbac6b in selftests::string_view::element_access_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/element_access/char/1.cc:66 3.7 y 0x0000555555dbac83 in selftests::string_view::element_access_empty::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/element_access/char/empty.cc:25 3.8 y 0x0000555555dbae91 in selftests::string_view::element_access_front_back::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/element_access/char/front_back.cc:38 3.9 y 0x0000555555dbb2bd in selftests::string_view::inserters_2::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/inserters/char/2.cc:84 3.10 y 0x0000555555dbb429 in selftests::string_view::modifiers_remove_prefix::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/modifiers/remove_prefix/char/1.cc:58 3.11 y 0x0000555555dbb575 in selftests::string_view::modifiers_remove_suffix::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/modifiers/remove_suffix/char/1.cc:58 3.12 y 0x0000555555dbbd38 in selftests::string_view::operations_compare_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/compare/char/1.cc:127 3.13 y 0x0000555555dbbe7b in selftests::string_view::operations_compare_13650::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/compare/char/13650.cc:45 3.14 y 0x0000555555dbbf6a in selftests::string_view::operations_copy_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/copy/char/1.cc:41 3.15 y 0x0000555555dbc03b in selftests::string_view::operations_data_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/data/char/1.cc:39 3.16 y 0x0000555555dbc5fe in selftests::string_view::operations_find_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/find/char/1.cc:160 3.17 y 0x0000555555dbcb60 in selftests::string_view::operations_find_2::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/find/char/2.cc:158 3.18 y 0x0000555555dbd1c1 in selftests::string_view::operations_find_3::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/find/char/3.cc:158 3.19 y 0x0000555555dbd26c in selftests::string_view::operations_find_4::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/find/char/4.cc:40 3.20 y 0x0000555555dbd83f in selftests::string_view::operations_rfind_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/rfind/char/1.cc:90 3.21 y 0x0000555555dbda98 in selftests::string_view::operations_rfind_2::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/rfind/char/2.cc:48 3.22 y 0x0000555555dbde4c in selftests::string_view::operations_rfind_3::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/rfind/char/3.cc:63 3.23 y 0x0000555555dbe189 in selftests::string_view::operations_substr_1::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operations/substr/char/1.cc:74 3.24 y 0x0000555555dbffdb in selftests::string_view::operators_2::main() at /home/pedro/gdb/binutils-gdb/src/gdb/unittests/basic_string_view/operators/char/2.cc:366 4 breakpoint keep y error 4.1 y 0x00005555557f5142 in gcc_c_plugin::error(char const*) const at /home/pedro/gdb/binutils-gdb/src/gdb/../include/gcc-c-fe.def:198 4.2 y 0x00005555557fe1f4 in gcc_cp_plugin::error(char const*) const at /home/pedro/gdb/binutils-gdb/src/gdb/../include/gcc-cp-fe.def:983 4.3 y 0x0000555555f81c5e in error(char const*, ...) at /home/pedro/gdb/binutils-gdb/src/gdbsupport/errors.cc:39 4.4 y 0x00007ffff65ee4e0 <icu_66::RBBIRuleScanner::error(UErrorCode)> 4.5 y 0x00007ffff685e070 <icu_66::RegexCompile::error(UErrorCode)> 4.6 y 0x00007ffff6cd4e86 in error at /usr/include/x86_64-linux-gnu/bits/error.h:40 4.7 y 0x00007ffff71c2190 in __error at error.c:274 Documentation change included. No testsuite changes yet until there's agreement on the previous patch. Change-Id: Ic42ad8565e79ca67bfebb22cbb4794ea816fd08b
596 lines
16 KiB
C
596 lines
16 KiB
C
/* Everything about syscall catchpoints, for GDB.
|
|
|
|
Copyright (C) 2009-2022 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include <ctype.h>
|
|
#include "breakpoint.h"
|
|
#include "gdbcmd.h"
|
|
#include "inferior.h"
|
|
#include "cli/cli-utils.h"
|
|
#include "annotate.h"
|
|
#include "mi/mi-common.h"
|
|
#include "valprint.h"
|
|
#include "arch-utils.h"
|
|
#include "observable.h"
|
|
#include "xml-syscall.h"
|
|
#include "cli/cli-style.h"
|
|
#include "cli/cli-decode.h"
|
|
|
|
/* An instance of this type is used to represent a syscall
|
|
catchpoint. */
|
|
|
|
struct syscall_catchpoint : public catchpoint
|
|
{
|
|
syscall_catchpoint (struct gdbarch *gdbarch, bool tempflag,
|
|
std::vector<int> &&calls)
|
|
: catchpoint (gdbarch, tempflag, nullptr),
|
|
syscalls_to_be_caught (std::move (calls))
|
|
{
|
|
}
|
|
|
|
int insert_location (struct bp_location *) override;
|
|
int remove_location (struct bp_location *,
|
|
enum remove_bp_reason reason) override;
|
|
int breakpoint_hit (const struct bp_location *bl,
|
|
const address_space *aspace,
|
|
CORE_ADDR bp_addr,
|
|
const target_waitstatus &ws) override;
|
|
enum print_stop_action print_it (const bpstat *bs) const override;
|
|
bool print_one (bp_location **, bool) const override;
|
|
void print_mention () const override;
|
|
void print_recreate (struct ui_file *fp) const override;
|
|
|
|
/* Syscall numbers used for the 'catch syscall' feature. If no
|
|
syscall has been specified for filtering, it is empty.
|
|
Otherwise, it holds a list of all syscalls to be caught. */
|
|
std::vector<int> syscalls_to_be_caught;
|
|
};
|
|
|
|
struct catch_syscall_inferior_data
|
|
{
|
|
/* 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. */
|
|
std::vector<int> syscalls_counts;
|
|
|
|
/* This counts all syscall catch requests, so we can readily determine
|
|
if any catching is necessary. */
|
|
int total_syscalls_count;
|
|
};
|
|
|
|
static const struct inferior_key<struct catch_syscall_inferior_data>
|
|
catch_syscall_inferior_data;
|
|
|
|
static struct catch_syscall_inferior_data *
|
|
get_catch_syscall_inferior_data (struct inferior *inf)
|
|
{
|
|
struct catch_syscall_inferior_data *inf_data;
|
|
|
|
inf_data = catch_syscall_inferior_data.get (inf);
|
|
if (inf_data == NULL)
|
|
inf_data = catch_syscall_inferior_data.emplace (inf);
|
|
|
|
return inf_data;
|
|
}
|
|
|
|
/* Implement the "insert" method for syscall catchpoints. */
|
|
|
|
int
|
|
syscall_catchpoint::insert_location (struct bp_location *bl)
|
|
{
|
|
struct inferior *inf = current_inferior ();
|
|
struct catch_syscall_inferior_data *inf_data
|
|
= get_catch_syscall_inferior_data (inf);
|
|
|
|
++inf_data->total_syscalls_count;
|
|
if (syscalls_to_be_caught.empty ())
|
|
++inf_data->any_syscall_count;
|
|
else
|
|
{
|
|
for (int iter : syscalls_to_be_caught)
|
|
{
|
|
if (iter >= inf_data->syscalls_counts.size ())
|
|
inf_data->syscalls_counts.resize (iter + 1);
|
|
++inf_data->syscalls_counts[iter];
|
|
}
|
|
}
|
|
|
|
return target_set_syscall_catchpoint (inferior_ptid.pid (),
|
|
inf_data->total_syscalls_count != 0,
|
|
inf_data->any_syscall_count,
|
|
inf_data->syscalls_counts);
|
|
}
|
|
|
|
/* Implement the "remove" method for syscall catchpoints. */
|
|
|
|
int
|
|
syscall_catchpoint::remove_location (struct bp_location *bl,
|
|
enum remove_bp_reason reason)
|
|
{
|
|
struct inferior *inf = current_inferior ();
|
|
struct catch_syscall_inferior_data *inf_data
|
|
= get_catch_syscall_inferior_data (inf);
|
|
|
|
--inf_data->total_syscalls_count;
|
|
if (syscalls_to_be_caught.empty ())
|
|
--inf_data->any_syscall_count;
|
|
else
|
|
{
|
|
for (int iter : syscalls_to_be_caught)
|
|
{
|
|
if (iter >= inf_data->syscalls_counts.size ())
|
|
/* Shouldn't happen. */
|
|
continue;
|
|
--inf_data->syscalls_counts[iter];
|
|
}
|
|
}
|
|
|
|
return target_set_syscall_catchpoint (inferior_ptid.pid (),
|
|
inf_data->total_syscalls_count != 0,
|
|
inf_data->any_syscall_count,
|
|
inf_data->syscalls_counts);
|
|
}
|
|
|
|
/* Implement the "breakpoint_hit" method for syscall catchpoints. */
|
|
|
|
int
|
|
syscall_catchpoint::breakpoint_hit (const struct bp_location *bl,
|
|
const address_space *aspace,
|
|
CORE_ADDR bp_addr,
|
|
const target_waitstatus &ws)
|
|
{
|
|
/* 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 (ws.kind () != TARGET_WAITKIND_SYSCALL_ENTRY
|
|
&& ws.kind () != TARGET_WAITKIND_SYSCALL_RETURN)
|
|
return 0;
|
|
|
|
syscall_number = ws.syscall_number ();
|
|
|
|
/* Now, checking if the syscall is the same. */
|
|
if (!syscalls_to_be_caught.empty ())
|
|
{
|
|
for (int iter : syscalls_to_be_caught)
|
|
if (syscall_number == iter)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Implement the "print_it" method for syscall catchpoints. */
|
|
|
|
enum print_stop_action
|
|
syscall_catchpoint::print_it (const bpstat *bs) const
|
|
{
|
|
struct ui_out *uiout = current_uiout;
|
|
struct breakpoint *b = bs->breakpoint_at;
|
|
/* 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". */
|
|
struct target_waitstatus last;
|
|
struct syscall s;
|
|
struct gdbarch *gdbarch = b->gdbarch;
|
|
|
|
get_last_target_status (nullptr, nullptr, &last);
|
|
|
|
get_syscall_by_number (gdbarch, last.syscall_number (), &s);
|
|
|
|
annotate_catchpoint (b->number);
|
|
maybe_print_thread_hit_breakpoint (uiout);
|
|
|
|
if (b->disposition == disp_del)
|
|
uiout->text ("Temporary catchpoint ");
|
|
else
|
|
uiout->text ("Catchpoint ");
|
|
if (uiout->is_mi_like_p ())
|
|
{
|
|
uiout->field_string ("reason",
|
|
async_reason_lookup (last.kind () == TARGET_WAITKIND_SYSCALL_ENTRY
|
|
? EXEC_ASYNC_SYSCALL_ENTRY
|
|
: EXEC_ASYNC_SYSCALL_RETURN));
|
|
uiout->field_string ("disp", bpdisp_text (b->disposition));
|
|
}
|
|
uiout->field_signed ("bkptno", b->number);
|
|
|
|
if (last.kind () == TARGET_WAITKIND_SYSCALL_ENTRY)
|
|
uiout->text (" (call to syscall ");
|
|
else
|
|
uiout->text (" (returned from syscall ");
|
|
|
|
if (s.name == NULL || uiout->is_mi_like_p ())
|
|
uiout->field_signed ("syscall-number", last.syscall_number ());
|
|
if (s.name != NULL)
|
|
uiout->field_string ("syscall-name", s.name);
|
|
|
|
uiout->text ("), ");
|
|
|
|
return PRINT_SRC_AND_LOC;
|
|
}
|
|
|
|
/* Implement the "print_one" method for syscall catchpoints. */
|
|
|
|
bool
|
|
syscall_catchpoint::print_one (bp_location **last_loc,
|
|
bool print_address_col) const
|
|
{
|
|
struct ui_out *uiout = current_uiout;
|
|
struct gdbarch *gdbarch = loc->owner->gdbarch;
|
|
|
|
/* 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 (print_address_col)
|
|
uiout->field_skip ("addr");
|
|
annotate_field (5);
|
|
|
|
if (syscalls_to_be_caught.size () > 1)
|
|
uiout->text ("syscalls \"");
|
|
else
|
|
uiout->text ("syscall \"");
|
|
|
|
if (!syscalls_to_be_caught.empty ())
|
|
{
|
|
std::string text;
|
|
|
|
bool first = true;
|
|
for (int iter : syscalls_to_be_caught)
|
|
{
|
|
struct syscall s;
|
|
get_syscall_by_number (gdbarch, iter, &s);
|
|
|
|
if (!first)
|
|
text += ", ";
|
|
first = false;
|
|
|
|
if (s.name != NULL)
|
|
text += s.name;
|
|
else
|
|
text += std::to_string (iter);
|
|
}
|
|
uiout->field_string ("what", text.c_str ());
|
|
}
|
|
else
|
|
uiout->field_string ("what", "<any syscall>", metadata_style.style ());
|
|
uiout->text ("\" ");
|
|
|
|
if (uiout->is_mi_like_p ())
|
|
uiout->field_string ("catch-type", "syscall");
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Implement the "print_mention" method for syscall catchpoints. */
|
|
|
|
void
|
|
syscall_catchpoint::print_mention () const
|
|
{
|
|
struct gdbarch *gdbarch = loc->owner->gdbarch;
|
|
|
|
if (!syscalls_to_be_caught.empty ())
|
|
{
|
|
if (syscalls_to_be_caught.size () > 1)
|
|
gdb_printf (_("Catchpoint %d (syscalls"), number);
|
|
else
|
|
gdb_printf (_("Catchpoint %d (syscall"), number);
|
|
|
|
for (int iter : syscalls_to_be_caught)
|
|
{
|
|
struct syscall s;
|
|
get_syscall_by_number (gdbarch, iter, &s);
|
|
|
|
if (s.name != NULL)
|
|
gdb_printf (" '%s' [%d]", s.name, s.number);
|
|
else
|
|
gdb_printf (" %d", s.number);
|
|
}
|
|
gdb_printf (")");
|
|
}
|
|
else
|
|
gdb_printf (_("Catchpoint %d (any syscall)"), number);
|
|
}
|
|
|
|
/* Implement the "print_recreate" method for syscall catchpoints. */
|
|
|
|
void
|
|
syscall_catchpoint::print_recreate (struct ui_file *fp) const
|
|
{
|
|
struct gdbarch *gdbarch = loc->gdbarch;
|
|
|
|
gdb_printf (fp, "catch syscall");
|
|
|
|
for (int iter : syscalls_to_be_caught)
|
|
{
|
|
struct syscall s;
|
|
|
|
get_syscall_by_number (gdbarch, iter, &s);
|
|
if (s.name != NULL)
|
|
gdb_printf (fp, " %s", s.name);
|
|
else
|
|
gdb_printf (fp, " %d", s.number);
|
|
}
|
|
|
|
print_recreate_thread (fp);
|
|
}
|
|
|
|
/* Returns non-zero if 'b' is a syscall catchpoint. */
|
|
|
|
static int
|
|
syscall_catchpoint_p (struct breakpoint *b)
|
|
{
|
|
return dynamic_cast<syscall_catchpoint *> (b) != nullptr;
|
|
}
|
|
|
|
static void
|
|
create_syscall_event_catchpoint (int tempflag, std::vector<int> &&filter)
|
|
{
|
|
struct gdbarch *gdbarch = get_current_arch ();
|
|
|
|
std::unique_ptr<syscall_catchpoint> c
|
|
(new syscall_catchpoint (gdbarch, tempflag, std::move (filter)));
|
|
|
|
install_breakpoint (0, std::move (c), 1);
|
|
}
|
|
|
|
/* Splits the argument using space as delimiter. */
|
|
|
|
static std::vector<int>
|
|
catch_syscall_split_args (const char *arg)
|
|
{
|
|
std::vector<int> result;
|
|
struct gdbarch *gdbarch = target_gdbarch ();
|
|
|
|
while (*arg != '\0')
|
|
{
|
|
int i, syscall_number;
|
|
char *endptr;
|
|
char cur_name[128];
|
|
struct syscall s;
|
|
|
|
/* Skip whitespace. */
|
|
arg = skip_spaces (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, group, or a number. */
|
|
syscall_number = (int) strtol (cur_name, &endptr, 0);
|
|
if (*endptr == '\0')
|
|
{
|
|
if (syscall_number < 0)
|
|
error (_("Unknown syscall number '%d'."), syscall_number);
|
|
get_syscall_by_number (gdbarch, syscall_number, &s);
|
|
result.push_back (s.number);
|
|
}
|
|
else if (startswith (cur_name, "g:")
|
|
|| startswith (cur_name, "group:"))
|
|
{
|
|
/* We have a syscall group. Let's expand it into a syscall
|
|
list before inserting. */
|
|
const char *group_name;
|
|
|
|
/* Skip over "g:" and "group:" prefix strings. */
|
|
group_name = strchr (cur_name, ':') + 1;
|
|
|
|
if (!get_syscalls_by_group (gdbarch, group_name, &result))
|
|
error (_("Unknown syscall group '%s'."), group_name);
|
|
}
|
|
else
|
|
{
|
|
/* We have a name. Let's check if it's valid and fetch a
|
|
list of matching numbers. */
|
|
if (!get_syscalls_by_name (gdbarch, cur_name, &result))
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Implement the "catch syscall" command. */
|
|
|
|
static void
|
|
catch_syscall_command_1 (const char *arg, int from_tty,
|
|
struct cmd_list_element *command)
|
|
{
|
|
int tempflag;
|
|
std::vector<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 architecture yet."));
|
|
|
|
tempflag = command->context () == CATCH_TEMPORARY;
|
|
|
|
arg = skip_spaces (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 (gdbarch, 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);
|
|
|
|
create_syscall_event_catchpoint (tempflag, std::move (filter));
|
|
}
|
|
|
|
|
|
/* 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 catch_syscall_inferior_data *inf_data
|
|
= get_catch_syscall_inferior_data (current_inferior ());
|
|
|
|
return inf_data->total_syscalls_count != 0;
|
|
}
|
|
|
|
/* Helper function for catching_syscall_number. return true if B is a syscall
|
|
catchpoint for SYSCALL_NUMBER, else false. */
|
|
|
|
static bool
|
|
catching_syscall_number_1 (struct breakpoint *b, int syscall_number)
|
|
{
|
|
|
|
if (is_syscall_catchpoint_enabled (b))
|
|
{
|
|
struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
|
|
|
|
if (!c->syscalls_to_be_caught.empty ())
|
|
{
|
|
for (int iter : c->syscalls_to_be_caught)
|
|
if (syscall_number == iter)
|
|
return true;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
catching_syscall_number (int syscall_number)
|
|
{
|
|
for (breakpoint *b : all_breakpoints ())
|
|
if (catching_syscall_number_1 (b, syscall_number))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Complete syscall names. Used by "catch syscall". */
|
|
|
|
static void
|
|
catch_syscall_completer (struct cmd_list_element *cmd,
|
|
completion_tracker &tracker,
|
|
const char *text, const char *word)
|
|
{
|
|
struct gdbarch *gdbarch = get_current_arch ();
|
|
gdb::unique_xmalloc_ptr<const char *> group_list;
|
|
const char *prefix;
|
|
|
|
/* Completion considers ':' to be a word separator, so we use this to
|
|
verify whether the previous word was a group prefix. If so, we
|
|
build the completion list using group names only. */
|
|
for (prefix = word; prefix != text && prefix[-1] != ' '; prefix--)
|
|
;
|
|
|
|
if (startswith (prefix, "g:") || startswith (prefix, "group:"))
|
|
{
|
|
/* Perform completion inside 'group:' namespace only. */
|
|
group_list.reset (get_syscall_group_names (gdbarch));
|
|
if (group_list != NULL)
|
|
complete_on_enum (tracker, group_list.get (), word, word);
|
|
}
|
|
else
|
|
{
|
|
/* Complete with both, syscall names and groups. */
|
|
gdb::unique_xmalloc_ptr<const char *> syscall_list
|
|
(get_syscall_names (gdbarch));
|
|
group_list.reset (get_syscall_group_names (gdbarch));
|
|
|
|
const char **group_ptr = group_list.get ();
|
|
|
|
/* Hold on to strings while we're using them. */
|
|
std::vector<std::string> holders;
|
|
|
|
/* Append "group:" prefix to syscall groups. */
|
|
for (int i = 0; group_ptr[i] != NULL; i++)
|
|
holders.push_back (string_printf ("group:%s", group_ptr[i]));
|
|
|
|
for (int i = 0; group_ptr[i] != NULL; i++)
|
|
group_ptr[i] = holders[i].c_str ();
|
|
|
|
if (syscall_list != NULL)
|
|
complete_on_enum (tracker, syscall_list.get (), word, word);
|
|
if (group_list != NULL)
|
|
complete_on_enum (tracker, group_ptr, word, word);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear_syscall_counts (struct inferior *inf)
|
|
{
|
|
struct catch_syscall_inferior_data *inf_data
|
|
= get_catch_syscall_inferior_data (inf);
|
|
|
|
inf_data->total_syscalls_count = 0;
|
|
inf_data->any_syscall_count = 0;
|
|
inf_data->syscalls_counts.clear ();
|
|
}
|
|
|
|
void _initialize_break_catch_syscall ();
|
|
void
|
|
_initialize_break_catch_syscall ()
|
|
{
|
|
gdb::observers::inferior_exit.attach (clear_syscall_counts,
|
|
"break-catch-syscall");
|
|
|
|
add_catch_command ("syscall", _("\
|
|
Catch system calls by their names, groups and/or numbers.\n\
|
|
Arguments say which system calls to catch. If no arguments are given,\n\
|
|
every system call will be caught. Arguments, if given, should be one\n\
|
|
or more system call names (if your system supports that), system call\n\
|
|
groups or system call numbers."),
|
|
catch_syscall_command_1,
|
|
catch_syscall_completer,
|
|
CATCH_PERMANENT,
|
|
CATCH_TEMPORARY);
|
|
}
|