Files
binutils-gdb/gdb/dwarf2/cooked-index-worker.c
Simon Marchi cc55260231 gdb: add some scoped_time_its to profile startup time
I'm investigating some issues where GDB takes a lot of time to start
up (read: for the DWARF index to be ready to do anything useful).
Adding those scoped_time_it instances helped me gain some insights about
where GDB spends time.  I think they would be useful to have upstream,
to make investigating future problems easier.  It would also be useful
to be able to give some numbers in the commit messages.

Here's an example of what GDB outputs:

    Time for "minsyms install worker": wall 0.045, user 0.040, sys 0.004, user+sys 0.044, 97.8 % CPU
    Time for "minsyms install worker": wall 0.511, user 0.457, sys 0.048, user+sys 0.505, 98.8 % CPU
    Time for "minsyms install worker": wall 1.513, user 1.389, sys 0.111, user+sys 1.500, 99.1 % CPU
    Time for "minsyms install worker": wall 1.688, user 1.451, sys 0.102, user+sys 1.553, 92.0 % CPU
    Time for "minsyms install worker": wall 1.897, user 1.518, sys 0.089, user+sys 1.607, 84.7 % CPU
    Time for "minsyms install worker": wall 2.811, user 2.558, sys 0.231, user+sys 2.789, 99.2 % CPU
    Time for "minsyms install worker": wall 3.257, user 3.049, sys 0.188, user+sys 3.237, 99.4 % CPU
    Time for "minsyms install worker": wall 3.617, user 3.089, sys 0.211, user+sys 3.300, 91.2 % CPU
    Time for "DWARF indexing worker": wall 19.517, user 0.894, sys 0.075, user+sys 0.969, 5.0 % CPU
    Time for "DWARF indexing worker": wall 19.807, user 0.891, sys 0.086, user+sys 0.977, 4.9 % CPU
    Time for "DWARF indexing worker": wall 20.270, user 1.559, sys 0.119, user+sys 1.678, 8.3 % CPU
    Time for "DWARF indexing worker": wall 20.329, user 1.878, sys 0.147, user+sys 2.025, 10.0 % CPU
    Time for "DWARF indexing worker": wall 20.848, user 3.483, sys 0.224, user+sys 3.707, 17.8 % CPU
    Time for "DWARF indexing worker": wall 21.088, user 4.285, sys 0.295, user+sys 4.580, 21.7 % CPU
    Time for "DWARF indexing worker": wall 21.109, user 4.501, sys 0.274, user+sys 4.775, 22.6 % CPU
    Time for "DWARF indexing worker": wall 21.198, user 5.087, sys 0.319, user+sys 5.406, 25.5 % CPU
    Time for "DWARF skeletonless type units": wall 4.024, user 3.858, sys 0.115, user+sys 3.973, 98.7 % CPU
    Time for "DWARF add parent map": wall 0.092, user 0.086, sys 0.004, user+sys 0.090, 97.8 % CPU
    Time for "DWARF finalize worker": wall 0.278, user 0.241, sys 0.009, user+sys 0.250, 89.9 % CPU
    Time for "DWARF finalize worker": wall 0.307, user 0.304, sys 0.000, user+sys 0.304, 99.0 % CPU
    Time for "DWARF finalize worker": wall 0.727, user 0.719, sys 0.000, user+sys 0.719, 98.9 % CPU
    Time for "DWARF finalize worker": wall 0.913, user 0.901, sys 0.003, user+sys 0.904, 99.0 % CPU
    Time for "DWARF finalize worker": wall 0.776, user 0.767, sys 0.004, user+sys 0.771, 99.4 % CPU
    Time for "DWARF finalize worker": wall 1.897, user 1.869, sys 0.006, user+sys 1.875, 98.8 % CPU
    Time for "DWARF finalize worker": wall 2.534, user 2.512, sys 0.005, user+sys 2.517, 99.3 % CPU
    Time for "DWARF finalize worker": wall 2.607, user 2.583, sys 0.006, user+sys 2.589, 99.3 % CPU
    Time for "DWARF finalize worker": wall 3.142, user 3.094, sys 0.022, user+sys 3.116, 99.2 % CPU

Change-Id: I9453589b9005c3226499428ae9cab9f4a8c22904
Approved-By: Tom Tromey <tom@tromey.com>
2025-04-29 15:10:17 -04:00

261 lines
6.6 KiB
C

