Compare commits

...

6 Commits

Author SHA1 Message Date
Aaron Merey
3e6430952a gdb/testsuite/gdb.debuginfod: Add lazy downloading tests
Add some tests for 'set debuginfod enabled lazy', .gdb_index downloading
and deferred debuginfo downloading.
2023-05-23 11:45:00 -04:00
Aaron Merey
6496753f01 gdb/debuginfod: Add .debug_line downloading
v1 can be found here:
https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html

v2 merges dwarf_decode_line_header_separate with
dwarf_decode_line_header and read_formatted_entries_separate with
read_formatted_entries in order to reduce code duplication.
2023-05-23 11:44:57 -04:00
Aaron Merey
bf803c840c gdb/debuginfod: Support on-demand debuginfo downloading
At the beginning of a session, gdb may attempt to download debuginfo
for all shared libraries associated with the process or core file
being debugged.  This can be a waste of time and storage space when much
of the debuginfo ends up not being used during the session.

To reduce the gdb's startup latency and to download only the debuginfo
that is really needed, this patch adds on-demand, a.k.a lazy, downloading.

When 'set debuginfo enabled lazy' is on, gdb will attempt to download
a .gdb_index for each shared library instead of its full debuginfo.
Each debuginfo download will be deferred until gdb needs to expand symtabs
associated with the debuginfo's index.

Additionally, all separate debug objfiles are placed in objfiles_list
after their parent, instead of before.  This ensures that any deferred
debuginfo downloaded during iteration over objfiles_list aren't skipped
by the loop.  I also modified objfiles_safe so that it returns a safe
reverse iterator.  Reverse iteration prevents iterator invalidation by
ensuring that separate debug objfiles are deleted before their parent objfile.
2023-05-23 11:44:51 -04:00
Aaron Merey
8de61c8491 gdb: Buffer output during print_thread and print_frame_info
Introduce new ui_file buffer_file to temporarily collect output
during print_thread and print_frame_info.  This ensures that
output from these functions is not interrupted by debuginfod
progress messages.

With the addition of deferred debuginfo downloading it is possible
for download progress messages to print during frame and thread
printing.  Without any intervention we can end up with poorly
formatted output:

    (gdb) backtrace
    [...]
    #8  0x00007fbe8af7d7cf in pygi_invoke_c_callable (Downloading separate debug info for /lib64/libpython3.11.so.1.0
    function_cache=0x561221b224d0, state=<optimized out>...

To fix this we accumulate the frame/thread output in a buffer_file
and have progress messages skip the buffer and print to gdb_stdout
immediately.  This ensures progress messages print first, followed by
uninterrupted frame/thread info:

    (gdb) backtrace
    [...]
    Downloading separate debug info for /lib64/libpython3.11.so.1.0
    #8  0x00007fbe8af7d7cf in pygi_invoke_c_callable (function_cache=0x561221b224d0, state=<optimized out>...
2023-05-23 11:43:28 -04:00
Aaron Merey
aee7ee692a gdb: add 'lazy' setting for command 'set debuginfod enabled'
'set debuginfod enabled lazy' turns on debuginfod downloading like
'set debuginfod enabled on' but also enables ELF/DWARFs section
downloading via debuginfod_section_query.

If support for debuginfod section queries was not found at configure
time, 'set debuginfod enabled lazy' will print an error message
indicating the missing support and default to 'set debuginfod enabled on'.

Also update the help text and gdb.texinfo section for 'set debuginfod enabled'
with information on the lazy setting.
2023-05-23 11:40:12 -04:00
Aaron Merey
255f319779 gdb/debuginfod: Add debuginfod_section_query
Add new function debuginfod_section_query.  This function queries
debuginfod servers for an individual ELF/DWARF section associated with
a given build-id.

Also check for libdebuginfod version >= 0.188 at configure time.
debuginfod_section_query simply returns -ENOSYS if this condition
is not met.
2023-05-23 11:40:07 -04:00
41 changed files with 1707 additions and 308 deletions

View File

@@ -26,3 +26,30 @@ else
AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
fi
])
AC_DEFUN([AC_DEBUGINFOD_SECTION],
[
# Handle optional debuginfod support as well as optional section downloading support
AC_ARG_WITH([debuginfod],
AC_HELP_STRING([--with-debuginfod], [Enable debuginfo lookups with debuginfod (auto/yes/no)]),
[], [with_debuginfod=auto])
AC_MSG_CHECKING([whether to use debuginfod])
AC_MSG_RESULT([$with_debuginfod])
if test "x$with_debuginfod" != xno; then
PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.188],
[AC_DEFINE([HAVE_DEBUGINFOD_FIND_SECTION], [1],
[Define to 1 if debuginfod section downloading is supported.])],
[AC_MSG_WARN([libdebuginfod is missing or some features may be unavailable.])])
PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.179],
[AC_DEFINE([HAVE_LIBDEBUGINFOD], [1], [Define to 1 if debuginfod is enabled.])],
[if test "x$with_debuginfod" = xyes; then
AC_MSG_ERROR(["--with-debuginfod was given, but libdebuginfod is missing or unusable."])
else
AC_MSG_WARN([libdebuginfod is missing or unusable; some features may be unavailable.])
fi])
else
AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
fi
])

View File

@@ -263,6 +263,19 @@ cli_ui_out::do_redirect (ui_file *outstream)
m_streams.pop_back ();
}
void
cli_ui_out::do_redirect_to_buffer (buffer_file *buf_file)
{
if (buf_file != nullptr)
{
gdb_assert (m_streams.size () >= 1);
buf_file->set_stream (m_streams.back ());
do_redirect (buf_file);
}
else
m_streams.pop_back ();
}
/* Initialize a progress update to be displayed with
cli_ui_out::do_progress_notify. */
@@ -299,7 +312,8 @@ cli_ui_out::do_progress_notify (const std::string &msg,
double howmuch, double total)
{
int chars_per_line = get_chars_per_line ();
struct ui_file *stream = m_streams.back ();
struct ui_file *stream
= buffer_file::get_unbuffered_stream (m_streams.back ());
cli_progress_info &info (m_progress_info.back ());
if (chars_per_line > MAX_CHARS_PER_LINE)
@@ -384,7 +398,8 @@ cli_ui_out::do_progress_notify (const std::string &msg,
void
cli_ui_out::clear_progress_notify ()
{
struct ui_file *stream = m_streams.back ();
struct ui_file *stream
= buffer_file::get_unbuffered_stream (m_streams.back ());
int chars_per_line = get_chars_per_line ();
scoped_restore save_pagination
@@ -413,10 +428,11 @@ void
cli_ui_out::do_progress_end ()
{
struct ui_file *stream = m_streams.back ();
m_progress_info.pop_back ();
if (stream->isatty ())
clear_progress_notify ();
m_progress_info.pop_back ();
}
/* local functions */

View File

@@ -71,6 +71,7 @@ protected:
virtual void do_wrap_hint (int indent) override;
virtual void do_flush () override;
virtual void do_redirect (struct ui_file *outstream) override;
virtual void do_redirect_to_buffer (buffer_file *buf_file) override;
virtual void do_progress_start () override;
virtual void do_progress_notify (const std::string &, const char *,

View File

@@ -99,6 +99,9 @@
/* define if the compiler supports basic C++11 syntax */
#undef HAVE_CXX11
/* Define to 1 if debuginfod section downloading is supported. */
#undef HAVE_DEBUGINFOD_FIND_SECTION
/* Define to 1 if you have the declaration of `ADDR_NO_RANDOMIZE', and to 0 if
you don't. */
#undef HAVE_DECL_ADDR_NO_RANDOMIZE

102
gdb/configure vendored
View File

@@ -18349,7 +18349,7 @@ esac
# Handle optional debuginfod support
# Handle optional debuginfod support
# Handle optional debuginfod support as well as optional section downloading support
# Check whether --with-debuginfod was given.
if test "${with_debuginfod+set}" = set; then :
@@ -18365,6 +18365,106 @@ $as_echo "$with_debuginfod" >&6; }
if test "x$with_debuginfod" != xno; then
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.188" >&5
$as_echo_n "checking for libdebuginfod >= 0.188... " >&6; }
if test -n "$DEBUGINFOD_CFLAGS"; then
pkg_cv_DEBUGINFOD_CFLAGS="$DEBUGINFOD_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.188" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$DEBUGINFOD_LIBS"; then
pkg_cv_DEBUGINFOD_LIBS="$DEBUGINFOD_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.188" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = no; then
pkg_save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $pkg_cv_DEBUGINFOD_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
else
pkg_failed=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$pkg_save_LDFLAGS
fi
if test $pkg_failed = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
else
DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$DEBUGINFOD_PKG_ERRORS" >&5
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or some features may be unavailable." >&5
$as_echo "$as_me: WARNING: libdebuginfod is missing or some features may be unavailable." >&2;}
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or some features may be unavailable." >&5
$as_echo "$as_me: WARNING: libdebuginfod is missing or some features may be unavailable." >&2;}
else
DEBUGINFOD_CFLAGS=$pkg_cv_DEBUGINFOD_CFLAGS
DEBUGINFOD_LIBS=$pkg_cv_DEBUGINFOD_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_DEBUGINFOD_FIND_SECTION 1" >>confdefs.h
fi
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.179" >&5
$as_echo_n "checking for libdebuginfod >= 0.179... " >&6; }

View File

