gdb: try to load libthread_db only after reading all shared libraries when attaching

Message ID 20210330172149.1724381-1-simon.marchi@polymtl.ca
State New
Headers show
Series
  • gdb: try to load libthread_db only after reading all shared libraries when attaching
Related show

Commit Message

Simon Marchi via Gdb-patches March 30, 2021, 5:21 p.m.
Note: this is an ugly patch, I'd rather not push it like this, but I
send it to start the discussion and gather feedback.

When trying to attach to a pthread process on a Linux system with glibc 2.33,
we get:

    $ ./gdb -q -nx --data-directory=data-directory -p 1472010
    Attaching to process 1472010
    [New LWP 1472013]
    [New LWP 1472014]
    [New LWP 1472015]
    Error while reading shared library symbols for /usr/lib/libpthread.so.0:
    Cannot find user-level thread for LWP 1472015: generic error
    0x00007ffff6d3637f in poll () from /usr/lib/libc.so.6
    (gdb)

When attaching to a process, GDB reads the shared library list from the
process.  For each shared library (if "set auto-solib-add" is on), it
reads its symbols and calls the "new_objfile" observable.

The libthread-db code monitors this observable, and if it sees an
objfile named somewhat like "libpthread.so" go by, it tries to load
libthread_db.so in the GDB process itself.  libthread_db knows how to
navigate libpthread's data structures to get information about the
existing threads.

To locate data structures, libthread_db calls ps_pglobal_lookup
(implemented in proc-service.c), passing in a symbol name and expecting
an address in return.

Before glibc 2.33, libthread_db always asked for symbols found in
libpthread.  There was no ordering problem: since we were always trying
to load libthread_db in reaction to processing libpthread (and reading
in its symbols) and libthread_db only asked symbols from libpthread, the
requested symbols could always be found.  Starting with glibc 2.33,
libthread_db now asks for a symbol name that can be found in
/lib/ld-linux-x86-64.so.2 (_rtld_global).  And the ordering in which GDB
reads the shared library list from the inferior is unfortunate, in that
libpthread is processed before ld-linux.  So when loading libthread_db
in reaction to processing libpthread, and libthread_db requests the
symbol that is from ld-linux, GDB is not yet able to supply it.

