| /* Interface between gdb and its extension languages. |
| |
| Copyright (C) 2014-2024 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/>. */ |
| |
| /* Note: With few exceptions, external functions and variables in this file |
| have "ext_lang" in the name, and no other symbol in gdb does. */ |
| |
| #include <signal.h> |
| #include "target.h" |
| #include "auto-load.h" |
| #include "breakpoint.h" |
| #include "event-top.h" |
| #include "extension.h" |
| #include "extension-priv.h" |
| #include "observable.h" |
| #include "cli/cli-script.h" |
| #include "python/python.h" |
| #include "guile/guile.h" |
| #include <array> |
| #include "inferior.h" |
| |
| static script_sourcer_func source_gdb_script; |
| static objfile_script_sourcer_func source_gdb_objfile_script; |
| |
| /* GDB's own scripting language. |
| This exists, in part, to support auto-loading ${prog}-gdb.gdb scripts. */ |
| |
| static const struct extension_language_script_ops |
| extension_language_gdb_script_ops = |
| { |
| source_gdb_script, |
| source_gdb_objfile_script, |
| NULL, /* objfile_script_executor */ |
| auto_load_gdb_scripts_enabled |
| }; |
| |
| const struct extension_language_defn extension_language_gdb = |
| { |
| EXT_LANG_GDB, |
| "gdb", |
| "GDB", |
| |
| /* We fall back to interpreting a script as a GDB script if it doesn't |
| match the other scripting languages, but for consistency's sake |
| give it a formal suffix. */ |
| ".gdb", |
| "-gdb.gdb", |
| |
| /* cli_control_type: This is never used: GDB's own scripting language |
| has a variety of control types (if, while, etc.). */ |
| commands_control, |
| |
| &extension_language_gdb_script_ops, |
| |
| /* The rest of the extension language interface isn't supported by GDB's own |
| extension/scripting language. */ |
| NULL |
| }; |
| |
| /* NULL-terminated table of all external (non-native) extension languages. |
| |
| The order of appearance in the table is important. |
| When multiple extension languages provide the same feature, for example |
| a pretty-printer for a particular type, which one gets used? |
| The algorithm employed here is "the first one wins". For example, in |
| the case of pretty-printers this means the first one to provide a |
| pretty-printed value is the one that is used. This algorithm is employed |
| throughout. */ |
| |
| static const std::array<const extension_language_defn *, 2> extension_languages |
| { |
| /* To preserve existing behaviour, python should always appear first. */ |
| &extension_language_python, |
| &extension_language_guile, |
| }; |
| |
| /* Return a pointer to the struct extension_language_defn object of |
| extension language LANG. |
| This always returns a non-NULL pointer, even if support for the language |
| is not compiled into this copy of GDB. */ |
| |
| const struct extension_language_defn * |
| get_ext_lang_defn (enum extension_language lang) |
| { |
| gdb_assert (lang != EXT_LANG_NONE); |
| |
| if (lang == EXT_LANG_GDB) |
| return &extension_language_gdb; |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->language == lang) |
| return extlang; |
| } |
| |
| gdb_assert_not_reached ("unable to find extension_language_defn"); |
| } |
| |
| /* Return TRUE if FILE has extension EXTENSION. */ |
| |
| static int |
| has_extension (const char *file, const char *extension) |
| { |
| int file_len = strlen (file); |
| int extension_len = strlen (extension); |
| |
| return (file_len > extension_len |
| && strcmp (&file[file_len - extension_len], extension) == 0); |
| } |
| |
| /* Return the extension language of FILE, or NULL if |
| the extension language of FILE is not recognized. |
| This is done by looking at the file's suffix. */ |
| |
| const struct extension_language_defn * |
| get_ext_lang_of_file (const char *file) |
| { |
| if (has_extension (file, extension_language_gdb.suffix)) |
| return &extension_language_gdb; |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (has_extension (file, extlang->suffix)) |
| return extlang; |
| } |
| |
| return NULL; |
| } |
| |
| /* Return non-zero if support for the specified extension language |
| is compiled in. */ |
| |
| int |
| ext_lang_present_p (const struct extension_language_defn *extlang) |
| { |
| return extlang->script_ops != NULL; |
| } |
| |
| /* Return non-zero if the specified extension language has successfully |
| initialized. */ |
| |
| int |
| ext_lang_initialized_p (const struct extension_language_defn *extlang) |
| { |
| if (extlang->ops != NULL) |
| { |
| /* This method is required. */ |
| gdb_assert (extlang->ops->initialized != NULL); |
| return extlang->ops->initialized (extlang); |
| } |
| |
| return 0; |
| } |
| |
| /* Throw an error indicating EXTLANG is not supported in this copy of GDB. */ |
| |
| void |
| throw_ext_lang_unsupported (const struct extension_language_defn *extlang) |
| { |
| error (_("Scripting in the \"%s\" language is not supported" |
| " in this copy of GDB."), |
| ext_lang_capitalized_name (extlang)); |
| } |
| |
| /* Methods for GDB's own extension/scripting language. */ |
| |
| /* The extension_language_script_ops.script_sourcer "method". */ |
| |
| static void |
| source_gdb_script (const struct extension_language_defn *extlang, |
| FILE *stream, const char *file) |
| { |
| script_from_file (stream, file); |
| } |
| |
| /* The extension_language_script_ops.objfile_script_sourcer "method". */ |
| |
| static void |
| source_gdb_objfile_script (const struct extension_language_defn *extlang, |
| struct objfile *objfile, |
| FILE *stream, const char *file) |
| { |
| script_from_file (stream, file); |
| } |
| |
| /* Accessors for "public" attributes of struct extension_language. */ |
| |
| /* Return the "name" field of EXTLANG. */ |
| |
| const char * |
| ext_lang_name (const struct extension_language_defn *extlang) |
| { |
| return extlang->name; |
| } |
| |
| /* Return the "capitalized_name" field of EXTLANG. */ |
| |
| const char * |
| ext_lang_capitalized_name (const struct extension_language_defn *extlang) |
| { |
| return extlang->capitalized_name; |
| } |
| |
| /* Return the "suffix" field of EXTLANG. */ |
| |
| const char * |
| ext_lang_suffix (const struct extension_language_defn *extlang) |
| { |
| return extlang->suffix; |
| } |
| |
| /* Return the "auto_load_suffix" field of EXTLANG. */ |
| |
| const char * |
| ext_lang_auto_load_suffix (const struct extension_language_defn *extlang) |
| { |
| return extlang->auto_load_suffix; |
| } |
| |
| /* extension_language_script_ops wrappers. */ |
| |
| /* Return the script "sourcer" function for EXTLANG. |
| This is the function that loads and processes a script. |
| If support for this language isn't compiled in, NULL is returned. */ |
| |
| script_sourcer_func * |
| ext_lang_script_sourcer (const struct extension_language_defn *extlang) |
| { |
| if (extlang->script_ops == NULL) |
| return NULL; |
| |
| /* The extension language is required to implement this function. */ |
| gdb_assert (extlang->script_ops->script_sourcer != NULL); |
| |
| return extlang->script_ops->script_sourcer; |
| } |
| |
| /* Return the objfile script "sourcer" function for EXTLANG. |
| This is the function that loads and processes a script for a particular |
| objfile. |
| If support for this language isn't compiled in, NULL is returned. */ |
| |
| objfile_script_sourcer_func * |
| ext_lang_objfile_script_sourcer (const struct extension_language_defn *extlang) |
| { |
| if (extlang->script_ops == NULL) |
| return NULL; |
| |
| /* The extension language is required to implement this function. */ |
| gdb_assert (extlang->script_ops->objfile_script_sourcer != NULL); |
| |
| return extlang->script_ops->objfile_script_sourcer; |
| } |
| |
| /* Return the objfile script "executor" function for EXTLANG. |
| This is the function that executes a script for a particular objfile. |
| If support for this language isn't compiled in, NULL is returned. |
| The extension language is not required to implement this function. */ |
| |
| objfile_script_executor_func * |
| ext_lang_objfile_script_executor |
| (const struct extension_language_defn *extlang) |
| { |
| if (extlang->script_ops == NULL) |
| return NULL; |
| |
| return extlang->script_ops->objfile_script_executor; |
| } |
| |
| /* See extension.h. */ |
| |
| bool |
| ext_lang_auto_load_enabled (const struct extension_language_defn *extlang) |
| { |
| if (extlang->script_ops == NULL) |
| return false; |
| |
| /* The extension language is required to implement this function. */ |
| gdb_assert (extlang->script_ops->auto_load_enabled != NULL); |
| |
| return extlang->script_ops->auto_load_enabled (extlang); |
| } |
| |
| |
| /* RAII class used to temporarily return SIG to its default handler. */ |
| |
| template<int SIG> |
| struct scoped_default_signal |
| { |
| scoped_default_signal () |
| { m_old_sig_handler = signal (SIG, SIG_DFL); } |
| |
| ~scoped_default_signal () |
| { signal (SIG, m_old_sig_handler); } |
| |
| DISABLE_COPY_AND_ASSIGN (scoped_default_signal); |
| |
| private: |
| /* The previous signal handler that needs to be restored. */ |
| sighandler_t m_old_sig_handler; |
| }; |
| |
| /* Class to temporarily return SIGINT to its default handler. */ |
| |
| using scoped_default_sigint = scoped_default_signal<SIGINT>; |
| |
| /* Functions that iterate over all extension languages. |
| These only iterate over external extension languages, not including |
| GDB's own extension/scripting language, unless otherwise indicated. */ |
| |
| /* Wrapper to call the extension_language_ops.initialize "method" for each |
| compiled-in extension language. */ |
| |
| void |
| ext_lang_initialization (void) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->ops->initialize != NULL) |
| { |
| scoped_default_sigint set_sigint_to_default_handler; |
| extlang->ops->initialize (extlang); |
| } |
| } |
| } |
| |
| /* See extension.h. */ |
| |
| void |
| ext_lang_shutdown () |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr && extlang->ops->shutdown != nullptr) |
| extlang->ops->shutdown (extlang); |
| } |
| } |
| |
| /* Invoke the appropriate extension_language_ops.eval_from_control_command |
| method to perform CMD, which is a list of commands in an extension language. |
| |
| This function is what implements, for example: |
| |
| python |
| print 42 |
| end |
| |
| in a GDB script. */ |
| |
| void |
| eval_ext_lang_from_control_command (struct command_line *cmd) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->cli_control_type == cmd->control_type) |
| { |
| if (extlang->ops != NULL |
| && extlang->ops->eval_from_control_command != NULL) |
| { |
| extlang->ops->eval_from_control_command (extlang, cmd); |
| return; |
| } |
| /* The requested extension language is not supported in this GDB. */ |
| throw_ext_lang_unsupported (extlang); |
| } |
| } |
| |
| gdb_assert_not_reached ("unknown extension language in command_line"); |
| } |
| |
| /* Search for and load scripts for OBJFILE written in extension languages. |
| This includes GDB's own scripting language. |
| |
| This function is what implements the loading of OBJFILE-gdb.py and |
| OBJFILE-gdb.gdb. */ |
| |
| void |
| auto_load_ext_lang_scripts_for_objfile (struct objfile *objfile) |
| { |
| const struct extension_language_defn *gdb = &extension_language_gdb; |
| if (ext_lang_auto_load_enabled (gdb)) |
| auto_load_objfile_script (objfile, gdb); |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && ext_lang_auto_load_enabled (extlang)) |
| auto_load_objfile_script (objfile, extlang); |
| } |
| } |
| |
| /* Interface to type pretty-printers implemented in an extension language. */ |
| |
| /* Call this at the start when preparing to pretty-print a type. |
| The result is a pointer to an opaque object (to the caller) to be passed |
| to apply_ext_lang_type_printers and free_ext_lang_type_printers. |
| |
| We don't know in advance which extension language will provide a |
| pretty-printer for the type, so all are initialized. */ |
| |
| ext_lang_type_printers::ext_lang_type_printers () |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->ops->start_type_printers != NULL) |
| extlang->ops->start_type_printers (extlang, this); |
| } |
| } |
| |
| /* Iteratively try the type pretty-printers specified by PRINTERS |
| according to the standard search order (specified by extension_languages), |
| returning the result of the first one that succeeds. |
| If there was an error, or if no printer succeeds, then NULL is returned. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| apply_ext_lang_type_printers (struct ext_lang_type_printers *printers, |
| struct type *type) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| gdb::unique_xmalloc_ptr<char> result; |
| enum ext_lang_rc rc; |
| |
| if (extlang->ops == nullptr |
| || extlang->ops->apply_type_printers == NULL) |
| continue; |
| rc = extlang->ops->apply_type_printers (extlang, printers, type, |
| &result); |
| switch (rc) |
| { |
| case EXT_LANG_RC_OK: |
| gdb_assert (result != nullptr); |
| return result; |
| case EXT_LANG_RC_ERROR: |
| return NULL; |
| case EXT_LANG_RC_NOP: |
| break; |
| default: |
| gdb_assert_not_reached ("bad return from apply_type_printers"); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| ext_lang_type_printers::~ext_lang_type_printers () |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->ops->free_type_printers != NULL) |
| extlang->ops->free_type_printers (extlang, this); |
| } |
| } |
| |
| /* Try to pretty-print a value onto stdio stream STREAM according to |
| OPTIONS. VAL is the object to print. Returns non-zero if the |
| value was successfully pretty-printed. |
| |
| Extension languages are tried in the order specified by |
| extension_languages. The first one to provide a pretty-printed |
| value "wins". |
| |
| If an error is encountered in a pretty-printer, no further extension |
| languages are tried. |
| Note: This is different than encountering a memory error trying to read a |
| value for pretty-printing. Here we're referring to, e.g., programming |
| errors that trigger an exception in the extension language. */ |
| |
| int |
| apply_ext_lang_val_pretty_printer (struct value *val, |
| struct ui_file *stream, int recurse, |
| const struct value_print_options *options, |
| const struct language_defn *language) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| enum ext_lang_rc rc; |
| |
| if (extlang->ops == nullptr |
| || extlang->ops->apply_val_pretty_printer == NULL) |
| continue; |
| rc = extlang->ops->apply_val_pretty_printer (extlang, val, stream, |
| recurse, options, language); |
| switch (rc) |
| { |
| case EXT_LANG_RC_OK: |
| return 1; |
| case EXT_LANG_RC_ERROR: |
| return 0; |
| case EXT_LANG_RC_NOP: |
| break; |
| default: |
| gdb_assert_not_reached ("bad return from apply_val_pretty_printer"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* GDB access to the "frame filter" feature. |
| FRAME is the source frame to start frame-filter invocation. FLAGS is an |
| integer holding the flags for printing. The following elements of |
| the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS: |
| PRINT_LEVEL is a flag indicating whether to print the frame's |
| relative level in the output. PRINT_FRAME_INFO is a flag that |
| indicates whether this function should print the frame |
| information, PRINT_ARGS is a flag that indicates whether to print |
| frame arguments, and PRINT_LOCALS, likewise, with frame local |
| variables. ARGS_TYPE is an enumerator describing the argument |
| format, OUT is the output stream to print. FRAME_LOW is the |
| beginning of the slice of frames to print, and FRAME_HIGH is the |
| upper limit of the frames to count. Returns EXT_LANG_BT_ERROR on error, |
| or EXT_LANG_BT_COMPLETED on success. |
| |
| Extension languages are tried in the order specified by |
| extension_languages. The first one to provide a filter "wins". |
| If there is an error (EXT_LANG_BT_ERROR) it is reported immediately |
| rather than trying filters in other extension languages. */ |
| |
| enum ext_lang_bt_status |
| apply_ext_lang_frame_filter (const frame_info_ptr &frame, |
| frame_filter_flags flags, |
| enum ext_lang_frame_args args_type, |
| struct ui_out *out, |
| int frame_low, int frame_high) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| enum ext_lang_bt_status status; |
| |
| if (extlang->ops == nullptr |
| || extlang->ops->apply_frame_filter == NULL) |
| continue; |
| status = extlang->ops->apply_frame_filter (extlang, frame, flags, |
| args_type, out, |
| frame_low, frame_high); |
| /* We use the filters from the first extension language that has |
| applicable filters. Also, an error is reported immediately |
| rather than continue trying. */ |
| if (status != EXT_LANG_BT_NO_FILTERS) |
| return status; |
| } |
| |
| return EXT_LANG_BT_NO_FILTERS; |
| } |
| |
| /* Used for registering the ptwrite filter to the current thread. */ |
| |
| void |
| apply_ext_lang_ptwrite_filter (btrace_thread_info *btinfo) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->ops->apply_ptwrite_filter != nullptr) |
| extlang->ops->apply_ptwrite_filter (extlang, btinfo); |
| } |
| } |
| |
| /* Update values held by the extension language when OBJFILE is discarded. |
| New global types must be created for every such value, which must then be |
| updated to use the new types. |
| The function typically just iterates over all appropriate values and |
| calls preserve_one_value for each one. |
| COPIED_TYPES is used to prevent cycles / duplicates and is passed to |
| preserve_one_value. */ |
| |
| void |
| preserve_ext_lang_values (struct objfile *objfile, htab_t copied_types) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->ops->preserve_values != NULL) |
| extlang->ops->preserve_values (extlang, objfile, copied_types); |
| } |
| } |
| |
| /* If there is a stop condition implemented in an extension language for |
| breakpoint B, return a pointer to the extension language's definition. |
| Otherwise return NULL. |
| If SKIP_LANG is not EXT_LANG_NONE, skip checking this language. |
| This is for the case where we're setting a new condition: Only one |
| condition is allowed, so when setting a condition for any particular |
| extension language, we need to check if any other extension language |
| already has a condition set. */ |
| |
| const struct extension_language_defn * |
| get_breakpoint_cond_ext_lang (struct breakpoint *b, |
| enum extension_language skip_lang) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->language != skip_lang |
| && extlang->ops->breakpoint_has_cond != NULL |
| && extlang->ops->breakpoint_has_cond (extlang, b)) |
| return extlang; |
| } |
| |
| return NULL; |
| } |
| |
| /* Return whether a stop condition for breakpoint B says to stop. |
| True is also returned if there is no stop condition for B. */ |
| |
| bool |
| breakpoint_ext_lang_cond_says_stop (struct breakpoint *b) |
| { |
| enum ext_lang_bp_stop stop = EXT_LANG_BP_STOP_UNSET; |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| /* There is a rule that a breakpoint can have at most one of any of a |
| CLI or extension language condition. However, Python hacks in "finish |
| breakpoints" on top of the "stop" check, so we have to call this for |
| every language, even if we could first determine whether a "stop" |
| method exists. */ |
| if (extlang->ops != nullptr |
| && extlang->ops->breakpoint_cond_says_stop != NULL) |
| { |
| enum ext_lang_bp_stop this_stop |
| = extlang->ops->breakpoint_cond_says_stop (extlang, b); |
| |
| if (this_stop != EXT_LANG_BP_STOP_UNSET) |
| { |
| /* Even though we have to check every extension language, only |
| one of them can return yes/no (because only one of them |
| can have a "stop" condition). */ |
| gdb_assert (stop == EXT_LANG_BP_STOP_UNSET); |
| stop = this_stop; |
| } |
| } |
| } |
| |
| return stop != EXT_LANG_BP_STOP_NO; |
| } |
| |
| /* ^C/SIGINT support. |
| This requires cooperation with the extension languages so the support |
| is defined here. */ |
| |
| #if CXX_STD_THREAD |
| |
| #include <mutex> |
| |
| /* DAP needs a way to interrupt the main thread, so we added |
| gdb.interrupt. However, as this can run from any thread, we need |
| locking for the current extension language. If threading is not |
| available, DAP will not start. |
| |
| This lock is held for accesses to quit_flag, active_ext_lang, and |
| cooperative_sigint_handling_disabled. */ |
| static std::recursive_mutex ext_lang_mutex; |
| |
| #endif /* CXX_STD_THREAD */ |
| |
| /* This flag tracks quit requests when we haven't called out to an |
| extension language. it also holds quit requests when we transition to |
| an extension language that doesn't have cooperative SIGINT handling. */ |
| static bool quit_flag; |
| |
| /* The current extension language we've called out to, or |
| extension_language_gdb if there isn't one. |
| This must be set everytime we call out to an extension language, and reset |
| to the previous value when it returns. Note that the previous value may |
| be a different (or the same) extension language. */ |
| static const struct extension_language_defn *active_ext_lang |
| = &extension_language_gdb; |
| |
| /* Install a SIGINT handler. */ |
| |
| static void |
| install_ext_sigint_handler (const struct signal_handler *handler_state) |
| { |
| gdb_assert (handler_state->handler_saved); |
| |
| install_sigint_handler (handler_state->handler); |
| } |
| |
| /* Install GDB's SIGINT handler, storing the previous version in *PREVIOUS. |
| As a simple optimization, if the previous version was GDB's SIGINT handler |
| then mark the previous handler as not having been saved, and thus it won't |
| be restored. */ |
| |
| static void |
| install_gdb_sigint_handler (struct signal_handler *previous) |
| { |
| /* Save here to simplify comparison. */ |
| sighandler_t handle_sigint_for_compare = handle_sigint; |
| |
| previous->handler = install_sigint_handler (handle_sigint); |
| if (previous->handler != handle_sigint_for_compare) |
| previous->handler_saved = 1; |
| else |
| previous->handler_saved = 0; |
| } |
| |
| #if GDB_SELF_TEST |
| namespace selftests { |
| void (*hook_set_active_ext_lang) () = nullptr; |
| } |
| #endif |
| |
| /* True if cooperative SIGINT handling is disabled. This is needed so |
| that calls to set_active_ext_lang do not re-enable cooperative |
| handling, which if enabled would make set_quit_flag store the |
| SIGINT in an extension language. */ |
| static bool cooperative_sigint_handling_disabled = false; |
| |
| scoped_disable_cooperative_sigint_handling::scoped_disable_cooperative_sigint_handling () |
| { |
| #if CXX_STD_THREAD |
| std::lock_guard guard (ext_lang_mutex); |
| #endif /* CXX_STD_THREAD */ |
| |
| /* Force the active extension language to the GDB scripting |
| language. This ensures that a previously saved SIGINT is moved |
| to the quit_flag global, as well as ensures that future SIGINTs |
| are also saved in the global. */ |
| m_prev_active_ext_lang_state |
| = set_active_ext_lang (&extension_language_gdb); |
| |
| /* Set the "cooperative SIGINT handling disabled" global flag, so |
| that a future call to set_active_ext_lang does not re-enable |
| cooperative mode. */ |
| m_prev_cooperative_sigint_handling_disabled |
| = cooperative_sigint_handling_disabled; |
| cooperative_sigint_handling_disabled = true; |
| } |
| |
| scoped_disable_cooperative_sigint_handling::~scoped_disable_cooperative_sigint_handling () |
| { |
| #if CXX_STD_THREAD |
| std::lock_guard guard (ext_lang_mutex); |
| #endif /* CXX_STD_THREAD */ |
| |
| cooperative_sigint_handling_disabled = m_prev_cooperative_sigint_handling_disabled; |
| restore_active_ext_lang (m_prev_active_ext_lang_state); |
| } |
| |
| /* Set the currently active extension language to NOW_ACTIVE. |
| The result is a pointer to a malloc'd block of memory to pass to |
| restore_active_ext_lang. |
| |
| N.B. This function must be called every time we call out to an extension |
| language, and the result must be passed to restore_active_ext_lang |
| afterwards. |
| |
| If there is a pending SIGINT it is "moved" to the now active extension |
| language, if it supports cooperative SIGINT handling (i.e., it provides |
| {clear,set,check}_quit_flag methods). If the extension language does not |
| support cooperative SIGINT handling, then the SIGINT is left queued and |
| we require the non-cooperative extension language to call check_quit_flag |
| at appropriate times. |
| It is important for the extension language to call check_quit_flag if it |
| installs its own SIGINT handler to prevent the situation where a SIGINT |
| is queued on entry, extension language code runs for a "long" time possibly |
| serving one or more SIGINTs, and then returns. Upon return, if |
| check_quit_flag is not called, the original SIGINT will be thrown. |
| Non-cooperative extension languages are free to install their own SIGINT |
| handler but the original must be restored upon return, either itself |
| or via restore_active_ext_lang. |
| |
| If cooperative SIGINT handling is force-disabled (e.g., we're in |
| the middle of handling an inferior event), then we don't actually |
| record NOW_ACTIVE as the current active extension language, so that |
| set_quit_flag saves the SIGINT in the global quit flag instead of |
| in the extension language. The caller does not need to concern |
| itself about this, though. The currently active extension language |
| concept only exists for cooperative SIGINT handling. */ |
| |
| struct active_ext_lang_state * |
| set_active_ext_lang (const struct extension_language_defn *now_active) |
| { |
| #if CXX_STD_THREAD |
| std::lock_guard guard (ext_lang_mutex); |
| #endif /* CXX_STD_THREAD */ |
| |
| #if GDB_SELF_TEST |
| if (selftests::hook_set_active_ext_lang) |
| selftests::hook_set_active_ext_lang (); |
| #endif |
| |
| /* If cooperative SIGINT handling was previously force-disabled, |
| make sure to not re-enable it (as NOW_ACTIVE could be a language |
| that supports cooperative SIGINT handling). */ |
| if (cooperative_sigint_handling_disabled) |
| { |
| /* Ensure set_quit_flag saves SIGINT in the quit_flag |
| global. */ |
| gdb_assert (active_ext_lang->ops == nullptr |
| || active_ext_lang->ops->check_quit_flag == nullptr); |
| |
| /* The only thing the caller can do with the result is pass it |
| to restore_active_ext_lang, which expects NULL when |
| cooperative SIGINT handling is disabled. */ |
| return nullptr; |
| } |
| |
| struct active_ext_lang_state *previous |
| = XCNEW (struct active_ext_lang_state); |
| |
| previous->ext_lang = active_ext_lang; |
| previous->sigint_handler.handler_saved = 0; |
| active_ext_lang = now_active; |
| |
| if (target_terminal::is_ours ()) |
| { |
| /* If the newly active extension language uses cooperative SIGINT |
| handling then ensure GDB's SIGINT handler is installed. */ |
| if (now_active->language == EXT_LANG_GDB |
| || now_active->ops->check_quit_flag != NULL) |
| install_gdb_sigint_handler (&previous->sigint_handler); |
| |
| /* If there's a SIGINT recorded in the cooperative extension languages, |
| move it to the new language, or save it in GDB's global flag if the |
| newly active extension language doesn't use cooperative SIGINT |
| handling. */ |
| if (check_quit_flag ()) |
| set_quit_flag (); |
| } |
| |
| return previous; |
| } |
| |
| /* Restore active extension language from PREVIOUS. */ |
| |
| void |
| restore_active_ext_lang (struct active_ext_lang_state *previous) |
| { |
| #if CXX_STD_THREAD |
| std::lock_guard guard (ext_lang_mutex); |
| #endif /* CXX_STD_THREAD */ |
| |
| if (cooperative_sigint_handling_disabled) |
| { |
| /* See set_active_ext_lang. */ |
| gdb_assert (previous == nullptr); |
| return; |
| } |
| |
| active_ext_lang = previous->ext_lang; |
| |
| if (target_terminal::is_ours ()) |
| { |
| /* Restore the previous SIGINT handler if one was saved. */ |
| if (previous->sigint_handler.handler_saved) |
| install_ext_sigint_handler (&previous->sigint_handler); |
| |
| /* If there's a SIGINT recorded in the cooperative extension languages, |
| move it to the new language, or save it in GDB's global flag if the |
| newly active extension language doesn't use cooperative SIGINT |
| handling. */ |
| if (check_quit_flag ()) |
| set_quit_flag (); |
| } |
| xfree (previous); |
| } |
| |
| /* See extension.h. */ |
| |
| void |
| set_quit_flag () |
| { |
| #if CXX_STD_THREAD |
| std::lock_guard guard (ext_lang_mutex); |
| #endif /* CXX_STD_THREAD */ |
| |
| if (active_ext_lang->ops != NULL |
| && active_ext_lang->ops->set_quit_flag != NULL) |
| active_ext_lang->ops->set_quit_flag (active_ext_lang); |
| else |
| { |
| quit_flag = true; |
| |
| /* Now wake up the event loop, or any interruptible_select. Do |
| this after setting the flag, because signals on Windows |
| actually run on a separate thread, and thus otherwise the |
| main code could be woken up and find quit_flag still |
| clear. */ |
| quit_serial_event_set (); |
| } |
| } |
| |
| /* See extension.h. */ |
| |
| bool |
| check_quit_flag () |
| { |
| #if CXX_STD_THREAD |
| std::lock_guard guard (ext_lang_mutex); |
| #endif /* CXX_STD_THREAD */ |
| |
| bool result = false; |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops != nullptr |
| && extlang->ops->check_quit_flag != NULL) |
| if (extlang->ops->check_quit_flag (extlang)) |
| result = true; |
| } |
| |
| /* This is written in a particular way to avoid races. */ |
| if (quit_flag) |
| { |
| /* No longer need to wake up the event loop or any |
| interruptible_select. The caller handles the quit |
| request. */ |
| quit_serial_event_clear (); |
| quit_flag = false; |
| result = true; |
| } |
| |
| return result; |
| } |
| |
| /* See extension.h. */ |
| |
| void |
| get_matching_xmethod_workers (struct type *type, const char *method_name, |
| std::vector<xmethod_worker_up> *workers) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| enum ext_lang_rc rc; |
| |
| /* If an extension language does not support xmethods, ignore |
| it. */ |
| if (extlang->ops == nullptr |
| || extlang->ops->get_matching_xmethod_workers == NULL) |
| continue; |
| |
| rc = extlang->ops->get_matching_xmethod_workers (extlang, |
| type, method_name, |
| workers); |
| if (rc == EXT_LANG_RC_ERROR) |
| error (_("Error while looking for matching xmethod workers " |
| "defined in %s."), extlang->capitalized_name); |
| } |
| } |
| |
| /* See extension.h. */ |
| |
| std::vector<type *> |
| xmethod_worker::get_arg_types () |
| { |
| std::vector<type *> type_array; |
| |
| ext_lang_rc rc = do_get_arg_types (&type_array); |
| if (rc == EXT_LANG_RC_ERROR) |
| error (_("Error while looking for arg types of a xmethod worker " |
| "defined in %s."), m_extlang->capitalized_name); |
| |
| return type_array; |
| } |
| |
| /* See extension.h. */ |
| |
| struct type * |
| xmethod_worker::get_result_type (value *object, gdb::array_view<value *> args) |
| { |
| type *result_type; |
| |
| ext_lang_rc rc = do_get_result_type (object, args, &result_type); |
| if (rc == EXT_LANG_RC_ERROR) |
| { |
| error (_("Error while fetching result type of an xmethod worker " |
| "defined in %s."), m_extlang->capitalized_name); |
| } |
| |
| return result_type; |
| } |
| |
| /* See extension.h. */ |
| |
| std::optional<std::string> |
| ext_lang_colorize (const std::string &filename, const std::string &contents) |
| { |
| std::optional<std::string> result; |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops == nullptr |
| || extlang->ops->colorize == nullptr) |
| continue; |
| result = extlang->ops->colorize (filename, contents); |
| if (result.has_value ()) |
| return result; |
| } |
| |
| return result; |
| } |
| |
| /* See extension.h. */ |
| |
| std::optional<std::string> |
| ext_lang_colorize_disasm (const std::string &content, gdbarch *gdbarch) |
| { |
| std::optional<std::string> result; |
| |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops == nullptr |
| || extlang->ops->colorize_disasm == nullptr) |
| continue; |
| result = extlang->ops->colorize_disasm (content, gdbarch); |
| if (result.has_value ()) |
| return result; |
| } |
| |
| return result; |
| } |
| |
| /* See extension.h. */ |
| |
| std::optional<int> |
| ext_lang_print_insn (struct gdbarch *gdbarch, CORE_ADDR address, |
| struct disassemble_info *info) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops == nullptr |
| || extlang->ops->print_insn == nullptr) |
| continue; |
| std::optional<int> length |
| = extlang->ops->print_insn (gdbarch, address, info); |
| if (length.has_value ()) |
| return length; |
| } |
| |
| return {}; |
| } |
| |
| /* See extension.h. */ |
| |
| ext_lang_missing_debuginfo_result |
| ext_lang_handle_missing_debuginfo (struct objfile *objfile) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| if (extlang->ops == nullptr |
| || extlang->ops->handle_missing_debuginfo == nullptr) |
| continue; |
| ext_lang_missing_debuginfo_result result |
| = extlang->ops->handle_missing_debuginfo (extlang, objfile); |
| if (!result.filename ().empty () || result.try_again ()) |
| return result; |
| } |
| |
| return {}; |
| } |
| |
| /* Called via an observer before gdb prints its prompt. |
| Iterate over the extension languages giving them a chance to |
| change the prompt. The first one to change the prompt wins, |
| and no further languages are tried. */ |
| |
| static void |
| ext_lang_before_prompt (const char *current_gdb_prompt) |
| { |
| for (const struct extension_language_defn *extlang : extension_languages) |
| { |
| enum ext_lang_rc rc; |
| |
| if (extlang->ops == nullptr |
| || extlang->ops->before_prompt == NULL) |
| continue; |
| rc = extlang->ops->before_prompt (extlang, current_gdb_prompt); |
| switch (rc) |
| { |
| case EXT_LANG_RC_OK: |
| case EXT_LANG_RC_ERROR: |
| return; |
| case EXT_LANG_RC_NOP: |
| break; |
| default: |
| gdb_assert_not_reached ("bad return from before_prompt"); |
| } |
| } |
| } |
| |
| void _initialize_extension (); |
| void |
| _initialize_extension () |
| { |
| gdb::observers::before_prompt.attach (ext_lang_before_prompt, "extension"); |
| } |