@@ -342,7 +342,7 @@ case $host_os in
esac
# Handle optional debuginfod support
AC_DEBUGINFOD
AC_DEBUGINFOD_SECTION
# Libunwind support for ia64.
AC_ARG_WITH(libunwind-ia64,

View File

@@ -34,12 +34,14 @@ static cmd_list_element *show_debuginfod_prefix_list;
static const char debuginfod_on[] = "on";
static const char debuginfod_off[] = "off";
static const char debuginfod_ask[] = "ask";
static const char debuginfod_lazy[] = "lazy";
static const char *debuginfod_enabled_enum[] =
{
debuginfod_on,
debuginfod_off,
debuginfod_ask,
debuginfod_lazy,
nullptr
};
@@ -80,6 +82,15 @@ debuginfod_exec_query (const unsigned char *build_id,
return scoped_fd (-ENOSYS);
}
scoped_fd
debuginfod_section_query (const unsigned char *build_id,
int build_id_len,
const char *filename,
const char *section_name,
gdb::unique_xmalloc_ptr<char> *destname)
{
return scoped_fd (-ENOSYS);
}
#define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
#else
@@ -148,7 +159,8 @@ progressfn (debuginfod_client *c, long cur, long total)
if (check_quit_flag ())
{
gdb_printf ("Cancelling download of %s %s...\n",
ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
gdb_printf (outstream, "Cancelling download of %s %s...\n",
data->desc, styled_fname.c_str ());
return 1;
}
@@ -264,10 +276,14 @@ static void
print_outcome (int fd, const char *desc, const char *fname)
{
if (fd < 0 && fd != -ENOENT)
gdb_printf (_("Download failed: %s. Continuing without %s %ps.\n"),
safe_strerror (-fd),
desc,
styled_string (file_name_style.style (), fname));
{
ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
gdb_printf (outstream,
_("Download failed: %s. Continuing without %s %ps.\n"),
safe_strerror (-fd),
desc,
styled_string (file_name_style.style (), fname));
}
}
/* See debuginfod-support.h */
@@ -401,6 +417,56 @@ debuginfod_exec_query (const unsigned char *build_id,
return fd;
}
/* See debuginfod-support.h */
scoped_fd
debuginfod_section_query (const unsigned char *build_id,
int build_id_len,
const char *filename,
const char *section_name,
gdb::unique_xmalloc_ptr<char> *destname)
{
#if !defined (HAVE_DEBUGINFOD_FIND_SECTION)
return scoped_fd (-ENOSYS);
#else
if (debuginfod_enabled != debuginfod_lazy || !debuginfod_is_enabled ())
return scoped_fd (-ENOSYS);
debuginfod_client *c = get_debuginfod_client ();
if (c == nullptr)
return scoped_fd (-ENOMEM);
char *dname = nullptr;
std::string desc = std::string ("section ") + section_name + " for";
scoped_fd fd;
gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
{
user_data data (desc.c_str (), filename);
debuginfod_set_user_data (c, &data);
if (target_supports_terminal_ours ())
{
term_state.emplace ();
target_terminal::ours ();
}
fd = scoped_fd (debuginfod_find_section (c, build_id, build_id_len,
section_name, &dname));
debuginfod_set_user_data (c, nullptr);
}
print_outcome (fd.get (), desc.c_str (), filename);
if (fd.get () >= 0 && destname != nullptr)
destname->reset (dname);
return fd;
#endif /* HAVE_DEBUGINFOD_FIND_SECTION */
}
#endif
/* Set callback for "set debuginfod enabled". */
@@ -409,6 +475,14 @@ static void
set_debuginfod_enabled (const char *value)
{
#if defined(HAVE_LIBDEBUGINFOD)
#if !defined(HAVE_DEBUGINFOD_FIND_SECTION)
if (value == debuginfod_lazy)
{
debuginfod_enabled = debuginfod_on;
error (_("Support for lazy downloading is not compiled into GDB. " \
"Defaulting to \"on\"."));
}
#endif
debuginfod_enabled = value;
#else
/* Disabling debuginfod when gdb is not built with it is a no-op. */
@@ -508,8 +582,12 @@ _initialize_debuginfod ()
_("Set whether to use debuginfod."),
_("Show whether to use debuginfod."),
_("\
When on, enable the use of debuginfod to download missing debug info and\n\
source files."),
When set to \"on\", enable the use of debuginfod to download missing\n\
debug info and source files. \"off\" disables the use of debuginfod.\n\
When set to \"ask\", a prompt may ask whether to enable or disable\n\
debuginfod. When set to \"lazy\", debug info downloading will be\n\
deferred until it is required. GDB may also download components of\n\
debug info instead of entire files." ),
set_debuginfod_enabled,
get_debuginfod_enabled,
show_debuginfod_enabled,

View File

@@ -81,4 +81,28 @@ extern scoped_fd debuginfod_exec_query (const unsigned char *build_id,
const char *filename,
gdb::unique_xmalloc_ptr<char>
*destname);
/* Query debuginfod servers for the binary contents of a ELF/DWARF section
from a file matching BUILD_ID. BUILD_ID can be given as a binary blob
or a null-terminated string. If given as a binary blob, BUILD_ID_LEN
should be the number of bytes. If given as a null-terminated string,
BUILD_ID_LEN should be 0.
FILENAME should be the name or path associated with the file matching
BUILD_ID. It is used for printing messages to the user.
SECTION_NAME should be the name of an ELF/DWARF section.
If the file is successfully retrieved, return a file descriptor and store
the file's local path in DESTNAME. If unsuccessful, print an error message
and return a negative errno. If GDB is not built with debuginfod or
libdebuginfod does not support section queries, this function returns
-ENOSYS. */
extern scoped_fd debuginfod_section_query (const unsigned char *build_id,
int build_id_len,
const char *filename,
const char *section_name,
gdb::unique_xmalloc_ptr<char>
*destname);
#endif /* DEBUGINFOD_SUPPORT_H */

View File

@@ -49332,10 +49332,15 @@ debug info or source files. By default, @code{debuginfod enabled} is set to
attempting to perform the next query. By default, @code{debuginfod enabled}
is set to @code{ask} for interactive sessions.
@item set debuginfod enabled lazy
@value{GDBN} will attempt to defer downloading entire debug info files until
necessary. @value{GDBN} may instead download individual components of the
debug info files such as @code{.gdb_index}.
@kindex show debuginfod enabled
@item show debuginfod enabled
Display whether @code{debuginfod enabled} is set to @code{on}, @code{off} or
@code{ask}.
Display whether @code{debuginfod enabled} is set to @code{on}, @code{off},
@code{ask} or @code{lazy}.
@kindex set debuginfod urls
@cindex configure debuginfod URLs

View File

@@ -1609,6 +1609,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
return dwarf2_frame_bfd_data.set (abfd, unit);
}
/* See frame.h. */
void
dwarf2_clear_frame_data (struct objfile *objfile)
{
bfd *abfd = objfile->obfd.get ();
if (gdb_bfd_requires_relocations (abfd))
dwarf2_frame_objfile_data.clear (objfile);
else
dwarf2_frame_bfd_data.clear (abfd);
}
/* Find the FDE for *PC. Return a pointer to the FDE, and store the
initial location associated with it into *PC. */

View File

@@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
extern const struct frame_base *
dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
/* Delete OBJFILEs comp_unit. */
extern void dwarf2_clear_frame_data (struct objfile * objfile);
/* Compute the DWARF CFA for a frame. */
CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);

View File

@@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
return {};
}
/* See index-cache.h. */
gdb::array_view<const gdb_byte>
index_cache::lookup_gdb_index_debuginfod (const char *index_path,
std::unique_ptr<index_cache_resource> *resource)
{
try
{
/* Try to map that file. */
index_cache_resource_mmap *mmap_resource
= new index_cache_resource_mmap (index_path);
/* Hand the resource to the caller. */
resource->reset (mmap_resource);
return gdb::array_view<const gdb_byte>
((const gdb_byte *) mmap_resource->mapping.get (),
mmap_resource->mapping.size ());
}
catch (const gdb_exception_error &except)
{
warning (_("Unable to read %s: %s"), index_path, except.what ());
}
return {};
}
#else /* !HAVE_SYS_MMAN_H */
/* See dwarf-index-cache.h. This is a no-op on unsupported systems. */
@@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
return {};
}
gdb::array_view<const gdb_byte>
index_cache::lookup_gdb_index_debuginfod (const char *index_path,
std::unique_ptr<index_cache_resource> *resource)
{
return {};
}
#endif
/* See dwarf-index-cache.h. */

View File

@@ -67,6 +67,19 @@ public:
lookup_gdb_index (const bfd_build_id *build_id,
std::unique_ptr<index_cache_resource> *resource);
/* Look for an index file located at INDEX_PATH in the debuginfod cache.
Unlike lookup_gdb_index, this function does not exit early if the
index cache has not been enabled.
If found, return the contents as an array_view and store the underlying
resources (allocated memory, mapped file, etc) in RESOURCE. The returned
array_view is valid as long as RESOURCE is not destroyed.
If no matching index file is found, return an empty array view. */
gdb::array_view<const gdb_byte>
lookup_gdb_index_debuginfod (const char *index_path,
std::unique_ptr<index_cache_resource> *resource);
/* Return the number of cache hits. */
unsigned int n_hits () const
{ return m_n_hits; }

View File