/* DWARF index storage
Copyright (C) 2022-2025 Free Software Foundation, Inc.
This file is part of GDB.
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 "dwarf2/cooked-index-worker.h"
#include "dwarf2/cooked-index.h"
#include "gdbsupport/thread-pool.h"
#include "maint.h"
#include "run-on-main-thread.h"
#include "event-top.h"
#include "exceptions.h"
/* See cooked-index-worker.h. */
cooked_index_worker_result::cooked_index_worker_result ()
: m_shard (new cooked_index_shard)
{
}
/* See cooked-index-worker.h. */
cutu_reader *
cooked_index_worker_result::get_reader (dwarf2_per_cu *per_cu)
{
auto it = m_reader_hash.find (*per_cu);
return it != m_reader_hash.end () ? it->get () : nullptr;
}
/* See cooked-index-worker.h. */
cutu_reader *
cooked_index_worker_result::preserve (cutu_reader_up reader)
{
m_abbrev_table_cache.add (reader->release_abbrev_table ());
auto [it, inserted] = m_reader_hash.insert (std::move (reader));
gdb_assert (inserted);
return it->get();
}
/* See cooked-index-worker.h. */
std::uint64_t
cooked_index_worker_result::cutu_reader_hash::operator()
(const cutu_reader_up &reader) const noexcept
{
return (*this) (*reader->cu ()->per_cu);
}
/* See cooked-index-worker.h. */
std::uint64_t
cooked_index_worker_result::cutu_reader_hash::operator() (const dwarf2_per_cu &per_cu)
const noexcept
{
return per_cu.index;
}
/* See cooked-index-worker.h. */
bool
cooked_index_worker_result::cutu_reader_eq::operator() (const cutu_reader_up &a,
const cutu_reader_up &b) const noexcept
{
return (*this) (*a->cu ()->per_cu, b);
}
/* See cooked-index-worker.h. */
bool cooked_index_worker_result::cutu_reader_eq::operator()
(const dwarf2_per_cu &per_cu, const cutu_reader_up &reader) const noexcept
{
return per_cu.index == reader->cu ()->per_cu->index;
}
/* See cooked-index-worker.h. */
void
cooked_index_worker_result::emit_complaints_and_exceptions
(gdb::unordered_set<gdb_exception> &seen_exceptions)
{
gdb_assert (is_main_thread ());
re_emit_complaints (m_complaints);
/* Only show a given exception a single time. */
for (auto &one_exc : m_exceptions)
if (seen_exceptions.insert (one_exc).second)
exception_print (gdb_stderr, one_exc);
}
/* See cooked-index-worker.h. */
void
cooked_index_worker::start ()
{
gdb::thread_pool::g_thread_pool->post_task ([this] ()
{
try
{
do_reading ();
}
catch (const gdb_exception &exc)
{
m_failed = exc;
set (cooked_state::CACHE_DONE);
}
bfd_thread_cleanup ();
});
}
/* See cooked-index-worker.h. */
bool
cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
{
bool done;
#if CXX_STD_THREAD
{
std::unique_lock<std::mutex> lock (m_mutex);
/* This may be called from a non-main thread -- this functionality
is needed for the index cache -- but in this case we require
that the desired state already have been attained. */
gdb_assert (is_main_thread () || desired_state <= m_state);
while (desired_state > m_state)
{
if (allow_quit)
{
std::chrono::milliseconds duration { 15 };
if (m_cond.wait_for (lock, duration) == std::cv_status::timeout)
QUIT;
}
else
m_cond.wait (lock);
}
done = m_state == cooked_state::CACHE_DONE;
}
#else
/* Without threads, all the work is done immediately on the main
thread, and there is never anything to wait for. */
done = desired_state == cooked_state::CACHE_DONE;
#endif /* CXX_STD_THREAD */
/* Only the main thread is allowed to report complaints and the
like. */
if (!is_main_thread ())
return false;
if (m_reported)
return done;
m_reported = true;
/* Emit warnings first, maybe they were emitted before an exception
(if any) was thrown. */
m_warnings.emit ();
if (m_failed.has_value ())
{
/* do_reading failed -- report it. */
exception_print (gdb_stderr, *m_failed);
m_failed.reset ();
return done;
}
/* Only show a given exception a single time. */
gdb::unordered_set<gdb_exception> seen_exceptions;
for (auto &one_result : m_results)
one_result.emit_complaints_and_exceptions (seen_exceptions);
print_stats ();
struct objfile *objfile = m_per_objfile->objfile;
dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
cooked_index *table
= (gdb::checked_static_cast<cooked_index *>
(per_bfd->index_table.get ()));
auto_obstack temp_storage;
enum language lang = language_unknown;
const char *main_name = table->get_main_name (&temp_storage, &lang);
if (main_name != nullptr)
set_objfile_main_name (objfile, main_name, lang);
/* dwarf_read_debug_printf ("Done building psymtabs of %s", */
/* objfile_name (objfile)); */
return done;
}
/* See cooked-index-worker.h. */
void
cooked_index_worker::set (cooked_state desired_state)
{
gdb_assert (desired_state != cooked_state::INITIAL);
#if CXX_STD_THREAD
std::lock_guard<std::mutex> guard (m_mutex);
gdb_assert (desired_state > m_state);
m_state = desired_state;
m_cond.notify_one ();
#else
/* Without threads, all the work is done immediately on the main
thread, and there is never anything to do. */
#endif /* CXX_STD_THREAD */
}
/* See cooked-index-worker.h. */
void
cooked_index_worker::write_to_cache (const cooked_index *idx)
{
if (idx != nullptr)
{
/* Writing to the index cache may cause a warning to be emitted.
See PR symtab/30837. This arranges to capture all such
warnings. This is safe because we know the deferred_warnings
object isn't in use by any other thread at this point. */
scoped_restore_warning_hook defer (&m_warnings);
m_cache_store.store ();
}
}
/* See cooked-index-worker.h. */
void
cooked_index_worker::done_reading ()
{
{
scoped_time_it time_it ("DWARF add parent map");
for (auto &one_result : m_results)
m_all_parents_map.add_map (*one_result.get_parent_map ());
}
dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
cooked_index *table
= (gdb::checked_static_cast<cooked_index *>
(per_bfd->index_table.get ()));
table->set_contents ();
}