From fcb4437118d0bbc82cf56f76d00c44c0220c0005 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Mon, 2 Jul 2012 20:28:38 +0000 Subject: [PATCH] gdb/ * linux-thread-db.c (inferior_has_bug): New function. (thread_db_find_new_threads_silently): Return boolean as checked by inferior_has_bug, describe it in the comments. (try_thread_db_load_1): Move call to thread_db_find_new_threads_silently earlier. Abort the initialization if it returned non-zero. (thread_db_find_new_threads_2): Preinitialize ERR. Check errors also if UNTIL_NO_NEW, gdb/testsuite/ * gdb.threads/gcore-thread.exp: Remove variable libthread_db_seen. Wrap the test into loop for corefile and core0file. --- gdb/ChangeLog | 10 ++ gdb/linux-thread-db.c | 111 +++++++++++++++++---- gdb/testsuite/ChangeLog | 5 + gdb/testsuite/gdb.threads/gcore-thread.exp | 60 +++++------ 4 files changed, 128 insertions(+), 58 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 49d8b3054f..7186f5cd24 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2012-07-02 Jan Kratochvil + + * linux-thread-db.c (inferior_has_bug): New function. + (thread_db_find_new_threads_silently): Return boolean as checked by + inferior_has_bug, describe it in the comments. + (try_thread_db_load_1): Move call to thread_db_find_new_threads_silently + earlier. Abort the initialization if it returned non-zero. + (thread_db_find_new_threads_2): Preinitialize ERR. Check errors also + if UNTIL_NO_NEW, + 2012-07-02 Doug Evans * dwarf2read.c (maybe_queue_comp_unit): Move definition next to others diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index bc8282955d..dff8fc2598 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -576,6 +576,37 @@ enable_thread_event (int event, CORE_ADDR *bp) return TD_OK; } +/* Verify inferior's '\0'-terminated symbol VER_SYMBOL starts with "%d.%d" and + return 1 if this version is lower (and not equal) to + VER_MAJOR_MIN.VER_MINOR_MIN. Return 0 in all other cases. */ + +static int +inferior_has_bug (const char *ver_symbol, int ver_major_min, int ver_minor_min) +{ + struct minimal_symbol *version_msym; + CORE_ADDR version_addr; + char *version; + int err, got, retval = 0; + + version_msym = lookup_minimal_symbol (ver_symbol, NULL, NULL); + if (version_msym == NULL) + return 0; + + version_addr = SYMBOL_VALUE_ADDRESS (version_msym); + got = target_read_string (version_addr, &version, 32, &err); + if (err == 0 && memchr (version, 0, got) == &version[got -1]) + { + int major, minor; + + retval = (sscanf (version, "%d.%d", &major, &minor) == 2 + && (major < ver_major_min + || (major == ver_major_min && minor < ver_minor_min))); + } + xfree (version); + + return retval; +} + static void enable_thread_event_reporting (void) { @@ -643,9 +674,13 @@ enable_thread_event_reporting (void) } } -/* Same as thread_db_find_new_threads_1, but silently ignore errors. */ +/* Similar as thread_db_find_new_threads_1, but try to silently ignore errors + if appropriate. -static void + Return 1 if the caller should abort libthread_db initialization. Return 0 + otherwise. */ + +static int thread_db_find_new_threads_silently (ptid_t ptid) { volatile struct gdb_exception except; @@ -655,11 +690,36 @@ thread_db_find_new_threads_silently (ptid_t ptid) thread_db_find_new_threads_2 (ptid, 1); } - if (except.reason < 0 && libthread_db_debug) + if (except.reason < 0) { - exception_fprintf (gdb_stderr, except, - "Warning: thread_db_find_new_threads_silently: "); + if (libthread_db_debug) + exception_fprintf (gdb_stderr, except, + "Warning: thread_db_find_new_threads_silently: "); + + /* There is a bug fixed between nptl 2.6.1 and 2.7 by + commit 7d9d8bd18906fdd17364f372b160d7ab896ce909 + where calls to td_thr_get_info fail with TD_ERR for statically linked + executables if td_thr_get_info is called before glibc has initialized + itself. + + If the nptl bug is NOT present in the inferior and still thread_db + reports an error return 1. It means the inferior has corrupted thread + list and GDB should fall back only to LWPs. + + If the nptl bug is present in the inferior return 0 to silently ignore + such errors, and let gdb enumerate threads again later. In such case + GDB cannot properly display LWPs if the inferior thread list is + corrupted. */ + + if (!inferior_has_bug ("nptl_version", 2, 7)) + { + exception_fprintf (gdb_stderr, except, + _("Warning: couldn't activate thread debugging " + "using libthread_db: ")); + return 1; + } } + return 0; } /* Lookup a library in which given symbol resides. @@ -762,6 +822,14 @@ try_thread_db_load_1 (struct thread_db_info *info) info->td_thr_event_enable_p = dlsym (info->handle, "td_thr_event_enable"); info->td_thr_tls_get_addr_p = dlsym (info->handle, "td_thr_tls_get_addr"); + if (thread_db_find_new_threads_silently (inferior_ptid) != 0) + { + /* Even if libthread_db initializes, if the thread list is + corrupted, we'd not manage to list any threads. Better reject this + thread_db, and fall back to at least listing LWPs. */ + return 0; + } + printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n")); if (libthread_db_debug || *libthread_db_search_path) @@ -785,12 +853,6 @@ try_thread_db_load_1 (struct thread_db_info *info) if (target_has_execution) enable_thread_event_reporting (); - /* There appears to be a bug in glibc-2.3.6: calls to td_thr_get_info fail - with TD_ERR for statically linked executables if td_thr_get_info is - called before glibc has initialized itself. Silently ignore such - errors, and let gdb enumerate threads again later. */ - thread_db_find_new_threads_silently (inferior_ptid); - return 1; } @@ -1132,6 +1194,12 @@ thread_db_new_objfile (struct objfile *objfile) correctly. */ if (objfile != NULL + /* libpthread with separate debug info has its debug info file already + loaded (and notified without successfult thread_db initialization)) + the time observer_notify_new_objfile is called for the library itself. + Static executables have their separate debug info loaded already + before the inferior has started. */ + && objfile->separate_debug_objfile_backlink == NULL /* Only check for thread_db if we loaded libpthread, or if this is the main symbol file. We need to check OBJF_MAINLINE to handle the case of debugging @@ -1612,7 +1680,7 @@ find_new_threads_once (struct thread_db_info *info, int iteration, static void thread_db_find_new_threads_2 (ptid_t ptid, int until_no_new) { - td_err_e err; + td_err_e err = TD_OK; struct thread_db_info *info; int pid = ptid_get_pid (ptid); int i, loop; @@ -1628,17 +1696,18 @@ thread_db_find_new_threads_2 (ptid_t ptid, int until_no_new) The 4 is a heuristic: there is an inherent race here, and I have seen that 2 iterations in a row are not always sufficient to "capture" all threads. */ - for (i = 0, loop = 0; loop < 4; ++i, ++loop) - if (find_new_threads_once (info, i, NULL) != 0) - /* Found some new threads. Restart the loop from beginning. */ - loop = -1; + for (i = 0, loop = 0; loop < 4 && err == TD_OK; ++i, ++loop) + if (find_new_threads_once (info, i, &err) != 0) + { + /* Found some new threads. Restart the loop from beginning. */ + loop = -1; + } } else - { - find_new_threads_once (info, 0, &err); - if (err != TD_OK) - error (_("Cannot find new threads: %s"), thread_db_err_str (err)); - } + find_new_threads_once (info, 0, &err); + + if (err != TD_OK) + error (_("Cannot find new threads: %s"), thread_db_err_str (err)); } static void diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 246f293a56..34351ff5cd 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2012-07-02 Jan Kratochvil + + * gdb.threads/gcore-thread.exp: Remove variable libthread_db_seen. + Wrap the test into loop for corefile and core0file. + 2012-07-02 Jan Kratochvil * gdb.dwarf2/fission-reread.S: Remove .section attributes. diff --git a/gdb/testsuite/gdb.threads/gcore-thread.exp b/gdb/testsuite/gdb.threads/gcore-thread.exp index a47f931dc9..37eadd3864 100644 --- a/gdb/testsuite/gdb.threads/gcore-thread.exp +++ b/gdb/testsuite/gdb.threads/gcore-thread.exp @@ -141,11 +141,9 @@ proc load_core { corefile } { global gdb_prompt global libthread_db_seen - set libthread_db_seen 0 gdb_test_multiple "core $corefile" \ "re-load generated corefile" { -re "\\\[Thread debugging using \[^ \r\n\]* enabled\\\]\r\n" { - set libthread_db_seen 1 exp_continue } -re " is not a core dump:.*\r\n$gdb_prompt $" { @@ -168,39 +166,27 @@ proc load_core { corefile } { return 1 } -if ![load_core $corefile] { - return -} - -# FIXME: now what can we test about the thread state? -# We do not know for certain that there should be at least -# three threads, because who knows what kind of many-to-one -# mapping various OS's may do? Let's assume that there must -# be at least two threads: - -gdb_test "info threads" ".*${nl} 2 ${horiz}${nl}\\* 1 .*" \ - "corefile contains at least two threads" - -# One thread in the corefile should be in the "thread2" function. - -gdb_test "info threads" ".* thread2 .*" \ - "a corefile thread is executing thread2" - -# The thread2 thread should be marked as the current thread. - -gdb_test "info threads" ".*${nl}\\* ${horiz} thread2 .*" \ - "thread2 is current thread in corefile" - - -# Test the uninitialized thread list. - -if {"$core0file" != "" && [load_core $core0file]} { - set test "zeroed-threads cannot be listed" - - if {!$libthread_db_seen} { - verbose -log "No libthread_db loaded - -Wl,-z,relro compilation?" - xfail $test - } else { - gdb_test "info threads" "Cannot find new threads: .*" $test +foreach name { corefile core0file } { with_test_prefix $name { + if ![load_core [subst $$name]] { + continue } -} + + # FIXME: now what can we test about the thread state? + # We do not know for certain that there should be at least + # three threads, because who knows what kind of many-to-one + # mapping various OS's may do? Let's assume that there must + # be at least two threads: + + gdb_test "info threads" ".*${nl} 2 ${horiz}${nl}\\* 1 .*" \ + "corefile contains at least two threads" + + # One thread in the corefile should be in the "thread2" function. + + gdb_test "info threads" ".* thread2 .*" \ + "a corefile thread is executing thread2" + + # The thread2 thread should be marked as the current thread. + + gdb_test "info threads" ".*${nl}\\* ${horiz} thread2 .*" \ + "thread2 is current thread in corefile" +}}