@@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
{
LONGEST length = read_initial_length (abfd, buf, bytes_read);
gdb_assert (cu_header->initial_length_size == 4
|| cu_header->initial_length_size == 8
|| cu_header->initial_length_size == 12);
if (cu_header != nullptr)
{
gdb_assert (cu_header->initial_length_size == 4
|| cu_header->initial_length_size == 8
|| cu_header->initial_length_size == 12);
if (cu_header->initial_length_size != *bytes_read)
complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
if (cu_header->initial_length_size != *bytes_read)
complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
}
*offset_size = (*bytes_read == 4) ? 4 : 8;
return length;
}
/* Read directory or file name entry format, starting with byte of
format count entries, ULEB128 pairs of entry formats, ULEB128 of
entries count and the entries themselves in the described entry
format. */
/* Like read_formatted_entries but the .debug_line and .debug_line_str
are stored in LINE_BUFP and LINE_STR_DATA. This is used for cases
where these sections are read from separate files without necessarily
having access to the entire debuginfo file they originate from. */
static void
read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
const gdb_byte **bufp, struct line_header *lh,
unsigned int offset_size,
void (*callback) (struct line_header *lh,
const char *name,
dir_index d_index,
unsigned int mod_time,
unsigned int length))
read_formatted_entries
(bfd *parent_bfd, const gdb_byte **line_bufp,
const gdb::array_view<const gdb_byte> line_str_data,
struct line_header *lh,
unsigned int offset_size,
void (*callback) (struct line_header *lh,
const char *name,
dir_index d_index,
unsigned int mod_time,
unsigned int length))
{
gdb_byte format_count, formati;
ULONGEST data_count, datai;
const gdb_byte *buf = *bufp;
const gdb_byte *buf = *line_bufp;
const gdb_byte *str_buf = line_str_data.data ();
const gdb_byte *format_header_data;
unsigned int bytes_read;
format_count = read_1_byte (abfd, buf);
format_count = read_1_byte (parent_bfd, buf);
buf += 1;
format_header_data = buf;
for (formati = 0; formati < format_count; formati++)
{
read_unsigned_leb128 (abfd, buf, &bytes_read);
read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
buf += bytes_read;
read_unsigned_leb128 (abfd, buf, &bytes_read);
read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
buf += bytes_read;
}
data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
buf += bytes_read;
for (datai = 0; datai < data_count; datai++)
{
@@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
for (formati = 0; formati < format_count; formati++)
{
ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
format += bytes_read;
ULONGEST form = read_unsigned_leb128 (abfd, format, &bytes_read);
ULONGEST form = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
format += bytes_read;
gdb::optional<const char *> string;
@@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
switch (form)
{
case DW_FORM_string:
string.emplace (read_direct_string (abfd, buf, &bytes_read));
string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
buf += bytes_read;
break;
case DW_FORM_line_strp:
{
const char *str
= per_objfile->read_line_string (buf, offset_size);
if (line_str_data.empty ())
error (_("Dwarf Error: DW_FORM_line_strp used without " \
"required section"));
if (line_str_data.size () <= offset_size)
error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
"of section .debug_line"));
ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
const char *str;
if (str_buf[str_offset] == '\0')
str = nullptr;
else
str = (const char *) (str_buf + str_offset);
string.emplace (str);
buf += offset_size;
break;
}
break;
case DW_FORM_data1:
uint.emplace (read_1_byte (abfd, buf));
uint.emplace (read_1_byte (parent_bfd, buf));
buf += 1;
break;
case DW_FORM_data2:
uint.emplace (read_2_bytes (abfd, buf));
uint.emplace (read_2_bytes (parent_bfd, buf));
buf += 2;
break;
case DW_FORM_data4:
uint.emplace (read_4_bytes (abfd, buf));
uint.emplace (read_4_bytes (parent_bfd, buf));
buf += 4;
break;
case DW_FORM_data8:
uint.emplace (read_8_bytes (abfd, buf));
uint.emplace (read_8_bytes (parent_bfd, buf));
buf += 8;
break;
@@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
break;
case DW_FORM_udata:
uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
buf += bytes_read;
break;
@@ -248,28 +267,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
}
*bufp = buf;
*line_bufp = buf;
}
/* See line-header.h. */
line_header_up
dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
dwarf2_per_objfile *per_objfile,
struct dwarf2_section_info *section,
const struct comp_unit_head *cu_header,
const char *comp_dir)
dwarf_decode_line_header (bfd *parent_bfd,
gdb::array_view<const gdb_byte> line_data,
gdb::array_view<const gdb_byte> line_str_data,
const gdb_byte **debug_line_ptr,
bool is_dwz,
const struct comp_unit_head *cu_header,
const char *comp_dir)
{
const gdb_byte *line_ptr;
const gdb_byte *line_ptr, *buf;
unsigned int bytes_read, offset_size;
int i;
const char *cur_dir, *cur_file;
bfd *abfd = section->get_bfd_owner ();
buf = *debug_line_ptr;
/* Make sure that at least there's room for the total_length field.
That could be 12 bytes long, but we're just going to fudge that. */
if (to_underlying (sect_off) + 4 >= section->size)
if (buf + 4 >= line_data.data () + line_data.size ())
{
dwarf2_statement_list_fits_in_line_number_section_complaint ();
return 0;
@@ -277,62 +298,65 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
line_header_up lh (new line_header (comp_dir));
lh->sect_off = sect_off;
lh->sect_off = (sect_offset) (buf - line_data.data ());
lh->offset_in_dwz = is_dwz;
line_ptr = section->buffer + to_underlying (sect_off);
line_ptr = buf;
/* Read in the header. */
LONGEST unit_length
= read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
= read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
&bytes_read, &offset_size);
line_ptr += bytes_read;
const gdb_byte *start_here = line_ptr;
if (line_ptr + unit_length > (section->buffer + section->size))
if (line_ptr + unit_length > buf + line_data.size ())
{
dwarf2_statement_list_fits_in_line_number_section_complaint ();
return 0;
}
const gdb_byte *start_here = line_ptr;
lh->statement_program_end = start_here + unit_length;
lh->version = read_2_bytes (abfd, line_ptr);
lh->version = read_2_bytes (parent_bfd, line_ptr);
line_ptr += 2;
if (lh->version > 5)
{
/* This is a version we don't understand. The format could have
changed in ways we don't handle properly so just punt. */
complaint (_("unsupported version in .debug_line section"));
return NULL;
return nullptr;
}
if (lh->version >= 5)
{
gdb_byte segment_selector_size;
/* Skip address size. */
read_1_byte (abfd, line_ptr);
read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
segment_selector_size = read_1_byte (abfd, line_ptr);
segment_selector_size = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
if (segment_selector_size != 0)
{
complaint (_("unsupported segment selector size %u "
"in .debug_line section"),
segment_selector_size);
return NULL;
return nullptr;
}
}
LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
line_ptr += offset_size;
lh->statement_program_start = line_ptr + header_length;
lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
if (lh->version >= 4)
{
lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
}
else
@@ -345,41 +369,47 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
"in `.debug_line' section"));
}
lh->default_is_stmt = read_1_byte (abfd, line_ptr);
lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
lh->line_base = read_1_signed_byte (abfd, line_ptr);
lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
line_ptr += 1;
lh->line_range = read_1_byte (abfd, line_ptr);
lh->line_range = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
lh->opcode_base = read_1_byte (abfd, line_ptr);
lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
lh->standard_opcode_lengths[0] = 1; /* This should never be used anyway. */
for (i = 1; i < lh->opcode_base; ++i)
{
lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
line_ptr += 1;
}
if (lh->version >= 5)
{
/* Read directory table. */
read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
offset_size,
[] (struct line_header *header, const char *name,
dir_index d_index, unsigned int mod_time,
unsigned int length)
read_formatted_entries
(parent_bfd, &line_ptr, line_str_data,
lh.get (), offset_size,
[] (struct line_header *header, const char *name,
dir_index d_index, unsigned int mod_time,
unsigned int length)
{
header->add_include_dir (name);
});
/* Read file name table. */
read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
offset_size,
[] (struct line_header *header, const char *name,
dir_index d_index, unsigned int mod_time,
unsigned int length)
read_formatted_entries
(parent_bfd, &line_ptr, line_str_data,
lh.get (), offset_size,
[] (struct line_header *header, const char *name,
dir_index d_index, unsigned int mod_time,
unsigned int length)
{
header->add_file_name (name, d_index, mod_time, length);
});
@@ -387,7 +417,7 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
else
{
/* Read directory table. */
while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
{
line_ptr += bytes_read;
lh->add_include_dir (cur_dir);
@@ -395,17 +425,17 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
line_ptr += bytes_read;
/* Read file name table. */
while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
{
unsigned int mod_time, length;
dir_index d_index;
line_ptr += bytes_read;
d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
line_ptr += bytes_read;
mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
line_ptr += bytes_read;
length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
line_ptr += bytes_read;
lh->add_file_name (cur_file, d_index, mod_time, length);
@@ -413,9 +443,40 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
line_ptr += bytes_read;
}
if (line_ptr > (section->buffer + section->size))
if (line_ptr > (buf + line_data.size ()))
complaint (_("line number info header doesn't "
"fit in `.debug_line' section"));
*debug_line_ptr += unit_length + offset_size;
return lh;
}
line_header_up
dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
dwarf2_per_objfile *per_objfile,
struct dwarf2_section_info *section,
const struct comp_unit_head *cu_header,
const char *comp_dir)
{
struct objfile *objfile = per_objfile->objfile;
struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
/* Read .debug_line. */
dwarf2_section_info *line_sec = &per_bfd->line;
bfd_size_type line_size = line_sec->get_size (objfile);
gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
/* Read .debug_line_str. */
dwarf2_section_info *line_str_sec = &per_bfd->line_str;
bfd_size_type line_str_size = line_str_sec->get_size (objfile);
gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
line_str_size);
const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
return dwarf_decode_line_header
(per_bfd->obfd, line, line_str, &line_ptr,
is_dwz, cu_header, comp_dir);
}

View File

@@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
const char *comp_dir);
/* Like above but the .debug_line and .debug_line_str are stored in
LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
statement program header within LINE_DATA. */
extern line_header_up dwarf_decode_line_header
(bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
gdb::array_view<const gdb_byte> line_str_data,
const gdb_byte **debug_line_ptr, bool is_dwz,
const comp_unit_head *cu_header, const char *comp_dir);
#endif /* DWARF2_LINE_HEADER_H */

View File

@@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
extern void dwarf2_build_frame_info (struct objfile *);
/* Query debuginfod for the .gdb_index associated with OBJFILE. If
successful, create an objfile to hold the .gdb_index information
and act as a placeholder until the full debuginfo needs to be
downloaded. */
extern bool dwarf2_has_separate_index (struct objfile *);
#endif /* DWARF2_PUBLIC_H */

View File

