From nobody Fri Jul 24 23:30:10 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6A438339371 for ; Wed, 22 Jul 2026 10:25:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784715920; cv=none; b=QvZQD62EvmVza9WZGGfs3O3NYa7tO4igsDvUDG++9c5HxHsS4UIIpeUXCSxLlypcw1iEh9jJ9dCrNez0r/O1cqDuGCY0S1ztxg0TJyVq4+cT0cGBfLmJMioCOQ6L6kuBCze/vxVJub4QlerPDpMJ7WVChD9H1rufNXpFezwWQBw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784715920; c=relaxed/simple; bh=FRbnHj9bnq3muav99zIW0sn/lrRSQR0HOYaHHl/2QP4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=FzEDEbI4ucYejWMK/RNz6ul510rNNrNjrwCSyc8Cbeq1+5iWWrBj9qdRdw2VsRvuK7GyUmkCd23zOXQ2IRX5/6uJoYw6nNLd4TyJSbGBZrmd6tEIMRRItl+9EwiZVmW93jxeEKcSEwAF5kNbYxxtixewwzBck9JVyLHi/og7Ld8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gASVaNRP; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gASVaNRP" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4954f5e8020so20285265e9.2 for ; Wed, 22 Jul 2026 03:25:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784715917; x=1785320717; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=4brpqsgpOGYaU8Xko9TkV4N1SpH1YwC7JnF9u87bPWg=; b=gASVaNRPW35qlmD6H4pN/mNS5umKGsGZzlB6Ggxtt2A4ybhIN29DEj3rQ5MoPfuejH GjenOS9bIe7MV9qVKxiHTvbRFcJtTwvDUUeqcq9iaCmnS6gd6uWI3Ogj1M9OqSYSj79Y eJrF9xJWfZipVfutGLI2XkcjbV+Xon3ff1qALWatfJcJjXBfkdsZ6dqS9rSdlN51g/Kx 7G60VB8BT125M4CvdHBq2U6qgh+ESd9arGS2Y3QFIN/GDeyfsL85ootaEzQ+xaOTeRr0 e68vxJwZinA6EgcZI539r1q6km2L+VUINyy6yuZB/UEr2vv/hjnxUmj1X9/rfLXH2GLy 6k5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784715917; x=1785320717; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=4brpqsgpOGYaU8Xko9TkV4N1SpH1YwC7JnF9u87bPWg=; b=LzUnXRqUUX2FYDGbZt/Fgfni7rIb2rZU1Qy/qhXg8U2ryszoJpw6dzLAcPzZi5vcRS F1uq7T5OdOaqX7Ho282Y9Vj3+zXhMw/CADxFhER7qCLwaVvF6WP2i0EgUJ7LUSf0LVEq KH9HOaG1Pzhh55wvh0yOD5EKBtqan0T6O5pZ3h9Ktb+imj5mj0LTJJJc0odazg241BkB BCPvRD+SBZHss7AuGHilbaytNGAuvqBTWk5FY9wbP4XDtDIVXhgkV+6pruVKPlqf2CSb kqlhIBdaKyY2N62cogmS9fpzPAPJYcbHVfBGJOOuHihQQoSGocPYzuP920kz3U9oCQyx wi6Q== X-Gm-Message-State: AOJu0Yx708DeqqlT+58XFGo8hYtXvXwp+wVG2d35rtfKjRV0wDHhVQ7K ubT71D0i/3eX7Qj7ANe3IELnob99SSEF5l+UzCpZShlh9cZa4Ojluyb8 X-Gm-Gg: AR+sD10MMz5/Yy5mP8uiuBQu8JqB/93mDyZUO3pX8VwR3v0KZB74DYGPcbo5Xa8wn9q ECH+MtHHC+NBF3NeFvrg1HnGTVgIJC2J5PPsvtw1mTT3jC4+uyKLicVHXdOEgb76qX+Rga4zfKk HjGglPqDJvDWHJIZ9uV2xSkSPEBlJqNR5HCVpcEGiNQNmCImeS4lnek5CuD2R41OF+I0dEtguER q8xDQtt1tH5hrhdMhln+69YnJp4JGtd3SLsdNr6DYJmaqSqpUEXyPk0UiNe72Vge2aXG7j3/GOC KMfd57CCbA3WOs+WaftHMOOG0SsPxFY0LS1R5BZsH/UkWdNME3486ETlefZkVScfjxchUjWoXzm iekSA0PFUMnRwJK0sa6dDlCUx16ASkimfSGn/ZRPFWndmtMOX9ud9GcpE X-Received: by 2002:a05:600c:3143:b0:493:e79e:da98 with SMTP id 5b1f17b1804b1-4954a413071mr265438035e9.39.1784715916551; Wed, 22 Jul 2026 03:25:16 -0700 (PDT) Received: from fedora ([95.143.48.79]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4956bc68d4esm26973475e9.0.2026.07.22.03.25.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jul 2026 03:25:15 -0700 (PDT) From: Alessio Podda To: linux-perf-users@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark , Shimin Guo , Alessio Podda Subject: [PATCH] perf unwind-libdw: Fix unwinding of multi-threaded processes Date: Wed, 22 Jul 2026 12:24:07 +0200 Message-ID: <20260722102407.3755154-1-aleph.pi.gh@gmail.com> X-Mailer: git-send-email 2.55.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The libdw callback API has two levels: dwfl_getthread_frames() first finds the requested thread using the next_thread() or get_thread() callback and then walks its stack. Since perf only has a snapshot of the stack of a single thread, it provides a stubbed-out API that always returns the pid the Dwfl was attached with (i.e. whatever was passed to dwfl_attach_state()), rather than the actual sampled thread's TID. Commit 6b2658b3f36a ("perf unwind-libdw: Don't discard loaded ELF/DWARF after every unwind") changed libdw unwinding from recreating the Dwfl object for each sample to caching it in struct maps, which is shared by every thread in the process. It left next_thread() unchanged. Since the pid passed to dwfl_attach_state() is only set at creation, only the thread of the first sample is ever found. As a result, dwfl_getthread_frames() fails with ESRCH when asked to unwind a sample from another thread. Make next_thread() return the current sample's TID, provide get_thread() so libdw can find it directly, and pass the process PID expected by dwfl_attach_state(). This allows libdw to unwind samples from every thread in a multi-threaded process. Fixes: 6b2658b3f36a ("perf unwind-libdw: Don't discard loaded ELF/DWARF aft= er every unwind") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Alessio Podda --- A standalone reproducer is available at: https://github.com/kryggird/perf-bug-repro/tree/019c8ae97f9e4370ef2265d7b= adc3269e2a5044f Run it with the perf binary being tested: ./reproduce.sh /path/to/perf Results: perf 7.1.3-201.fc44.x86_64: 1517 of 1522 samples have no userspace callchain patched perf 7.2.rc4.g1590cf032971: 15 of 1449 samples have no userspace callchain tools/perf/util/unwind-libdw.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 7f35042be567..3735ef83c8c4 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -173,14 +173,30 @@ static int entry(u64 ip, struct unwind_info *ui) return 0; } =20 -static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) +static pid_t next_thread(Dwfl *dwfl __maybe_unused, void *arg, void **thre= ad_argp) { + struct dwfl_ui_thread_info *dwfl_ui_ti =3D arg; + /* We want only single thread to be processed. */ if (*thread_argp !=3D NULL) return 0; =20 + assert(dwfl_ui_ti->ui !=3D NULL); *thread_argp =3D arg; - return dwfl_pid(dwfl); + return thread__tid(dwfl_ui_ti->ui->thread); +} + +static bool get_thread(Dwfl *dwfl __maybe_unused, pid_t tid, void *arg, + void **thread_argp) +{ + struct dwfl_ui_thread_info *dwfl_ui_ti =3D arg; + + assert(dwfl_ui_ti->ui !=3D NULL); + if (tid !=3D thread__tid(dwfl_ui_ti->ui->thread)) + return false; + + *thread_argp =3D arg; + return true; } =20 static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, @@ -306,6 +322,7 @@ static bool libdw_set_initial_registers(Dwfl_Thread *th= read, void *arg) =20 static const Dwfl_Thread_Callbacks callbacks =3D { .next_thread =3D next_thread, + .get_thread =3D get_thread, .memory_read =3D memory_read, .set_initial_registers =3D libdw_set_initial_registers, }; @@ -400,7 +417,7 @@ int libdw__get_entries(unwind_entry_cb_t cb, void *arg, if (err) goto out; =20 - dwfl_attach_state(dwfl, /*elf=3D*/NULL, thread__tid(thread), &callbacks, + dwfl_attach_state(dwfl, /*elf=3D*/NULL, thread__pid(thread), &callbacks, /* Dwfl thread function argument*/dwfl_ui_ti); // Ignore thread already attached error. =20 base-commit: b95f03f04d475aa6719d15a636ddf32222d55657 --=20 2.55.0