That symbol lookup happens in the thread_from_lwp function, when we call
td_ta_map_lwp2thr_p, and an exception is thrown at this point:

    #0  0x00007ffff6681012 in __cxxabiv1::__cxa_throw (obj=0x60e000006100, tinfo=0x555560033b50 <typeinfo for gdb_exception_error>, dest=0x55555d9404bc <gdb_exception_error::~gdb_exception_error()>) at /build/gcc/src/gcc/libstdc++-v3/libsupc++/eh_throw.cc:78
    #1  0x000055555e5d3734 in throw_it(return_reason, errors, const char *, typedef __va_list_tag __va_list_tag *) (reason=RETURN_ERROR, error=GENERIC_ERROR, fmt=0x55555f0c5360 "Cannot find user-level thread for LWP %ld: %s", ap=0x7fffffffaae0) at /home/simark/src/binutils-gdb/gdbsupport/common-exceptions.cc:200
    #2  0x000055555e5d37d4 in throw_verror (error=GENERIC_ERROR, fmt=0x55555f0c5360 "Cannot find user-level thread for LWP %ld: %s", ap=0x7fffffffaae0) at /home/simark/src/binutils-gdb/gdbsupport/common-exceptions.cc:208
    #3  0x000055555e0b0ed2 in verror (string=0x55555f0c5360 "Cannot find user-level thread for LWP %ld: %s", args=0x7fffffffaae0) at /home/simark/src/binutils-gdb/gdb/utils.c:171
    #4  0x000055555e5e898a in error (fmt=0x55555f0c5360 "Cannot find user-level thread for LWP %ld: %s") at /home/simark/src/binutils-gdb/gdbsupport/errors.cc:43
    #5  0x000055555d06b4bc in thread_from_lwp (stopped=0x617000035d80, ptid=...) at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:418
    #6  0x000055555d07040d in try_thread_db_load_1 (info=0x60c000011140) at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:912
    #7  0x000055555d071103 in try_thread_db_load (library=0x55555f0c62a0 "libthread_db.so.1", check_auto_load_safe=false) at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:1014
    #8  0x000055555d072168 in try_thread_db_load_from_sdir () at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:1091
    #9  0x000055555d072d1c in thread_db_load_search () at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:1146
    #10 0x000055555d07365c in thread_db_load () at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:1203
    #11 0x000055555d07373e in check_for_thread_db () at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:1246
    #12 0x000055555d0738ab in thread_db_new_objfile (objfile=0x61300000c0c0) at /home/simark/src/binutils-gdb/gdb/linux-thread-db.c:1275
    #13 0x000055555bd10740 in std::__invoke_impl<void, void (*&)(objfile*), objfile*> (__f=@0x616000068d88: 0x55555d073745 <thread_db_new_objfile(objfile*)>) at /usr/include/c++/10.2.0/bits/invoke.h:60
    #14 0x000055555bd02096 in std::__invoke_r<void, void (*&)(objfile*), objfile*> (__fn=@0x616000068d88: 0x55555d073745 <thread_db_new_objfile(objfile*)>) at /usr/include/c++/10.2.0/bits/invoke.h:153
    #15 0x000055555bce0392 in std::_Function_handler<void (objfile*), void (*)(objfile*)>::_M_invoke(std::_Any_data const&, objfile*&&) (__functor=..., __args#0=@0x7fffffffb4a0: 0x61300000c0c0) at /usr/include/c++/10.2.0/bits/std_function.h:291
    #16 0x000055555d3595c0 in std::function<void (objfile*)>::operator()(objfile*) const (this=0x616000068d88, __args#0=0x61300000c0c0) at /usr/include/c++/10.2.0/bits/std_function.h:622
    #17 0x000055555d356b7f in gdb::observers::observable<objfile*>::notify (this=0x555566727020 <gdb::observers::new_objfile>, args#0=0x61300000c0c0) at /home/simark/src/binutils-gdb/gdb/../gdbsupport/observable.h:106
    #18 0x000055555da3f228 in symbol_file_add_with_addrs (abfd=0x61200001ccc0, name=0x6190000d9090 "/usr/lib/libpthread.so.0", add_flags=..., addrs=0x7fffffffbc10, flags=..., parent=0x0) at /home/simark/src/binutils-gdb/gdb/symfile.c:1131
    #19 0x000055555da3f763 in symbol_file_add_from_bfd (abfd=0x61200001ccc0, name=0x6190000d9090 "/usr/lib/libpthread.so.0", add_flags=<error reading variable: Cannot access memory at address 0xffffffffffffffb0>, addrs=0x7fffffffbc10, flags=<error reading variable: Cannot access memory at address 0xffffffffffffffc0>, parent=0x0) at /home/simark/src/binutils-gdb/gdb/symfile.c:1167
    #20 0x000055555d95f9fa in solib_read_symbols (so=0x6190000d8e80, flags=...) at /home/simark/src/binutils-gdb/gdb/solib.c:681
    #21 0x000055555d96233d in solib_add (pattern=0x0, from_tty=0, readsyms=1) at /home/simark/src/binutils-gdb/gdb/solib.c:987
    #22 0x000055555d93646e in enable_break (info=0x608000008f20, from_tty=0) at /home/simark/src/binutils-gdb/gdb/solib-svr4.c:2238
    #23 0x000055555d93cfc0 in svr4_solib_create_inferior_hook (from_tty=0) at /home/simark/src/binutils-gdb/gdb/solib-svr4.c:3049
    #24 0x000055555d96610d in solib_create_inferior_hook (from_tty=0) at /home/simark/src/binutils-gdb/gdb/solib.c:1195
    #25 0x000055555cdee318 in post_create_inferior (from_tty=0) at /home/simark/src/binutils-gdb/gdb/infcmd.c:318
    #26 0x000055555ce00e6e in setup_inferior (from_tty=0) at /home/simark/src/binutils-gdb/gdb/infcmd.c:2439
    #27 0x000055555ce59c34 in handle_one (event=...) at /home/simark/src/binutils-gdb/gdb/infrun.c:4887
    #28 0x000055555ce5cd00 in stop_all_threads () at /home/simark/src/binutils-gdb/gdb/infrun.c:5064
    #29 0x000055555ce7f0da in stop_waiting (ecs=0x7fffffffd170) at /home/simark/src/binutils-gdb/gdb/infrun.c:8006
    #30 0x000055555ce67f5c in handle_signal_stop (ecs=0x7fffffffd170) at /home/simark/src/binutils-gdb/gdb/infrun.c:6062
    #31 0x000055555ce63653 in handle_inferior_event (ecs=0x7fffffffd170) at /home/simark/src/binutils-gdb/gdb/infrun.c:5727
    #32 0x000055555ce4f297 in fetch_inferior_event () at /home/simark/src/binutils-gdb/gdb/infrun.c:4105
    #33 0x000055555cdbe3bf in inferior_event_handler (event_type=INF_REG_EVENT) at /home/simark/src/binutils-gdb/gdb/inf-loop.c:42
    #34 0x000055555d018047 in handle_target_event (error=0, client_data=0x0) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:4060
    #35 0x000055555e5ea77e in handle_file_event (file_ptr=0x60600008b1c0, ready_mask=1) at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:575
    #36 0x000055555e5eb09c in gdb_wait_for_event (block=0) at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:701
    #37 0x000055555e5e8d19 in gdb_do_one_event () at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:212
    #38 0x000055555dd6e0d4 in wait_sync_command_done () at /home/simark/src/binutils-gdb/gdb/top.c:528
    #39 0x000055555dd6e372 in maybe_wait_sync_command_done (was_sync=0) at /home/simark/src/binutils-gdb/gdb/top.c:545
    #40 0x000055555d0ec7c8 in catch_command_errors (command=0x55555ce01bb8 <attach_command(char const*, int)>, arg=0x7fffffffe28d "1472010", from_tty=1, do_bp_actions=false) at /home/simark/src/binutils-gdb/gdb/main.c:452
    #41 0x000055555d0f03ad in captured_main_1 (context=0x7fffffffdd10) at /home/simark/src/binutils-gdb/gdb/main.c:1149
    #42 0x000055555d0f1239 in captured_main (data=0x7fffffffdd10) at /home/simark/src/binutils-gdb/gdb/main.c:1232
    #43 0x000055555d0f1315 in gdb_main (args=0x7fffffffdd10) at /home/simark/src/binutils-gdb/gdb/main.c:1257
    #44 0x000055555bb70cf9 in main (argc=7, argv=0x7fffffffde88) at /home/simark/src/binutils-gdb/gdb/gdb.c:32

