From patchwork Mon Apr 10 13:40:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yao Qi X-Patchwork-Id: 19941 Received: (qmail 107116 invoked by alias); 10 Apr 2017 13:40:42 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 107057 invoked by uid 89); 10 Apr 2017 13:40:39 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.1 required=5.0 tests=BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy= X-HELO: mail-wm0-f54.google.com Received: from mail-wm0-f54.google.com (HELO mail-wm0-f54.google.com) (74.125.82.54) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 10 Apr 2017 13:40:37 +0000 Received: by mail-wm0-f54.google.com with SMTP id w64so39315908wma.0 for ; Mon, 10 Apr 2017 06:40:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version:content-transfer-encoding; bh=KBMwHtodgsOnv/2LOt8t5YxQZEZ0QD8r2zxT8p22vCA=; b=aru5X9PRFUJiV/Q8R8DGpMxzfF8k87KURl+djPb/daiq9Tj3Kce9XwSYyLT/vgWqPe vHG2/ENZBeLcbujuoCHeZBLTDL8M50L/dAAYRkV3DoJp85cqIa0OF7Nq1gIVGzV5e0Sb i3Rb+4jM0TEiUrn+VWfFoKt6F7BqfeMylkvON+1ZkCf0d77ld4M8c9CkOTmde9ySPeeM aUfzNPtITcvVhOqzhZUNqVMDRFJVCAA7S0ouFRB+e15aVo+ZBkfjafmQ83hM/HgOed3t JD0Ml2EBuR9be/kFpMuG/7G1p810HM7ppm89FvPjHP7yKeo0K1VqyK6koGwY+Xgyv3he yLYw== X-Gm-Message-State: AN3rC/5qY6LhFLWZ3j6exfpz/r2hLTMbfCZNwY7G2OVA4dpcOn6N58ti Rj1da2E2l1mptg== X-Received: by 10.28.88.2 with SMTP id m2mr10876029wmb.12.1491831636387; Mon, 10 Apr 2017 06:40:36 -0700 (PDT) Received: from E107787-LIN ([194.214.185.158]) by smtp.gmail.com with ESMTPSA id b31sm12224080wrb.7.2017.04.10.06.40.35 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Mon, 10 Apr 2017 06:40:35 -0700 (PDT) From: Yao Qi To: Pedro Alves Cc: gdb-patches@sourceware.org Subject: Re: [PATCH 2/2] Don't delete thread_info if refcount isn't zero References: <1f525e52-f547-63ac-0a31-e92686c9caf8@redhat.com> <1491426942-6306-1-git-send-email-yao.qi@linaro.org> <1491426942-6306-3-git-send-email-yao.qi@linaro.org> <9c8ed015-2eb0-9b5c-affc-b5ba59179a7f@redhat.com> <86a87sk1wz.fsf@gmail.com> <228668fb-05be-3f9b-9290-c12059a25cab@redhat.com> Date: Mon, 10 Apr 2017 14:40:34 +0100 In-Reply-To: <228668fb-05be-3f9b-9290-c12059a25cab@redhat.com> (Pedro Alves's message of "Fri, 7 Apr 2017 11:29:30 +0100") Message-ID: <861st0l6st.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) MIME-Version: 1.0 X-IsSubscribed: yes Pedro Alves writes: > I'm not sure what you mean by "wrong". I'm saying that the old comment > still makes sense. That comment is talking about the > "find_inferior_ptid (old->thread->ptid) != NULL" line, which is a > lookup for an _inferior_ not a thread. Both the inferior and thread_info I thought it is about a thread. > object are still around, but the process may have exited/been detached > meanwhile, and consequently the inferior's "pid" field is now > zero. And in that case, we don't restore back the selected thread. I move the comment close to "find_inferior_ptid (old->thread->ptid) != NULL" line, to make it clearer. Patch below is pushed in. =================================================================^M ^[[1m^[[31m==2291==ERROR: AddressSanitizer: heap-use-after-free on address 0x6160000999c4 at pc 0x000000826022 bp 0x7ffd28a8ff70 sp 0x7ffd28a8ff60^M ^[[1m^[[0m^[[1m^[[34mREAD of size 4 at 0x6160000999c4 thread T0^[[1m^[[0m^M #0 0x826021 in release_stop_context_cleanup ../../binutils-gdb/gdb/infrun.c:8203^M #1 0x72798a in do_my_cleanups ../../binutils-gdb/gdb/common/cleanups.c:154^M #2 0x727a32 in do_cleanups(cleanup*) ../../binutils-gdb/gdb/common/cleanups.c:176^M #3 0x826895 in normal_stop() ../../binutils-gdb/gdb/infrun.c:8381^M #4 0x815208 in fetch_inferior_event(void*) ../../binutils-gdb/gdb/infrun.c:4011^M #5 0x868aca in inferior_event_handler(inferior_event_type, void*) ../../binutils-gdb/gdb/inf-loop.c:44^M .... ^[[1m^[[32m0x6160000999c4 is located 68 bytes inside of 568-byte region [0x616000099980,0x616000099bb8)^M ^[[1m^[[0m^[[1m^[[35mfreed by thread T0 here:^[[1m^[[0m^M #0 0x7fb0bc1312ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)^M #1 0xb8c62f in xfree(void*) ../../binutils-gdb/gdb/common/common-utils.c:100^M #2 0x83df67 in free_thread ../../binutils-gdb/gdb/thread.c:207^M #3 0x83dfd2 in init_thread_list() ../../binutils-gdb/gdb/thread.c:223^M #4 0x805494 in kill_command ../../binutils-gdb/gdb/infcmd.c:2595^M .... Detaching from program: /home/yao.qi/SourceCode/gnu/build-with-asan/gdb/testsuite/outputs/gdb.threads/threadapply/threadapply, process 2399^M =================================================================^M ^[[1m^[[31m==2387==ERROR: AddressSanitizer: heap-use-after-free on address 0x6160000a98c0 at pc 0x00000083fd28 bp 0x7ffd401c3110 sp 0x7ffd401c3100^M ^[[1m^[[0m^[[1m^[[34mREAD of size 4 at 0x6160000a98c0 thread T0^[[1m^[[0m^M #0 0x83fd27 in thread_alive ../../binutils-gdb/gdb/thread.c:741^M #1 0x844277 in thread_apply_all_command ../../binutils-gdb/gdb/thread.c:1804^M .... ^M ^[[1m^[[32m0x6160000a98c0 is located 64 bytes inside of 568-byte region [0x6160000a9880,0x6160000a9ab8)^M ^[[1m^[[0m^[[1m^[[35mfreed by thread T0 here:^[[1m^[[0m^M #0 0x7f59a7e322ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)^M #1 0xb8c62f in xfree(void*) ../../binutils-gdb/gdb/common/common-utils.c:100^M #2 0x83df67 in free_thread ../../binutils-gdb/gdb/thread.c:207^M #3 0x83dfd2 in init_thread_list() ../../binutils-gdb/gdb/thread.c:223^M This patch fixes the issue by deleting thread_info object if it is deletable, otherwise, mark it as exited (by set_thread_exited). Function set_thread_exited is shared from delete_thread_1. This patch also moves field "refcount" to private and methods incref and decref. Additionally, we stop using "ptid_t" in "struct current_thread_cleanup" to reference threads, instead we use "thread_info" directly. Due to this change, we don't need restore_current_thread_ptid_changed anymore. gdb: 2017-04-10 Yao Qi PR gdb/19942 * gdbthread.h (thread_info::deletable): New method. (thread_info::incref): New method. (thread_info::decref): New method. (thread_info::refcount): Move it to private. * infrun.c (save_stop_context): Call inc_refcount. (release_stop_context_cleanup): Likewise. * thread.c (set_thread_exited): New function. (init_thread_list): Delete "tp" only it is deletable, otherwise call set_thread_exited. (delete_thread_1): Call set_thread_exited. (current_thread_cleanup) : Remove. : New field. (restore_current_thread_ptid_changed): Removed. (do_restore_current_thread_cleanup): Adjust. (restore_current_thread_cleanup_dtor): Don't call find_thread_ptid. (set_thread_refcount): Use dec_refcount. (make_cleanup_restore_current_thread): Adjust. (thread_apply_all_command): Call inc_refcount. (_initialize_thread): Don't call observer_attach_thread_ptid_changed. diff --git a/gdb/ChangeLog b/gdb/ChangeLog index a1f8b49..68d8f34 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,30 @@ 2017-04-10 Yao Qi + PR gdb/19942 + * gdbthread.h (thread_info::deletable): New method. + (thread_info::incref): New method. + (thread_info::decref): New method. + (thread_info::refcount): Move it to private. + * infrun.c (save_stop_context): Call inc_refcount. + (release_stop_context_cleanup): Likewise. + * thread.c (set_thread_exited): New function. + (init_thread_list): Delete "tp" only it is deletable, otherwise + call set_thread_exited. + (delete_thread_1): Call set_thread_exited. + (current_thread_cleanup) : Remove. + : New field. + (restore_current_thread_ptid_changed): Removed. + (do_restore_current_thread_cleanup): Adjust. + (restore_current_thread_cleanup_dtor): Don't call + find_thread_ptid. + (set_thread_refcount): Use dec_refcount. + (make_cleanup_restore_current_thread): Adjust. + (thread_apply_all_command): Call inc_refcount. + (_initialize_thread): Don't call + observer_attach_thread_ptid_changed. + +2017-04-10 Yao Qi + * thread.c (delete_thread_1): Hoist code on marking thread as exited. diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 9a16fe6..4cd7390 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -183,6 +183,27 @@ public: explicit thread_info (inferior *inf, ptid_t ptid); ~thread_info (); + bool deletable () const + { + /* If this is the current thread, or there's code out there that + relies on it existing (m_refcount > 0) we can't delete yet. */ + return (m_refcount == 0 && !ptid_equal (ptid, inferior_ptid)); + } + + /* Increase the refcount. */ + void incref () + { + gdb_assert (m_refcount >= 0); + m_refcount++; + } + + /* Decrease the refcount. */ + void decref () + { + m_refcount--; + gdb_assert (m_refcount >= 0); + } + struct thread_info *next = NULL; ptid_t ptid; /* "Actual process id"; In fact, this may be overloaded with @@ -254,11 +275,6 @@ public: but STATE will still be THREAD_RUNNING. */ enum thread_state state = THREAD_STOPPED; - /* If this is > 0, then it means there's code out there that relies - on this thread being listed. Don't delete it from the lists even - if we detect it exiting. */ - int refcount = 0; - /* State of GDB control of inferior thread execution. See `struct thread_control_state'. */ thread_control_state control {}; @@ -346,6 +362,13 @@ public: fields point to self. */ struct thread_info *step_over_prev = NULL; struct thread_info *step_over_next = NULL; + +private: + + /* If this is > 0, then it means there's code out there that relies + on this thread being listed. Don't delete it from the lists even + if we detect it exiting. */ + int m_refcount = 0; }; /* Create an empty thread list, or empty the existing one. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index c8c2d6e..b5eb4ab 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -8165,7 +8165,7 @@ save_stop_context (void) /* Take a strong reference so that the thread can't be deleted yet. */ sc->thread = inferior_thread (); - sc->thread->refcount++; + sc->thread->incref (); } else sc->thread = NULL; @@ -8182,7 +8182,7 @@ release_stop_context_cleanup (void *arg) struct stop_context *sc = (struct stop_context *) arg; if (sc->thread != NULL) - sc->thread->refcount--; + sc->thread->decref (); xfree (sc); } diff --git a/gdb/thread.c b/gdb/thread.c index 2e9da53..2d936cd 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -192,6 +192,27 @@ clear_thread_inferior_resources (struct thread_info *tp) thread_cancel_execution_command (tp); } +/* Set the TP's state as exited. */ + +static void +set_thread_exited (thread_info *tp, int silent) +{ + /* Dead threads don't need to step-over. Remove from queue. */ + if (tp->step_over_next != NULL) + thread_step_over_chain_remove (tp); + + if (tp->state != THREAD_EXITED) + { + observer_notify_thread_exit (tp, silent); + + /* Tag it as exited. */ + tp->state = THREAD_EXITED; + + /* Clear breakpoints, etc. associated with this thread. */ + clear_thread_inferior_resources (tp); + } +} + void init_thread_list (void) { @@ -205,7 +226,10 @@ init_thread_list (void) for (tp = thread_list; tp; tp = tpnext) { tpnext = tp->next; - delete tp; + if (tp->deletable ()) + delete tp; + else + set_thread_exited (tp, 1); } thread_list = NULL; @@ -430,25 +454,9 @@ delete_thread_1 (ptid_t ptid, int silent) if (!tp) return; - /* Dead threads don't need to step-over. Remove from queue. */ - if (tp->step_over_next != NULL) - thread_step_over_chain_remove (tp); + set_thread_exited (tp, silent); - if (tp->state != THREAD_EXITED) - { - observer_notify_thread_exit (tp, silent); - - /* Tag it as exited. */ - tp->state = THREAD_EXITED; - - /* Clear breakpoints, etc. associated with this thread. */ - clear_thread_inferior_resources (tp); - } - - /* If this is the current thread, or there's code out there that - relies on it existing (refcount > 0) we can't delete yet. */ - if (tp->refcount > 0 - || ptid_equal (tp->ptid, inferior_ptid)) + if (!tp->deletable ()) { /* Will be really deleted some other time. */ return; @@ -1546,7 +1554,7 @@ struct current_thread_cleanup 'current_thread_cleanup_chain' below. */ struct current_thread_cleanup *next; - ptid_t inferior_ptid; + thread_info *thread; struct frame_id selected_frame_id; int selected_frame_level; int was_stopped; @@ -1561,37 +1569,21 @@ struct current_thread_cleanup succeeds. */ static struct current_thread_cleanup *current_thread_cleanup_chain; -/* A thread_ptid_changed observer. Update all currently installed - current_thread_cleanup cleanups that want to switch back to - OLD_PTID to switch back to NEW_PTID instead. */ - -static void -restore_current_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid) -{ - struct current_thread_cleanup *it; - - for (it = current_thread_cleanup_chain; it != NULL; it = it->next) - { - if (ptid_equal (it->inferior_ptid, old_ptid)) - it->inferior_ptid = new_ptid; - } -} - static void do_restore_current_thread_cleanup (void *arg) { - struct thread_info *tp; struct current_thread_cleanup *old = (struct current_thread_cleanup *) arg; - tp = find_thread_ptid (old->inferior_ptid); - - /* If the previously selected thread belonged to a process that has - in the mean time been deleted (due to normal exit, detach, etc.), - then don't revert back to it, but instead simply drop back to no - thread selected. */ - if (tp - && find_inferior_ptid (tp->ptid) != NULL) - restore_current_thread (old->inferior_ptid); + /* If an entry of thread_info was previously selected, it won't be + deleted because we've increased its refcount. The thread represented + by this thread_info entry may have already exited (due to normal exit, + detach, etc), so the thread_info.state is THREAD_EXITED. */ + if (old->thread != NULL + /* If the previously selected thread belonged to a process that has + in the mean time exited (or killed, detached, etc.), then don't revert + back to it, but instead simply drop back to no thread selected. */ + && find_inferior_ptid (old->thread->ptid) != NULL) + restore_current_thread (old->thread->ptid); else { restore_current_thread (null_ptid); @@ -1619,9 +1611,9 @@ restore_current_thread_cleanup_dtor (void *arg) current_thread_cleanup_chain = current_thread_cleanup_chain->next; - tp = find_thread_ptid (old->inferior_ptid); - if (tp) - tp->refcount--; + if (old->thread != NULL) + old->thread->decref (); + inf = find_inferior_id (old->inf_id); if (inf != NULL) inf->removable = old->was_removable; @@ -1638,7 +1630,7 @@ set_thread_refcount (void *data) = (struct thread_array_cleanup *) data; for (k = 0; k != ta_cleanup->count; k++) - ta_cleanup->tp_array[k]->refcount--; + ta_cleanup->tp_array[k]->decref (); } struct cleanup * @@ -1646,7 +1638,7 @@ make_cleanup_restore_current_thread (void) { struct current_thread_cleanup *old = XNEW (struct current_thread_cleanup); - old->inferior_ptid = inferior_ptid; + old->thread = NULL; old->inf_id = current_inferior ()->num; old->was_removable = current_inferior ()->removable; @@ -1679,7 +1671,8 @@ make_cleanup_restore_current_thread (void) struct thread_info *tp = find_thread_ptid (inferior_ptid); if (tp) - tp->refcount++; + tp->incref (); + old->thread = tp; } current_inferior ()->removable = 0; @@ -1796,7 +1789,7 @@ thread_apply_all_command (char *cmd, int from_tty) ALL_NON_EXITED_THREADS (tp) { tp_array[i] = tp; - tp->refcount++; + tp->incref (); i++; } /* Because we skipped exited threads, we may end up with fewer @@ -2286,6 +2279,4 @@ Show printing of thread events (such as thread start and exit)."), NULL, create_internalvar_type_lazy ("_thread", &thread_funcs, NULL); create_internalvar_type_lazy ("_gthread", >hread_funcs, NULL); - - observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed); }