@@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
}
};
struct mapped_debug_line;
typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
struct dwarf2_gdb_index : public dwarf2_base_index_functions
{
/* This dumps minimal information about the index.
@@ -136,6 +139,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
gdb.dwarf2/gdb-index.exp testcase. */
void dump (struct objfile *objfile) override;
/* Calls do_expand_matching_symbols and downloads debuginfo if necessary. */
void expand_matching_symbols
(struct objfile *,
const lookup_name_info &lookup_name,
@@ -143,6 +147,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
int global,
symbol_compare_ftype *ordered_compare) override;
void do_expand_matching_symbols
(struct objfile *,
const lookup_name_info &lookup_name,
domain_enum domain,
int global,
symbol_compare_ftype *ordered_compare);
/* Calls do_expand_symtabs_matching and downloads debuginfo if necessary. */
bool expand_symtabs_matching
(struct objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
@@ -152,8 +164,68 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
block_search_flags search_flags,
domain_enum domain,
enum search_domain kind) override;
bool do_expand_symtabs_matching
(struct objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
const lookup_name_info *lookup_name,
gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
block_search_flags search_flags,
domain_enum domain,
enum search_domain kind);
/* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
debuginfo if necessary. */
void expand_all_symtabs (struct objfile *objfile) override;
/* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
debuginfo if necessary. */
struct symtab *find_last_source_symtab (struct objfile *objfile) override;
/* Filename information related to this .gdb_index. */
mapped_debug_line_up mdl;
/* Return true if any of the filenames in this .gdb_index's .debug_line
mapping match FILE_MATCHER. Initializes the mapping if necessary. */
bool filename_in_debug_line
(objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
};
void
dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
{
try
{
dwarf2_base_index_functions::expand_all_symtabs (objfile);
}
catch (gdb_exception e)
{
if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
exception_print (gdb_stderr, e);
else
read_full_dwarf_from_debuginfod (objfile, this);
}
}
struct symtab *
dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
{
try
{
return dwarf2_base_index_functions::find_last_source_symtab (objfile);
}
catch (gdb_exception e)
{
if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
exception_print (gdb_stderr, e);
else
read_full_dwarf_from_debuginfod (objfile, this);
return nullptr;
}
}
/* This dumps minimal information about the index.
It is called via "mt print objfiles".
One use is to verify .gdb_index has been loaded by the
@@ -315,7 +387,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
}
void
dwarf2_gdb_index::expand_matching_symbols
dwarf2_gdb_index::do_expand_matching_symbols
(struct objfile *objfile,
const lookup_name_info &name, domain_enum domain,
int global,
@@ -353,6 +425,29 @@ dwarf2_gdb_index::expand_matching_symbols
}, per_objfile);
}
void
dwarf2_gdb_index::expand_matching_symbols
(struct objfile *objfile,
const lookup_name_info &lookup_name,
domain_enum domain,
int global,
symbol_compare_ftype *ordered_compare)
{
try
{
do_expand_matching_symbols (objfile, lookup_name, domain,
global, ordered_compare);
}
catch (gdb_exception e)
{
if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
exception_print (gdb_stderr, e);
else
read_full_dwarf_from_debuginfod (objfile, this);
return;
}
}
/* Helper for dw2_expand_matching symtabs. Called on each symbol
matched, to expand corresponding CUs that were marked. IDX is the
index of the symbol name that matched. */
@@ -455,7 +550,7 @@ dw2_expand_marked_cus
}
bool
dwarf2_gdb_index::expand_symtabs_matching
dwarf2_gdb_index::do_expand_symtabs_matching
(struct objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
const lookup_name_info *lookup_name,
@@ -504,6 +599,54 @@ dwarf2_gdb_index::expand_symtabs_matching
return result;
}
bool
dwarf2_gdb_index::filename_in_debug_line
(objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
{
if (mdl == nullptr)
mdl.reset (new mapped_debug_line (objfile));
return mdl->contains_matching_filename (file_matcher);
}
bool
dwarf2_gdb_index::expand_symtabs_matching
(struct objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
const lookup_name_info *lookup_name,
gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
block_search_flags search_flags,
domain_enum domain,
enum search_domain kind)
{
if (objfile->flags & OBJF_READNEVER)
return false;
try
{
return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
symbol_matcher, expansion_notify,
search_flags, domain, kind);
}
catch (gdb_exception e)
{
if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
{
exception_print (gdb_stderr, e);
return false;
}
if (file_matcher != nullptr
&& !filename_in_debug_line (objfile, file_matcher))
return true;
read_full_dwarf_from_debuginfod (objfile, this);
return true;
}
}
quick_symbol_functions_up
mapped_gdb_index::make_quick_functions () const
{
@@ -797,28 +940,32 @@ dwarf2_read_gdb_index
/* If there is a .dwz file, read it so we can get its CU list as
well. */
dwz = dwarf2_get_dwz_file (per_bfd);
if (dwz != NULL)
if (get_gdb_index_contents_dwz != nullptr)
{
mapped_gdb_index dwz_map;
const gdb_byte *dwz_types_ignore;
offset_type dwz_types_elements_ignore;
dwz = dwarf2_get_dwz_file (per_bfd);
gdb::array_view<const gdb_byte> dwz_index_content
= get_gdb_index_contents_dwz (objfile, dwz);
if (dwz_index_content.empty ())
return 0;
if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
1, dwz_index_content, &dwz_map,
&dwz_list, &dwz_list_elements,
&dwz_types_ignore,
&dwz_types_elements_ignore))
if (dwz != nullptr)
{
warning (_("could not read '.gdb_index' section from %s; skipping"),
bfd_get_filename (dwz->dwz_bfd.get ()));
return 0;
gdb::array_view<const gdb_byte> dwz_index_content
= get_gdb_index_contents_dwz (objfile, dwz);
if (dwz_index_content.empty ())
return 0;
if (!read_gdb_index_from_buffer (bfd_get_filename
(dwz->dwz_bfd.get ()),
1, dwz_index_content, &dwz_map,
&dwz_list, &dwz_list_elements,
&dwz_types_ignore,
&dwz_types_elements_ignore))
{
warning (_("could not read '.gdb_index' section from %s; skipping"),
bfd_get_filename (dwz->dwz_bfd.get ()));
return 0;
}
}
}

View File

@@ -34,6 +34,7 @@
#include "dwarf2/attribute.h"
#include "dwarf2/comp-unit-head.h"
#include "dwarf2/cu.h"
#include "dwarf2/frame.h"
#include "dwarf2/index-cache.h"
#include "dwarf2/index-common.h"
#include "dwarf2/leb.h"
@@ -80,6 +81,7 @@
#include "gdbsupport/gdb_optional.h"
#include "gdbsupport/underlying.h"
#include "gdbsupport/hash_enum.h"
#include "gdbsupport/scoped_mmap.h"
#include "filename-seen-cache.h"
#include "producer.h"
#include <fcntl.h>
@@ -95,6 +97,8 @@
#include "split-name.h"
#include "gdbsupport/parallel-for.h"
#include "gdbsupport/thread-pool.h"
#include "inferior.h"
#include "debuginfod-support.h"
/* When == 1, print basic high level tracing messages.
When > 1, be more verbose.
@@ -2112,6 +2116,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
return this_cu->file_names;
}
#if !HAVE_SYS_MMAN_H
bool
mapped_debug_line::contains_matching_filename
(gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
{
return false;
}
gdb::array_view<const gdb_byte>
mapped_debug_line::read_debug_line_separate
(char *filename, std::unique_ptr<index_cache_resource> *resource)
{
return {};
}
bool
mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
{
return false;
}
#else /* !HAVE_SYS_MMAN_H */
struct line_resource_mmap final : public index_cache_resource
{
/* Try to mmap FILENAME. Throw an exception on failure, including if the
file doesn't exist. */
line_resource_mmap (const char *filename)
: mapping (mmap_file (filename))
{}
scoped_mmap mapping;
};
/* See read.h. */
bool
mapped_debug_line::contains_matching_filename
(gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
{
for (line_header_up &lh : line_headers)
for (file_entry &fe : lh->file_names ())
{
const char *filename = fe.name;
if (file_matcher (fe.name, false))
return true;
bool basename_match = file_matcher (lbasename (fe.name), true);
if (!basenames_may_differ && !basename_match)
continue;
/* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
until DWARF5. Since we don't have access to the CU at this
point we just check for a partial match on the filename.
If there is a match, the full debuginfo will be downloaded
ane the match will be re-evalute with DW_AT_comp_dir. */
if (lh->version < 5 && fe.d_index == 0)
return basename_match;
const char *dirname = fe.include_dir (&*lh);
std::string fullname;
if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
fullname = filename;
else
fullname = std::string (dirname) + SLASH_STRING + filename;
gdb::unique_xmalloc_ptr<char> rewritten
= rewrite_source_path (fullname.c_str ());
if (rewritten != nullptr)
fullname = rewritten.release ();
if (file_matcher (fullname.c_str (), false))
return true;
}
return false;
}
/* See read.h. */
gdb::array_view<const gdb_byte>
mapped_debug_line::read_debug_line_separate
(char *filename, std::unique_ptr<index_cache_resource> *resource)
{
if (filename == nullptr)
return {};
try
{
line_resource_mmap *mmap_resource
= new line_resource_mmap (filename);
resource->reset (mmap_resource);
return gdb::array_view<const gdb_byte>
((const gdb_byte *) mmap_resource->mapping.get (),
mmap_resource->mapping.size ());
}
catch (const gdb_exception &except)
{
exception_print (gdb_stderr, except);
}
return {};
}
/* See read.h. */
bool
mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
{
const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
if (build_id == nullptr)
return false;
gdb::unique_xmalloc_ptr<char> line_path;
scoped_fd line_fd = debuginfod_section_query (build_id->data,
build_id->size,
bfd_get_filename
(objfile->obfd.get ()),
".debug_line",
&line_path);
if (line_fd.get () < 0)
return false;
gdb::unique_xmalloc_ptr<char> line_str_path;
scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
build_id->size,
bfd_get_filename
(objfile->obfd.get ()),
".debug_line_str",
&line_str_path);
line_data = read_debug_line_separate (line_path.get (), &line_resource);
line_str_data = read_debug_line_separate (line_str_path.get (),
&line_str_resource);
const gdb_byte *line_ptr = line_data.data ();
while (line_ptr < line_data.data () + line_data.size ())
{
line_header_up lh
= dwarf_decode_line_header (objfile->obfd.get (),
line_data, line_str_data,
&line_ptr, false,
nullptr, nullptr);
line_headers.emplace_back (lh.release ());
}
return true;
}
#endif /* !HAVE_SYS_MMAN_H */
mapped_debug_line::mapped_debug_line (objfile *objfile)
{
if (!read_debug_line_from_debuginfod (objfile))
line_headers.clear ();
}
/* A helper for the "quick" functions which computes and caches the
real path for a given file name from the line table. */
@@ -3168,7 +3336,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
}
struct compunit_symtab *
dwarf2_base_index_functions::find_pc_sect_compunit_symtab
dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
(struct objfile *objfile,
struct bound_minimal_symbol msymbol,
CORE_ADDR pc,
@@ -3199,6 +3367,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
return result;
}
struct compunit_symtab *
dwarf2_base_index_functions::find_pc_sect_compunit_symtab
(struct objfile *objfile,
struct bound_minimal_symbol msymbol,
CORE_ADDR pc,
struct obj_section *section,
int warn_if_readin)
{
if (objfile->flags & OBJF_READNEVER)
return nullptr;
try
{
return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
section, warn_if_readin);
}
catch (gdb_exception e)
{
if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
exception_print (gdb_stderr, e);
else
read_full_dwarf_from_debuginfod (objfile, this);
return nullptr;
}
}
void
dwarf2_base_index_functions::map_symbol_filenames
(struct objfile *objfile,
@@ -3355,6 +3549,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
}
/* Query debuginfod for the .gdb_index matching OBJFILE's build-id. Return the
contents if successful. */
static gdb::array_view<const gdb_byte>
get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
{
const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
if (build_id == nullptr)
return {};
gdb::unique_xmalloc_ptr<char> index_path;
scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
bfd_get_filename
(objfile->obfd.get ()),
".gdb_index",
&index_path);
if (fd.get () < 0)
return {};
return global_index_cache.lookup_gdb_index_debuginfod
(index_path.get (), &per_bfd->index_cache_res);
}
static quick_symbol_functions_up make_cooked_index_funcs ();
/* See dwarf2/public.h. */
@@ -3420,10 +3637,106 @@ dwarf2_initialize_objfile (struct objfile *objfile)
return;
}
if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
&& dwarf2_read_gdb_index (per_objfile,
get_gdb_index_contents_from_debuginfod,
nullptr))
{
dwarf_read_debug_printf ("found .gdb_index from debuginfod");
objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
return;
}
global_index_cache.miss ();
objfile->qf.push_front (make_cooked_index_funcs ());
}
/* See read.h. */
void
read_full_dwarf_from_debuginfod (struct objfile *objfile,
dwarf2_base_index_functions *fncs)
{
gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
const char *filename;
gdb_bfd_ref_ptr debug_bfd;
gdb::unique_xmalloc_ptr<char> symfile_path;
scoped_fd fd;
if (build_id == nullptr)
goto unset;
filename = bfd_get_filename (objfile->obfd.get ());
fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
filename, &symfile_path);
if (fd.get () < 0)
goto unset;
/* Separate debuginfo successfully retrieved from server. */
debug_bfd = symfile_bfd_open (symfile_path.get ());
if (debug_bfd == nullptr
|| !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
{
warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
filename);
goto unset;
}
/* This may also trigger a dwz download. */
symbol_file_add_separate (debug_bfd, symfile_path.get (),
current_inferior ()->symfile_flags, objfile);
/* Clear frame data so it can be recalculated using DWARF. */
dwarf2_clear_frame_data (objfile);
unset:
objfile->flags &= ~OBJF_DOWNLOAD_DEFERRED;
/* Avoid reading this objfile's index from now on. If available the
separate debug objfile's index will be used instead, since it actually
contains the symbols and CUs referenced in the index. */
objfile->remove_partial_symbol (fncs);
}
/* See public.h. */
bool
dwarf2_has_separate_index (struct objfile *objfile)
{
if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
return true;
if (objfile->flags & OBJF_MAINLINE)
return false;
if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
return false;
gdb::unique_xmalloc_ptr<char> index_path;
const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
if (build_id == nullptr)
return false;
scoped_fd fd = debuginfod_section_query (build_id->data,
build_id->size,
bfd_get_filename
(objfile->obfd.get ()),
".gdb_index",
&index_path);
if (fd.get () < 0)
return false;
/* We found a separate .gdb_index file so a separate debuginfo file
should exist, but we don't want to download it until necessary.
Attach the index to this objfile and defer the debuginfo download
until gdb needs to expand symtabs referenced by the index. */
objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
dwarf2_initialize_objfile (objfile);
return true;
}
/* Build a partial symbol table. */