The exception is caught here:

    #0  __cxxabiv1::__cxa_begin_catch (exc_obj_in=0x60e0000060e0) at /build/gcc/src/gcc/libstdc++-v3/libsupc++/eh_catch.cc:84
    #1  0x000055555d95fded in solib_read_symbols (so=0x6190000d8e80, flags=...) at /home/simark/src/binutils-gdb/gdb/solib.c:689
    #2  0x000055555d96233d in solib_add (pattern=0x0, from_tty=0, readsyms=1) at /home/simark/src/binutils-gdb/gdb/solib.c:987
    #3  0x000055555d93646e in enable_break (info=0x608000008f20, from_tty=0) at /home/simark/src/binutils-gdb/gdb/solib-svr4.c:2238
    #4  0x000055555d93cfc0 in svr4_solib_create_inferior_hook (from_tty=0) at /home/simark/src/binutils-gdb/gdb/solib-svr4.c:3049
    #5  0x000055555d96610d in solib_create_inferior_hook (from_tty=0) at /home/simark/src/binutils-gdb/gdb/solib.c:1195
    #6  0x000055555cdee318 in post_create_inferior (from_tty=0) at /home/simark/src/binutils-gdb/gdb/infcmd.c:318
    #7  0x000055555ce00e6e in setup_inferior (from_tty=0) at /home/simark/src/binutils-gdb/gdb/infcmd.c:2439
    #8  0x000055555ce59c34 in handle_one (event=...) at /home/simark/src/binutils-gdb/gdb/infrun.c:4887
    #9  0x000055555ce5cd00 in stop_all_threads () at /home/simark/src/binutils-gdb/gdb/infrun.c:5064
    #10 0x000055555ce7f0da in stop_waiting (ecs=0x7fffffffd170) at /home/simark/src/binutils-gdb/gdb/infrun.c:8006
    #11 0x000055555ce67f5c in handle_signal_stop (ecs=0x7fffffffd170) at /home/simark/src/binutils-gdb/gdb/infrun.c:6062
    #12 0x000055555ce63653 in handle_inferior_event (ecs=0x7fffffffd170) at /home/simark/src/binutils-gdb/gdb/infrun.c:5727
    #13 0x000055555ce4f297 in fetch_inferior_event () at /home/simark/src/binutils-gdb/gdb/infrun.c:4105
    #14 0x000055555cdbe3bf in inferior_event_handler (event_type=INF_REG_EVENT) at /home/simark/src/binutils-gdb/gdb/inf-loop.c:42
    #15 0x000055555d018047 in handle_target_event (error=0, client_data=0x0) at /home/simark/src/binutils-gdb/gdb/linux-nat.c:4060
    #16 0x000055555e5ea77e in handle_file_event (file_ptr=0x60600008b1c0, ready_mask=1) at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:575
    #17 0x000055555e5eb09c in gdb_wait_for_event (block=0) at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:701
    #18 0x000055555e5e8d19 in gdb_do_one_event () at /home/simark/src/binutils-gdb/gdbsupport/event-loop.cc:212
    #19 0x000055555dd6e0d4 in wait_sync_command_done () at /home/simark/src/binutils-gdb/gdb/top.c:528
    #20 0x000055555dd6e372 in maybe_wait_sync_command_done (was_sync=0) at /home/simark/src/binutils-gdb/gdb/top.c:545
    #21 0x000055555d0ec7c8 in catch_command_errors (command=0x55555ce01bb8 <attach_command(char const*, int)>, arg=0x7fffffffe28d "1472010", from_tty=1, do_bp_actions=false) at /home/simark/src/binutils-gdb/gdb/main.c:452
    #22 0x000055555d0f03ad in captured_main_1 (context=0x7fffffffdd10) at /home/simark/src/binutils-gdb/gdb/main.c:1149
    #23 0x000055555d0f1239 in captured_main (data=0x7fffffffdd10) at /home/simark/src/binutils-gdb/gdb/main.c:1232
    #24 0x000055555d0f1315 in gdb_main (args=0x7fffffffdd10) at /home/simark/src/binutils-gdb/gdb/main.c:1257
    #25 0x000055555bb70cf9 in main (argc=7, argv=0x7fffffffde88) at /home/simark/src/binutils-gdb/gdb/gdb.c:32

