/* Everything about vfork catchpoints, for GDB. Copyright (C) 1986-2023 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 . */ #include "defs.h" #include "annotate.h" #include "arch-utils.h" #include "breakpoint.h" #include "cli/cli-decode.h" #include "inferior.h" #include "mi/mi-common.h" #include "target.h" #include "valprint.h" /* An instance of this type is used to represent a fork or vfork catchpoint. A breakpoint is really of this type iff its ops pointer points to CATCH_FORK_BREAKPOINT_OPS. */ struct fork_catchpoint : public catchpoint { fork_catchpoint (struct gdbarch *gdbarch, bool temp, const char *cond_string, bool is_vfork_) : catchpoint (gdbarch, temp, cond_string), is_vfork (is_vfork_) { } 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 **) const override; void print_mention () const override; void print_recreate (struct ui_file *fp) const override; /* True if the breakpoint is for vfork, false for fork. */ bool is_vfork; /* Process id of a child process whose forking triggered this catchpoint. This field is only valid immediately after this catchpoint has triggered. */ ptid_t forked_inferior_pid = null_ptid; }; /* Implement the "insert" method for fork catchpoints. */ int fork_catchpoint::insert_location (struct bp_location *bl) { if (is_vfork) return target_insert_vfork_catchpoint (inferior_ptid.pid ()); else return target_insert_fork_catchpoint (inferior_ptid.pid ()); } /* Implement the "remove" method for fork catchpoints. */ int fork_catchpoint::remove_location (struct bp_location *bl, enum remove_bp_reason reason) { if (is_vfork) return target_remove_vfork_catchpoint (inferior_ptid.pid ()); else return target_remove_fork_catchpoint (inferior_ptid.pid ()); } /* Implement the "breakpoint_hit" method for fork catchpoints. */ int fork_catchpoint::breakpoint_hit (const struct bp_location *bl, const address_space *aspace, CORE_ADDR bp_addr, const target_waitstatus &ws) { if (ws.kind () != (is_vfork ? TARGET_WAITKIND_VFORKED : TARGET_WAITKIND_FORKED)) return 0; forked_inferior_pid = ws.child_ptid (); return 1; } /* Implement the "print_it" method for fork catchpoints. */ enum print_stop_action fork_catchpoint::print_it (const bpstat *bs) const { struct ui_out *uiout = current_uiout; annotate_catchpoint (number); maybe_print_thread_hit_breakpoint (uiout); if (disposition == disp_del) uiout->text ("Temporary catchpoint "); else uiout->text ("Catchpoint "); if (uiout->is_mi_like_p ()) { uiout->field_string ("reason", async_reason_lookup (is_vfork ? EXEC_ASYNC_VFORK : EXEC_ASYNC_FORK)); uiout->field_string ("disp", bpdisp_text (disposition)); } uiout->field_signed ("bkptno", number); if (is_vfork) uiout->text (" (vforked process "); else uiout->text (" (forked process "); uiout->field_signed ("newpid", forked_inferior_pid.pid ()); uiout->text ("), "); return PRINT_SRC_AND_LOC; } /* Implement the "print_one" method for fork catchpoints. */ bool fork_catchpoint::print_one (bp_location **last_loc) const { 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); const char *name = is_vfork ? "vfork" : "fork"; uiout->text (name); if (forked_inferior_pid != null_ptid) { uiout->text (", process "); uiout->field_signed ("what", forked_inferior_pid.pid ()); uiout->spaces (1); } if (uiout->is_mi_like_p ()) uiout->field_string ("catch-type", name); return true; } /* Implement the "print_mention" method for fork catchpoints. */ void fork_catchpoint::print_mention () const { gdb_printf (_("Catchpoint %d (%s)"), number, is_vfork ? "vfork" : "fork"); } /* Implement the "print_recreate" method for fork catchpoints. */ void fork_catchpoint::print_recreate (struct ui_file *fp) const { gdb_printf (fp, "catch %s", is_vfork ? "vfork" : "fork"); print_recreate_thread (fp); } static void create_fork_vfork_event_catchpoint (struct gdbarch *gdbarch, bool temp, const char *cond_string, bool is_vfork) { std::unique_ptr c (new fork_catchpoint (gdbarch, temp, cond_string, is_vfork)); install_breakpoint (0, std::move (c), 1); } enum catch_fork_kind { catch_fork_temporary, catch_vfork_temporary, catch_fork_permanent, catch_vfork_permanent }; static void catch_fork_command_1 (const char *arg, int from_tty, struct cmd_list_element *command) { struct gdbarch *gdbarch = get_current_arch (); const char *cond_string = NULL; catch_fork_kind fork_kind; fork_kind = (catch_fork_kind) (uintptr_t) command->context (); bool temp = (fork_kind == catch_fork_temporary || fork_kind == catch_vfork_temporary); if (!arg) arg = ""; arg = skip_spaces (arg); /* The allowed syntax is: catch [v]fork catch [v]fork if First, check if there's an if clause. */ cond_string = ep_parse_optional_if_clause (&arg); if ((*arg != '\0') && !isspace (*arg)) error (_("Junk at end of arguments.")); /* If this target supports it, create a fork or vfork catchpoint and enable reporting of such events. */ switch (fork_kind) { case catch_fork_temporary: case catch_fork_permanent: create_fork_vfork_event_catchpoint (gdbarch, temp, cond_string, false); break; case catch_vfork_temporary: case catch_vfork_permanent: create_fork_vfork_event_catchpoint (gdbarch, temp, cond_string, true); break; default: error (_("unsupported or unknown fork kind; cannot catch it")); break; } } void _initialize_break_catch_fork (); void _initialize_break_catch_fork () { 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); }