View File

@@ -34,6 +34,7 @@
#include "gdbsupport/hash_enum.h"
#include "gdbsupport/function-view.h"
#include "gdbsupport/packed.h"
#include "dwarf2/line-header.h"
/* Hold 'maintenance (set|show) dwarf' commands. */
extern struct cmd_list_element *set_dwarf_cmdlist;
@@ -866,6 +867,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
override final;
struct compunit_symtab *do_find_pc_sect_compunit_symtab
(struct objfile *objfile, struct bound_minimal_symbol msymbol,
CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
struct compunit_symtab *find_compunit_symtab_by_address
(struct objfile *objfile, CORE_ADDR address) override
{
@@ -942,4 +947,40 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
dwarf2_section_info *section,
addrmap *mutable_map);
/* If OBJFILE contains information from a separately downloaded .gdb_index,
attempt to download the full debuginfo. */
extern void read_full_dwarf_from_debuginfod (struct objfile *,
dwarf2_base_index_functions *);
struct mapped_debug_line
{
mapped_debug_line (objfile *objfile);
/* Return true if any of the mapped .debug_line's filenames match
FILE_MATCHER. */
bool contains_matching_filename
(gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
private:
std::vector<line_header_up> line_headers;
gdb::array_view<const gdb_byte> line_data;
gdb::array_view<const gdb_byte> line_str_data;
std::unique_ptr<index_cache_resource> line_resource;
std::unique_ptr<index_cache_resource> line_str_resource;
/* Download the .debug_line and .debug_line_str associated with OBJFILE
and populate line_headers. */
bool read_debug_line_from_debuginfod (objfile *objfile);
/* Initialize line_data and line_str_data with the .debug_line and
.debug_line_str downloaded read_debug_line_from_debuginfod. */
gdb::array_view<const gdb_byte> read_debug_line_separate
(char *filename, std::unique_ptr<index_cache_resource> *resource);
};
#endif /* DWARF2READ_H */

View File

@@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
section = get_containing_section ();
gdb_assert (!section->is_virtual);
}
gdb_assert (section->s.section != nullptr);
if (section->s.section == nullptr)
error (_("Can't find owner of DWARF section."));
return section->s.section->owner;
}

View File

@@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
symbol_file_add_separate (debug_bfd, debugfile.c_str (),
symfile_flags, objfile);
}
else
else if (!dwarf2_has_separate_index (objfile))
{
has_dwarf2 = false;
const struct bfd_build_id *build_id

View File

@@ -194,6 +194,28 @@ mi_ui_out::do_redirect (ui_file *outstream)
m_streams.pop_back ();
}
void
mi_ui_out::do_redirect_to_buffer (buffer_file *buf_file)
{
if (buf_file != nullptr)
{
gdb_assert (m_streams.size () >= 1);
ui_file *stream = m_streams.back ();
string_file *sstream = dynamic_cast<string_file *>(stream);
if (sstream != nullptr)
{
buf_file->write (sstream->data (), sstream->size ());
sstream->clear ();
}
buf_file->set_stream (stream);
m_streams.push_back (buf_file);
}
else
m_streams.pop_back ();
}
void
mi_ui_out::field_separator ()
{

View File

@@ -79,6 +79,7 @@ protected:
virtual void do_wrap_hint (int indent) override;
virtual void do_flush () override;
virtual void do_redirect (struct ui_file *outstream) override;
virtual void do_redirect_to_buffer (buffer_file *buf_file) override;
virtual bool do_is_mi_like_p () const override
{ return true; }

View File

@@ -60,6 +60,10 @@ enum objfile_flag : unsigned
/* User requested that we do not read this objfile's symbolic
information. */
OBJF_READNEVER = 1 << 6,
/* A separate .gdb_index has been downloaded for this objfile.
Debuginfo for this objfile can be downloaded when required. */
OBJF_DOWNLOAD_DEFERRED = 1 << 7,
};
DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);

View File

@@ -52,6 +52,7 @@
#include "gdb_bfd.h"
#include "btrace.h"
#include "gdbsupport/pathstuff.h"
#include "symfile.h"
#include <algorithm>
#include <vector>

View File

@@ -613,6 +613,17 @@ public:
/* See quick_symbol_functions. */
void require_partial_symbols (bool verbose);
/* Remove TARGET from this objfile's collection of quick_symbol_functions. */
void remove_partial_symbol (quick_symbol_functions *target)
{
for (quick_symbol_functions_up &qf_up : qf)
if (qf_up.get () == target)
{
qf.remove (qf_up);
return;
}
}
/* Return the relocation offset applied to SECTION. */
CORE_ADDR section_offset (bfd_section *section) const
{
@@ -699,13 +710,20 @@ public:
private:
using qf_list = std::forward_list<quick_symbol_functions_up>;
using unwrapping_qf_range = iterator_range<unwrapping_iterator<qf_list::iterator>>;
using qf_safe_range = basic_safe_range<unwrapping_qf_range>;
/* Ensure that partial symbols have been read and return the "quick" (aka
partial) symbol functions for this symbol reader. */
const std::forward_list<quick_symbol_functions_up> &
qf_safe_range
qf_require_partial_symbols ()
{
this->require_partial_symbols (true);
return qf;
return qf_safe_range
(unwrapping_qf_range
(unwrapping_iterator<qf_list::iterator> (qf.begin ()),
unwrapping_iterator<qf_list::iterator> (qf.end ())));
}
public:

View File

@@ -139,19 +139,19 @@ program_space::free_all_objfiles ()
void
program_space::add_objfile (std::unique_ptr<objfile> &&objfile,
struct objfile *before)
struct objfile *after)
{
if (before == nullptr)
if (after == nullptr)
objfiles_list.push_back (std::move (objfile));
else
{
auto iter = std::find_if (objfiles_list.begin (), objfiles_list.end (),
[=] (const std::unique_ptr<::objfile> &objf)
{
return objf.get () == before;
return objf.get () == after;
});
gdb_assert (iter != objfiles_list.end ());
objfiles_list.insert (iter, std::move (objfile));
objfiles_list.insert (++iter, std::move (objfile));
}
}

View File