Catching the exception at this point means that the thread_db_info
object for this inferior will be left in place, despite the failure to
load libthread_db.  This means that there won't be further attempts at
loading libthread_db, because thread_db_load will exit early.  This
patch adds a try/catch around calling try_thread_db_load_1 in
try_thread_db_load, such that if some exception is thrown while trying
to load libthread_db, we reset / delete the thread_db_info for that
inferior.  That alone makes attach work fine again, because
check_for_thread_db is called again in the thread_db_inferior_created
observer (that is after we learned about all shared libraries), and
libthread_db is successfully loaded then.

I think that's a good way to make it work: when attaching, the
inferior_created observable is called once everything has stabilized,
when we learned about all shared libraries, so that is a good time to
try to load libthread_db.

The only problem then is that when we first try (and fail) to load
libthread_db, in reaction to learning about libpthread, we show this
warning:

    warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available.

This is misleading, because we do succeed in loading it later.  So when
attaching, I think we want to defer trying to load libthread_db until we
have learned about all shared libraries.  And that leads to the ugly
part of the patch.  What I've implemented in this patch is to delay
clearing the inferior::needs_setup flag, so that we can check for it in
thread_db_load.

I considered other solutions, like having a "new_objfiles" observable,
which would fire once after a series of objfiles are created.  When
attaching, we would get a single "new_objfiles" notification after all
shared library objfiles have been created, so that the thread-db code
could use that.  But it didn't seem nice anymore when actually
implementing it.

