/* Helper routines for C++ support in GDB.
- Copyright (C) 2002-2019 Free Software Foundation, Inc.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
Contributed by MontaVista Software.
#include "gdbsupport/gdb_setjmp.h"
#include "safe-ctype.h"
#include "gdbsupport/selftest.h"
+#include "gdbsupport/gdb-sigmask.h"
+#include <atomic>
+#include "event-top.h"
+#include "run-on-main-thread.h"
#define d_left(dc) (dc)->u.s_binary.left
#define d_right(dc) (dc)->u.s_binary.right
}
/* If the type is a typedef or namespace alias, replace it. */
- if (TYPE_CODE (otype) == TYPE_CODE_TYPEDEF
- || TYPE_CODE (otype) == TYPE_CODE_NAMESPACE)
+ if (otype->code () == TYPE_CODE_TYPEDEF
+ || otype->code () == TYPE_CODE_NAMESPACE)
{
long len;
int is_anon;
If the symbol is typedef and its type name is the same
as the symbol's name, e.g., "typedef struct foo foo;". */
- if (TYPE_NAME (type) != nullptr
- && strcmp (TYPE_NAME (type), name) == 0)
+ if (type->name () != nullptr
+ && strcmp (type->name (), name) == 0)
return 0;
- is_anon = (TYPE_NAME (type) == NULL
- && (TYPE_CODE (type) == TYPE_CODE_ENUM
- || TYPE_CODE (type) == TYPE_CODE_STRUCT
- || TYPE_CODE (type) == TYPE_CODE_UNION));
+ is_anon = (type->name () == NULL
+ && (type->code () == TYPE_CODE_ENUM
+ || type->code () == TYPE_CODE_STRUCT
+ || type->code () == TYPE_CODE_UNION));
if (is_anon)
{
struct type *last = otype;
/* Find the last typedef for the type. */
while (TYPE_TARGET_TYPE (last) != NULL
- && (TYPE_CODE (TYPE_TARGET_TYPE (last))
+ && (TYPE_TARGET_TYPE (last)->code ()
== TYPE_CODE_TYPEDEF))
last = TYPE_TARGET_TYPE (last);
Canonicalize the name again, and store it in the
current node (RET_COMP). */
- std::string canon = cp_canonicalize_string_no_typedefs (name);
+ gdb::unique_xmalloc_ptr<char> canon
+ = cp_canonicalize_string_no_typedefs (name);
- if (!canon.empty ())
+ if (canon != nullptr)
{
/* Copy the canonicalization into the obstack. */
- name = copy_string_to_obstack (&info->obstack, canon.c_str (), &len);
+ name = copy_string_to_obstack (&info->obstack, canon.get (), &len);
}
ret_comp->u.s_name.s = name;
/* Parse STRING and convert it to canonical form, resolving any
typedefs. If parsing fails, or if STRING is already canonical,
- return the empty string. Otherwise return the canonical form. If
+ return nullptr. Otherwise return the canonical form. If
FINDER is not NULL, then type components are passed to FINDER to be
looked up. DATA is passed verbatim to FINDER. */
-std::string
+gdb::unique_xmalloc_ptr<char>
cp_canonicalize_string_full (const char *string,
canonicalization_ftype *finder,
void *data)
{
- std::string ret;
unsigned int estimated_len;
std::unique_ptr<demangle_parse_info> info;
estimated_len);
gdb_assert (us);
- ret = us.get ();
/* Finally, compare the original string with the computed
name, returning NULL if they are the same. */
- if (ret == string)
- return std::string ();
+ if (strcmp (us.get (), string) == 0)
+ return nullptr;
+
+ return us;
}
- return ret;
+ return nullptr;
}
/* Like cp_canonicalize_string_full, but always passes NULL for
FINDER. */
-std::string
+gdb::unique_xmalloc_ptr<char>
cp_canonicalize_string_no_typedefs (const char *string)
{
return cp_canonicalize_string_full (string, NULL, NULL);
}
/* Parse STRING and convert it to canonical form. If parsing fails,
- or if STRING is already canonical, return the empty string.
+ or if STRING is already canonical, return nullptr.
Otherwise return the canonical form. */
-std::string
+gdb::unique_xmalloc_ptr<char>
cp_canonicalize_string (const char *string)
{
std::unique_ptr<demangle_parse_info> info;
unsigned int estimated_len;
if (cp_already_canonical (string))
- return std::string ();
+ return nullptr;
info = cp_demangled_name_to_comp (string, NULL);
if (info == NULL)
- return std::string ();
+ return nullptr;
estimated_len = strlen (string) * 2;
gdb::unique_xmalloc_ptr<char> us (cp_comp_to_string (info->tree,
{
warning (_("internal error: string \"%s\" failed to be canonicalized"),
string);
- return std::string ();
+ return nullptr;
}
- std::string ret (us.get ());
-
- if (ret == string)
- return std::string ();
+ if (strcmp (us.get (), string) == 0)
+ return nullptr;
- return ret;
+ return us;
}
/* Convert a mangled name to a demangle_component tree. *MEMORY is
/* skip any symbols that we've already considered. */
for (symbol *listed_sym : *overload_list)
- if (strcmp (SYMBOL_LINKAGE_NAME (sym),
- SYMBOL_LINKAGE_NAME (listed_sym)) == 0)
+ if (strcmp (sym->linkage_name (), listed_sym->linkage_name ()) == 0)
return;
/* Get the demangled name without parameters */
gdb::unique_xmalloc_ptr<char> sym_name
- = cp_remove_params (SYMBOL_NATURAL_NAME (sym));
+ = cp_remove_params (sym->natural_name ());
if (!sym_name)
return;
const char *type_name;
int i, prefix_len;
- while (TYPE_CODE (type) == TYPE_CODE_PTR
+ while (type->code () == TYPE_CODE_PTR
|| TYPE_IS_REFERENCE (type)
- || TYPE_CODE (type) == TYPE_CODE_ARRAY
- || TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
+ || type->code () == TYPE_CODE_ARRAY
+ || type->code () == TYPE_CODE_TYPEDEF)
{
- if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
- type = check_typedef(type);
+ if (type->code () == TYPE_CODE_TYPEDEF)
+ type = check_typedef (type);
else
type = TYPE_TARGET_TYPE (type);
}
- type_name = TYPE_NAME (type);
+ type_name = type->name ();
if (type_name == NULL)
return;
}
/* Check public base type */
- if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+ if (type->code () == TYPE_CODE_STRUCT)
for (i = 0; i < TYPE_N_BASECLASSES (type); i++)
{
if (BASETYPE_VIA_PUBLIC (type, i))
rtti_type = check_typedef (SYMBOL_TYPE (rtti_sym));
- switch (TYPE_CODE (rtti_type))
+ switch (rtti_type->code ())
{
case TYPE_CODE_STRUCT:
break;
/* Stack context and environment for demangler crash recovery. */
-static SIGJMP_BUF gdb_demangle_jmp_buf;
+static thread_local SIGJMP_BUF *gdb_demangle_jmp_buf;
-/* If nonzero, attempt to dump core from the signal handler. */
+/* If true, attempt to dump core from the signal handler. */
-static int gdb_demangle_attempt_core_dump = 1;
+static std::atomic<bool> gdb_demangle_attempt_core_dump;
/* Signal handler for gdb_demangle. */
if (fork () == 0)
dump_core ();
- gdb_demangle_attempt_core_dump = 0;
+ gdb_demangle_attempt_core_dump = false;
}
- SIGLONGJMP (gdb_demangle_jmp_buf, signo);
+ SIGLONGJMP (*gdb_demangle_jmp_buf, signo);
+}
+
+/* A helper for gdb_demangle that reports a demangling failure. */
+
+static void
+report_failed_demangle (const char *name, bool core_dump_allowed,
+ int crash_signal)
+{
+ static bool error_reported = false;
+
+ if (!error_reported)
+ {
+ std::string short_msg
+ = string_printf (_("unable to demangle '%s' "
+ "(demangler failed with signal %d)"),
+ name, crash_signal);
+
+ std::string long_msg
+ = string_printf ("%s:%d: %s: %s", __FILE__, __LINE__,
+ "demangler-warning", short_msg.c_str ());
+
+ target_terminal::scoped_restore_terminal_state term_state;
+ target_terminal::ours_for_output ();
+
+ begin_line ();
+ if (core_dump_allowed)
+ fprintf_unfiltered (gdb_stderr,
+ _("%s\nAttempting to dump core.\n"),
+ long_msg.c_str ());
+ else
+ warn_cant_dump_core (long_msg.c_str ());
+
+ demangler_warning (__FILE__, __LINE__, "%s", short_msg.c_str ());
+
+ error_reported = true;
+ }
}
#endif
int crash_signal = 0;
#ifdef HAVE_WORKING_FORK
-#if defined (HAVE_SIGACTION) && defined (SA_RESTART)
- struct sigaction sa, old_sa;
-#else
- sighandler_t ofunc;
-#endif
- static int core_dump_allowed = -1;
-
- if (core_dump_allowed == -1)
- {
- core_dump_allowed = can_dump_core (LIMIT_CUR);
-
- if (!core_dump_allowed)
- gdb_demangle_attempt_core_dump = 0;
- }
-
+ scoped_restore restore_segv
+ = make_scoped_restore (&thread_local_segv_handler,
+ catch_demangler_crashes
+ ? gdb_demangle_signal_handler
+ : nullptr);
+
+ bool core_dump_allowed = gdb_demangle_attempt_core_dump;
+ SIGJMP_BUF jmp_buf;
+ scoped_restore restore_jmp_buf
+ = make_scoped_restore (&gdb_demangle_jmp_buf, &jmp_buf);
if (catch_demangler_crashes)
{
-#if defined (HAVE_SIGACTION) && defined (SA_RESTART)
- sa.sa_handler = gdb_demangle_signal_handler;
- sigemptyset (&sa.sa_mask);
-#ifdef HAVE_SIGALTSTACK
- sa.sa_flags = SA_ONSTACK;
-#else
- sa.sa_flags = 0;
-#endif
- sigaction (SIGSEGV, &sa, &old_sa);
-#else
- ofunc = signal (SIGSEGV, gdb_demangle_signal_handler);
-#endif
-
/* The signal handler may keep the signal blocked when we longjmp out
of it. If we have sigprocmask, we can use it to unblock the signal
afterwards and we can avoid the performance overhead of saving the
signal mask just in case the signal gets triggered. Otherwise, just
tell sigsetjmp to save the mask. */
#ifdef HAVE_SIGPROCMASK
- crash_signal = SIGSETJMP (gdb_demangle_jmp_buf, 0);
+ crash_signal = SIGSETJMP (*gdb_demangle_jmp_buf, 0);
#else
- crash_signal = SIGSETJMP (gdb_demangle_jmp_buf, 1);
+ crash_signal = SIGSETJMP (*gdb_demangle_jmp_buf, 1);
#endif
}
#endif
#ifdef HAVE_WORKING_FORK
if (catch_demangler_crashes)
{
-#if defined (HAVE_SIGACTION) && defined (SA_RESTART)
- sigaction (SIGSEGV, &old_sa, NULL);
-#else
- signal (SIGSEGV, ofunc);
-#endif
-
if (crash_signal != 0)
- {
- static int error_reported = 0;
-
+ {
#ifdef HAVE_SIGPROCMASK
/* If we got the signal, SIGSEGV may still be blocked; restore it. */
sigset_t segv_sig_set;
sigemptyset (&segv_sig_set);
sigaddset (&segv_sig_set, SIGSEGV);
- sigprocmask (SIG_UNBLOCK, &segv_sig_set, NULL);
+ gdb_sigmask (SIG_UNBLOCK, &segv_sig_set, NULL);
#endif
- if (!error_reported)
- {
- std::string short_msg
- = string_printf (_("unable to demangle '%s' "
- "(demangler failed with signal %d)"),
- name, crash_signal);
-
- std::string long_msg
- = string_printf ("%s:%d: %s: %s", __FILE__, __LINE__,
- "demangler-warning", short_msg.c_str ());
-
- target_terminal::scoped_restore_terminal_state term_state;
- target_terminal::ours_for_output ();
-
- begin_line ();
- if (core_dump_allowed)
- fprintf_unfiltered (gdb_stderr,
- _("%s\nAttempting to dump core.\n"),
- long_msg.c_str ());
- else
- warn_cant_dump_core (long_msg.c_str ());
-
- demangler_warning (__FILE__, __LINE__, "%s", short_msg.c_str ());
-
- error_reported = 1;
- }
-
- result = NULL;
- }
+ /* If there was a failure, we can't report it here, because
+ we might be in a background thread. Instead, arrange for
+ the reporting to happen on the main thread. */
+ std::string copy = name;
+ run_on_main_thread ([=] ()
+ {
+ report_failed_demangle (copy.c_str (), core_dump_allowed,
+ crash_signal);
+ });
+
+ result = NULL;
+ }
}
#endif
namespace selftests {
-void
+static void
test_cp_symbol_name_matches ()
{
#define CHECK_MATCH(SYMBOL, INPUT) \
quote (const char *str)
{
if (str != NULL)
- return std::string (1, '\"') + str + '\"';
+ return std::string (1, '"') + str + '"';
else
return "<null>";
}
#endif /* GDB_SELF_CHECK */
-/* Don't allow just "maintenance cplus". */
-
-static void
-maint_cplus_command (const char *arg, int from_tty)
-{
- printf_unfiltered (_("\"maintenance cplus\" must be followed "
- "by the name of a command.\n"));
- help_list (maint_cplus_cmd_list,
- "maintenance cplus ",
- all_commands, gdb_stdout);
-}
-
/* This is a front end for cp_find_first_component, for unit testing.
Be careful when using it: see the NOTE above
cp_find_first_component. */
cplus_print_vtable (value);
}
+void _initialize_cp_support ();
void
-_initialize_cp_support (void)
+_initialize_cp_support ()
{
- add_prefix_cmd ("cplus", class_maintenance,
- maint_cplus_command,
- _("C++ maintenance commands."),
- &maint_cplus_cmd_list,
- "maintenance cplus ",
- 0, &maintenancelist);
+ add_basic_prefix_cmd ("cplus", class_maintenance,
+ _("C++ maintenance commands."),
+ &maint_cplus_cmd_list,
+ "maintenance cplus ",
+ 0, &maintenancelist);
add_alias_cmd ("cp", "cplus",
class_maintenance, 1,
&maintenancelist);
NULL,
&maintenance_set_cmdlist,
&maintenance_show_cmdlist);
+
+ gdb_demangle_attempt_core_dump = can_dump_core (LIMIT_CUR);
#endif
#if GDB_SELF_TEST