@@ -40,56 +40,141 @@ struct address_space;
struct program_space;
struct so_list;
typedef std::list<std::unique_ptr<objfile>> objfile_list;
/* An iterator that wraps an iterator over std::unique_ptr, and dereferences
the returned object. This is useful for iterating over a list of shared
pointers and returning raw pointers -- which helped avoid touching a lot
of code when changing how objfiles are managed. */
/* An iterator that wraps an iterator over std::unique_ptr<objfile>,
and dereferences the returned object. This is useful for iterating
over a list of shared pointers and returning raw pointers -- which
helped avoid touching a lot of code when changing how objfiles are
managed. */
class unwrapping_objfile_iterator
template<typename UniquePtrIter>
class unwrapping_iterator
{
public:
typedef unwrapping_iterator self_type;
typedef typename UniquePtrIter::value_type::pointer value_type;
typedef typename UniquePtrIter::reference reference;
typedef typename UniquePtrIter::pointer pointer;
typedef typename UniquePtrIter::iterator_category iterator_category;
typedef typename UniquePtrIter::difference_type difference_type;
typedef unwrapping_objfile_iterator self_type;
unwrapping_iterator (UniquePtrIter iter)
: m_iter (std::move (iter))
{
}
value_type operator* () const
{
return m_iter->get ();
}
unwrapping_iterator operator++ ()
{
++m_iter;
return *this;
}
bool operator!= (const unwrapping_iterator &other) const
{
return m_iter != other.m_iter;
}
private:
/* The underlying iterator. */
UniquePtrIter m_iter;
};
typedef std::list<std::unique_ptr<objfile>> objfile_list;
/* An reverse iterator that wraps an iterator over objfile_list, and
dereferences the returned object. This is useful for reverse iterating
over a list of shared pointers and returning raw pointers -- which helped
avoid touching a lot of code when changing how objfiles are managed. */
class unwrapping_reverse_objfile_iterator
{
public:
typedef unwrapping_reverse_objfile_iterator self_type;
typedef typename ::objfile *value_type;
typedef typename ::objfile &reference;
typedef typename ::objfile **pointer;
typedef typename objfile_list::iterator::iterator_category iterator_category;
typedef typename objfile_list::iterator::difference_type difference_type;
unwrapping_objfile_iterator (objfile_list::iterator iter)
: m_iter (std::move (iter))
{
}
objfile *operator* () const
value_type operator* () const
{
return m_iter->get ();
}
unwrapping_objfile_iterator operator++ ()
unwrapping_reverse_objfile_iterator operator++ ()
{
++m_iter;
if (m_iter != m_begin)
--m_iter;
else
{
/* We can't decrement M_ITER since it is the begin iterator of the
objfile list. Set M_ITER to the list's end iterator to indicate
this is now one-past-the-end. */
m_iter = m_end;
/* Overwrite M_BEGIN to avoid possibly copying an invalid iterator. */
m_begin = m_end;
}
return *this;
}
bool operator!= (const unwrapping_objfile_iterator &other) const
bool operator!= (const unwrapping_reverse_objfile_iterator &other) const
{
return m_iter != other.m_iter;
}
private:
/* Return an unwrapping reverse iterator starting at the last element of
OBJF_LIST. */
static unwrapping_reverse_objfile_iterator begin (objfile_list &objf_list)
{
auto begin = objf_list.begin ();
auto end = objf_list.end ();
auto rev_begin = objf_list.end ();
/* The underlying iterator. */
objfile_list::iterator m_iter;
/* Start REV_BEGIN on the last objfile in OBJF_LIST. */
if (begin != end)
--rev_begin;
return unwrapping_reverse_objfile_iterator (rev_begin, begin, end);
}
/* Return a one-past-the-end unwrapping reverse iterator. */
static unwrapping_reverse_objfile_iterator end (objfile_list &objf_list)
{
return unwrapping_reverse_objfile_iterator (objf_list.end (),
objf_list.end (),
objf_list.end ());
}
private:
/* This begin and end methods should be used to create these objects. */
unwrapping_reverse_objfile_iterator (objfile_list::iterator iter,
objfile_list::iterator begin,
objfile_list::iterator end)
: m_iter (std::move (iter)), m_begin (std::move (begin)),
m_end (std::move (end))
{
}
/* The underlying iterator. */
objfile_list::iterator m_iter;
/* The underlying iterator pointing to the first objfile in the sequence. Used
to track when to stop decrementing M_ITER. */
objfile_list::iterator m_begin;
/* The underlying iterator's one-past-the-end. */
objfile_list::iterator m_end;
};
/* A range that returns unwrapping_iterators. */
/* A range that returns unwrapping_objfile_iterators. */
using unwrapping_objfile_range = iterator_range<unwrapping_objfile_iterator>;
using unwrapping_objfile_range
= iterator_range<unwrapping_iterator<objfile_list::iterator>>;
/* A program space represents a symbolic view of an address space.
Roughly speaking, it holds all the data associated with a
@@ -209,11 +294,12 @@ struct program_space
objfiles_range objfiles ()
{
return objfiles_range
(unwrapping_objfile_iterator (objfiles_list.begin ()),
unwrapping_objfile_iterator (objfiles_list.end ()));
(unwrapping_iterator<objfile_list::iterator> (objfiles_list.begin ()),
unwrapping_iterator<objfile_list::iterator> (objfiles_list.end ()));
}
using objfiles_safe_range = basic_safe_range<objfiles_range>;
using objfiles_reverse_range = iterator_range<unwrapping_reverse_objfile_iterator>;
using objfiles_safe_reverse_range = basic_safe_range<objfiles_reverse_range>;
/* An iterable object that can be used to iterate over all objfiles.
The basic use is in a foreach, like:
@@ -221,20 +307,25 @@ struct program_space
for (objfile *objf : pspace->objfiles_safe ()) { ... }
This variant uses a basic_safe_iterator so that objfiles can be
deleted during iteration. */
objfiles_safe_range objfiles_safe ()
deleted during iteration.
The use of a reverse iterator helps ensure that separate debug
objfiles are deleted before their parent objfile. This prevents
the invalidation of an iterator due to the deletion of a parent
objfile. */
objfiles_safe_reverse_range objfiles_safe ()
{
return objfiles_safe_range
(objfiles_range
(unwrapping_objfile_iterator (objfiles_list.begin ()),
unwrapping_objfile_iterator (objfiles_list.end ())));
return objfiles_safe_reverse_range
(objfiles_reverse_range
(unwrapping_reverse_objfile_iterator::begin (objfiles_list),
unwrapping_reverse_objfile_iterator::end (objfiles_list)));
}
/* Add OBJFILE to the list of objfiles, putting it just before
BEFORE. If BEFORE is nullptr, it will go at the end of the
/* Add OBJFILE to the list of objfiles, putting it just after
AFTER. If AFTER is nullptr, it will go at the end of the
list. */
void add_objfile (std::unique_ptr<objfile> &&objfile,
struct objfile *before);
struct objfile *after);
/* Remove OBJFILE from the list of objfiles. */
void remove_objfile (struct objfile *objfile);

View File