Change-Id: I7a279836cfbb2b362b4fde11b196b4aab82f5efb
---
 gdb/infcmd.c          |  7 ++-----
 gdb/linux-thread-db.c | 24 +++++++++++++++++++-----
 2 files changed, 21 insertions(+), 10 deletions(-)

-- 
2.30.1

Comments

Tom Tromey April 2, 2021, 5:14 p.m. | #1
Simon> Before glibc 2.33, libthread_db always asked for symbols found in
Simon> libpthread.  There was no ordering problem: since we were always trying
Simon> to load libthread_db in reaction to processing libpthread (and reading
Simon> in its symbols) and libthread_db only asked symbols from libpthread, the
Simon> requested symbols could always be found.  Starting with glibc 2.33,
Simon> libthread_db now asks for a symbol name that can be found in
Simon> /lib/ld-linux-x86-64.so.2 (_rtld_global).  And the ordering in which GDB
Simon> reads the shared library list from the inferior is unfortunate, in that
Simon> libpthread is processed before ld-linux.  So when loading libthread_db
Simon> in reaction to processing libpthread, and libthread_db requests the
Simon> symbol that is from ld-linux, GDB is not yet able to supply it.

Adding to my dislike of libthread_db.  Linux would be improved if it
were removed entirely.

Simon> I think that's a good way to make it work: when attaching, the
Simon> inferior_created observable is called once everything has stabilized,
Simon> when we learned about all shared libraries, so that is a good time to
Simon> try to load libthread_db.

Yeah.  We already have other code doing something similar --
SYMFILE_DEFER_BP_RESET was added to defer a breakpoint reset until all
the available shared libraries were loaded, the idea here being to avoid
multiple expensive resets one after another.

Simon> I considered other solutions, like having a "new_objfiles" observable,
Simon> which would fire once after a series of objfiles are created.  When
Simon> attaching, we would get a single "new_objfiles" notification after all
Simon> shared library objfiles have been created, so that the thread-db code
Simon> could use that.  But it didn't seem nice anymore when actually
Simon> implementing it.

Perhaps the SYMFILE_DEFER_BP_RESET approach could be generalized somehow.
Or is that too closely related to what you tried?

Also, does gdbserver need a similar treatment?  I know it has to load
libthread_db (one of the problems with this library) but is the decision
to do so drive by gdb or by itself?  I don't recall.

Tom
Simon Marchi via Gdb-patches April 6, 2021, 9:08 p.m. | #2
On Fri, 02 Apr 2021 11:14:23 -0600
Tom Tromey <tom@tromey.com> wrote:

> Also, does gdbserver need a similar treatment?  I know it has to load

> libthread_db (one of the problems with this library) but is the decision

> to do so drive by gdb or by itself?  I don't recall.


gdbserver definitely has problems too.  It needs gdb to look up
symbols (via the qSymbol packet), but is only able to do so at certain
times.

