/* General utility routines for GDB, the GNU debugger.
- Copyright (C) 1986-2020 Free Software Foundation, Inc.
+ Copyright (C) 1986-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbarch.h"
#include "cli-out.h"
#include "gdbsupport/gdb-safe-ctype.h"
+#include "bt-utils.h"
void (*deprecated_error_begin_hook) (void);
/* Prototypes for local functions */
static void vfprintf_maybe_filtered (struct ui_file *, const char *,
- va_list, bool, bool)
+ va_list, bool)
ATTRIBUTE_PRINTF (2, 0);
static void fputs_maybe_filtered (const char *, struct ui_file *, int);
setrlimit (RLIMIT_CORE, &rlim);
#endif /* HAVE_SETRLIMIT */
+ /* Ensure that the SIGABRT we're about to raise will immediately cause
+ GDB to exit and dump core, we don't want to trigger GDB's printing of
+ a backtrace to the console here. */
+ signal (SIGABRT, SIG_DFL);
+
abort (); /* ARI: abort */
}
NULL
};
-/* Print a message reporting an internal error/warning. Ask the user
- if they want to continue, dump core, or just exit. Return
- something to indicate a quit. */
+/* Data structure used to control how the internal_vproblem function
+ should behave. An instance of this structure is created for each
+ problem type that GDB supports. */
struct internal_problem
{
+ /* The name of this problem type. This must not contain white space as
+ this string is used to build command names. */
const char *name;
- int user_settable_should_quit;
+
+ /* When this is true then a user command is created (based on NAME) that
+ allows the SHOULD_QUIT field to be modified, otherwise, SHOULD_QUIT
+ can't be changed from its default value by the user. */
+ bool user_settable_should_quit;
+
+ /* Reference a value from internal_problem_modes to indicate if GDB
+ should quit when it hits a problem of this type. */
const char *should_quit;
- int user_settable_should_dump_core;
+
+ /* Like USER_SETTABLE_SHOULD_QUIT but for SHOULD_DUMP_CORE. */
+ bool user_settable_should_dump_core;
+
+ /* Like SHOULD_QUIT, but whether GDB should dump core. */
const char *should_dump_core;
+
+ /* Like USER_SETTABLE_SHOULD_QUIT but for SHOULD_PRINT_BACKTRACE. */
+ bool user_settable_should_print_backtrace;
+
+ /* When this is true GDB will print a backtrace when a problem of this
+ type is encountered. */
+ bool should_print_backtrace;
};
/* Report a problem, internal to GDB, to the user. Once the problem
abort_with_message (msg);
default:
dejavu = 3;
- /* Newer GLIBC versions put the warn_unused_result attribute
- on write, but this is one of those rare cases where
- ignoring the return value is correct. Casting to (void)
- does not fix this problem. This is the solution suggested
- at http://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509. */
+ /* Newer GLIBC versions put the warn_unused_result attribute
+ on write, but this is one of those rare cases where
+ ignoring the return value is correct. Casting to (void)
+ does not fix this problem. This is the solution suggested
+ at http://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509. */
if (write (STDERR_FILENO, msg, sizeof (msg)) != sizeof (msg))
- abort (); /* ARI: abort */
+ abort (); /* ARI: abort */
exit (1);
}
}
/* Emit the message unless query will emit it below. */
if (problem->should_quit != internal_problem_ask
|| !confirm
- || !filtered_printing_initialized ())
+ || !filtered_printing_initialized ()
+ || problem->should_print_backtrace)
fprintf_unfiltered (gdb_stderr, "%s\n", reason.c_str ());
+ if (problem->should_print_backtrace)
+ gdb_internal_backtrace ();
+
if (problem->should_quit == internal_problem_ask)
{
/* Default (yes/batch case) is to quit GDB. When in batch mode
if (!confirm || !filtered_printing_initialized ())
quit_p = 1;
else
- quit_p = query (_("%s\nQuit this debugging session? "),
+ quit_p = query (_("%s\nQuit this debugging session? "),
reason.c_str ());
}
else if (problem->should_quit == internal_problem_yes)
}
static struct internal_problem internal_error_problem = {
- "internal-error", 1, internal_problem_ask, 1, internal_problem_ask
+ "internal-error", true, internal_problem_ask, true, internal_problem_ask,
+ true, GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON
};
void
}
static struct internal_problem internal_warning_problem = {
- "internal-warning", 1, internal_problem_ask, 1, internal_problem_ask
+ "internal-warning", true, internal_problem_ask, true, internal_problem_ask,
+ true, false
};
void
}
static struct internal_problem demangler_warning_problem = {
- "demangler-warning", 1, internal_problem_ask, 0, internal_problem_no
+ "demangler-warning", true, internal_problem_ask, false, internal_problem_no,
+ false, false
};
void
{
struct cmd_list_element **set_cmd_list;
struct cmd_list_element **show_cmd_list;
- char *set_doc;
- char *show_doc;
set_cmd_list = XNEW (struct cmd_list_element *);
show_cmd_list = XNEW (struct cmd_list_element *);
*set_cmd_list = NULL;
*show_cmd_list = NULL;
- set_doc = xstrprintf (_("Configure what GDB does when %s is detected."),
- problem->name);
+ /* The add_basic_prefix_cmd and add_show_prefix_cmd functions take
+ ownership of the string passed in, which is why we don't need to free
+ set_doc and show_doc in this function. */
+ const char *set_doc
+ = xstrprintf (_("Configure what GDB does when %s is detected."),
+ problem->name);
+ const char *show_doc
+ = xstrprintf (_("Show what GDB does when %s is detected."),
+ problem->name);
- show_doc = xstrprintf (_("Show what GDB does when %s is detected."),
- problem->name);
-
- add_basic_prefix_cmd (problem->name, class_maintenance, set_doc,
- set_cmd_list,
- concat ("maintenance set ", problem->name, " ",
- (char *) NULL),
- 0/*allow-unknown*/, &maintenance_set_cmdlist);
-
- add_show_prefix_cmd (problem->name, class_maintenance, show_doc,
- show_cmd_list,
- concat ("maintenance show ", problem->name, " ",
- (char *) NULL),
- 0/*allow-unknown*/, &maintenance_show_cmdlist);
+ add_setshow_prefix_cmd (problem->name, class_maintenance,
+ set_doc, show_doc, set_cmd_list, show_cmd_list,
+ &maintenance_set_cmdlist, &maintenance_show_cmdlist);
if (problem->user_settable_should_quit)
{
- set_doc = xstrprintf (_("Set whether GDB should quit "
- "when an %s is detected."),
- problem->name);
- show_doc = xstrprintf (_("Show whether GDB will quit "
- "when an %s is detected."),
- problem->name);
+ std::string set_quit_doc
+ = string_printf (_("Set whether GDB should quit when an %s is "
+ "detected."), problem->name);
+ std::string show_quit_doc
+ = string_printf (_("Show whether GDB will quit when an %s is "
+ "detected."), problem->name);
add_setshow_enum_cmd ("quit", class_maintenance,
internal_problem_modes,
&problem->should_quit,
- set_doc,
- show_doc,
+ set_quit_doc.c_str (),
+ show_quit_doc.c_str (),
NULL, /* help_doc */
NULL, /* setfunc */
NULL, /* showfunc */
set_cmd_list,
show_cmd_list);
-
- xfree (set_doc);
- xfree (show_doc);
}
if (problem->user_settable_should_dump_core)
{
- set_doc = xstrprintf (_("Set whether GDB should create a core "
- "file of GDB when %s is detected."),
- problem->name);
- show_doc = xstrprintf (_("Show whether GDB will create a core "
- "file of GDB when %s is detected."),
- problem->name);
+ std::string set_core_doc
+ = string_printf (_("Set whether GDB should create a core file of "
+ "GDB when %s is detected."), problem->name);
+ std::string show_core_doc
+ = string_printf (_("Show whether GDB will create a core file of "
+ "GDB when %s is detected."), problem->name);
add_setshow_enum_cmd ("corefile", class_maintenance,
internal_problem_modes,
&problem->should_dump_core,
- set_doc,
- show_doc,
+ set_core_doc.c_str (),
+ show_core_doc.c_str (),
NULL, /* help_doc */
NULL, /* setfunc */
NULL, /* showfunc */
set_cmd_list,
show_cmd_list);
+ }
- xfree (set_doc);
- xfree (show_doc);
+ if (problem->user_settable_should_print_backtrace)
+ {
+ std::string set_bt_doc
+ = string_printf (_("Set whether GDB should print a backtrace of "
+ "GDB when %s is detected."), problem->name);
+ std::string show_bt_doc
+ = string_printf (_("Show whether GDB will print a backtrace of "
+ "GDB when %s is detected."), problem->name);
+ add_setshow_boolean_cmd ("backtrace", class_maintenance,
+ &problem->should_print_backtrace,
+ set_bt_doc.c_str (),
+ show_bt_doc.c_str (),
+ NULL, /* help_doc */
+ gdb_internal_backtrace_set_cmd,
+ NULL, /* showfunc */
+ set_cmd_list,
+ show_cmd_list);
}
}
#else
if (job_control
/* If there is no terminal switching for this target, then we can't
- possibly get screwed by the lack of job control. */
+ possibly get screwed by the lack of job control. */
|| !target_supports_terminal_ours ())
throw_quit ("Quit");
else
return orglen;
}
+/* See utils.h. */
+
+ULONGEST
+uinteger_pow (ULONGEST v1, LONGEST v2)
+{
+ if (v2 < 0)
+ {
+ if (v1 == 0)
+ error (_("Attempt to raise 0 to negative power."));
+ else
+ return 0;
+ }
+ else
+ {
+ /* The Russian Peasant's Algorithm. */
+ ULONGEST v;
+
+ v = 1;
+ for (;;)
+ {
+ if (v2 & 1L)
+ v *= v1;
+ v2 >>= 1;
+ if (v2 == 0)
+ return v;
+ v1 *= v1;
+ }
+ }
+}
+
void
print_spaces (int n, struct ui_file *file)
{
if (answer >= 'a')
answer -= 040;
/* Check answer. For the non-default, the user must specify
- the non-default explicitly. */
+ the non-default explicitly. */
if (answer == not_def_answer)
{
retval = !def_value;
break;
}
/* Otherwise, if a default was specified, the user may either
- specify the required input or have it default by entering
- nothing. */
+ specify the required input or have it default by entering
+ nothing. */
if (answer == def_answer
|| (defchar != '\0' && answer == '\0'))
{
chars_per_line = cols;
/* Readline should have fetched the termcap entry for us.
- Only try to use tgetnum function if rl_get_screen_size
- did not return a useful value. */
+ Only try to use tgetnum function if rl_get_screen_size
+ did not return a useful value. */
if (((rows <= 0) && (tgetnum ((char *) "li") < 0))
/* Also disable paging if inside Emacs. $EMACS was used
before Emacs v25.1, $INSIDE_EMACS is used since then. */
emit_style_escape (const ui_file_style &style,
struct ui_file *stream = nullptr)
{
- applied_style = style;
+ if (applied_style != style)
+ {
+ applied_style = style;
- if (stream == nullptr)
- wrap_buffer.append (style.to_ansi ());
- else
- stream->puts (style.to_ansi ().c_str ());
+ if (stream == nullptr)
+ wrap_buffer.append (style.to_ansi ());
+ else
+ stream->puts (style.to_ansi ().c_str ());
+ }
}
/* Set the current output style. This will affect future uses of the
stream->flush ();
}
+/* See utils.h. */
+
+int
+get_chars_per_line ()
+{
+ return chars_per_line;
+}
+
/* Indicate that if the next sequence of characters overflows the line,
a newline should be inserted here rather than when it hits the end.
If INDENT is non-null, it is a string to be printed to indent the
{
wrap_buffer.push_back ('\t');
/* Shifting right by 3 produces the number of tab stops
- we have already passed, and then adding one and
- shifting left 3 advances to the next tab stop. */
+ we have already passed, and then adding one and
+ shifting left 3 advances to the next tab stop. */
chars_printed = ((chars_printed >> 3) + 1) << 3;
lineptr++;
}
don't increment chars_printed here. */
lineptr += skip_bytes;
}
+ else if (*lineptr == '\r')
+ {
+ wrap_buffer.push_back (*lineptr);
+ chars_printed = 0;
+ lineptr++;
+ }
else
{
wrap_buffer.push_back (*lineptr);
prompt is given; and to avoid emitting style
sequences in the middle of a run of text, we track
this as well. */
- ui_file_style save_style;
+ ui_file_style save_style = applied_style;
bool did_paginate = false;
chars_printed = 0;
lines_printed++;
if (wrap_column)
{
- save_style = wrap_style;
+ /* We are about to insert a newline at an historic
+ location in the WRAP_BUFFER. Before we do we want to
+ restore the default style. To know if we actually
+ need to insert an escape sequence we must restore the
+ current applied style to how it was at the WRAP_COLUMN
+ location. */
+ applied_style = wrap_style;
if (stream->can_emit_style_escape ())
emit_style_escape (ui_file_style (), stream);
/* If we aren't actually wrapping, don't output
stream->puts ("\n");
}
else
- {
- save_style = applied_style;
- flush_wrap_buffer (stream);
- }
+ flush_wrap_buffer (stream);
/* Possible new page. Note that
PAGINATION_DISABLED_FOR_COMMAND might be set during
if (wrap_column)
{
stream->puts (wrap_indent);
+
+ /* Having finished inserting the wrapping we should
+ restore the style as it was at the WRAP_COLUMN. */
if (stream->can_emit_style_escape ())
- emit_style_escape (save_style, stream);
+ emit_style_escape (wrap_style, stream);
+
+ /* The WRAP_BUFFER will still contain content, and that
+ content might set some alternative style. Restore
+ APPLIED_STYLE as it was before we started wrapping,
+ this reflects the current style for the last character
+ in WRAP_BUFFER. */
+ applied_style = save_style;
+
/* FIXME, this strlen is what prevents wrap_indent from
containing tabs. However, if we recurse to print it
and count its chars, we risk trouble if wrap_indent is
fputs_styled (const char *linebuffer, const ui_file_style &style,
struct ui_file *stream)
{
- /* This just makes it so we emit somewhat fewer escape
- sequences. */
- if (style.is_default ())
- fputs_maybe_filtered (linebuffer, stream, 1);
- else
- {
- set_output_style (stream, style);
- fputs_maybe_filtered (linebuffer, stream, 1);
- set_output_style (stream, ui_file_style ());
- }
+ set_output_style (stream, style);
+ fputs_maybe_filtered (linebuffer, stream, 1);
+ set_output_style (stream, ui_file_style ());
}
/* See utils.h. */
fputs_styled_unfiltered (const char *linebuffer, const ui_file_style &style,
struct ui_file *stream)
{
- /* This just makes it so we emit somewhat fewer escape
- sequences. */
- if (style.is_default ())
- fputs_maybe_filtered (linebuffer, stream, 0);
- else
- {
- set_output_style (stream, style);
- fputs_maybe_filtered (linebuffer, stream, 0);
- set_output_style (stream, ui_file_style ());
- }
+ set_output_style (stream, style);
+ fputs_maybe_filtered (linebuffer, stream, 0);
+ set_output_style (stream, ui_file_style ());
}
/* See utils.h. */
static void
vfprintf_maybe_filtered (struct ui_file *stream, const char *format,
- va_list args, bool filter, bool gdbfmt)
+ va_list args, bool filter)
{
- if (gdbfmt)
- {
- ui_out_flags flags = disallow_ui_out_field;
- if (!filter)
- flags |= unfiltered_output;
- cli_ui_out (stream, flags).vmessage (applied_style, format, args);
- }
- else
- {
- std::string str = string_vprintf (format, args);
- fputs_maybe_filtered (str.c_str (), stream, filter);
- }
+ ui_out_flags flags = disallow_ui_out_field;
+ if (!filter)
+ flags |= unfiltered_output;
+ cli_ui_out (stream, flags).vmessage (applied_style, format, args);
}
void
vfprintf_filtered (struct ui_file *stream, const char *format, va_list args)
{
- vfprintf_maybe_filtered (stream, format, args, true, true);
+ vfprintf_maybe_filtered (stream, format, args, true);
}
void
{
if (debug_timestamp && stream == gdb_stdlog)
{
- using namespace std::chrono;
- int len, need_nl;
+ static bool needs_timestamp = true;
+
+ /* Print timestamp if previous print ended with a \n. */
+ if (needs_timestamp)
+ {
+ using namespace std::chrono;
+
+ steady_clock::time_point now = steady_clock::now ();
+ seconds s = duration_cast<seconds> (now.time_since_epoch ());
+ microseconds us = duration_cast<microseconds> (now.time_since_epoch () - s);
+ std::string timestamp = string_printf ("%ld.%06ld ",
+ (long) s.count (),
+ (long) us.count ());
+ fputs_unfiltered (timestamp.c_str (), stream);
+ }
+ /* Print the message. */
string_file sfile;
cli_ui_out (&sfile, 0).vmessage (ui_file_style (), format, args);
std::string linebuffer = std::move (sfile.string ());
+ fputs_unfiltered (linebuffer.c_str (), stream);
- steady_clock::time_point now = steady_clock::now ();
- seconds s = duration_cast<seconds> (now.time_since_epoch ());
- microseconds us = duration_cast<microseconds> (now.time_since_epoch () - s);
-
- len = linebuffer.size ();
- need_nl = (len > 0 && linebuffer[len - 1] != '\n');
-
- std::string timestamp = string_printf ("%ld.%06ld %s%s",
- (long) s.count (),
- (long) us.count (),
- linebuffer.c_str (),
- need_nl ? "\n": "");
- fputs_unfiltered (timestamp.c_str (), stream);
+ size_t len = linebuffer.length ();
+ needs_timestamp = (len > 0 && linebuffer[len - 1] == '\n');
}
else
- vfprintf_maybe_filtered (stream, format, args, false, true);
+ vfprintf_maybe_filtered (stream, format, args, false);
}
void
vprintf_filtered (const char *format, va_list args)
{
- vfprintf_maybe_filtered (gdb_stdout, format, args, true, false);
+ vfprintf_filtered (gdb_stdout, format, args);
}
void
va_end (args);
}
-/* Like fprintf_filtered, but prints its result indented.
- Called as fprintfi_filtered (spaces, stream, format, ...); */
-
-void
-fprintfi_filtered (int spaces, struct ui_file *stream, const char *format,
- ...)
-{
- va_list args;
-
- va_start (args, format);
- print_spaces_filtered (spaces, stream);
-
- vfprintf_filtered (stream, format, args);
- va_end (args);
-}
-
/* See utils.h. */
void
std::string str = string_vprintf (format, args);
if (!str.empty ())
{
- if (!style.is_default ())
- set_output_style (stream, style);
+ set_output_style (stream, style);
fputs_maybe_filtered (str.c_str (), stream, filter);
- if (!style.is_default ())
- set_output_style (stream, ui_file_style ());
+ set_output_style (stream, ui_file_style ());
}
}
va_end (args);
}
-/* Like printf_filtered, but prints it's result indented.
- Called as printfi_filtered (spaces, format, ...); */
-
-void
-printfi_filtered (int spaces, const char *format, ...)
-{
- va_list args;
-
- va_start (args, format);
- print_spaces_filtered (spaces, gdb_stdout);
- vfprintf_filtered (gdb_stdout, format, args);
- va_end (args);
-}
-
/* Easy -- but watch out!
This routine is *not* a replacement for puts()! puts() appends a newline.
fprintf_symbol_filtered (struct ui_file *stream, const char *name,
enum language lang, int arg_mode)
{
- char *demangled;
-
if (name != NULL)
{
/* If user wants to see raw output, no problem. */
}
else
{
- demangled = language_demangle (language_def (lang), name, arg_mode);
- fputs_filtered (demangled ? demangled : name, stream);
- if (demangled != NULL)
- {
- xfree (demangled);
- }
+ gdb::unique_xmalloc_ptr<char> demangled
+ = language_demangle (language_def (lang), name, arg_mode);
+ fputs_filtered (demangled ? demangled.get () : name, stream);
}
}
}
return !strcmp (lhs, rhs);
}
-/* See utils.h. */
-
-int
-streq_hash (const void *lhs, const void *rhs)
-{
- return streq ((const char *) lhs, (const char *) rhs);
-}
-
\f
/*
gdb_realpath_check_trailer ("", "");
}
+/* Test the gdb_argv::as_array_view method. */
+
+static void
+gdb_argv_as_array_view_test ()
+{
+ {
+ gdb_argv argv;
+
+ gdb::array_view<char *> view = argv.as_array_view ();
+
+ SELF_CHECK (view.data () == nullptr);
+ SELF_CHECK (view.size () == 0);
+ }
+ {
+ gdb_argv argv ("une bonne 50");
+
+ gdb::array_view<char *> view = argv.as_array_view ();
+
+ SELF_CHECK (view.size () == 3);
+ SELF_CHECK (strcmp (view[0], "une") == 0);
+ SELF_CHECK (strcmp (view[1], "bonne") == 0);
+ SELF_CHECK (strcmp (view[2], "50") == 0);
+ }
+}
+
#endif /* GDB_SELF_TEST */
/* Allocation function for the libiberty hash table which uses an
if ((s == string || IS_DIR_SEPARATOR (s[-1])
|| s[-1] == DIRNAME_SEPARATOR)
- && (s[from_len] == '\0' || IS_DIR_SEPARATOR (s[from_len])
+ && (s[from_len] == '\0' || IS_DIR_SEPARATOR (s[from_len])
|| s[from_len] == DIRNAME_SEPARATOR))
{
char *string_new;
#if GDB_SELF_TEST
selftests::register_test ("gdb_realpath", gdb_realpath_tests);
+ selftests::register_test ("gdb_argv_array_view", gdb_argv_as_array_view_test);
#endif
}