@@ -220,7 +220,8 @@ static void print_frame_local_vars (frame_info_ptr frame,
const char *regexp, const char *t_regexp,
int num_tabs, struct ui_file *stream);
static void print_frame (const frame_print_options &opts,
static void print_frame (struct ui_out *uiout,
const frame_print_options &opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
struct symtab_and_line sal);
@@ -1020,16 +1021,15 @@ get_user_print_what_frame_info (gdb::optional<enum print_what> *what)
Used in "where" output, and to emit breakpoint or step
messages. */
void
print_frame_info (const frame_print_options &fp_opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
int set_current_sal)
static void
do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
int set_current_sal)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
int source_print;
int location_print;
struct ui_out *uiout = current_uiout;
if (!current_uiout->is_mi_like_p ()
&& fp_opts.print_frame_info != print_frame_info_auto)
@@ -1105,7 +1105,8 @@ print_frame_info (const frame_print_options &fp_opts,
|| print_what == LOC_AND_ADDRESS
|| print_what == SHORT_LOCATION);
if (location_print || !sal.symtab)
print_frame (fp_opts, frame, print_level, print_what, print_args, sal);
print_frame (uiout, fp_opts, frame, print_level,
print_what, print_args, sal);
source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
@@ -1185,6 +1186,23 @@ print_frame_info (const frame_print_options &fp_opts,
gdb_flush (gdb_stdout);
}
/* Redirect output to a temporary buffer for the duration
of do_print_frame_info. */
void
print_frame_info (const frame_print_options &fp_opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
int set_current_sal)
{
using ftype = void (ui_out *, const frame_print_options &, frame_info_ptr,
int, enum print_what, int, int);
do_with_buffered_output<ftype> (do_print_frame_info, current_uiout,
fp_opts, frame, print_level, print_what,
print_args, set_current_sal);
}
/* See stack.h. */
void
@@ -1309,13 +1327,13 @@ find_frame_funname (frame_info_ptr frame, enum language *funlang,
}
static void
print_frame (const frame_print_options &fp_opts,
print_frame (struct ui_out *uiout,
const frame_print_options &fp_opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
struct symtab_and_line sal)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
struct ui_out *uiout = current_uiout;
enum language funlang = language_unknown;
struct value_print_options opts;
struct symbol *func;

View File

@@ -109,9 +109,9 @@ objfile::has_unexpanded_symtabs ()
objfile_debug_name (this));
bool result = false;
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
if (iter->has_unexpanded_symtabs (this))
if (qf->has_unexpanded_symtabs (this))
{
result = true;
break;
@@ -134,9 +134,9 @@ objfile::find_last_source_symtab ()
gdb_printf (gdb_stdlog, "qf->find_last_source_symtab (%s)\n",
objfile_debug_name (this));
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
retval = iter->find_last_source_symtab (this);
retval = qf->find_last_source_symtab (this);
if (retval != nullptr)
break;
}
@@ -167,8 +167,8 @@ objfile::forget_cached_source_info ()
}
}
for (const auto &iter : qf_require_partial_symbols ())
iter->forget_cached_source_info (this);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->forget_cached_source_info (this);
}
bool
@@ -214,17 +214,17 @@ objfile::map_symtabs_matching_filename
return result;
};
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
if (!iter->expand_symtabs_matching (this,
match_one_filename,
nullptr,
nullptr,
on_expansion,
(SEARCH_GLOBAL_BLOCK
| SEARCH_STATIC_BLOCK),
UNDEF_DOMAIN,
ALL_DOMAIN))
if (!qf->expand_symtabs_matching (this,
match_one_filename,
nullptr,
nullptr,
on_expansion,
(SEARCH_GLOBAL_BLOCK
| SEARCH_STATIC_BLOCK),
UNDEF_DOMAIN,
ALL_DOMAIN))
{
retval = false;
break;
@@ -283,18 +283,18 @@ objfile::lookup_symbol (block_enum kind, const char *name, domain_enum domain)
return true;
};
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
if (!iter->expand_symtabs_matching (this,
nullptr,
&lookup_name,
nullptr,
search_one_symtab,
kind == GLOBAL_BLOCK
? SEARCH_GLOBAL_BLOCK
: SEARCH_STATIC_BLOCK,
domain,
ALL_DOMAIN))
if (!qf->expand_symtabs_matching (this,
nullptr,
&lookup_name,
nullptr,
search_one_symtab,
kind == GLOBAL_BLOCK
? SEARCH_GLOBAL_BLOCK
: SEARCH_STATIC_BLOCK,
domain,
ALL_DOMAIN))
break;
}
@@ -314,8 +314,8 @@ objfile::print_stats (bool print_bcache)
gdb_printf (gdb_stdlog, "qf->print_stats (%s, %d)\n",
objfile_debug_name (this), print_bcache);
for (const auto &iter : qf_require_partial_symbols ())
iter->print_stats (this, print_bcache);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->print_stats (this, print_bcache);
}
void
@@ -340,16 +340,16 @@ objfile::expand_symtabs_for_function (const char *func_name)
lookup_name_info base_lookup (func_name, symbol_name_match_type::FULL);
lookup_name_info lookup_name = base_lookup.make_ignore_params ();
for (const auto &iter : qf_require_partial_symbols ())
iter->expand_symtabs_matching (this,
nullptr,
&lookup_name,
nullptr,
nullptr,
(SEARCH_GLOBAL_BLOCK
| SEARCH_STATIC_BLOCK),
VAR_DOMAIN,
ALL_DOMAIN);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->expand_symtabs_matching (this,
nullptr,
&lookup_name,
nullptr,
nullptr,
(SEARCH_GLOBAL_BLOCK
| SEARCH_STATIC_BLOCK),
VAR_DOMAIN,
ALL_DOMAIN);
}
void
@@ -359,8 +359,8 @@ objfile::expand_all_symtabs ()
gdb_printf (gdb_stdlog, "qf->expand_all_symtabs (%s)\n",
objfile_debug_name (this));
for (const auto &iter : qf_require_partial_symbols ())
iter->expand_all_symtabs (this);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->expand_all_symtabs (this);
}
void
@@ -377,16 +377,16 @@ objfile::expand_symtabs_with_fullname (const char *fullname)
return filename_cmp (basenames ? basename : fullname, filename) == 0;
};
for (const auto &iter : qf_require_partial_symbols ())
iter->expand_symtabs_matching (this,
file_matcher,
nullptr,
nullptr,
nullptr,
(SEARCH_GLOBAL_BLOCK
| SEARCH_STATIC_BLOCK),
UNDEF_DOMAIN,
ALL_DOMAIN);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->expand_symtabs_matching (this,
file_matcher,
nullptr,
nullptr,
nullptr,
(SEARCH_GLOBAL_BLOCK
| SEARCH_STATIC_BLOCK),
UNDEF_DOMAIN,
ALL_DOMAIN);
}
void
@@ -402,9 +402,9 @@ objfile::expand_matching_symbols
domain_name (domain), global,
host_address_to_string (ordered_compare));
for (const auto &iter : qf_require_partial_symbols ())
iter->expand_matching_symbols (this, name, domain, global,
ordered_compare);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->expand_matching_symbols (this, name, domain, global,
ordered_compare);
}
bool
@@ -429,10 +429,10 @@ objfile::expand_symtabs_matching
host_address_to_string (&expansion_notify),
search_domain_name (kind));
for (const auto &iter : qf_require_partial_symbols ())
if (!iter->expand_symtabs_matching (this, file_matcher, lookup_name,
symbol_matcher, expansion_notify,
search_flags, domain, kind))
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
if (!qf->expand_symtabs_matching (this, file_matcher, lookup_name,
symbol_matcher, expansion_notify,
search_flags, domain, kind))
return false;
return true;
}
@@ -454,10 +454,10 @@ objfile::find_pc_sect_compunit_symtab (struct bound_minimal_symbol msymbol,
host_address_to_string (section),
warn_if_readin);
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
retval = iter->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
warn_if_readin);
retval = qf->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
warn_if_readin);
if (retval != nullptr)
break;
}
@@ -482,8 +482,8 @@ objfile::map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
objfile_debug_name (this),
need_fullname);
for (const auto &iter : qf_require_partial_symbols ())
iter->map_symbol_filenames (this, fun, need_fullname);
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
qf->map_symbol_filenames (this, fun, need_fullname);
}
struct compunit_symtab *
@@ -496,9 +496,9 @@ objfile::find_compunit_symtab_by_address (CORE_ADDR address)
hex_string (address));
struct compunit_symtab *result = NULL;
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
result = iter->find_compunit_symtab_by_address (this, address);
result = qf->find_compunit_symtab_by_address (this, address);
if (result != nullptr)
break;
}
@@ -521,10 +521,10 @@ objfile::lookup_global_symbol_language (const char *name,
enum language result = language_unknown;
*symbol_found_p = false;
for (const auto &iter : qf_require_partial_symbols ())
for (quick_symbol_functions *qf : qf_require_partial_symbols ())
{
result = iter->lookup_global_symbol_language (this, name, domain,
symbol_found_p);
result = qf->lookup_global_symbol_language (this, name, domain,
symbol_found_p);
if (*symbol_found_p)
break;
}

View File

@@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
static void
finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
{
struct objfile *parent = objfile->separate_debug_objfile_backlink;
bool was_deferred
= (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
/* If this is the main symbol file we have to clean up all users of the
old main symbol file. Otherwise it is sufficient to fixup all the
breakpoints that may have been redefined by this symbol file. */
@@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
clear_symtab_users (add_flags);
}
else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
&& !was_deferred)
{
breakpoint_re_set ();
}

View File

@@ -42,6 +42,23 @@ if { [gdb_compile "$sourcetmp" "${binfile}2" executable {debug build-id}] != ""
return -1
}
set sectfile "section"
set sectsrc $srcdir/$subdir/section.c
set sectexec [standard_output_file $sectfile]
set libfile "libsection"
set libsrc $srcdir/$subdir/$libfile.c
set lib_sl [standard_output_file $libfile.sl]
set lib_opts [list debug build-id ]
set exec_opts [list debug build-id shlib=$lib_sl]
if { [gdb_compile_shlib $libsrc $lib_sl $lib_opts] != ""
|| [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
untested "failed to compile"
return -1
}
# Write some assembly that just has a .gnu_debugaltlink section.
# Copied from testsuite/gdb.dwarf2/dwzbuildid.exp.
proc write_just_debugaltlink {filename dwzname buildid} {
@@ -94,6 +111,7 @@ proc write_dwarf_file {filename buildid {value 99}} {
}
set corefile [standard_output_file "corefile"]
set lazy_support -1
# Setup the global variable DEBUGDIR as a directory containing the
# debug information for the test executable.
@@ -103,6 +121,8 @@ set corefile [standard_output_file "corefile"]
# running.
proc_with_prefix no_url { } {
global binfile outputdir debugdir
global sectexec lib_sl libfile lazy_support
global gdb_prompt
setenv DEBUGINFOD_URLS ""
@@ -119,11 +139,18 @@ proc_with_prefix no_url { } {
return -1
}
if { [gdb_gnu_strip_debug $lib_sl ""] != 0} {
fail "strip shlib debuginfo"
return -1
}
set debugdir [standard_output_file "debug"]
set debuginfo [standard_output_file "fetch_src_and_symbols.debug"]
set debuginfo_shlib [standard_output_file $libfile.sl.debug]
file mkdir $debugdir
file rename -force $debuginfo $debugdir
file rename -force $debuginfo_shlib $debugdir
# Test that GDB cannot find symbols without debuginfod.
clean_restart $binfile
@@ -171,6 +198,25 @@ proc_with_prefix no_url { } {
clean_restart
gdb_test "core $::corefile" ".*in ?? ().*" "file [file tail $::corefile]"
clean_restart
# Check for lazy downloading support.
gdb_test_multiple "set debuginfod enabled lazy" "check for lazy" {
-re ".*lazy downloading is not compiled into GDB.*\n.*${gdb_prompt} $" {
set lazy_support 0
}
-re ".*${gdb_prompt} $" {
set lazy_support 1
}
}
if {$lazy_support == 1} {
gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
gdb_test "start" "" "file [file tail $sectexec] start no url"
gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*libsection.*" "lib no debug"
} else {
untested "lazy support no_url"
}
}
# Test that GDB prints the debuginfod URLs when loading files. URLS
@@ -208,6 +254,7 @@ proc disable_delete_breakpoints {} {
# expected debug information.
proc_with_prefix local_url { } {
global binfile outputdir debugdir db
global sectexec lib_sl libfile lazy_support
set url [start_debuginfod $db $debugdir]
if { $url == "" } {
@@ -256,6 +303,17 @@ proc_with_prefix local_url { } {
"file [file tail ${binfile}_alt.o]" \
$enable_debuginfod_question "y"
if {$lazy_support == 1} {
# GDB should now download .gdb_index.
clean_restart
gdb_test "set debuginfod enabled lazy" "" "set lazy urls"
gdb_test "file $sectexec" "" "file [file tail $sectexec] file urls"
set res ".*Download.*\.gdb_index.*libsection.*\r\nDownload.*debug info.*libsection.*"
gdb_test "start $sectexec" $res "file [file tail $sectexec] start urls"
} else {
untested "lazy support urls"
}
# Configure debuginfod with commands.
unsetenv DEBUGINFOD_URLS
clean_restart

View File

@@ -0,0 +1,24 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2020-2023 Free Software Foundation, Inc.
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/>. */
#include <stdio.h>
void
libsection_test ()
{
printf ("In libsection\n");
}

View File

@@ -0,0 +1,28 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2020-2023 Free Software Foundation, Inc.
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/>. */
#include <fcntl.h>
extern void libsection_test ();
int
main()
{
(void) open ("", 0);
libsection_test ();
return 0;
}

View File

@@ -135,7 +135,7 @@ gdb_test "p main" "= {<text variable, no debug info>} $hex <main>" \
gdb_py_test_silent_cmd "python objfile.add_separate_debug_file(\"${binfile}\")" \
"Add separate debug file file" 1
gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[0\]" \
gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[1\]" \
"Get separate debug info objfile" 1
gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \

View File

@@ -1012,6 +1012,106 @@ thread_target_id_str (thread_info *tp)
return target_id;
}
/* Print thread TP. GLOBAL_IDS indicates whether REQUESTED_THREADS
is a list of global or per-inferior thread ids. */
static void
do_print_thread (ui_out *uiout, const char *requested_threads,
int global_ids, int pid, int show_global_ids,
int default_inf_num, thread_info *tp,
thread_info *current_thread)
{
int core;
if (!should_print_thread (requested_threads, default_inf_num,
global_ids, pid, tp))
return;
ui_out_emit_tuple tuple_emitter (uiout, NULL);
if (!uiout->is_mi_like_p ())
{
if (tp == current_thread)
uiout->field_string ("current", "*");
else
uiout->field_skip ("current");
uiout->field_string ("id-in-tg", print_thread_id (tp));
}
if (show_global_ids || uiout->is_mi_like_p ())
uiout->field_signed ("id", tp->global_num);
/* Switch to the thread (and inferior / target). */
switch_to_thread (tp);
/* For the CLI, we stuff everything into the target-id field.
This is a gross hack to make the output come out looking
correct. The underlying problem here is that ui-out has no
way to specify that a field's space allocation should be
shared by several fields. For MI, we do the right thing
instead. */
if (uiout->is_mi_like_p ())
{
uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
const char *extra_info = target_extra_thread_info (tp);
if (extra_info != nullptr)
uiout->field_string ("details", extra_info);
const char *name = thread_name (tp);
if (name != NULL)
uiout->field_string ("name", name);
}
else
{
uiout->field_string ("target-id", thread_target_id_str (tp));
}
if (tp->state == THREAD_RUNNING)
uiout->text ("(running)\n");
else
{
/* The switch above put us at the top of the stack (leaf
frame). */
print_stack_frame (get_selected_frame (NULL),
/* For MI output, print frame level. */
uiout->is_mi_like_p (),
LOCATION, 0);
}
if (uiout->is_mi_like_p ())
{
const char *state = "stopped";
if (tp->state == THREAD_RUNNING)
state = "running";
uiout->field_string ("state", state);
}
core = target_core_of_thread (tp->ptid);
if (uiout->is_mi_like_p () && core != -1)
uiout->field_signed ("core", core);
}
/* Redirect output to a temporary buffer for the duration
of do_print_thread. */
static void
print_thread (ui_out *uiout, const char *requested_threads,
int global_ids, int pid, int show_global_ids,
int default_inf_num, thread_info *tp, thread_info *current_thread)
{
using ftype = void (ui_out *, const char *, int, int, int,
int, thread_info *, thread_info *);
do_with_buffered_output<ftype>
(do_print_thread, uiout, requested_threads, global_ids, pid,
show_global_ids, default_inf_num, tp, current_thread);
}
/* Like print_thread_info, but in addition, GLOBAL_IDS indicates
whether REQUESTED_THREADS is a list of global or per-inferior
thread ids. */
@@ -1095,82 +1195,12 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
for (inferior *inf : all_inferiors ())
for (thread_info *tp : inf->threads ())
{
int core;
any_thread = true;
if (tp == current_thread && tp->state == THREAD_EXITED)
current_exited = true;
if (!should_print_thread (requested_threads, default_inf_num,
global_ids, pid, tp))
continue;
ui_out_emit_tuple tuple_emitter (uiout, NULL);
if (!uiout->is_mi_like_p ())
{
if (tp == current_thread)
uiout->field_string ("current", "*");
else
uiout->field_skip ("current");
uiout->field_string ("id-in-tg", print_thread_id (tp));
}
if (show_global_ids || uiout->is_mi_like_p ())
uiout->field_signed ("id", tp->global_num);
/* Switch to the thread (and inferior / target). */
switch_to_thread (tp);
/* For the CLI, we stuff everything into the target-id field.
This is a gross hack to make the output come out looking
correct. The underlying problem here is that ui-out has no
way to specify that a field's space allocation should be
shared by several fields. For MI, we do the right thing
instead. */
if (uiout->is_mi_like_p ())
{
uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
const char *extra_info = target_extra_thread_info (tp);
if (extra_info != nullptr)
uiout->field_string ("details", extra_info);
const char *name = thread_name (tp);
if (name != NULL)
uiout->field_string ("name", name);
}
else
{
uiout->field_string ("target-id", thread_target_id_str (tp));
}
if (tp->state == THREAD_RUNNING)
uiout->text ("(running)\n");
else
{
/* The switch above put us at the top of the stack (leaf
frame). */
print_stack_frame (get_selected_frame (NULL),
/* For MI output, print frame level. */
uiout->is_mi_like_p (),
LOCATION, 0);
}
if (uiout->is_mi_like_p ())
{
const char *state = "stopped";
if (tp->state == THREAD_RUNNING)
state = "running";
uiout->field_string ("state", state);
}
core = target_core_of_thread (tp->ptid);
if (uiout->is_mi_like_p () && core != -1)
uiout->field_signed ("core", core);
print_thread (uiout, requested_threads, global_ids, pid,
show_global_ids, default_inf_num, tp, current_thread);
}
/* This end scope restores the current thread and the frame

View File

@@ -234,6 +234,83 @@ string_file::can_emit_style_escape ()
/* See ui-file.h. */
void
buffer_file::wrap_here (int indent)
{
m_string_wraps.emplace (m_string_wraps.end (),
string_wrap_pair (m_string, indent));
m_string.clear ();
}
/* See ui-file.h. */
void
buffer_file::flush_to_stream ()
{
if (m_stream == nullptr)
return;
/* Add m_string to m_string_wraps with no corresponding wrap_here. */
wrap_here (-1);
for (string_wrap_pair pair : m_string_wraps)
{
std::string buf = std::move (pair.first);
size_t size = buf.size ();
/* Write each line separately. */
for (size_t prev = 0, cur = 0; cur < size; ++cur)
if (buf.at (cur) == '\n' || cur == size - 1)
{
std::string sub = buf.substr (prev, cur - prev + 1);
m_stream->puts (sub.c_str ());
prev = cur + 1;
}
if (pair.second >= 0)
m_stream->wrap_here (pair.second);
}
m_string_wraps.clear ();
}
/* See ui-file.h. */
ui_file *
buffer_file::get_unbuffered_stream (ui_file *stream)
{
buffer_file *buf = dynamic_cast<buffer_file *> (stream);
if (buf == nullptr || buf->m_stream == nullptr)
return stream;
return get_unbuffered_stream (buf->m_stream);
}
/* See ui-file.h. */
void
buffer_file::set_stream (ui_file *stream)
{
if (m_stream == nullptr)
m_stream = stream;
}
/* See ui-file.h. */
bool
buffer_file::isatty ()
{
if (m_stream != nullptr)
return m_stream->isatty ();
return false;
}
stdio_file::stdio_file (FILE *file, bool close_p)
{
set_stream (file);

View File

@@ -220,13 +220,53 @@ public:
bool empty () const { return m_string.empty (); }
void clear () { return m_string.clear (); }
private:
protected:
/* The internal buffer. */
std::string m_string;
bool m_term_out;
};
/* A string_file implementation that collects output on behalf of a
given ui_file. Provides access to the underlying ui_file so
that buffering can be selectively bypassed. */
class buffer_file : public string_file
{
public:
explicit buffer_file (bool term_out)
: string_file (term_out), m_stream (nullptr)
{}
/* Associate STREAM with this buffer_file. */
void set_stream (ui_file *stream);
/* Record the wrap hint. When flushing the buffer, the underlying
ui_file's wrap_here will be called at the current point in the output. */
void wrap_here (int indent) override;
/* Flush collected output to the underlying ui_file. */
void flush_to_stream ();
/* Return true if the underlying stream is a tty. */
bool isatty () override;
/* Return a pointer to STREAM's underlying ui_file. Recursively called until
a non-buffer_file is found. */
static ui_file *get_unbuffered_stream (ui_file *stream);
private:
/* The underlying output stream. */
ui_file *m_stream;
typedef std::pair<std::string, int> string_wrap_pair;
/* A collection of strings paired with an int representing the argument
to a wrap_here call. */
std::vector<string_wrap_pair> m_string_wraps;
};
/* A ui_file implementation that maps directly onto <stdio.h>'s FILE.
A stdio_file can either own its underlying file, or not. If it
owns the file, then destroying the stdio_file closes the underlying

View File

@@ -799,6 +799,12 @@ ui_out::redirect (ui_file *outstream)
do_redirect (outstream);
}
void
ui_out::redirect_to_buffer (buffer_file *buf_file)
{
do_redirect_to_buffer (buf_file);
}
/* Test the flags against the mask given. */
ui_out_flags
ui_out::test_flags (ui_out_flags mask)
@@ -871,3 +877,17 @@ ui_out::ui_out (ui_out_flags flags)
ui_out::~ui_out ()
{
}
ui_out_buffer_pop::ui_out_buffer_pop (ui_out *uiout)
: m_uiout (uiout), m_buf_file (uiout->can_emit_style_escape ()),
m_prev_gdb_stdout (gdb_stdout)
{
m_uiout->redirect_to_buffer (&m_buf_file);
gdb_stdout = &m_buf_file;
}
ui_out_buffer_pop::~ui_out_buffer_pop ()
{
m_uiout->redirect_to_buffer (nullptr);
gdb_stdout = m_prev_gdb_stdout;
}

View File

@@ -262,6 +262,9 @@ class ui_out
/* Redirect the output of a ui_out object temporarily. */
void redirect (ui_file *outstream);
/* Redirect the output of a ui_out object to a buffer_file temporarily. */
void redirect_to_buffer (buffer_file *buf_file);
ui_out_flags test_flags (ui_out_flags mask);
/* HACK: Code in GDB is currently checking to see the type of ui_out
@@ -360,6 +363,7 @@ protected:
virtual void do_wrap_hint (int indent) = 0;
virtual void do_flush () = 0;
virtual void do_redirect (struct ui_file *outstream) = 0;
virtual void do_redirect_to_buffer (buffer_file *buf_file) = 0;
virtual void do_progress_start () = 0;
virtual void do_progress_notify (const std::string &, const char *,
@@ -470,4 +474,66 @@ private:
struct ui_out *m_uiout;
};
/* On construction, redirect a uiout and gdb_stdout to a buffer_file.
On destruction restore uiout and gdb_stdout. */
class ui_out_buffer_pop
{
public:
ui_out_buffer_pop (ui_out *uiout);
~ui_out_buffer_pop ();
/* Flush buffered output to the underlying output stream. */
void flush ()
{
m_buf_file.flush_to_stream ();
}
ui_out_buffer_pop (const ui_out_buffer_pop &) = delete;
ui_out_buffer_pop &operator= (const ui_out_buffer_pop &) = delete;
private:
/* ui_out being temporarily redirected. */
struct ui_out *m_uiout;
/* Buffer which output is temporarily redirected to for the lifetime of
this object. */
buffer_file m_buf_file;
/* Original gdb_stdout at the time of this object's construction. */
ui_file *m_prev_gdb_stdout;
};
/* Redirect output to a buffer_file for the duration of FUNC. */
template<typename F, typename... Arg>
void
do_with_buffered_output (F func, ui_out *uiout, Arg... args)
{
ui_out_buffer_pop buf (uiout);
try
{
func (uiout, std::forward<Arg> (args)...);
}
catch (gdb_exception &ex)
{
/* Ideally flush would be called in the destructor of buf,
however flushing might cause an exception to be thrown.
Catch it and ensure the first exception propagates. */
try
{
buf.flush ();
}
catch (const gdb_exception &ignore)
{
}
throw_exception (std::move (ex));
}
/* Try was successful. Let any further exceptions propagate. */
buf.flush ();
}
#endif /* UI_OUT_H */

View File

@@ -60,5 +60,4 @@ struct iterator_range
private:
IteratorType m_begin, m_end;
};
#endif /* GDBSUPPORT_ITERATOR_RANGE_H */