123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- /* 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.
- A breakpoint is really of this type iff its ops pointer points to
- CATCH_SYSCALL_BREAKPOINT_OPS. */
- struct syscall_catchpoint : public breakpoint
- {
- /* 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" breakpoint_ops method for syscall
- catchpoints. */
- static int
- insert_catch_syscall (struct bp_location *bl)
- {
- struct syscall_catchpoint *c = (struct syscall_catchpoint *) bl->owner;
- struct inferior *inf = current_inferior ();
- struct catch_syscall_inferior_data *inf_data
- = get_catch_syscall_inferior_data (inf);
- ++inf_data->total_syscalls_count;
- if (c->syscalls_to_be_caught.empty ())
- ++inf_data->any_syscall_count;
- else
- {
- for (int iter : c->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" breakpoint_ops method for syscall
- catchpoints. */
- static int
- remove_catch_syscall (struct bp_location *bl, enum remove_bp_reason reason)
- {
- struct syscall_catchpoint *c = (struct syscall_catchpoint *) bl->owner;
- struct inferior *inf = current_inferior ();
- struct catch_syscall_inferior_data *inf_data
- = get_catch_syscall_inferior_data (inf);
- --inf_data->total_syscalls_count;
- if (c->syscalls_to_be_caught.empty ())
- --inf_data->any_syscall_count;
- else
- {
- for (int iter : c->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" breakpoint_ops method for syscall
- catchpoints. */
- static int
- breakpoint_hit_catch_syscall (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;
- const struct syscall_catchpoint *c
- = (const struct syscall_catchpoint *) bl->owner;
- 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 (!c->syscalls_to_be_caught.empty ())
- {
- for (int iter : c->syscalls_to_be_caught)
- if (syscall_number == iter)
- return 1;
- return 0;
- }
- return 1;
- }
- /* Implement the "print_it" breakpoint_ops method for syscall
- catchpoints. */
- static enum print_stop_action
- print_it_catch_syscall (bpstat *bs)
- {
- 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 = bs->bp_location_at->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" breakpoint_ops method for syscall
- catchpoints. */
- static void
- print_one_catch_syscall (struct breakpoint *b,
- struct bp_location **last_loc)
- {
- struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
- struct value_print_options opts;
- struct ui_out *uiout = current_uiout;
- struct gdbarch *gdbarch = b->loc->gdbarch;
- 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)
- uiout->field_skip ("addr");
- annotate_field (5);
- if (c->syscalls_to_be_caught.size () > 1)
- uiout->text ("syscalls \"");
- else
- uiout->text ("syscall \"");
- if (!c->syscalls_to_be_caught.empty ())
- {
- std::string text;
- bool first = true;
- for (int iter : c->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");
- }
- /* Implement the "print_mention" breakpoint_ops method for syscall
- catchpoints. */
- static void
- print_mention_catch_syscall (struct breakpoint *b)
- {
- struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
- struct gdbarch *gdbarch = b->loc->gdbarch;
- if (!c->syscalls_to_be_caught.empty ())
- {
- if (c->syscalls_to_be_caught.size () > 1)
- gdb_printf (_("Catchpoint %d (syscalls"), b->number);
- else
- gdb_printf (_("Catchpoint %d (syscall"), b->number);
- for (int iter : c->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)"),
- b->number);
- }
- /* Implement the "print_recreate" breakpoint_ops method for syscall
- catchpoints. */
- static void
- print_recreate_catch_syscall (struct breakpoint *b, struct ui_file *fp)
- {
- struct syscall_catchpoint *c = (struct syscall_catchpoint *) b;
- struct gdbarch *gdbarch = b->loc->gdbarch;
- gdb_printf (fp, "catch syscall");
- for (int iter : c->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 (b, fp);
- }
- /* The breakpoint_ops structure to be used in syscall catchpoints. */
- static struct breakpoint_ops catch_syscall_breakpoint_ops;
- /* Returns non-zero if 'b' is a syscall catchpoint. */
- static int
- syscall_catchpoint_p (struct breakpoint *b)
- {
- return (b->ops == &catch_syscall_breakpoint_ops);
- }
- static void
- create_syscall_event_catchpoint (int tempflag, std::vector<int> &&filter,
- const struct breakpoint_ops *ops)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- std::unique_ptr<syscall_catchpoint> c (new syscall_catchpoint ());
- init_catchpoint (c.get (), gdbarch, tempflag, NULL, ops);
- c->syscalls_to_be_caught = 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),
- &catch_syscall_breakpoint_ops);
- }
- /* 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 ();
- }
- static void
- initialize_syscall_catchpoint_ops (void)
- {
- struct breakpoint_ops *ops;
- initialize_breakpoint_ops ();
- /* Syscall catchpoints. */
- ops = &catch_syscall_breakpoint_ops;
- *ops = base_breakpoint_ops;
- ops->insert_location = insert_catch_syscall;
- ops->remove_location = remove_catch_syscall;
- ops->breakpoint_hit = breakpoint_hit_catch_syscall;
- ops->print_it = print_it_catch_syscall;
- ops->print_one = print_one_catch_syscall;
- ops->print_mention = print_mention_catch_syscall;
- ops->print_recreate = print_recreate_catch_syscall;
- }
- void _initialize_break_catch_syscall ();
- void
- _initialize_break_catch_syscall ()
- {
- initialize_syscall_catchpoint_ops ();
- 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);
- }
|