From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 57378 invoked by alias); 7 Oct 2019 09:05:52 -0000 Mailing-List: contact elfutils-devel-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Post: List-Help: List-Subscribe: Sender: elfutils-devel-owner@sourceware.org Received: (qmail 57301 invoked by uid 89); 7 Oct 2019 09:05:52 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Checked: by ClamAV 0.100.3 on sourceware.org X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 spammy=3666, H*RU:209.85.210.194, remain, sk:interna X-Spam-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on sourceware.org X-Spam-Level: X-HELO: mail-pf1-f194.google.com Received: from mail-pf1-f194.google.com (HELO mail-pf1-f194.google.com) (209.85.210.194) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 07 Oct 2019 09:05:50 +0000 Received: by mail-pf1-f194.google.com with SMTP id b128so8253031pfa.1 for ; Mon, 07 Oct 2019 02:05:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=alUpmSUyU6nU2yT4wL09xoY9a1/3N6dU9U1jIPbAJrk=; b=BI8Smh2ni3pjdxNbO2dRwbMM0eA+i1amSjd4lJ2nBdwJt4qNccL+7lsgVkcKR2X0bC s2tcsgMq4yqO9gG8nZoi7kUTyNMrMeLKqeM2dfajsUYUekVbTaRjrRYEhKeO5Pf7QD+x VS29J7Om7poY31c8x+D2IFdIyWI2lj1nqsD/+AXdToXXvUdZpuIeTTJlT/Ub/x1ADf/2 ytCo85WrBs8f4WSfyHSNtUvbfXFdQi+CfHm6lKDCgEMZZtZDIgE/VNDjpXPHp/GKmV3+ wvq5sVfmqUl+cx9OCfx6RlH27gbm0BeDaari0f5UHr8wNB9lnz4YXqApqbe3W5yyB8// Fi+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=alUpmSUyU6nU2yT4wL09xoY9a1/3N6dU9U1jIPbAJrk=; b=W6PNHUONjn3pmK9VbWnsQ1p5yO836QNLYpzKb7iA0Gym/GcXLVmYZJDd1fely2CIRZ Jc9kuOL5rZhsKaPdpjFYkUsDQl8FH7BmFuEoxl8nL7+5igozeXFQQqt39nrk3dsO0A+1 aZbgaVWhAKdNr9QUvgBLqecv0O02d9XKFALJzkOQKuPoR6JN/oWajDs9qJb1UWkXi1eb /vZjTYSN+FbBDV6nkJxHgo46Su7QGqExjdVUrKETrAMBniWCWn1kvZM1HGuSQBgUwn0c 7vPr7in+aY6Ny4FXrWWjsbyxTYf+cYKsnzHXJizB8K+eIIrinOFFVGdT2pzAxUMdxcxR 5a6A== X-Gm-Message-State: APjAAAXDyyNIVJv43UeyMOkoTfagUb1HkchoTGL7omgkjvmWW+DBhTY4 eIcQpCRwvtjOMDDjQsykkbW42l7Otjk= X-Google-Smtp-Source: APXvYqw0aGWR8qZkuy2YCjYLR8XTQ44XBkFty9WvNfitp60NTusTL+GlKvTESJtsAZpWL+PohzEMeg== X-Received: by 2002:a17:90a:f98f:: with SMTP id cq15mr31713986pjb.54.1570439148058; Mon, 07 Oct 2019 02:05:48 -0700 (PDT) Received: from vader.hsd1.wa.comcast.net ([2601:602:8b80:8e0:e6a7:a0ff:fe0b:c9a8]) by smtp.gmail.com with ESMTPSA id a17sm13855631pfi.178.2019.10.07.02.05.47 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Oct 2019 02:05:47 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Subject: [PATCH 3/5] libdwfl: add interface for attaching to/detaching from threads Date: Mon, 07 Oct 2019 09:05:00 -0000 Message-Id: <06926ca241daa563aba5398977b3d4acd70e47cd.1570438723.git.osandov@fb.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-IsSubscribed: yes X-SW-Source: 2019-q4/txt/msg00009.txt.bz2 From: Omar Sandoval libdwfl has implementations of attaching to/detaching from threads and unwinding stack traces. However, that functionality is only available through the dwfl_thread_getframes interface, which isn't very flexible. This adds two new functions, dwfl_attach_thread and dwfl_detach_thread, which separate the thread stopping functionality out of dwfl_thread_getframes. Additionally, it makes dwfl_thread_getframes cache the stack trace for threads stopped this way. This makes it possible to use the frames after dwfl_thread_getframes returns. Signed-off-by: Omar Sandoval --- libdw/ChangeLog | 6 ++ libdw/libdw.map | 5 ++ libdwfl/ChangeLog | 8 +++ libdwfl/dwfl_frame.c | 136 ++++++++++++++++++++++++++++++------------- libdwfl/libdwfl.h | 12 ++++ libdwfl/libdwflP.h | 3 +- 6 files changed, 128 insertions(+), 42 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 498cf0b7..1de8e2fc 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,9 @@ +2019-10-07 Omar Sandoval + + * libdw.map (ELFUTILS_0.178): New section. + Add dwfl_attach_thread. + Add dwfl_detach_thread. + 2019-07-05 Omar Sandoval * Makefile.am (libdw_so_LIBS): Replace libebl.a with libebl_pic.a. diff --git a/libdw/libdw.map b/libdw/libdw.map index decac05c..f20ffc2f 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -370,3 +370,8 @@ ELFUTILS_0.177 { # presume that NULL is only returned on error (otherwise ELF_K_NONE). dwelf_elf_begin; } ELFUTILS_0.175; +ELFUTILS_0.178 { + global: + dwfl_attach_thread; + dwfl_detach_thread; +} ELFUTILS_0.177; diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 07a1e8df..b7eaedca 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -7,6 +7,14 @@ (thread_free_all_states): Remove function. (free_states): Add function. (dwfl_thread_getframes): Don't update thread->unwound while unwinding. + Use start_unwind. + Cache frames when attached with dwfl_attach_thread. + (start_unwind): Add function. + (attach_thread_cb): Add function. + (dwfl_attach_thread): Add function. + (dwfl_detach_thread): Add function. + * libdwfl.h (dwfl_attach_thread): Add definition. + (dwfl_detach_thread): Add definition. * libdwflP.h (struct Dwfl_Thread): Update comment for unwound member. 2019-08-12 Mark Wielaard diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c index 5bbf850e..61fad8b9 100644 --- a/libdwfl/dwfl_frame.c +++ b/libdwfl/dwfl_frame.c @@ -103,6 +103,29 @@ state_alloc (Dwfl_Thread *thread) return state; } +static Dwfl_Frame * +start_unwind(Dwfl_Thread *thread) +{ + if (ebl_frame_nregs (thread->process->ebl) == 0) + { + __libdwfl_seterrno (DWFL_E_NO_UNWIND); + return NULL; + } + if (state_alloc (thread) == NULL) + { + __libdwfl_seterrno (DWFL_E_NOMEM); + return NULL; + } + if (! thread->process->callbacks->set_initial_registers (thread, + thread->callbacks_arg)) + { + free_states (thread->unwound); + thread->unwound = NULL; + return NULL; + } + return thread->unwound; +} + void internal_function __libdwfl_process_free (Dwfl_Process *process) @@ -366,6 +389,45 @@ getthread (Dwfl *dwfl, pid_t tid, return err; } +static int +attach_thread_cb(Dwfl_Thread *thread, void *arg) +{ + Dwfl_Thread *copied = malloc (sizeof (*copied)); + if (copied == NULL) + { + __libdwfl_seterrno (DWFL_E_NOMEM); + return DWARF_CB_ABORT; + } + *copied = *thread; + if (start_unwind (copied) == NULL) + { + free (copied); + return DWARF_CB_ABORT; + } + *(Dwfl_Thread **)arg = copied; + return DWARF_CB_OK; +} + +Dwfl_Thread * +dwfl_attach_thread(Dwfl *dwfl, pid_t tid) +{ + Dwfl_Thread *thread; + if (getthread (dwfl, tid, attach_thread_cb, &thread)) + return NULL; + return thread; +} + +void +dwfl_detach_thread(Dwfl_Thread *thread) +{ + if (thread == NULL) + return; + if (thread->process->callbacks->thread_detach) + thread->process->callbacks->thread_detach (thread, thread->callbacks_arg); + free_states (thread->unwound); + free (thread); +} + struct one_thread { int (*callback) (Dwfl_Frame *frame, void *arg); @@ -394,63 +456,55 @@ dwfl_thread_getframes (Dwfl_Thread *thread, int (*callback) (Dwfl_Frame *state, void *arg), void *arg) { - Ebl *ebl = thread->process->ebl; - if (ebl_frame_nregs (ebl) == 0) - { - __libdwfl_seterrno (DWFL_E_NO_UNWIND); - return -1; - } - if (state_alloc (thread) == NULL) - { - __libdwfl_seterrno (DWFL_E_NOMEM); - return -1; - } Dwfl_Process *process = thread->process; - if (! process->callbacks->set_initial_registers (thread, - thread->callbacks_arg)) - { - free_states (thread->unwound); - thread->unwound = NULL; - return -1; - } + int ret = -1; + bool cache = thread->unwound != NULL; + if (! cache && start_unwind (thread) == NULL) + return -1; Dwfl_Frame *state = thread->unwound; - thread->unwound = NULL; + if (! cache) + thread->unwound = NULL; if (! state_fetch_pc (state)) - { - if (process->callbacks->thread_detach) - process->callbacks->thread_detach (thread, thread->callbacks_arg); - free_states (state); - return -1; - } + goto out; do { int err = callback (state, arg); if (err != DWARF_CB_OK) { - if (process->callbacks->thread_detach) - process->callbacks->thread_detach (thread, thread->callbacks_arg); - free_states (state); - return err; + ret = err; + goto out; + } + if (state->unwound == NULL) + __libdwfl_frame_unwind (state); + else if (state->unwound->pc_state == DWFL_FRAME_STATE_ERROR) + { + /* This frame was previously cached as an error. We still return -1, + but we don't know what the original error was. */ + __libdwfl_seterrno (DWFL_E_NOERROR); } - __libdwfl_frame_unwind (state); Dwfl_Frame *next = state->unwound; - /* The old frame is no longer needed. */ - free (state); + if (! cache) + { + /* The old frame is no longer needed. */ + free (state); + } state = next; } while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET); - Dwfl_Error err = dwfl_errno (); - if (process->callbacks->thread_detach) - process->callbacks->thread_detach (thread, thread->callbacks_arg); - if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR) + if (state && state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED) + ret = 0; +out: + if (! cache) { + if (process->callbacks->thread_detach) + { + Dwfl_Error err = dwfl_errno (); + process->callbacks->thread_detach (thread, thread->callbacks_arg); + __libdwfl_seterrno (err); + } free_states (state); - __libdwfl_seterrno (err); - return -1; } - assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED); - free_states (state); - return 0; + return ret; } INTDEF(dwfl_thread_getframes) diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h index a0c1d357..a22afc78 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -775,6 +775,18 @@ int dwfl_getthreads (Dwfl *dwfl, void *arg) __nonnull_attribute__ (1, 2); +/* Attach to a thread. The returned thread must be detached and freed with + dwfl_detach_thread. Returns NULL on error. This calls the + set_initial_registers callback. While a thread is attached, + dwfl_thread_getframes will cache the unwound frames for the thread. They + remain valid until dwfl_detach_thread is called. */ +Dwfl_Thread *dwfl_attach_thread(Dwfl *dwfl, pid_t tid) + __nonnull_attribute__ (1); + +/* Detach from a thread that was attached with dwfl_attach_thread and free it. + This calls the detach_thread callback. */ +void dwfl_detach_thread(Dwfl_Thread *thread); + /* Iterate through the frames for a thread. Returns zero if all frames have been processed by the callback, returns -1 on error, or the value of the callback when not DWARF_CB_OK. -1 returned on error will diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 6b2d4867..c80d2051 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -242,7 +242,8 @@ struct Dwfl_Thread { Dwfl_Process *process; pid_t tid; - /* Bottom (innermost) frame while we're initializing, NULL afterwards. */ + /* Bottom (innermost) frame. If the stack trace is not cached, then this is + NULL except during initialization. */ Dwfl_Frame *unwound; void *callbacks_arg; }; -- 2.23.0