From patchwork Mon Apr 17 18:05:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Merey X-Patchwork-Id: 67829 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6B3423858410 for ; Mon, 17 Apr 2023 18:07:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6B3423858410 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1681754821; bh=USl/iB5kisnWyMDdi+IGh8/ULdVNb/VrY4IHt9Do8Lo=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=t4pJx7dhjc84/ooDpT+L1x9lz13HUyKZ0RS/AHBrK29rNapW4yBNAJC49SEfyfXbw sULVjMEZJmwMRhrqN4amKVqrPp5dj0Gs2XQBn0GFl8dhUCXtvE9gYXkaYd/rZuONj1 DtXJTJ9F7ZMZDftzHZkkBn02Kqt5Wa2inAdzeX/A= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 721573858C53 for ; Mon, 17 Apr 2023 18:06:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 721573858C53 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-166-CD8kcyQWO6OWrhbewS_qLw-1; Mon, 17 Apr 2023 14:06:30 -0400 X-MC-Unique: CD8kcyQWO6OWrhbewS_qLw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7A68C885620; Mon, 17 Apr 2023 18:06:30 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.34.213]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2204051FF; Mon, 17 Apr 2023 18:06:30 +0000 (UTC) To: gdb-patches@sourceware.org Cc: tom@tromey.com, Aaron Merey Subject: [PATCH 4/7 v2] gdb: Buffer output during print_thread and print_frame_info Date: Mon, 17 Apr 2023 14:05:56 -0400 Message-Id: <20230417180556.1213862-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-15.1 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Aaron Merey via Gdb-patches From: Aaron Merey Reply-To: Aaron Merey Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" v1 can be found here: https://sourceware.org/pipermail/gdb-patches/2023-February/197457.html v2 addresses Tom's feedback: https://sourceware.org/pipermail/gdb-patches/2023-March/197716.html To ensure that debuginfod progress messages appear correctly during the printing of frame and thread info, v2 removes newline tracking and instead uses buffered output streams described below. --- 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=... 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=... --- gdb/cli-out.c | 22 ++++- gdb/cli-out.h | 1 + gdb/debuginfod-support.c | 15 ++-- gdb/mi/mi-out.c | 22 +++++ gdb/mi/mi-out.h | 1 + gdb/stack.c | 38 ++++++--- gdb/thread.c | 174 +++++++++++++++++++++++---------------- gdb/ui-file.c | 77 +++++++++++++++++ gdb/ui-file.h | 42 +++++++++- gdb/ui-out.c | 20 +++++ gdb/ui-out.h | 66 +++++++++++++++ 11 files changed, 387 insertions(+), 91 deletions(-) diff --git a/gdb/cli-out.c b/gdb/cli-out.c index fdfd0f7f0cf..8162c1474ee 100644 --- a/gdb/cli-out.c +++ b/gdb/cli-out.c @@ -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_current_line () { - 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 (); if (!stream->isatty () @@ -410,10 +425,11 @@ void cli_ui_out::do_progress_end () { struct ui_file *stream = m_streams.back (); - m_progress_info.pop_back (); if (stream->isatty ()) clear_current_line (); + + m_progress_info.pop_back (); } /* local functions */ diff --git a/gdb/cli-out.h b/gdb/cli-out.h index 0729834cbc6..2bccaa8e9e8 100644 --- a/gdb/cli-out.h +++ b/gdb/cli-out.h @@ -71,6 +71,7 @@ class cli_ui_out : public ui_out 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 *, diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c index fb96dbaced1..22fa3ff2457 100644 --- a/gdb/debuginfod-support.c +++ b/gdb/debuginfod-support.c @@ -159,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; } @@ -275,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 */ diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c index 29a416a426d..8c4a920ac13 100644 --- a/gdb/mi/mi-out.c +++ b/gdb/mi/mi-out.c @@ -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(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 () { diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h index 10c9f8a4585..d2f2345daf5 100644 --- a/gdb/mi/mi-out.h +++ b/gdb/mi/mi-out.h @@ -79,6 +79,7 @@ class mi_ui_out : public ui_out 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; } diff --git a/gdb/stack.c b/gdb/stack.c index 03e903d901b..cf82ef88ea5 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -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); @@ -1026,16 +1027,15 @@ get_user_print_what_frame_info (gdb::optional *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) @@ -1111,7 +1111,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); @@ -1191,6 +1192,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 (do_print_frame_info, current_uiout, + fp_opts, frame, print_level, print_what, + print_args, set_current_sal); +} + /* See stack.h. */ void @@ -1315,13 +1333,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; diff --git a/gdb/thread.c b/gdb/thread.c index 4d97ed3f2d1..f224e003690 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -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 + (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 diff --git a/gdb/ui-file.c b/gdb/ui-file.c index e0814fe2b2d..af02f7defc0 100644 --- a/gdb/ui-file.c +++ b/gdb/ui-file.c @@ -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 (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); diff --git a/gdb/ui-file.h b/gdb/ui-file.h index de24620e247..49967b3cfc6 100644 --- a/gdb/ui-file.h +++ b/gdb/ui-file.h @@ -220,13 +220,53 @@ class string_file : public ui_file 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 string_wrap_pair; + + /* A collection of strings paired with an int representing the argument + to a wrap_here call. */ + std::vector m_string_wraps; +}; + /* A ui_file implementation that maps directly onto '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 diff --git a/gdb/ui-out.c b/gdb/ui-out.c index 9380630c320..3a54ea401b7 100644 --- a/gdb/ui-out.c +++ b/gdb/ui-out.c @@ -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; +} diff --git a/gdb/ui-out.h b/gdb/ui-out.h index ba5b1de68ab..1e24a4db2b0 100644 --- a/gdb/ui-out.h +++ b/gdb/ui-out.h @@ -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 @@ class ui_out 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 @@ class ui_out_redirect_pop 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 +void +do_with_buffered_output (F func, ui_out *uiout, Arg... args) +{ + ui_out_buffer_pop buf (uiout); + + try + { + func (uiout, std::forward (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 */ From patchwork Mon Apr 17 18:06:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Merey X-Patchwork-Id: 67830 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id F0AA73857704 for ; Mon, 17 Apr 2023 18:07:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F0AA73857704 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1681754853; bh=3Rmtm5qxE7aTP7lsMt1LljW390HIN6MsC3Fr/jEq24s=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=jzbbUnXYK3FNLFPFYA5u2yLCrWWPvnt1aTqTZXjBXH15dZSoDyNOh4X+mMJi0hiKs mUMmlu4kDbRvU+u9SznQ8gzOJWxP5YwFD7WtoTkOTMb3rKtvt8r54tqZtvVzfyBNXr ZoUFy0hgmZmDw+6QID77+XMTDavkndUOLp4/jvb8= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 5AF773857718 for ; Mon, 17 Apr 2023 18:07:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5AF773857718 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-583-4BEJNVEjP7mK1fG6VeEOQA-1; Mon, 17 Apr 2023 14:07:00 -0400 X-MC-Unique: 4BEJNVEjP7mK1fG6VeEOQA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 3B5461C09507; Mon, 17 Apr 2023 18:07:00 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.34.213]) by smtp.corp.redhat.com (Postfix) with ESMTP id 985EA2166B26; Mon, 17 Apr 2023 18:06:59 +0000 (UTC) To: gdb-patches@sourceware.org Cc: tom@tromey.com, Aaron Merey Subject: [PATCH 5/7 v2] gdb/debuginfod: Support on-demand debuginfo downloading Date: Mon, 17 Apr 2023 14:06:57 -0400 Message-Id: <20230417180657.1213901-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.6 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-15.2 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Aaron Merey via Gdb-patches From: Aaron Merey Reply-To: Aaron Merey Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" v1 can be found here: https://sourceware.org/pipermail/gdb-patches/2023-February/197460.html v2 addresses Tom's feedback: https://sourceware.org/pipermail/gdb-patches/2023-March/197717.html v2 no longer uses "stub" separate debug objfiles that own the separate index and are reinitialized when full debuginfo is downloaded. Instead the parent objfile now owns the separate index and the separate debug objfile is initialized in the usual way when full debuginfo is downloaded. This approach is simpler, less error prone and it enables sharing of downloaded indices across inferiors. To facilitate this, the unwrapping_objfile_iterator was made more generic in order to support safe iteration over objfile's qf list. During iteration over a qf list, full debuginfo might be downloaded and a separate debug objfile created. Since we want gdb to now use that debug objfile for anything index related, we remove the qf associated with the downloaded index from the parent objfile. --- 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. Because these indices are significantly smaller than their corresponding debuginfo, this generally reduces the total amount of data gdb downloads by a large margin. Reductions of 80%-95% have been commonly observed when debugging large GUI programs. (gdb) set debuginfod enabled lazy (gdb) start Downloading section .gdb_index for /lib64/libcurl.so.4 [...] 1826 client->server_mhandle = curl_multi_init (); (gdb) step Downloading separate debug info for /lib64/libcurl.so.4 Downloading separate debug info for [libcurl dwz] Downloading source file /usr/src/debug/curl-7.85.0-6.fc37.x86_64/build-full/lib/../../lib/multi.c curl_multi_init () at ../../lib/multi.c:457 457 { (gdb) --- gdb/dwarf2/frame.c | 13 +++ gdb/dwarf2/frame.h | 4 + gdb/dwarf2/index-cache.c | 33 ++++++++ gdb/dwarf2/index-cache.h | 13 +++ gdb/dwarf2/public.h | 7 ++ gdb/dwarf2/read-gdb-index.c | 157 +++++++++++++++++++++++++++++++----- gdb/dwarf2/read.c | 146 ++++++++++++++++++++++++++++++++- gdb/dwarf2/read.h | 10 +++ gdb/dwarf2/section.c | 3 +- gdb/elfread.c | 2 +- gdb/objfile-flags.h | 4 + gdb/objfiles.c | 9 ++- gdb/objfiles.h | 22 ++++- gdb/progspace.h | 50 ++++++------ gdb/symfile-debug.c | 136 +++++++++++++++---------------- gdb/symfile.c | 7 +- 16 files changed, 497 insertions(+), 119 deletions(-) diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c index a561aaf3100..3613f8252a7 100644 --- a/gdb/dwarf2/frame.c +++ b/gdb/dwarf2/frame.c @@ -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. */ diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h index 5643e557513..2391e313e7c 100644 --- a/gdb/dwarf2/frame.h +++ b/gdb/dwarf2/frame.h @@ -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); diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c index 79ab706ee9d..bbafcd321b2 100644 --- a/gdb/dwarf2/index-cache.c +++ b/gdb/dwarf2/index-cache.c @@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id, return {}; } +/* See index-cache.h. */ + +gdb::array_view +index_cache::lookup_gdb_index_debuginfod (const char *index_path, + std::unique_ptr *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 *) 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 +index_cache::lookup_gdb_index_debuginfod (const char *index_path, + std::unique_ptr *resource) +{ + return {}; +} #endif /* See dwarf-index-cache.h. */ diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h index 1efff17049f..e400afd5123 100644 --- a/gdb/dwarf2/index-cache.h +++ b/gdb/dwarf2/index-cache.h @@ -67,6 +67,19 @@ class index_cache lookup_gdb_index (const bfd_build_id *build_id, std::unique_ptr *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 + lookup_gdb_index_debuginfod (const char *index_path, + std::unique_ptr *resource); + /* Return the number of cache hits. */ unsigned int n_hits () const { return m_n_hits; } diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h index 0e74857eb1a..4a44cdbc223 100644 --- a/gdb/dwarf2/public.h +++ b/gdb/dwarf2/public.h @@ -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 */ diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index 1006386cb2d..895fbede7c2 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -136,6 +136,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 +144,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 file_matcher, @@ -152,8 +161,59 @@ 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 file_matcher, + const lookup_name_info *lookup_name, + gdb::function_view symbol_matcher, + gdb::function_view 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; }; +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 +375,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 +413,30 @@ 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 +539,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 file_matcher, const lookup_name_info *lookup_name, @@ -504,6 +588,39 @@ dwarf2_gdb_index::expand_symtabs_matching return result; } +bool +dwarf2_gdb_index::expand_symtabs_matching + (struct objfile *objfile, + gdb::function_view file_matcher, + const lookup_name_info *lookup_name, + gdb::function_view symbol_matcher, + gdb::function_view 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; + } + + read_full_dwarf_from_debuginfod (objfile, this); + return true; + } +} + quick_symbol_functions_up mapped_gdb_index::make_quick_functions () const { @@ -797,28 +914,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 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 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; + } } } diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 8f35b973f3e..e561ec035e7 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -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" @@ -95,6 +96,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. @@ -3163,7 +3166,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, @@ -3194,6 +3197,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, @@ -3350,6 +3379,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 +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 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. */ @@ -3415,10 +3467,102 @@ 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 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 index_path; + const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ()); + 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. */ diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 37023a20709..e3131693b81 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -866,6 +866,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 +946,10 @@ 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 *); + #endif /* DWARF2READ_H */ diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c index c9ef41893ee..8cb09e3381a 100644 --- a/gdb/dwarf2/section.c +++ b/gdb/dwarf2/section.c @@ -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; } diff --git a/gdb/elfread.c b/gdb/elfread.c index 0305bf21894..01eccfaac36 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -1239,7 +1239,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 diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h index 9dee2ee51a0..fb3f741c899 100644 --- a/gdb/objfile-flags.h +++ b/gdb/objfile-flags.h @@ -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); diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 9caebfefd59..3ed491b7e4b 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -52,6 +52,7 @@ #include "gdb_bfd.h" #include "btrace.h" #include "gdbsupport/pathstuff.h" +#include "symfile.h" #include #include @@ -454,8 +455,12 @@ objfile::make (gdb_bfd_ref_ptr bfd_, const char *name_, objfile_flags flags_, if (parent != nullptr) add_separate_debug_objfile (result, parent); - current_program_space->add_objfile (std::unique_ptr (result), - parent); + if (parent != nullptr && parent->flags & OBJF_DOWNLOAD_DEFERRED) + current_program_space->add_objfile (std::unique_ptr (result), + nullptr); + else + current_program_space->add_objfile (std::unique_ptr (result), + parent); /* Rebuild section map next time we need it. */ get_objfile_pspace_data (current_program_space)->new_objfiles_available = 1; diff --git a/gdb/objfiles.h b/gdb/objfiles.h index 342aa09ac6a..d00c2b21933 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -587,6 +587,17 @@ struct objfile /* 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 { @@ -611,13 +622,20 @@ struct objfile private: + using qf_list = std::forward_list; + using unwrapping_qf_range = iterator_range>; + using qf_safe_range = basic_safe_range; + /* Ensure that partial symbols have been read and return the "quick" (aka partial) symbol functions for this symbol reader. */ - const std::forward_list & + qf_safe_range qf_require_partial_symbols () { this->require_partial_symbols (true); - return qf; + return qf_safe_range + (unwrapping_qf_range + (unwrapping_iterator (qf.begin ()), + unwrapping_iterator (qf.end ()))); } public: diff --git a/gdb/progspace.h b/gdb/progspace.h index 85215f0e2f1..b4be7b14dd3 100644 --- a/gdb/progspace.h +++ b/gdb/progspace.h @@ -40,42 +40,40 @@ struct address_space; struct program_space; struct so_list; -typedef std::list> 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, 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 +class unwrapping_iterator { public: - typedef unwrapping_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; + 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; - unwrapping_objfile_iterator (objfile_list::iterator iter) + unwrapping_iterator (UniquePtrIter iter) : m_iter (std::move (iter)) { } - objfile *operator* () const + value_type operator* () const { return m_iter->get (); } - unwrapping_objfile_iterator operator++ () + unwrapping_iterator operator++ () { ++m_iter; return *this; } - bool operator!= (const unwrapping_objfile_iterator &other) const + bool operator!= (const unwrapping_iterator &other) const { return m_iter != other.m_iter; } @@ -83,13 +81,15 @@ class unwrapping_objfile_iterator private: /* The underlying iterator. */ - objfile_list::iterator m_iter; + UniquePtrIter m_iter; }; +typedef std::list> objfile_list; -/* A range that returns unwrapping_objfile_iterators. */ +/* A range that returns unwrapping_iterators. */ -using unwrapping_objfile_range = iterator_range; +using unwrapping_objfile_range + = iterator_range>; /* A program space represents a symbolic view of an address space. Roughly speaking, it holds all the data associated with a @@ -209,8 +209,8 @@ struct program_space objfiles_range objfiles () { return objfiles_range - (unwrapping_objfile_iterator (objfiles_list.begin ()), - unwrapping_objfile_iterator (objfiles_list.end ())); + (unwrapping_iterator (objfiles_list.begin ()), + unwrapping_iterator (objfiles_list.end ())); } using objfiles_safe_range = basic_safe_range; @@ -226,8 +226,8 @@ struct program_space { return objfiles_safe_range (objfiles_range - (unwrapping_objfile_iterator (objfiles_list.begin ()), - unwrapping_objfile_iterator (objfiles_list.end ()))); + (unwrapping_iterator (objfiles_list.begin ()), + unwrapping_iterator (objfiles_list.end ()))); } /* Add OBJFILE to the list of objfiles, putting it just before diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c index 9db5c47a8ce..784b81b5ca6 100644 --- a/gdb/symfile-debug.c +++ b/gdb/symfile-debug.c @@ -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 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; } diff --git a/gdb/symfile.c b/gdb/symfile.c index 8ae2177b159..9c943c4d5be 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -992,6 +992,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. */ @@ -1002,7 +1006,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 (); } From patchwork Mon Apr 17 18:07:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Merey X-Patchwork-Id: 67831 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D7DE7385840D for ; Mon, 17 Apr 2023 18:08:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D7DE7385840D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1681754897; bh=T8TMJgFJ/a5+dfOZ+52rVBKNwJ0CWp88XM1IJRBREwA=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=UDmYIH9mFKDtN943BrY3YcExKRkjJiV2Ap20mtC1+Aco5tTBe9HGkp0BWmQpD1YT3 KbMDCD43YcarbBVu9rRfWwgp228ff6HPc0VVLtIDhleGfJHRzb7pC+4bgKxKICjSnR NzjRHVUrAKR6QIyA7kRxgrATwwRzP7oZYWOuuVdU= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 5CFBC3858C53 for ; Mon, 17 Apr 2023 18:07:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5CFBC3858C53 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-153-QUV5J9lPMhuXQo9hIkCBng-1; Mon, 17 Apr 2023 14:07:49 -0400 X-MC-Unique: QUV5J9lPMhuXQo9hIkCBng-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8D243811E7C; Mon, 17 Apr 2023 18:07:49 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.34.213]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3623F492B0C; Mon, 17 Apr 2023 18:07:49 +0000 (UTC) To: gdb-patches@sourceware.org Cc: tom@tromey.com, Aaron Merey Subject: [PATCH 7/7 v2] gdb/debuginfod: Add .debug_line downloading Date: Mon, 17 Apr 2023 14:07:43 -0400 Message-Id: <20230417180743.1213952-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Aaron Merey via Gdb-patches From: Aaron Merey Reply-To: Aaron Merey Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" 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. --- 'set debuginfod enabled lazy' allows gdb to download .gdb_index files in order to defer full debuginfo downloads. However .gdb_index does not contain any information regarding source filenames. When a gdb command includes a filename argument (ex. 'break main.c:50'), this results in the mass downloading of all deferred debuginfo so gdb can search the debuginfo for matching source filenames. This can result in unnecessary downloading. To improve this, have gdb instead download each debuginfo's .debug_line (and .debug_line_str if using DWARF5) when executing these commands. Download full debuginfo only when its .debug_line contains a matching filename. Since the combined size of .debug_line and .debug_line_str is only about 1% the size of the corresponding debuginfo, significant time can be saved by checking these sections before choosing to download a deferred debuginfo. --- gdb/dwarf2/line-header.c | 215 +++++++++++++++++++++++------------- gdb/dwarf2/line-header.h | 10 ++ gdb/dwarf2/read-gdb-index.c | 27 +++++ gdb/dwarf2/read.c | 165 +++++++++++++++++++++++++++ gdb/dwarf2/read.h | 31 ++++++ 5 files changed, 371 insertions(+), 77 deletions(-) diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c index 9d74c8fe75b..5eaff9c5a48 100644 --- a/gdb/dwarf2/line-header.c +++ b/gdb/dwarf2/line-header.c @@ -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 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 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; @@ -244,28 +263,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 line_data, + gdb::array_view 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; @@ -273,62 +294,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; + line_ptr += bytes_read; - 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 @@ -341,41 +365,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); }); @@ -383,7 +413,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); @@ -391,17 +421,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); @@ -409,9 +439,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 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 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); +} diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h index 59a42e336f5..44e32828ddb 100644 --- a/gdb/dwarf2/line-header.h +++ b/gdb/dwarf2/line-header.h @@ -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 line_data, + gdb::array_view 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 */ diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index 895fbede7c2..fe33f23fdfa 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base } }; +struct mapped_debug_line; +typedef std::unique_ptr mapped_debug_line_up; + struct dwarf2_gdb_index : public dwarf2_base_index_functions { /* This dumps minimal information about the index. @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions /* 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 file_matcher); }; void @@ -588,6 +600,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching return result; } +bool +dwarf2_gdb_index::filename_in_debug_line + (objfile *objfile, + gdb::function_view 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, @@ -616,6 +639,10 @@ dwarf2_gdb_index::expand_symtabs_matching return false; } + if (file_matcher != nullptr + && !filename_in_debug_line (objfile, file_matcher)) + return true; + read_full_dwarf_from_debuginfod (objfile, this); return true; } diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index e561ec035e7..39ee56d7204 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -81,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 @@ -2110,6 +2111,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 file_matcher) +{ + return false; +} + +gdb::array_view +mapped_debug_line::read_debug_line_separate + (char *filename, std::unique_ptr *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 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 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 +mapped_debug_line::read_debug_line_separate + (char *filename, std::unique_ptr *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 *) 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 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 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. */ diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index e3131693b81..b8a8b76bde0 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -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; @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile, 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 file_matcher); + +private: + std::vector line_headers; + + gdb::array_view line_data; + gdb::array_view line_str_data; + + std::unique_ptr line_resource; + std::unique_ptr 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 read_debug_line_separate + (char *filename, std::unique_ptr *resource); +}; #endif /* DWARF2READ_H */