123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- /* Everything about signal catchpoints, for GDB.
- Copyright (C) 2011-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 "arch-utils.h"
- #include <ctype.h>
- #include "breakpoint.h"
- #include "gdbcmd.h"
- #include "inferior.h"
- #include "infrun.h"
- #include "annotate.h"
- #include "valprint.h"
- #include "cli/cli-utils.h"
- #include "completer.h"
- #include "cli/cli-style.h"
- #include "cli/cli-decode.h"
- #include <string>
- #define INTERNAL_SIGNAL(x) ((x) == GDB_SIGNAL_TRAP || (x) == GDB_SIGNAL_INT)
- /* An instance of this type is used to represent a signal catchpoint.
- A breakpoint is really of this type iff its ops pointer points to
- SIGNAL_CATCHPOINT_OPS. */
- struct signal_catchpoint : public breakpoint
- {
- /* Signal numbers used for the 'catch signal' feature. If no signal
- has been specified for filtering, it is empty. Otherwise,
- it holds a list of all signals to be caught. */
- std::vector<gdb_signal> signals_to_be_caught;
- /* If SIGNALS_TO_BE_CAUGHT is empty, then all "ordinary" signals are
- caught. If CATCH_ALL is true, then internal signals are caught
- as well. If SIGNALS_TO_BE_CAUGHT is not empty, then this field
- is ignored. */
- bool catch_all;
- };
- /* The breakpoint_ops structure to be used in signal catchpoints. */
- static struct breakpoint_ops signal_catchpoint_ops;
- /* Count of each signal. */
- static unsigned int signal_catch_counts[GDB_SIGNAL_LAST];
- /* A convenience wrapper for gdb_signal_to_name that returns the
- integer value if the name is not known. */
- static const char *
- signal_to_name_or_int (enum gdb_signal sig)
- {
- const char *result = gdb_signal_to_name (sig);
- if (strcmp (result, "?") == 0)
- result = plongest (sig);
- return result;
- }
- /* Implement the "insert_location" breakpoint_ops method for signal
- catchpoints. */
- static int
- signal_catchpoint_insert_location (struct bp_location *bl)
- {
- struct signal_catchpoint *c = (struct signal_catchpoint *) bl->owner;
- if (!c->signals_to_be_caught.empty ())
- {
- for (gdb_signal iter : c->signals_to_be_caught)
- ++signal_catch_counts[iter];
- }
- else
- {
- for (int i = 0; i < GDB_SIGNAL_LAST; ++i)
- {
- if (c->catch_all || !INTERNAL_SIGNAL (i))
- ++signal_catch_counts[i];
- }
- }
- signal_catch_update (signal_catch_counts);
- return 0;
- }
- /* Implement the "remove_location" breakpoint_ops method for signal
- catchpoints. */
- static int
- signal_catchpoint_remove_location (struct bp_location *bl,
- enum remove_bp_reason reason)
- {
- struct signal_catchpoint *c = (struct signal_catchpoint *) bl->owner;
- if (!c->signals_to_be_caught.empty ())
- {
- for (gdb_signal iter : c->signals_to_be_caught)
- {
- gdb_assert (signal_catch_counts[iter] > 0);
- --signal_catch_counts[iter];
- }
- }
- else
- {
- for (int i = 0; i < GDB_SIGNAL_LAST; ++i)
- {
- if (c->catch_all || !INTERNAL_SIGNAL (i))
- {
- gdb_assert (signal_catch_counts[i] > 0);
- --signal_catch_counts[i];
- }
- }
- }
- signal_catch_update (signal_catch_counts);
- return 0;
- }
- /* Implement the "breakpoint_hit" breakpoint_ops method for signal
- catchpoints. */
- static int
- signal_catchpoint_breakpoint_hit (const struct bp_location *bl,
- const address_space *aspace,
- CORE_ADDR bp_addr,
- const target_waitstatus &ws)
- {
- const struct signal_catchpoint *c
- = (const struct signal_catchpoint *) bl->owner;
- gdb_signal signal_number;
- if (ws.kind () != TARGET_WAITKIND_STOPPED)
- return 0;
- signal_number = ws.sig ();
- /* If we are catching specific signals in this breakpoint, then we
- must guarantee that the called signal is the same signal we are
- catching. */
- if (!c->signals_to_be_caught.empty ())
- {
- for (gdb_signal iter : c->signals_to_be_caught)
- if (signal_number == iter)
- return 1;
- /* Not the same. */
- return 0;
- }
- else
- return c->catch_all || !INTERNAL_SIGNAL (signal_number);
- }
- /* Implement the "print_it" breakpoint_ops method for signal
- catchpoints. */
- static enum print_stop_action
- signal_catchpoint_print_it (bpstat *bs)
- {
- struct breakpoint *b = bs->breakpoint_at;
- struct target_waitstatus last;
- const char *signal_name;
- struct ui_out *uiout = current_uiout;
- get_last_target_status (nullptr, nullptr, &last);
- signal_name = signal_to_name_or_int (last.sig ());
- annotate_catchpoint (b->number);
- maybe_print_thread_hit_breakpoint (uiout);
- gdb_printf (_("Catchpoint %d (signal %s), "), b->number, signal_name);
- return PRINT_SRC_AND_LOC;
- }
- /* Implement the "print_one" breakpoint_ops method for signal
- catchpoints. */
- static void
- signal_catchpoint_print_one (struct breakpoint *b,
- struct bp_location **last_loc)
- {
- struct signal_catchpoint *c = (struct signal_catchpoint *) b;
- struct value_print_options opts;
- struct ui_out *uiout = current_uiout;
- 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->signals_to_be_caught.size () > 1)
- uiout->text ("signals \"");
- else
- uiout->text ("signal \"");
- if (!c->signals_to_be_caught.empty ())
- {
- std::string text;
- bool first = true;
- for (gdb_signal iter : c->signals_to_be_caught)
- {
- const char *name = signal_to_name_or_int (iter);
- if (!first)
- text += " ";
- first = false;
- text += name;
- }
- uiout->field_string ("what", text);
- }
- else
- uiout->field_string ("what",
- c->catch_all ? "<any signal>" : "<standard signals>",
- metadata_style.style ());
- uiout->text ("\" ");
- if (uiout->is_mi_like_p ())
- uiout->field_string ("catch-type", "signal");
- }
- /* Implement the "print_mention" breakpoint_ops method for signal
- catchpoints. */
- static void
- signal_catchpoint_print_mention (struct breakpoint *b)
- {
- struct signal_catchpoint *c = (struct signal_catchpoint *) b;
- if (!c->signals_to_be_caught.empty ())
- {
- if (c->signals_to_be_caught.size () > 1)
- gdb_printf (_("Catchpoint %d (signals"), b->number);
- else
- gdb_printf (_("Catchpoint %d (signal"), b->number);
- for (gdb_signal iter : c->signals_to_be_caught)
- {
- const char *name = signal_to_name_or_int (iter);
- gdb_printf (" %s", name);
- }
- gdb_printf (")");
- }
- else if (c->catch_all)
- gdb_printf (_("Catchpoint %d (any signal)"), b->number);
- else
- gdb_printf (_("Catchpoint %d (standard signals)"), b->number);
- }
- /* Implement the "print_recreate" breakpoint_ops method for signal
- catchpoints. */
- static void
- signal_catchpoint_print_recreate (struct breakpoint *b, struct ui_file *fp)
- {
- struct signal_catchpoint *c = (struct signal_catchpoint *) b;
- gdb_printf (fp, "catch signal");
- if (!c->signals_to_be_caught.empty ())
- {
- for (gdb_signal iter : c->signals_to_be_caught)
- gdb_printf (fp, " %s", signal_to_name_or_int (iter));
- }
- else if (c->catch_all)
- gdb_printf (fp, " all");
- gdb_putc ('\n', fp);
- }
- /* Implement the "explains_signal" breakpoint_ops method for signal
- catchpoints. */
- static int
- signal_catchpoint_explains_signal (struct breakpoint *b, enum gdb_signal sig)
- {
- return 1;
- }
- /* Create a new signal catchpoint. TEMPFLAG is true if this should be
- a temporary catchpoint. FILTER is the list of signals to catch; it
- can be empty, meaning all signals. CATCH_ALL is a flag indicating
- whether signals used internally by gdb should be caught; it is only
- valid if FILTER is NULL. If FILTER is empty and CATCH_ALL is zero,
- then internal signals like SIGTRAP are not caught. */
- static void
- create_signal_catchpoint (int tempflag, std::vector<gdb_signal> &&filter,
- bool catch_all)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- std::unique_ptr<signal_catchpoint> c (new signal_catchpoint ());
- init_catchpoint (c.get (), gdbarch, tempflag, NULL, &signal_catchpoint_ops);
- c->signals_to_be_caught = std::move (filter);
- c->catch_all = catch_all;
- install_breakpoint (0, std::move (c), 1);
- }
- /* Splits the argument using space as delimiter. Returns a filter
- list, which is empty if no filtering is required. */
- static std::vector<gdb_signal>
- catch_signal_split_args (const char *arg, bool *catch_all)
- {
- std::vector<gdb_signal> result;
- bool first = true;
- while (*arg != '\0')
- {
- int num;
- gdb_signal signal_number;
- char *endptr;
- std::string one_arg = extract_arg (&arg);
- if (one_arg.empty ())
- break;
- /* Check for the special flag "all". */
- if (one_arg == "all")
- {
- arg = skip_spaces (arg);
- if (*arg != '\0' || !first)
- error (_("'all' cannot be caught with other signals"));
- *catch_all = true;
- gdb_assert (result.empty ());
- return result;
- }
- first = false;
- /* Check if the user provided a signal name or a number. */
- num = (int) strtol (one_arg.c_str (), &endptr, 0);
- if (*endptr == '\0')
- signal_number = gdb_signal_from_command (num);
- else
- {
- signal_number = gdb_signal_from_name (one_arg.c_str ());
- if (signal_number == GDB_SIGNAL_UNKNOWN)
- error (_("Unknown signal name '%s'."), one_arg.c_str ());
- }
- result.push_back (signal_number);
- }
- result.shrink_to_fit ();
- return result;
- }
- /* Implement the "catch signal" command. */
- static void
- catch_signal_command (const char *arg, int from_tty,
- struct cmd_list_element *command)
- {
- int tempflag;
- bool catch_all = false;
- std::vector<gdb_signal> filter;
- tempflag = command->context () == CATCH_TEMPORARY;
- arg = skip_spaces (arg);
- /* The allowed syntax is:
- catch signal
- catch signal <name | number> [<name | number> ... <name | number>]
- Let's check if there's a signal name. */
- if (arg != NULL)
- filter = catch_signal_split_args (arg, &catch_all);
- create_signal_catchpoint (tempflag, std::move (filter), catch_all);
- }
- static void
- initialize_signal_catchpoint_ops (void)
- {
- struct breakpoint_ops *ops;
- initialize_breakpoint_ops ();
- ops = &signal_catchpoint_ops;
- *ops = base_breakpoint_ops;
- ops->insert_location = signal_catchpoint_insert_location;
- ops->remove_location = signal_catchpoint_remove_location;
- ops->breakpoint_hit = signal_catchpoint_breakpoint_hit;
- ops->print_it = signal_catchpoint_print_it;
- ops->print_one = signal_catchpoint_print_one;
- ops->print_mention = signal_catchpoint_print_mention;
- ops->print_recreate = signal_catchpoint_print_recreate;
- ops->explains_signal = signal_catchpoint_explains_signal;
- }
- void _initialize_break_catch_sig ();
- void
- _initialize_break_catch_sig ()
- {
- initialize_signal_catchpoint_ops ();
- add_catch_command ("signal", _("\
- Catch signals by their names and/or numbers.\n\
- Usage: catch signal [[NAME|NUMBER] [NAME|NUMBER]...|all]\n\
- Arguments say which signals to catch. If no arguments\n\
- are given, every \"normal\" signal will be caught.\n\
- The argument \"all\" means to also catch signals used by GDB.\n\
- Arguments, if given, should be one or more signal names\n\
- (if your system supports that), or signal numbers."),
- catch_signal_command,
- signal_completer,
- CATCH_PERMANENT,
- CATCH_TEMPORARY);
- }
|