/* 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 "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;
return 0;
}
+/* Helper for replace_typedefs_qualified_name to handle
+ DEMANGLE_COMPONENT_TEMPLATE. TMPL is the template node. BUF is
+ the buffer that holds the qualified name being built by
+ replace_typedefs_qualified_name. REPL is the node that will be
+ rewritten as a DEMANGLE_COMPONENT_NAME node holding the 'template
+ plus template arguments' name with typedefs replaced. */
+
+static bool
+replace_typedefs_template (struct demangle_parse_info *info,
+ string_file &buf,
+ struct demangle_component *tmpl,
+ struct demangle_component *repl,
+ canonicalization_ftype *finder,
+ void *data)
+{
+ demangle_component *tmpl_arglist = d_right (tmpl);
+
+ /* Replace typedefs in the template argument list. */
+ replace_typedefs (info, tmpl_arglist, finder, data);
+
+ /* Convert 'template + replaced template argument list' to a string
+ and replace the REPL node. */
+ gdb::unique_xmalloc_ptr<char> tmpl_str = cp_comp_to_string (tmpl, 100);
+ if (tmpl_str == nullptr)
+ {
+ /* If something went astray, abort typedef substitutions. */
+ return false;
+ }
+ buf.puts (tmpl_str.get ());
+
+ repl->type = DEMANGLE_COMPONENT_NAME;
+ repl->u.s_name.s = obstack_strdup (&info->obstack, buf.string ());
+ repl->u.s_name.len = buf.size ();
+ return true;
+}
+
/* Replace any typedefs appearing in the qualified name
(DEMANGLE_COMPONENT_QUAL_NAME) represented in RET_COMP for the name parse
given in INFO. */
substituted name. */
while (comp->type == DEMANGLE_COMPONENT_QUAL_NAME)
{
+ if (d_left (comp)->type == DEMANGLE_COMPONENT_TEMPLATE)
+ {
+ /* Convert 'template + replaced template argument list' to a
+ string and replace the top DEMANGLE_COMPONENT_QUAL_NAME
+ node. */
+ if (!replace_typedefs_template (info, buf,
+ d_left (comp), d_left (ret_comp),
+ finder, data))
+ return;
+
+ buf.clear ();
+ d_right (ret_comp) = d_right (comp);
+ comp = ret_comp;
+
+ /* Fallback to DEMANGLE_COMPONENT_NAME processing. We want
+ to call inspect_type for this template, in case we have a
+ template alias, like:
+ template<typename T> using alias = base<int, t>;
+ in which case we want inspect_type to do a replacement like:
+ alias<int> -> base<int, int>
+ */
+ }
+
if (d_left (comp)->type == DEMANGLE_COMPONENT_NAME)
{
struct demangle_component newobj;
comp = d_right (comp);
}
- /* If the next component is DEMANGLE_COMPONENT_NAME, save the qualified
- name assembled above and append the name given by COMP. Then use this
- reassembled name to check for a typedef. */
+ /* If the next component is DEMANGLE_COMPONENT_TEMPLATE or
+ DEMANGLE_COMPONENT_NAME, save the qualified name assembled above
+ and append the name given by COMP. Then use this reassembled
+ name to check for a typedef. */
- if (comp->type == DEMANGLE_COMPONENT_NAME)
+ if (comp->type == DEMANGLE_COMPONENT_TEMPLATE)
+ {
+ /* Replace the top (DEMANGLE_COMPONENT_QUAL_NAME) node with a
+ DEMANGLE_COMPONENT_NAME node containing the whole name. */
+ if (!replace_typedefs_template (info, buf, comp, ret_comp, finder, data))
+ return;
+ inspect_type (info, ret_comp, finder, data);
+ }
+ else if (comp->type == DEMANGLE_COMPONENT_NAME)
{
buf.write (comp->u.s_name.s, comp->u.s_name.len);
/* 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
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;
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
/* See cp-support.h. */
-int
-gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
-{
- *demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
- return *demangled != NULL;
-}
-
-/* See cp-support.h. */
-
unsigned int
cp_search_name_hash (const char *search_name)
{
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