I'm trying to puzzle it out now...

Kevin
Simon Marchi via Gdb-patches April 7, 2021, 2:45 a.m. | #3
On 2021-04-02 1:14 p.m., Tom Tromey wrote:
> Simon> Before glibc 2.33, libthread_db always asked for symbols found in

> Simon> libpthread.  There was no ordering problem: since we were always trying

> Simon> to load libthread_db in reaction to processing libpthread (and reading

> Simon> in its symbols) and libthread_db only asked symbols from libpthread, the

> Simon> requested symbols could always be found.  Starting with glibc 2.33,

> Simon> libthread_db now asks for a symbol name that can be found in

> Simon> /lib/ld-linux-x86-64.so.2 (_rtld_global).  And the ordering in which GDB

> Simon> reads the shared library list from the inferior is unfortunate, in that

> Simon> libpthread is processed before ld-linux.  So when loading libthread_db

> Simon> in reaction to processing libpthread, and libthread_db requests the

> Simon> symbol that is from ld-linux, GDB is not yet able to supply it.

> 

> Adding to my dislike of libthread_db.  Linux would be improved if it

> were removed entirely.

> 

> Simon> I think that's a good way to make it work: when attaching, the

> Simon> inferior_created observable is called once everything has stabilized,

> Simon> when we learned about all shared libraries, so that is a good time to

> Simon> try to load libthread_db.

> 

> Yeah.  We already have other code doing something similar --

> SYMFILE_DEFER_BP_RESET was added to defer a breakpoint reset until all

> the available shared libraries were loaded, the idea here being to avoid

> multiple expensive resets one after another.

> 

> Simon> I considered other solutions, like having a "new_objfiles" observable,

> Simon> which would fire once after a series of objfiles are created.  When

> Simon> attaching, we would get a single "new_objfiles" notification after all

> Simon> shared library objfiles have been created, so that the thread-db code

> Simon> could use that.  But it didn't seem nice anymore when actually

> Simon> implementing it.

> 

> Perhaps the SYMFILE_DEFER_BP_RESET approach could be generalized somehow.

> Or is that too closely related to what you tried?


If I understand correctly, the "bp reset" mechanism is: pass
SYMFILE_DEFER_BP_RESET when loading one or more objfile, and manually
call breakpoint_re_set after that.

I don't think we can directly use the same mechanism here: we don't
really want to create a SYMFILE_DEFER_LOADING_LIBTHREAD_DB flag, that
would be quite an abstraction breach.  Or do you mean a
SYMFILE_DEFER_CALLING_THE_NEW_OBJFILE_OBSERVABLE flag?

> Also, does gdbserver need a similar treatment?  I know it has to load

> libthread_db (one of the problems with this library) but is the decision

> to do so drive by gdb or by itself?  I don't recall.


Probably, I didn't think of testing with GDBserver, it's probably broken
there too.  I'll try next time I fiddle with this.

Simon
Simon Marchi via Gdb-patches April 7, 2021, 2:49 a.m. | #4
On 2021-04-06 5:08 p.m., Kevin Buettner via Gdb-patches wrote:
> On Fri, 02 Apr 2021 11:14:23 -0600

> Tom Tromey <tom@tromey.com> wrote:

> 

>> Also, does gdbserver need a similar treatment?  I know it has to load

>> libthread_db (one of the problems with this library) but is the decision

>> to do so drive by gdb or by itself?  I don't recall.

> 

> gdbserver definitely has problems too.  It needs gdb to look up

> symbols (via the qSymbol packet), but is only able to do so at certain

> times.

> 

> I'm trying to puzzle it out now...


Hi Kevin,

Just wondering, are you actively working on this problem?  I am not
currently assigned on this at $day_job, so all I can put on it is
leftovers of time here and there.  If you want to propose another patch
/ solution, feel free!

Simon
Simon Marchi via Gdb-patches April 7, 2021, 11:51 p.m. | #5
On Tue, 6 Apr 2021 22:49:50 -0400
Simon Marchi <simon.marchi@polymtl.ca> wrote:

> On 2021-04-06 5:08 p.m., Kevin Buettner via Gdb-patches wrote:

> > On Fri, 02 Apr 2021 11:14:23 -0600

> > Tom Tromey <tom@tromey.com> wrote:

> >   

> >> Also, does gdbserver need a similar treatment?  I know it has to load

> >> libthread_db (one of the problems with this library) but is the decision

> >> to do so drive by gdb or by itself?  I don't recall.  

> > 

> > gdbserver definitely has problems too.  It needs gdb to look up

> > symbols (via the qSymbol packet), but is only able to do so at certain

> > times.

> > 

> > I'm trying to puzzle it out now...  

> 

> Hi Kevin,

> 

> Just wondering, are you actively working on this problem?  I am not

> currently assigned on this at $day_job, so all I can put on it is

> leftovers of time here and there.  If you want to propose another patch

> / solution, feel free!


Hi Simon,

Yes, I'm actively working on it.  I first came across it while doing some
gdbserver testing, so that's where my focus is at the moment.  We
definitely need to come up with a solution (possibly different ones)
for both gdb and gdbserver.

Kevin
Tom Tromey April 8, 2021, 6:46 p.m. | #6
Simon> I don't think we can directly use the same mechanism here: we don't
Simon> really want to create a SYMFILE_DEFER_LOADING_LIBTHREAD_DB flag, that
Simon> would be quite an abstraction breach.  Or do you mean a
Simon> SYMFILE_DEFER_CALLING_THE_NEW_OBJFILE_OBSERVABLE flag?

Not sure, it just seemed to me that the idea is the same: when a number
of libraries become visible all at once, GDB should defer some work
until after they've all been handled internally.

Tom

Patch

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 9b0186dd391c..aaac17cc73b7 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -347,6 +347,8 @@  post_create_inferior (int from_tty)
      if the now pushed target supports hardware watchpoints.  */
   breakpoint_re_set ();
 
+  current_inferior ()->needs_setup = 0;
+
   gdb::observers::inferior_created.notify (current_inferior ());
 }
 
@@ -2418,11 +2420,6 @@  proceed_after_attach (inferior *inf)
 void
 setup_inferior (int from_tty)
 {
-  struct inferior *inferior;
-
-  inferior = current_inferior ();
-  inferior->needs_setup = 0;
-
   /* If no exec file is yet known, try to determine it from the
      process itself.  */
   if (get_exec_file (0) == NULL)
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index de8687e99c73..b7a500079fb2 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -1011,8 +1011,19 @@  try_thread_db_load (const char *library, bool check_auto_load_safe)
   if (strchr (library, '/') != NULL)
     info->filename = gdb_realpath (library).release ();
 
-  if (try_thread_db_load_1 (info))
-    return true;
+  try
+    {
+      if (try_thread_db_load_1 (info))
+	return true;
+    }
+  catch (const gdb_exception &except)
+    {
+      if (libthread_db_debug)
+	{
+	  exception_fprintf (gdb_stdlog, except,
+			     "Warning: try_thread_db_load: ");
+	}
+    }
 
   /* This library "refused" to work on current inferior.  */
   delete_thread_db_info (current_inferior ()->process_target (),
@@ -1183,10 +1194,13 @@  has_libpthread (void)
 static bool
 thread_db_load (void)
 {
-  struct thread_db_info *info;
+  inferior *inf = current_inferior ();
 
-  info = get_thread_db_info (current_inferior ()->process_target (),
-			     inferior_ptid.pid ());
+  if (inf->needs_setup)
+    return false;
+
+  thread_db_info *info = get_thread_db_info (inf->process_target (),
+					     inferior_ptid.pid ());
 
   if (info != NULL)
     return true;