From patchwork Thu Apr 24 21:47:03 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110961 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ACF613858D29 for ; Thu, 24 Apr 2025 21:48:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ACF613858D29 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=k0YAaudG; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=V8pL0Arm X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fout-b6-smtp.messagingengine.com (fout-b6-smtp.messagingengine.com [202.12.124.149]) by sourceware.org (Postfix) with ESMTPS id DE2F73858D21 for ; Thu, 24 Apr 2025 21:47:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DE2F73858D21 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DE2F73858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.149 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531263; cv=none; b=LJdwH8y9gijaE9pD38oAHsUJM1d1dPHXn9TyTE7yZo+QDd4wZMRJTrS0cpAQ08T6U4eKIj+P2AF965Gbz84be9hZYp6eWMJHkFZdgei2mAvXhRUKVXTCGnmocLFexWhHJdVSWEYx3XAGQM0srqCo7+FsBah9oBw5nXAhRYkL+PU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531263; c=relaxed/simple; bh=v52QpJa2HRWTtnhG7akgjjje6D4S0pKtdnWNgtaW86o=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=dzciwa3Fnm3DmS4cVxxtsWKk6JnHim/cHMTnQAhMXICroiUYQ5B3qhav+eraZRuutkA1VgbrRDi3rSu4UdQ/rV2tPHLtyo+5cy0Hmc4zeeFha1qxAqR70aNLAyRjSTUUzEC/NE2SyI9nirGwYuzLQ3c0GWn9wMwjFC+16fpnhT4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DE2F73858D21 Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44]) by mailfout.stl.internal (Postfix) with ESMTP id 6168F1140192; Thu, 24 Apr 2025 17:47:42 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Thu, 24 Apr 2025 17:47:42 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:message-id:mime-version:reply-to:subject:subject:to :to; s=fm3; t=1745531262; x=1745617662; bh=XN8q7DoSxqqHh2+kRLsyi vGxKRz4SgU92m4o/DoAg7A=; b=k0YAaudGtrahd+z4RNFUWpxK0NbfxkkaksY0r 0Bk2wZhs8c64u4ReEBXwWVk8R1kRhL4gEXYBdSKV+NC11mCQYiDyf4evC30euQJR kXouCJkRPlcTNxaizu5cay+57i8kQMEicStK1cel01uIfCbAu159g8hzBxOXp8WM 22+byU5eqdMQkI4Vv8tFv7CepqgT+SzoqUKL27wQj8VNrmX8xoiqDcwoL8gLeKfK XcKALaEixEuIGSLUoNDD448SgfraggVQ8bYwbnMIAeU1wKwDPL3cUPxtkgeiDh27 YRPdTf50lgVypdZVIJQmzX03hMmRGQSE0ozUe9eeacfsYbchg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:message-id:mime-version:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1745531262; x=1745617662; bh=XN8q7DoSxqqHh2+kRLsyivGxKRz4SgU92m4 o/DoAg7A=; b=V8pL0ArmW8ZgypkOBXTnDqHcw9b7PYQ1igUDktJHa4T+A30jXOT 0Fxxduv5uJC2B6sHX7JCDLtDrIC4QdDFSQy6MQtWK1GuhLLCljzrzndaV5r27Mos +pp8mHCYXxXbkcDaHzhYFTWoqKtqtBglxjONhtzHYmgfAaKptTmDQbKKTxqg9v3N FNMCH/bLznrBTsy2oypHQiTUguoa97dd94ahEj/nJbOr3sfaMMxVGE7Xucr8zg2o 1Tyt1GxLcKppVZ97CS+GZnzfY/HPnLPhNhJuOhi+sTI4zm3lpNEdvcKXZYojJCpk YscPPyW4rRZ+Mr5gw87dDugopf3v373Qq1w== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffoggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgrrhho vhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnhephfekfe dtgeetueehgffhjeeffeeihefhjeevvedvgfdvjeelfeduffegheelhfdunecuffhomhgr ihhnpehgnhhurdhorhhgpdhqthdqphhrohhjvggtthdrohhrghenucevlhhushhtvghruf hiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehsvghrhhgvihesshgvrhhhvghi rdhiohdpnhgspghrtghpthhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtoh epvghlfhhuthhilhhsqdguvghvvghlsehsohhurhgtvgifrghrvgdrohhrghdprhgtphht thhopehsvghrhhgvihesshgvrhhhvghirdhioh X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:47:41 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 01/12] libebl [1/12]: api for perf register handling, start with x86_64 Date: Thu, 24 Apr 2025 17:47:03 -0400 Message-ID: <20250424214715.306147-1-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 MIME-Version: 1.0 X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v2: - Merged commit 11 into commit 1 (both sample_base_addr/sample_pc and set_initial_registers_sample make sense to introduce in the same commit). - Added i386 backend and factored out common code. * * * First patch of a series that reworks eu-stacktrace functionality into a library interface for other profiling tools. * * * As it happens, Linux perf_events and DWARF can prescribe completely different layouts for the register file, requiring non-obvious code for translation. This makes sense to put in libebl if we want profilers to handle perf sample data with elfutils. Start with the x86_64/i386 implementation taken from eu-stacktrace. The code has been generalized to accept other perf register masks besides the 'preferred' one. * backends/Makefile.am (i386_SRCS): Add i386_initreg_sample.c. (x86_64_SRCS): Add x86_64_initreg_sample.c. (noinst_HEADERS): Add libebl_PERF_FLAGS.h, linux-perf-regs.c, x86_initreg_sample.c. * backends/libebl_PERF_FLAGS.h: New file. * backends/linux-perf-regs.c: New file. (perf_sample_find_reg): Index into a perf register array whose contents are described by regs_mask. The target index is the index of the register in the enum defined by linux arch/N/include/uapi/asm/perf_regs.h on arch N. * backends/x86_initreg_sample.c: New file, implements a generalized version of the eu-stacktrace register shuffling for x86-64/i386. * backends/x86_64_initreg_sample.c: New file, specializes x86_initreg_sample.c for x86-64. * backends/i386_initreg_sample.c: New file, specializes i386_initreg_sample.c for i386. * backends/x86_64_init.c (x86_64_init): Add initialization for set_initial_registers_sample, perf_frame_regs_mask, sample_base_addr, sample_pc. * backends/i386_init.c (i386_init): Add initialization for set_initial_registers_sample, perf_frame_regs_mask, sample_base_addr, sample_pc. * libebl/Makefile.am (libebl_a_SOURCES): Add eblinitreg_sample.c. * libebl/ebl-hooks.h (set_initial_registers_sample): New hook. (sample_base_addr): Ditto. (sample_pc): Ditto. * libebl/eblinitreg_sample.c: New file, implements ebl interface to set_initial_registers_sample, sample_base_addr, sample_pc backend hooks. * libebl/libebl.h (ebl_set_initial_registers_sample): New function. (ebl_perf_frame_regs_mask): New function. (ebl_sample_base_addr): New function. (ebl_sample_pc): New function. * libebl/libeblP.h (struct ebl): Add perf_frame_regs_mask field giving the preferred register mask. --- backends/Makefile.am | 10 +-- backends/i386_init.c | 10 ++- backends/i386_initreg_sample.c | 108 +++++++++++++++++++++++++++++++ backends/libebl_PERF_FLAGS.h | 58 +++++++++++++++++ backends/linux-perf-regs.c | 48 ++++++++++++++ backends/x86_64_init.c | 7 +- backends/x86_64_initreg_sample.c | 106 ++++++++++++++++++++++++++++++ backends/x86_initreg_sample.c | 90 ++++++++++++++++++++++++++ libebl/Makefile.am | 4 +- libebl/ebl-hooks.h | 18 +++++- libebl/eblinitreg_sample.c | 72 +++++++++++++++++++++ libebl/libebl.h | 31 ++++++++- libebl/libeblP.h | 7 +- 13 files changed, 557 insertions(+), 12 deletions(-) create mode 100644 backends/i386_initreg_sample.c create mode 100644 backends/libebl_PERF_FLAGS.h create mode 100644 backends/linux-perf-regs.c create mode 100644 backends/x86_64_initreg_sample.c create mode 100644 backends/x86_initreg_sample.c create mode 100644 libebl/eblinitreg_sample.c diff --git a/backends/Makefile.am b/backends/Makefile.am index 540d0c6c..8ccbdb50 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2000-2010, 2013, 2014 Red Hat, Inc. +## Copyright (C) 2000-2010, 2013, 2014, 2025 Red Hat, Inc. ## Copyright (C) 2012 Tilera Corporation ## This file is part of elfutils. ## @@ -41,13 +41,13 @@ modules = i386 sh x86_64 ia64 alpha arm aarch64 sparc ppc ppc64 s390 \ i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c i386_cfi.c \ i386_retval.c i386_regs.c i386_auxv.c \ - i386_initreg.c i386_unwind.c + i386_initreg.c i386_initreg_sample.c i386_unwind.c sh_SRCS = sh_init.c sh_symbol.c sh_corenote.c sh_regs.c sh_retval.c x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c x86_64_cfi.c \ x86_64_retval.c x86_64_regs.c x86_64_initreg.c \ - x86_64_unwind.c x32_corenote.c + x86_64_initreg_sample.c x86_64_unwind.c x32_corenote.c ia64_SRCS = ia64_init.c ia64_symbol.c ia64_regs.c ia64_retval.c @@ -119,7 +119,9 @@ libebl_backends_a_SOURCES = $(i386_SRCS) $(sh_SRCS) $(x86_64_SRCS) \ libebl_backends_pic_a_SOURCES = am_libebl_backends_pic_a_OBJECTS = $(libebl_backends_a_SOURCES:.c=.os) -noinst_HEADERS = libebl_CPU.h common-reloc.c linux-core-note.c x86_corenote.c +noinst_HEADERS = libebl_CPU.h libebl_PERF_FLAGS.h common-reloc.c \ + linux-core-note.c x86_corenote.c \ + linux-perf-regs.c x86_initreg_sample.c EXTRA_DIST = $(modules:=_reloc.def) diff --git a/backends/i386_init.c b/backends/i386_init.c index 579e5fad..e64ef6ed 100644 --- a/backends/i386_init.c +++ b/backends/i386_init.c @@ -1,5 +1,5 @@ /* Initialization of i386 specific backend library. - Copyright (C) 2000-2009, 2013, 2017 Red Hat, Inc. + Copyright (C) 2000-2009, 2013, 2017, 2025 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 2000. @@ -34,6 +34,7 @@ #define BACKEND i386_ #define RELOC_PREFIX R_386_ #include "libebl_CPU.h" +#include "libebl_PERF_FLAGS.h" /* This defines the common reloc hooks based on i386_reloc.def. */ #include "common-reloc.c" @@ -55,9 +56,14 @@ i386_init (Elf *elf __attribute__ ((unused)), HOOK (eh, auxv_info); HOOK (eh, disasm); HOOK (eh, abi_cfi); - /* gcc/config/ #define DWARF_FRAME_REGISTERS. For i386 it is 17, why? */ + /* gcc/config/ #define DWARF_FRAME_REGISTERS. For i386 it is 17, why? + (Likely an artifact of reusing that header between i386/x86_64.) */ eh->frame_nregs = 9; HOOK (eh, set_initial_registers_tid); + HOOK (eh, set_initial_registers_sample); + HOOK (eh, sample_base_addr); + HOOK (eh, sample_pc); + eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_I386; HOOK (eh, unwind); return eh; diff --git a/backends/i386_initreg_sample.c b/backends/i386_initreg_sample.c new file mode 100644 index 00000000..3df74412 --- /dev/null +++ b/backends/i386_initreg_sample.c @@ -0,0 +1,108 @@ +/* Populate process registers from a linux perf_events sample. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#if (defined __i386__ || defined __x86_64__) && defined(__linux__) +# include +# include +#endif + +#define BACKEND i386_ +#include "libebl_CPU.h" +#include "libebl_PERF_FLAGS.h" +#if (defined __i386__ || defined __x86_64__) && defined(__linux__) +# include "linux-perf-regs.c" +# include "x86_initreg_sample.c" +#endif + +/* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h, + enum perf_event_x86_regs: */ +Dwarf_Word +i386_sample_base_addr (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, + /* XXX hypothetically needed if abi varies + between samples in the same process; + not needed on x86 */ + uint32_t abi __attribute__((unused))) +{ +#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) + (void)regs; + (void)n_regs; + (void)regs_mask; + return 0; +#else /* __i386__ || __x86_64__ */ + (void)regs; + (void)n_regs; + (void)regs_mask; + return perf_sample_find_reg(regs, n_regs, regs_mask, + 7 /* index into perf_event_x86_regs */); +#endif +} + +Dwarf_Word +i386_sample_pc (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, + uint32_t abi __attribute__((unused))) +{ +#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) + (void)regs; + (void)n_regs; + (void)regs_mask; + return 0; +#else /* __i386__ || __x86_64__ */ + return perf_sample_find_reg(regs, n_regs, regs_mask, + 8 /* index into perf_event_x86_regs */); +#endif +} + +bool +i386_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi, + ebl_tid_registers_t *setfunc, + void *arg) +{ +#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) + (void)regs; + (void)n_regs; + (void)regs_mask; + (void)abi; + (void)setfunc; + (void)arg; + return false; +#else /* __i386__ || __x86_64__ */ + Dwarf_Word dwarf_regs[9]; + if (!x86_set_initial_registers_sample (regs, n_regs, regs_mask, + abi, dwarf_regs, 9)) + return false; + return setfunc (0, 9, dwarf_regs, arg); +#endif +} diff --git a/backends/libebl_PERF_FLAGS.h b/backends/libebl_PERF_FLAGS.h new file mode 100644 index 00000000..2ed45f0f --- /dev/null +++ b/backends/libebl_PERF_FLAGS.h @@ -0,0 +1,58 @@ +/* Linux perf_events sample_regs_user flags required for unwinding. + Internal only; elfutils library users should use ebl_perf_frame_regs_mask(). + + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef _LIBEBL_PERF_FLAGS_H +#define _LIBEBL_PERF_FLAGS_H 1 + +#if defined(__linux__) +# include +#endif + +#if defined(_ASM_X86_PERF_REGS_H) +/* See the code in x86_initreg_sample.c for list of required regs and + linux arch/.../include/asm/ptrace.h for matching pt_regs struct. */ +#define REG(R) (1ULL << PERF_REG_X86_ ## R) +/* FLAGS and segment regs are excluded from the following masks, + since they're not needed for unwinding. */ +#define PERF_FRAME_REGISTERS_I386 (REG(AX) | REG(BX) | REG(CX) | REG(DX) \ + | REG(SI) | REG(DI) | REG(BP) | REG(SP) | REG(IP)) +#define PERF_FRAME_REGISTERS_X86_64 (PERF_FRAME_REGISTERS_I386 | REG(R8) \ + | REG(R9) | REG(R10) | REG(R11) | REG(R12) | REG(R13) | REG(R14) | REG(R15)) +/* Register ordering defined in linux arch/x86/include/uapi/asm/perf_regs.h; + see the code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs() + and note how regs are added in the same order as the perf_regs.h enum. */ +#else +/* Since asm/perf_regs.h gives the register layout for a different arch, + we can't unwind x86_64 frames. */ +#define PERF_FRAME_REGISTERS_I386 0 +#define PERF_FRAME_REGISTERS_X86_64 0 +#endif + +#endif /* libebl_PERF_FLAGS.h */ diff --git a/backends/linux-perf-regs.c b/backends/linux-perf-regs.c new file mode 100644 index 00000000..22ad67c6 --- /dev/null +++ b/backends/linux-perf-regs.c @@ -0,0 +1,48 @@ +/* Common pieces for handling registers in a linux perf_events sample. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +static Dwarf_Word +perf_sample_find_reg (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, + int target) +{ + int j, k; uint64_t bit; + for (j = 0, k = 0, bit = 1; k < PERF_REG_X86_64_MAX; k++, bit <<= 1) + { + if (bit & regs_mask) { + if (n_regs <= (uint32_t) j) + return 0; /* regs_mask count doesn't match n_regs */ + if (k == target) + return regs[j]; + if (k > target) + return 0; /* regs_mask doesn't include desired reg */ + j++; + } + } + return 0; +} diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c index be965fa6..6a1cbc4b 100644 --- a/backends/x86_64_init.c +++ b/backends/x86_64_init.c @@ -1,5 +1,5 @@ /* Initialization of x86-64 specific backend library. - Copyright (C) 2002-2009, 2013, 2018 Red Hat, Inc. + Copyright (C) 2002-2009, 2013, 2018, 2025 Red Hat, Inc. Copyright (C) H.J. Lu , 2015. This file is part of elfutils. Written by Ulrich Drepper , 2002. @@ -35,6 +35,7 @@ #define BACKEND x86_64_ #define RELOC_PREFIX R_X86_64_ #include "libebl_CPU.h" +#include "libebl_PERF_FLAGS.h" /* This defines the common reloc hooks based on x86_64_reloc.def. */ #include "common-reloc.c" @@ -62,6 +63,10 @@ x86_64_init (Elf *elf __attribute__ ((unused)), /* gcc/config/ #define DWARF_FRAME_REGISTERS. */ eh->frame_nregs = 17; HOOK (eh, set_initial_registers_tid); + HOOK (eh, set_initial_registers_sample); + HOOK (eh, sample_base_addr); + HOOK (eh, sample_pc); + eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_X86_64; HOOK (eh, unwind); HOOK (eh, check_reloc_target_type); diff --git a/backends/x86_64_initreg_sample.c b/backends/x86_64_initreg_sample.c new file mode 100644 index 00000000..71d594bd --- /dev/null +++ b/backends/x86_64_initreg_sample.c @@ -0,0 +1,106 @@ +/* Populate process registers from a linux perf_events sample. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#if defined(__x86_64__) && defined(__linux__) +# include +# include +#endif + +#define BACKEND x86_64_ +#include "libebl_CPU.h" +#include "libebl_PERF_FLAGS.h" +#if defined(__x86_64__) && defined(__linux__) +# include "linux-perf-regs.c" +# include "x86_initreg_sample.c" +#endif + +/* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h, + enum perf_event_x86_regs: */ +Dwarf_Word +x86_64_sample_base_addr (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, + /* XXX hypothetically needed if abi varies + between samples in the same process; + not needed on x86*/ + uint32_t abi __attribute__((unused))) +{ +#if !defined(__x86_64__) || !defined(__linux__) + (void)regs; + (void)n_regs; + (void)regs_mask; + return 0; +#else /* __x86_64__ */ + return perf_sample_find_reg(regs, n_regs, regs_mask, + 7 /* index into perf_event_x86_regs */); +#endif +} + +Dwarf_Word +x86_64_sample_pc (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, + uint32_t abi __attribute__((unused))) +{ +#if !defined(__x86_64__) || !defined(__linux__) + (void)regs; + (void)n_regs; + (void)regs_mask; + return 0; +#else /* __x86_64__ */ + return perf_sample_find_reg(regs, n_regs, regs_mask, + 8 /* index into perf_event_x86_regs */); +#endif +} + +bool +x86_64_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi, + ebl_tid_registers_t *setfunc, + void *arg) +{ +#if !defined(__x86_64__) || !defined(__linux__) + (void)regs; + (void)n_regs; + (void)regs_mask; + (void)abi; + (void)setfunc; + (void)arg; + return false; +#else /* __x86_64__ */ + Dwarf_Word dwarf_regs[17]; + if (!x86_set_initial_registers_sample (regs, n_regs, regs_mask, + abi, dwarf_regs, 9)) + return false; + return setfunc (0, 17, dwarf_regs, arg); +#endif +} + diff --git a/backends/x86_initreg_sample.c b/backends/x86_initreg_sample.c new file mode 100644 index 00000000..e323bada --- /dev/null +++ b/backends/x86_initreg_sample.c @@ -0,0 +1,90 @@ +/* x86 linux perf_events register handling, pieces common to x86-64 and i386. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +static bool +x86_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi, + Dwarf_Word *dwarf_regs, int expected_regs) +{ +#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) + return false; +#else /* __i386__ || __x86_64__ */ + /* The following facts are needed to translate x86 registers correctly: + - perf register order seen in linux arch/x86/include/uapi/asm/perf_regs.h + The registers array is built in the same order as the enum! + (See the code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs().) + - EBL PERF_FRAME_REGS_MASK specifies all registers except segment and + flags. However, regs_mask might be a different set of registers. + Again, regs_mask bits are in asm/perf_regs.h enum order. + - dwarf register order seen in elfutils backends/{x86_64,i386}_initreg.c + (matching pt_regs struct in linux arch/x86/include/asm/ptrace.h) + and it's a fairly different register order! + + For comparison, you can study codereview.qt-project.org/gitweb?p=qt-creator/perfparser.git;a=blob;f=app/perfregisterinfo.cpp;hb=HEAD + and follow the code which uses those tables of magic numbers. + But it's better to follow original sources of truth for this. */ + + bool is_abi32 = (abi == PERF_SAMPLE_REGS_ABI_32); + + /* Locations of dwarf_regs in the perf_event_x86_regs enum order, + not the regs[i] array (which will include a subset of the regs): */ + static const int regs_i386[] = {0, 2, 3, 1, 7/*sp*/, 6, 4, 5, 8/*ip*/}; + static const int regs_x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7/*sp*/, + 16/*r8 after flags+segment*/, 17, 18, 19, 20, 21, 22, 23, + 8/*ip*/}; + const int *dwarf_to_perf = is_abi32 ? regs_i386 : regs_x86_64; + + /* Locations of perf_regs in the regs[] array, according to regs_mask: */ + int perf_to_regs[PERF_REG_X86_64_MAX]; + uint64_t expected_mask = is_abi32 ? PERF_FRAME_REGISTERS_I386 : PERF_FRAME_REGISTERS_X86_64; + int j, k; uint64_t bit; + /* TODO(REVIEW): Is it worth caching this perf_to_regs computation + as long as regs_mask is kept the same across repeated calls? */ + for (j = 0, k = 0, bit = 1; k < PERF_REG_X86_64_MAX; k++, bit <<= 1) + { + if ((bit & expected_mask) && (bit & regs_mask)) { + if (n_regs <= (uint32_t)j) + return false; /* regs_mask count doesn't match n_regs */ + perf_to_regs[k] = j; + j++; + } else { + perf_to_regs[k] = -1; + } + } + + for (int i = 0; i < expected_regs; i++) + { + k = dwarf_to_perf[i]; + j = perf_to_regs[k]; + if (j < 0) continue; + if (n_regs <= (uint32_t)j) continue; + dwarf_regs[i] = regs[j]; + } + return true; +#endif /* __i386__ || __x86_64__ */ +} diff --git a/libebl/Makefile.am b/libebl/Makefile.am index ea092b5a..3df12ce2 100644 --- a/libebl/Makefile.am +++ b/libebl/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2000-2010, 2013, 2016, 2017 Red Hat, Inc. +## Copyright (C) 2000-2010, 2013, 2016, 2017, 2025 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -51,7 +51,7 @@ libebl_a_SOURCES = eblopenbackend.c eblclosebackend.c eblreloctypename.c \ eblbsspltp.c eblretval.c eblreginfo.c eblnonerelocp.c \ eblrelativerelocp.c eblsysvhashentrysize.c eblauxvinfo.c \ eblcheckobjattr.c ebl_check_special_section.c \ - eblabicfi.c eblstother.c eblinitreg.c \ + eblabicfi.c eblstother.c eblinitreg.c eblinitreg_sample.c \ ebldwarftoregno.c eblnormalizepc.c eblunwind.c \ eblresolvesym.c eblcheckreloctargettype.c \ ebl_data_marker_symbol.c diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index d6437e53..05474fbc 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -1,5 +1,5 @@ /* Backend hook signatures internal interface for libebl. - Copyright (C) 2000-2011, 2013, 2014, 2016, 2017 Red Hat, Inc. + Copyright (C) 2000-2011, 2013, 2014, 2016, 2017, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -158,6 +158,22 @@ bool EBLHOOK(set_initial_registers_tid) (pid_t tid, ebl_tid_registers_t *setfunc, void *arg); +/* Set process data from a perf_events sample and call SETFUNC one or more times. + Method should be present only when EBL_PERF_FRAME_REGS_MASK > 0, otherwise the + backend doesn't support unwinding from perf_events data. */ +bool EBLHOOK(set_initial_registers_sample) (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi, + ebl_tid_registers_t *setfunc, + void *arg); + +/* Extract the stack address from a perf_events register sample. */ +Dwarf_Word EBLHOOK(sample_base_addr) (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi); + +/* Extract the instruction pointer from a perf_events register sample. */ +Dwarf_Word EBLHOOK(sample_pc) (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi); + /* Convert *REGNO as is in DWARF to a lower range suitable for Dwarf_Frame->REGS indexing. */ bool EBLHOOK(dwarf_to_regno) (Ebl *ebl, unsigned *regno); diff --git a/libebl/eblinitreg_sample.c b/libebl/eblinitreg_sample.c new file mode 100644 index 00000000..53244d1e --- /dev/null +++ b/libebl/eblinitreg_sample.c @@ -0,0 +1,72 @@ +/* Populate process Dwfl_Frame from perf_events sample. + + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +Dwarf_Word +ebl_sample_base_addr (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) +{ + assert (ebl->sample_base_addr != NULL); + return ebl->sample_base_addr (regs, n_regs, regs_mask, abi); +} + +Dwarf_Word +ebl_sample_pc (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) +{ + assert (ebl->sample_pc != NULL); + return ebl->sample_pc (regs, n_regs, regs_mask, abi); +} + +bool +ebl_set_initial_registers_sample (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi, + ebl_tid_registers_t *setfunc, + void *arg) +{ + /* If set_initial_registers_sample is unsupported then PERF_FRAME_REGS_MASK is zero. */ + assert (ebl->set_initial_registers_sample != NULL); + return ebl->set_initial_registers_sample (regs, n_regs, regs_mask, abi, setfunc, arg); +} + +uint64_t +ebl_perf_frame_regs_mask (Ebl *ebl) +{ + /* ebl is declared NN */ + return ebl->perf_frame_regs_mask; +} diff --git a/libebl/libebl.h b/libebl/libebl.h index 731001d3..a64d70e9 100644 --- a/libebl/libebl.h +++ b/libebl/libebl.h @@ -1,5 +1,5 @@ /* Interface for libebl. - Copyright (C) 2000-2010, 2013, 2014, 2015, 2016, 2017 Red Hat, Inc. + Copyright (C) 2000-2010, 2013, 2014, 2015, 2016, 2017, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -340,6 +340,35 @@ extern bool ebl_set_initial_registers_tid (Ebl *ebl, extern size_t ebl_frame_nregs (Ebl *ebl) __nonnull_attribute__ (1); +/* Callback to set process data from a linux perf_events sample. + EBL architecture has to have EBL_PERF_FRAME_REGS_MASK > 0, otherwise the + backend doesn't support unwinding from perf_events sample data. */ +extern bool ebl_set_initial_registers_sample (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi, + ebl_tid_registers_t *setfunc, + void *arg) + __nonnull_attribute__ (1, 2, 6); + +/* Extract the stack address from a perf_events register sample. */ +Dwarf_Word ebl_sample_base_addr (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) + __nonnull_attribute__ (1, 2); + +/* Extract the instruction pointer from a perf_events register sample. */ +Dwarf_Word ebl_sample_pc (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) + __nonnull_attribute__ (1, 2); + + +/* Preferred sample_regs_user mask to request from linux perf_events + to allow unwinding on EBL architecture. Omitting some of these + registers may result in failed or inaccurate unwinding. */ +extern uint64_t ebl_perf_frame_regs_mask (Ebl *ebl) + __nonnull_attribute__ (1); + /* Offset to apply to the value of the return_address_register, as fetched from a Dwarf CFI. This is used by some backends, where the return_address_register actually contains the call address. */ diff --git a/libebl/libeblP.h b/libebl/libeblP.h index c408ed97..be14cc20 100644 --- a/libebl/libeblP.h +++ b/libebl/libeblP.h @@ -1,5 +1,5 @@ /* Internal definitions for interface for libebl. - Copyright (C) 2000-2009, 2013, 2014 Red Hat, Inc. + Copyright (C) 2000-2009, 2013, 2014, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -60,6 +60,11 @@ struct ebl Ebl architecture can unwind iff FRAME_NREGS > 0. */ size_t frame_nregs; + /* Preferred sample_regs_user mask to request from linux perf_events + to allow unwinding. Ebl architecture supports unwinding from + perf_events sample data iff PERF_FRAME_REGS_MASK > 0. */ + uint64_t perf_frame_regs_mask; + /* Offset to apply to the value of the return_address_register, as fetched from a Dwarf CFI. This is used by some backends, where the return_address_register actually contains the call From patchwork Thu Apr 24 21:47:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110963 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 43ACA3858C50 for ; Thu, 24 Apr 2025 21:49:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 43ACA3858C50 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=fGGbVqVl; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=M9sAVou4 X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id C013C3858D21 for ; Thu, 24 Apr 2025 21:47:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C013C3858D21 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C013C3858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531277; cv=none; b=eq2YsPZdzPpDXcpRDPzbDs2MDW1nHZxMiAs7X+VebfA+QvPL9widfNxJX8qcUPnrGmyaWz2JVDncO+mLJWYSK8smlODcBscnyr0rFkDo5t23vwBfrJkc9d9Gj24j7MfkSzj8sU+fjKLbfmuj6/0nwazsIx4K4AamdfsYZtlzyhk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531277; c=relaxed/simple; bh=5P1i63ij34PCGCg1Fhib7f0Jn9QO/ituwrCDHsjyWe0=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=GETCclBO2jjEmtcDvtkolYe4r8zcRFGuqq6nKA9yLH3tnkVyc+2IfIRe4aZtLI9sZPaNyqFSiesfJwSclO9kvcd/e5SlKYsoEPEAxdiTcoaWifjFACHsvcTHmbe0IqGB58x/SBp+AUU5vViITgvETA9GR8L1XLk5WB2XH7uGmug= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C013C3858D21 Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44]) by mailfhigh.stl.internal (Postfix) with ESMTP id 4F913254020D; Thu, 24 Apr 2025 17:47:57 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Thu, 24 Apr 2025 17:47:57 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531277; x= 1745617677; bh=mq46sm17a3+mYyUXGcxjYdPV4in+MVgk03F6aC8T25A=; b=f GGbVqVlsMJ0PAhtI37gIjuzUyoPfnpeY4HrtS8XLt6lAxpjWGL7Ib5UttI49zJ5e B7yhBp3vPhJ07OL5QHR/fz8d6TNlIq9vnCGQGc53ODaawCteqlUGQCk3lJSCI9t1 G0/uKa6UneFBy7Kczve3NQgVe0ivA7FaBASYTMKgsN3sCKO1CWOxAbeFU1xwI4iJ UChfHBp8mnWSmq9e78BqYtYHkApa/5Gp4eAmU9P9nKQGJ1dYaYAo7tyw0cOBGnFr +76PZyMFuKR+NnH1T8/Em1nb5ura+bvr/CRGzvjsuaUHxaYXRw6A+hae1S9VjB5R TypNFt5PEnFTcQcJ9f+Xg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531277; x=1745617677; bh=m q46sm17a3+mYyUXGcxjYdPV4in+MVgk03F6aC8T25A=; b=M9sAVou4V32UbYUbu /lZQhykwoO/I8h23xMIsLBZNDWJb4IwFD6dPONW+ze7XpwV7Za9jbBZ8jjmCJIk7 o474bJrI+utpZ4UEctoRqCZbCA0TmxaesF67GlRcIYifo5JURLzmlR2ZG6j6kG86 hW6SmyRh2cf82kBQ87DspOEhzs24Cqg95YUDuOM/f3kh8pOjy9kzEcwDc45WxBEZ 363FQgiwIW8eDnAjTxR2ab075l+Uof4WoturyiRAW3U7fLhUWRVNLTo5K7ZgLCAn d7u/jtA35jVMfWGqgmR557ElDyuAVgyldZP+5lYkFLo1/bYjrCd8m8ZCC9vAJEI3 PtPDQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd euudfftdehgfetheehlefghefffeegieekheefgfdufefgudeifefhvefgudeknecuvehl uhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshgvrhhhvghise hsvghrhhgvihdrihhopdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdp rhgtphhtthhopegvlhhfuhhtihhlshdquggvvhgvlhesshhouhhrtggvfigrrhgvrdhorh hgpdhrtghpthhtohepshgvrhhhvghisehsvghrhhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:47:56 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 02/12] libdwfl [2/12]: expose setfunc callback for libdwflP+libebl clients Date: Thu, 24 Apr 2025 17:47:04 -0400 Message-ID: <20250424214715.306147-2-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v2: - No longer exposing this in public libdwfl.h api. * * * Renaming pid_set_initial_registers to __libdwfl_set_initial_registers_thread and adding to libdwflP.h. This callback was private to one file, but now we need to access it from the perf_events sampling code as well. * libdwfl/libdwflP.h (__libdwfl_set_initial_registers_thread): New function. * libdwfl/linux-pid-attach.c (__libdwfl_set_initial_registers_thread): Renamed from pid_thread_state_registers_cb. (pid_set_initial_registers): Pass the newly renamed callback. --- libdwfl/libdwflP.h | 11 ++++++++++- libdwfl/linux-pid-attach.c | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 6ec5c966..2b7eb8da 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwfl. - Copyright (C) 2005-2015, 2018, 2024 Red Hat, Inc. + Copyright (C) 2005-2015, 2018, 2024-2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -591,6 +591,15 @@ extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name, extern void __libdwfl_process_free (Dwfl_Process *process) internal_function; +/* Basic implementation of Dwfl_Thread_Callbacks.set_initial_registers. + ARG must be a Dwfl_Thread *. Calls dwfl_thread_state_register_pc + if firstreg is -1 (indicating arch PC), dwfl_thread_state_registers + otherwise. */ +extern bool __libdwfl_set_initial_registers_thread (int firstreg, + unsigned nregs, + const Dwarf_Word *regs, + void *arg); + /* Update STATE->unwound for the unwound frame. On error STATE->unwound == NULL or STATE->unwound->pc_state == DWFL_FRAME_STATE_ERROR; diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c index 0eec1e88..a6e4e41a 100644 --- a/libdwfl/linux-pid-attach.c +++ b/libdwfl/linux-pid-attach.c @@ -1,5 +1,5 @@ /* Get Dwarf Frame state for target live PID process. - Copyright (C) 2013, 2014, 2015, 2018 Red Hat, Inc. + Copyright (C) 2013, 2014, 2015, 2018, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -304,9 +304,11 @@ pid_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid, /* Implement the ebl_set_initial_registers_tid setfunc callback. */ -static bool -pid_thread_state_registers_cb (int firstreg, unsigned nregs, - const Dwarf_Word *regs, void *arg) +bool +/* XXX No internal_function annotation, + as this function gets passed as ebl_tid_registers_t *. */ +__libdwfl_set_initial_registers_thread (int firstreg, unsigned nregs, + const Dwarf_Word *regs, void *arg) { Dwfl_Thread *thread = (Dwfl_Thread *) arg; if (firstreg == -1) @@ -338,7 +340,7 @@ pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) Dwfl_Process *process = thread->process; Ebl *ebl = process->ebl; return ebl_set_initial_registers_tid (ebl, tid, - pid_thread_state_registers_cb, thread); + __libdwfl_set_initial_registers_thread, thread); } static void From patchwork Thu Apr 24 21:47:05 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110964 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 345663858D38 for ; Thu, 24 Apr 2025 21:49:45 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 345663858D38 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=HcNmAEBE; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=mk7pZnt9 X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id 595013858D38 for ; Thu, 24 Apr 2025 21:48:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 595013858D38 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 595013858D38 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531282; cv=none; b=J4H4VO3XNfsGg2USvoRehEBwfu7HEoYxgSu/dbcEMHTs8jyz9nzoUGAMR76LTScYZbXzfvoVV69G4FiIq3CHRbT7f5xllAjRzIVYLPWNd/rCahEZwBbZKKKNCquSoOa0sO8cVJzzn8WmXtMFkLfvV47a0Z55h+ZhXURVk9H6OM4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531282; c=relaxed/simple; bh=SVPNwMCKe3UiwsKTFP4ZIEjtMsd/T99TwzMhYZ/fdBM=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=E//l7oSLmZjY3nvxTyDpqttqrLNK72pnNqpyuNh8NXrMzZ9JqD+pLBGPYAPZU94nT7NDEqkXPpakOtCGfl8wwPaysTuHptvhLRsaGYOuEmTfyW6ADKscd4Hmt5QllauTyMO1KBceqVTz0jS/Nlh2P2WWAn66VOSNVRI0LUTqaYg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 595013858D38 Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfhigh.stl.internal (Postfix) with ESMTP id F0008254020D; Thu, 24 Apr 2025 17:48:01 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-01.internal (MEProxy); Thu, 24 Apr 2025 17:48:02 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531281; x= 1745617681; bh=WaistFNJ//002qBDUXtcv0MhD6WWCfY6X4UH/F03deY=; b=H cNmAEBEXpc9iMfujBQgDhgj5gQt65jdLJwRIXK+0ZR2cgczmhA5sa6Wd8noisVmI aqpYbrf1Fti7c3eP2irnBv0tHXmt10Gm7fhhLLw+u7WFqcv9ap4wzKeXRHdEg1OY 4qhzKXLOS++fRW72CYFHukhpJKnJluKYljK4jtecMPp+o8rtGLSbDxYUAFqgg8en k8rx6k2I9CuCS8/0KGUTc6bT2+EDBMmtD8HmKoxEmj+9Jyh6cPcLLq1j0m4K2jl1 vMDFkAKmcKf4w0+o9HkfPr4KSDX0dK7Eiwh9SDyFZMPps/JbKiobzygNraQiPClu 3D8WeeIvnm7TnJ/1Ynlqg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531281; x=1745617681; bh=W aistFNJ//002qBDUXtcv0MhD6WWCfY6X4UH/F03deY=; b=mk7pZnt9jK4gkH7NA mrYdR7+hpDl0MAQeegDvcnPuW80iQozAr6TnLzM1pP0UVRX+EXLPH9JUDqBLTs8+ N6VD1lzbC7kXpwle/x+dYwSeYvS5aEYDD7lmpsv6JuZZVCZ74X7fl1T5SwE0iDJ8 7gy5N3vrgXRiq/DsUkB6LnWnrR+qj7R9V5CrndB9qbUvPZgjQc187bvAZgva1Dpr 6TkZY7QFe4aYuHXQjMA5dvpk8d0ePzRcddiGgp9CjO4aFQeIUB3X0JitPyCt6UC9 gGAuRjlwP8W5NWJuLBDgFylPq5mwVcfJpfCXT4A5xKzYC8N3qGnVqGbZjnzoEhnl NrpjA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnhepge evleelieegheetheeuhffgtdettdfhjeejfeelveduffdvuefgvdfffedvfeegnecuffho mhgrihhnpehqthdqphhrohhjvggtthdrohhrghenucevlhhushhtvghrufhiiigvpedtne curfgrrhgrmhepmhgrihhlfhhrohhmpehsvghrhhgvihesshgvrhhhvghirdhiohdpnhgs pghrtghpthhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepvghlfhhuth hilhhsqdguvghvvghlsehsohhurhgtvgifrghrvgdrohhrghdprhgtphhtthhopehsvghr hhgvihesshgvrhhhvghirdhioh X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:01 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 03/12] libebl [3/12]: eu-stacktrace: use new register handling api Date: Thu, 24 Apr 2025 17:47:05 -0400 Message-ID: <20250424214715.306147-3-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Since __libdwfl_set_initial_registers_thread is now private to libdwfl, the modified code in this patch has been disabled. * * * Dummy commit to show how the sample_set_initial_registers callback in eu-stacktrace would use the proper libebl ebl_set_initial_registers_sample function (if it were public). * src/Makefile.am (stacktrace_LDADD): Add libebl. * src/stacktrace.c (sample_registers_cb): New function, though identical to pid_thread_state_registers_cb. (sample_set_initial_registers): (XXX Invoke ebl_set_initial_registers_sample instead of containing platform-specific code directly. This is now commented out. Patch12 in the series replaces with code in libdwfl_stacktrace/dwflst_perf_frame.c.) --- src/Makefile.am | 4 ++-- src/stacktrace.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index ed245fc1..6d713e88 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 1996-2014, 2016, 2024 Red Hat, Inc. +## Copyright (C) 1996-2014, 2016, 2024-2025 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -105,7 +105,7 @@ ar_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD) $(obstack_LIBS) unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) $(demanglelib) if ENABLE_STACKTRACE -stacktrace_LDADD = $(libelf) $(libdw) $(libeu) $(argp_LDADD) +stacktrace_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) endif elfcompress_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) elfclassify_LDADD = $(libelf) $(libdw) $(libeu) $(argp_LDADD) diff --git a/src/stacktrace.c b/src/stacktrace.c index d8699ce5..3f5950fb 100644 --- a/src/stacktrace.c +++ b/src/stacktrace.c @@ -1,5 +1,5 @@ /* Process a stream of stack samples into stack traces. - Copyright (C) 2023-2024 Red Hat, Inc. + Copyright (C) 2023-2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -93,9 +93,11 @@ * Includes: libdwfl data structures * *************************************/ +#include ELFUTILS_HEADER(ebl) /* #include ELFUTILS_HEADER(dwfl) */ #include "../libdwfl/libdwflP.h" -/* XXX: Private header needed for find_procfile, sysprof_init_dwfl */ +/* XXX: Private header needed for find_procfile, sysprof_init_dwfl, + sample_set_initial_registers. */ /************************************* * Includes: sysprof data structures * @@ -574,10 +576,31 @@ sample_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) return true; } -/* TODO: Need to generalize this code beyond x86 architectures. */ static bool -sample_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) +sample_set_initial_registers (Dwfl_Thread *thread, void *arg) { +#if 0 + /* TODO: __libdwfl_set_initial_registers_thread not exported from libdwfl, + after it was decided to be unsuitable for a public API. + + A subsequent patch in the series removes sample_set_initial_registers, + replacing it with code in libdwfl_stacktrace/dwflst_perf_frame.c. + Keeping this code commented-out for the record, cf how we would + implement if the set_initial_registers utility func was public. + + To *actually* make this work, would need to copy the set_initial_registers + implementation into stacktrace.c; not worth doing since the later patch + overrides this code. */ + struct __sample_arg *sample_arg = (struct __sample_arg *)arg; + dwfl_thread_state_register_pc (thread, sample_arg->pc); + Dwfl_Process *process = thread->process; + Ebl *ebl = process->ebl; + /* XXX Sysprof provides exactly the required registers for unwinding: */ + uint64_t regs_mask = ebl_perf_frame_regs_mask (ebl); + return ebl_set_initial_registers_sample + (ebl, sample_arg->regs, sample_arg->n_regs, regs_mask, sample_arg->abi, + __libdwfl_set_initial_registers_thread, thread); +#else /* The following facts are needed to translate x86 registers correctly: - perf register order seen in linux arch/x86/include/uapi/asm/perf_regs.h The registers array is built in the same order as the enum! @@ -592,7 +615,7 @@ sample_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) For comparison, you can study codereview.qt-project.org/gitweb?p=qt-creator/perfparser.git;a=blob;f=app/perfregisterinfo.cpp;hb=HEAD and follow the code which uses those tables of magic numbers. But it's better to follow original sources of truth for this. */ - struct __sample_arg *sample_arg = (struct __sample_arg *)thread_arg; + struct __sample_arg *sample_arg = (struct __sample_arg *)arg; bool is_abi32 = (sample_arg->abi == PERF_SAMPLE_REGS_ABI_32); static const int regs_i386[] = {0, 2, 3, 1, 7/*sp*/, 6, 4, 5, 8/*ip*/}; static const int regs_x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7/*sp*/, 9, 10, 11, 12, 13, 14, 15, 16, 8/*ip*/}; @@ -610,6 +633,7 @@ sample_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) dwfl_thread_state_registers (thread, i, 1, &sample_arg->regs[j]); } return true; +#endif } static void From patchwork Thu Apr 24 21:47:06 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110968 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6CF593858D38 for ; Thu, 24 Apr 2025 21:51:07 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6CF593858D38 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=j9jYAaqT; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=oQfbDqUY X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fout-b6-smtp.messagingengine.com (fout-b6-smtp.messagingengine.com [202.12.124.149]) by sourceware.org (Postfix) with ESMTPS id BAD9E3858D29 for ; Thu, 24 Apr 2025 21:48:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BAD9E3858D29 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BAD9E3858D29 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.149 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531286; cv=none; b=ItmKPcR5ckhCBisvHsqtix8BEFNpJ5NmDH1I+QpzrDuVf3GtuV8nHpX9iW2F2zdU8+k112nIEZn5DRj22Hm0fQVhOlg/sLGBUgRUCihTWVBDCaI7tSuJh6WqlSowR4whfAsjh+N9HM6FkLXFyAUgU0vMmwo7uq6U5940diRkBT4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531286; c=relaxed/simple; bh=o4v+XCdl9vGLAFHBo/d9CYELEBmah/T5tGc0t+u3Q+0=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=UgugYiMHiOumxpya3LB5AqydAgDTKfTpqLZMqOXk4ykCyV8KgTgsRHEOGZaWbumzlE7vaNHiaVb2ClCjwT39c/WvZIoDxIheuHhsuk8C/5bCdjBAIdeYKWwNzsF9a0wAUWsI5Y3kXUt6uNOncu/UFsrvxxhs6FBx45rTzblmGFg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BAD9E3858D29 Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44]) by mailfout.stl.internal (Postfix) with ESMTP id 5F8D11140192; Thu, 24 Apr 2025 17:48:06 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Thu, 24 Apr 2025 17:48:06 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531286; x= 1745617686; bh=hvwBQX7hdyoUMJj6xTHPzZsP2uae9ZrD6mlx8lnjXFY=; b=j 9jYAaqTztWVwqHtdgkboCfHnusMtRIBlEWdKoudo56ZoDGQDbAfhreEW87oc6f5y 7vu/1+QF71yK+aMWbnL4K5jr5YXnSgjFpSBSjnlzFjG7eRrj7FEoSavFx8D/x8C+ Gyc7rOfo1Z8JgK4F9wXOTiHl8p64h8OTR1LXobYEYzJeQECMWumOXLSpuvbmTrqr 1C4iXw+5R4LjhKSGNUB7g5PBe206UGYDvVNqTPTh13ImNP7nEGUUmxVsUY95ZyKr pIQZKOtzso76lCt4pG6BQ0LYNJ6yW/aIuLsxMup+Fme/zwJNei/K0qMgH9JcNTGg RjqV3ix1E6oVvio8DqWWA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531286; x=1745617686; bh=h vwBQX7hdyoUMJj6xTHPzZsP2uae9ZrD6mlx8lnjXFY=; b=oQfbDqUYZDzVPrzdE l9zvAK+aKL/1UX8Fp/WN4RV29bQttxvm59eruSOMssQi8dhETDqldJmQkOTO0vad 8juz/GGPaa96TdwLrJ2TmEEy7NjIg+W/xWFYzwPcDBTlL/8bVdPWwqMrUASs2Ifv 28KA+h8n7V+GQQd4XhGUwvF5Wt5Jh1xXzJpXeGmZcrhSXKqiLRJ7USWoJYBIJ3a7 I48X9jrDF/E8yECV9IcoAQ89FzjI1+69wtpp7FpE67LVm9mRTP+I9YhJ81bAcho4 S9dYuELq1H8EHPZT662KvI8pi+oLvE+sLN3bwoGJC2tVkv1uACQi+jP7xVBgDCiJ d0Gdw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnhephf duveetueelueetvefhtdekjeegffduhfdujefgudetteefudduheefveegiedvnecuffho mhgrihhnpehsphgvtgdrihhnpdhgnhhurdhorhhgnecuvehluhhsthgvrhfuihiivgeptd enucfrrghrrghmpehmrghilhhfrhhomhepshgvrhhhvghisehsvghrhhgvihdrihhopdhn sggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopegvlhhfuh htihhlshdquggvvhgvlhesshhouhhrtggvfigrrhgvrdhorhhgpdhrtghpthhtohepshgv rhhhvghisehsvghrhhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:05 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 04/12] libdwfl_stacktrace [4/12]: intro library, add dwflst_perf_sample_preferred_regs_mask Date: Thu, 24 Apr 2025 17:47:06 -0400 Message-ID: <20250424214715.306147-4-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v5: - Separate ELFUTILS_0.193_EXPERIMENTAL namespace, mark library experimental. Changes for v4: - Separate out libdwfl_stacktrace, as requested. Changes for v2: - guard the linux/perf_events.h include properly with an #if defined __linux__ * * * Subsequent patches in the series introduce a new library for tracking/caching Elf structs across multiple processes and wrapping the libebl perf register handling (since libebl is a private interface). In this patch, add the library and an interface to access the set of registers that libdwfl needs to allow proper unwinding of stack sample data. (Unwinding with a smaller set of registers can be attempted, but will yield a truncated call chain in most cases.) (Accessing the set of registers feels like an implementation detail, but the profiler invoking perf_event_open and elfutils unwinding code need to agree on the exact set of registers being requested. So it's best for the profiler to ask elfutils for this detail.) * libdwfl_stacktrace/Makefile.am: New file. * libdwfl_stacktrace/libdwfl_stacktrace.h: New file, library for tracking/caching Elf structs and unwinding across multiple processes. * libdwfl_stacktrace/libdwfl_stacktraceP.h: New file. * libdwfl_stacktrace/dwflst_perf_frame.c: New file. (dwflst_perf_sample_preferred_regs_mask): New function. * libdw/libdw.map: Add dwflst_perf_sample_preferred_regs_mask. * NEWS: Add NEWS item about libdwfl_stacktrace. * configure.ac: Add libdwfl_stacktrace/Makefile. * Makefile.am (SUBDIRS): Add libdwfl_stacktrace. * libdw/Makefile.am (libdw_so_LIBS): Add libdwfl_stacktrace. (libdwfl_stacktrace_objects): Add libdwfl_stacktrace.manifest. (libdw_a_LIBADD): Add libdwfl_stacktrace_objects. * config/elfutils.spec.in (%files devel): Add libdwfl_stacktrace.h. --- Makefile.am | 6 +-- NEWS | 6 +++ config/elfutils.spec.in | 1 + configure.ac | 3 ++ libdw/Makefile.am | 7 ++- libdw/libdw.map | 6 +++ libdwfl_stacktrace/Makefile.am | 63 ++++++++++++++++++++++++ libdwfl_stacktrace/dwflst_perf_frame.c | 63 ++++++++++++++++++++++++ libdwfl_stacktrace/libdwfl_stacktrace.h | 58 ++++++++++++++++++++++ libdwfl_stacktrace/libdwfl_stacktraceP.h | 36 ++++++++++++++ 10 files changed, 244 insertions(+), 5 deletions(-) create mode 100644 libdwfl_stacktrace/Makefile.am create mode 100644 libdwfl_stacktrace/dwflst_perf_frame.c create mode 100644 libdwfl_stacktrace/libdwfl_stacktrace.h create mode 100644 libdwfl_stacktrace/libdwfl_stacktraceP.h diff --git a/Makefile.am b/Makefile.am index 3a181d75..76e98f60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ ## Process this file with automake to create Makefile.in ## Configure input file for elfutils. ## -## Copyright (C) 1996-2006, 2008, 2009, 2015 Red Hat, Inc. +## Copyright (C) 1996-2006, 2008, 2009, 2015, 2025 Red Hat, Inc. ## ## This file is part of elfutils. ## @@ -28,8 +28,8 @@ AM_MAKEFLAGS = --no-print-directory pkginclude_HEADERS = version.h -SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl libdw \ - libasm debuginfod src po doc tests +SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl \ + libdwfl_stacktrace libdw libasm debuginfod src po doc tests EXTRA_DIST = elfutils.spec GPG-KEY NOTES CONTRIBUTING SECURITY \ COPYING COPYING-GPLV2 COPYING-LGPLV3 CONDUCT diff --git a/NEWS b/NEWS index 664c125b..c3c611e3 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,12 @@ debuginfod: Add CORS (webapp access) support to webapi. libdw: Add dwarf_language and dwarf_language_lower_bound functions. +libdwfl_stacktrace: Experimental new library interface for unwinding + stack samples into call chains, and tracking and + caching Elf data for multiple processes, building + on libdwfl. Initially supports perf_events stack + sample data. + Version 0.192 "New rules, faster tools" CONDUCT: A new code of conduct has been adopted. See the diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in index 96934514..37077365 100644 --- a/config/elfutils.spec.in +++ b/config/elfutils.spec.in @@ -300,6 +300,7 @@ fi #%%{_includedir}/elfutils/libasm.h %{_includedir}/elfutils/libdw.h %{_includedir}/elfutils/libdwfl.h +%{_includedir}/elfutils/libdwfl_stacktrace.h %{_includedir}/elfutils/libdwelf.h %{_includedir}/elfutils/version.h #%%{_libdir}/libasm.so diff --git a/configure.ac b/configure.ac index 27488e3f..92108b7f 100644 --- a/configure.ac +++ b/configure.ac @@ -700,6 +700,9 @@ AC_CONFIG_FILES([libdw/Makefile]) dnl Higher-level DWARF support library. AC_CONFIG_FILES([libdwfl/Makefile]) +dnl Higher-level DWARF stacktrace support library +AC_CONFIG_FILES([libdwfl_stacktrace/Makefile]) + dnl CPU handling library. AC_CONFIG_FILES([libcpu/Makefile]) diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 9dadc19b..f024d652 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018 Red Hat, Inc. +## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018, 2025 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -107,7 +107,7 @@ am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os) libdw_so_LIBS = ../libebl/libebl_pic.a ../backends/libebl_backends_pic.a \ ../libcpu/libcpu_pic.a libdw_pic.a ../libdwelf/libdwelf_pic.a \ - ../libdwfl/libdwfl_pic.a + ../libdwfl/libdwfl_pic.a ../libdwfl_stacktrace/libdwfl_stacktrace_pic.a libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(fts_LIBS) $(obstack_LIBS) $(zip_LIBS) -pthread libdw.so: $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS) @@ -135,6 +135,9 @@ uninstall: uninstall-am libdwfl_objects = $(shell cat ../libdwfl/libdwfl.manifest) libdw_a_LIBADD = $(addprefix ../libdwfl/,$(libdwfl_objects)) +libdwfl_stacktrace_objects = $(shell cat ../libdwfl_stacktrace/libdwfl_stacktrace.manifest) +libdw_a_LIBADD += $(addprefix ../libdwfl_stacktrace/,$(libdwfl_stacktrace_objects)) + libdwelf_objects = $(shell cat ../libdwelf/libdwelf.manifest) libdw_a_LIBADD += $(addprefix ../libdwelf/,$(libdwelf_objects)) diff --git a/libdw/libdw.map b/libdw/libdw.map index e7baf3c4..4f3fe6ba 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -391,3 +391,9 @@ ELFUTILS_0.193 { dwarf_language; dwarf_language_lower_bound; } ELFUTILS_0.192; + +/* XXX Experimental libdwfl_stacktrace API. */ +ELFUTILS_0.193_EXPERIMENTAL { + global: + dwflst_perf_sample_preferred_regs_mask; +}; diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am new file mode 100644 index 00000000..6364c292 --- /dev/null +++ b/libdwfl_stacktrace/Makefile.am @@ -0,0 +1,63 @@ +## Makefile.am for libdwfl_stacktrace library subdirectory in elfutils. +## +## Process this file with automake to create Makefile.in +## +## Copyright (C) 2025 Red Hat, Inc. +## This file is part of elfutils. +## +## This file is free software; you can redistribute it and/or modify +## it under the terms of either +## +## * the GNU Lesser General Public License as published by the Free +## Software Foundation; either version 3 of the License, or (at +## your option) any later version +## +## or +## +## * the GNU General Public License as published by the Free +## Software Foundation; either version 2 of the License, or (at +## your option) any later version +## +## or both in parallel, as here. +## +## elfutils is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received copies of the GNU General Public License and +## the GNU Lesser General Public License along with this program. If +## not, see . +## +include $(top_srcdir)/config/eu.am +AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(srcdir)/../libdwfl +VERSION = 1 + +noinst_LIBRARIES = libdwfl_stacktrace.a +noinst_LIBRARIES += libdwfl_stacktrace_pic.a + +pkginclude_HEADERS = libdwfl_stacktrace.h + + +libdwfl_stacktrace_a_SOURCES = dwflst_perf_frame.c + +libdwfl_stacktrace = $(libdw) +libdw = ../libdw/libdw.so +libelf = ../libelf/libelf.so +libebl = ../libebl/libebl.a +libeu = ../lib/libeu.a + +libdwfl_stacktrace_pic_a_SOURCES = +am_libdwfl_stacktrace_pic_a_OBJECTS = $(libdwfl_stacktrace_a_SOURCES:.c=.os) + +noinst_HEADERS = libdwfl_stacktraceP.h + +EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest + +libdwfl_stacktrace.manifest: $(libdwfl_stacktrace_a_OBJECTS) + $(AM_V_GEN)echo $^ > $@ + +MOSTLYCLEANFILES = $(am_libdwfl_stacktrace_a_OBJECTS) \ + $(am_libdwfl_stacktrace_pic_a_OBJECTS) +CLEANFILES = $(EXTRA_libdwfl_stacktrace_a_DEPENDENCIES) diff --git a/libdwfl_stacktrace/dwflst_perf_frame.c b/libdwfl_stacktrace/dwflst_perf_frame.c new file mode 100644 index 00000000..79e8e482 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_perf_frame.c @@ -0,0 +1,63 @@ +/* Get Dwarf Frame state for perf stack sample data. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined(__linux__) +# include +#endif + +#include "libdwfl_stacktraceP.h" + +Ebl *default_ebl = NULL; +GElf_Half default_ebl_machine = EM_NONE; + +uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine) +{ + /* XXX The most likely case is that this will only be called once, + for the current architecture. So we keep one Ebl* around for + answering this query and replace it in the unlikely case of + getting called with different architectures. */ + if (default_ebl != NULL && default_ebl_machine != machine) + { + ebl_closebackend(default_ebl); + default_ebl = NULL; + } + if (default_ebl == NULL) + { + default_ebl = ebl_openbackend_machine(machine); + default_ebl_machine = machine; + } + if (default_ebl != NULL) + return ebl_perf_frame_regs_mask (default_ebl); + return 0; +} + +/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h new file mode 100644 index 00000000..564f504a --- /dev/null +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -0,0 +1,58 @@ +/* Interfaces for libdwfl_stacktrace. + + XXX: This is an experimental initial version of the API, and is + liable to change in future releases of elfutils, especially as + we figure out how to generalize the work to other sample data + formats in addition to perf_events. + + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef _LIBDWFL_STACKTRACE_H +#define _LIBDWFL_STACKTRACE_H 1 + +#include "libdwfl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ + +/* Returns the linux perf_events register mask describing a set of + registers sufficient for unwinding on MACHINE, or 0 if libdwfl does + not handle perf_events samples for MACHINE. Does not take a Dwfl* + or Elf* since this is meant to allow a profiling tool to configure + perf_events to produce meaningful data for a libdwfl session to be + opened later. */ +uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine); + +#ifdef __cplusplus +} +#endif + +#endif /* libdwfl_stacktrace.h */ diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h new file mode 100644 index 00000000..fe6945fc --- /dev/null +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h @@ -0,0 +1,36 @@ +/* Internal definitions for libdwfl_stacktrace. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef _LIBDWFL_STACKTRACEP_H +#define _LIBDWFL_STACKTRACEP_H 1 + +#include + +#include "libdwflP.h" + +#endif /* libdwfl_stacktraceP.h */ From patchwork Thu Apr 24 21:47:07 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110962 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EA83B3858CDA for ; Thu, 24 Apr 2025 21:49:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EA83B3858CDA Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=sA45X5qe; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=aQVnnkWh X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id 9B2AD3858D21 for ; Thu, 24 Apr 2025 21:48:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9B2AD3858D21 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 9B2AD3858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531291; cv=none; b=evClVbrItOolWH7Pm75Sln6buAQ9YV6Pk+LyO1rqgETKTSdxmgVbSD9qZ50IY5vHrAdyfYB7buUoG/gIcU55bcaJt+cETshQRGKJOO6gshmz7gg1gJhdych/Ae4wh81vnMNEMvTiQh3SJY6FEGUBZIj+5T49H+G/SRn4o80xRtk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531291; c=relaxed/simple; bh=mpA8U54DrARzBdHVQ93A796dpev65qHPvgqw5frADlc=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=VgRRqNnmDM37w1OV70xrL5JtoBWO2JtHSh3SgJKmWfCJiQ/77ZvWhTSHi39+W1Uk98F1DqoGOsY1vAeBrKD96m8mL9pHy/s8I+M70sEZGlJJMMyvl5Ml3FicuCdTUBLuzJdlSBX+9urNVroIFvtZ1nhbBEqctFhzQiza6AHEuxo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9B2AD3858D21 Received: from phl-compute-11.internal (phl-compute-11.phl.internal [10.202.2.51]) by mailfhigh.stl.internal (Postfix) with ESMTP id 42001254020D; Thu, 24 Apr 2025 17:48:11 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-11.internal (MEProxy); Thu, 24 Apr 2025 17:48:11 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531291; x= 1745617691; bh=Md6WHvLArPZs7qeuPwU2zLijBGVgxUycKNBUapXs30U=; b=s A45X5qejs7ItLYZR9SCcwdZ7VF2RsJx+FK2yw3TdY8de3jt+AycJrs38dJmcyC7K 4y/HXjp7+exP9Nv0mphGruEdzZ7ilwlzx2HcO54ZJPx2jW6kRpWH3TZiGsepMVqI eXJqp01VWO6v7tmA9uup5pUICdtAar5SccJSbvDFc+BynO09NP9w3nBh7UZ2xH4U o51jLZN+IgkVRA/rv2RrBXgKEMt2OtV2SiUAGwyop8850wWbfD21H9MRLdvBx9gu ZNSaM7gGGqCVZlGd2eaYj4+6Nik50RvVXjDtx6plJrcI/ipMCOPc8Ml0o9unl4qY AGamdnTWvnfluvk3tyQfQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531291; x=1745617691; bh=M d6WHvLArPZs7qeuPwU2zLijBGVgxUycKNBUapXs30U=; b=aQVnnkWhJgBrgwIi/ y9metZupeXacqLDagtnAGTShwWoCvaKAR/XoLEYBML9uWsirHz99btws9ur+MedI QqO1zPpO1l+e7kfLmcSawzmBX7vCRJdJJyj7+BkNbJwYEgUfKsiX/7IcWeB2MJto Nunl4ViHpUFPbqZ0CXvWxVBwVlZEAb0Zw+6UcUzRpBCo9uIn6yS8uKwGPRkB9p3C TlWwlUM8mWg9aAcnddsCAEWrPNrefCbmt8uVPwY8EWeqOEI8fX4C4FUUIujjfBP0 hhnmM5npIvyLYz/U+14Rv9/sAXD+9aRvblNR56gp1hEWvopWzFi56Jtv4Q/yRQau 7SsVQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd dtheeljefhjefggefgteegveetfeetudejkeduvdeigfffjeduveffgeeltddunecuffho mhgrihhnpehgnhhurdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpe hmrghilhhfrhhomhepshgvrhhhvghisehsvghrhhgvihdrihhopdhnsggprhgtphhtthho pedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopegvlhhfuhhtihhlshdquggvvh gvlhesshhouhhrtggvfigrrhgvrdhorhhgpdhrtghpthhtohepshgvrhhhvghisehsvghr hhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:10 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 05/12] libdwfl_stacktrace [5/12]: introduce Dwflst_Process_Tracker Date: Thu, 24 Apr 2025 17:47:07 -0400 Message-ID: <20250424214715.306147-5-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Separate out libdwfl_stacktrace, as requested. * * * New data structure to coordinate caching Elf data among multiple Dwfl structs attached to different processes. Meant to reduce the overhead for profilers that use elfutils for unwinding. The caching is well-justified, as the prior approach (e.g. in eu-stacktrace, sysprof-live-unwinder) of creating a separate Dwfl per process was wildly redundant, opening ~hundreds of file descriptors just for each common library such as libc.so and leaking the data. Initial patch just introduces the struct, to be filled in by the rest of the patch series. * libdwfl_stacktrace/libdwfl_stacktrace.h (Dwflst_Process_Tracker): New struct. (dwflst_tracker_begin): New function. (dwflst_tracker_dwfl_begin): New function. (dwflst_tracker_end): New function. * libdw/libdw.map: Add new functions. * libdwfl_stacktrace/libdwfl_stacktraceP.h (struct Dwflst_Process_Tracker): New struct. * libdwfl/libdwflP.h (Dwflst_Process_Tracker): Typedef forward decl. (struct Dwfl): Add 'tracker' field. * libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): Add dwflst_process_tracker.c. * libdwfl_stacktrace/dwflst_process_tracker.c: New file. (dwflst_tracker_begin): Initialize the tracker. (dwflst_tracker_dwfl_begin): Initialize Dwfl * with specified tracker. (dwflst_tracker_end): Deallocate the tracker. --- libdw/libdw.map | 4 ++ libdwfl/libdwflP.h | 4 ++ libdwfl_stacktrace/Makefile.am | 3 +- libdwfl_stacktrace/dwflst_process_tracker.c | 66 +++++++++++++++++++++ libdwfl_stacktrace/libdwfl_stacktrace.h | 20 +++++++ libdwfl_stacktrace/libdwfl_stacktraceP.h | 6 ++ 6 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 libdwfl_stacktrace/dwflst_process_tracker.c diff --git a/libdw/libdw.map b/libdw/libdw.map index 4f3fe6ba..fb69a62a 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -396,4 +396,8 @@ ELFUTILS_0.193 { ELFUTILS_0.193_EXPERIMENTAL { global: dwflst_perf_sample_preferred_regs_mask; + dwflst_perf_sample_preferred_regs_mask; + dwflst_tracker_begin; + dwflst_tracker_dwfl_begin; + dwflst_tracker_end; }; diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 2b7eb8da..57305f81 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -111,9 +111,13 @@ struct Dwfl_User_Core int fd; /* close if >= 0. */ }; +/* forward decl from ../libdwfl_stacktrace/ */ +typedef struct Dwflst_Process_Tracker Dwflst_Process_Tracker; + struct Dwfl { const Dwfl_Callbacks *callbacks; + Dwflst_Process_Tracker *tracker; #ifdef ENABLE_LIBDEBUGINFOD debuginfod_client *debuginfod; #endif diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am index 6364c292..d57431c0 100644 --- a/libdwfl_stacktrace/Makefile.am +++ b/libdwfl_stacktrace/Makefile.am @@ -40,7 +40,8 @@ noinst_LIBRARIES += libdwfl_stacktrace_pic.a pkginclude_HEADERS = libdwfl_stacktrace.h -libdwfl_stacktrace_a_SOURCES = dwflst_perf_frame.c +libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ + dwflst_perf_frame.c libdwfl_stacktrace = $(libdw) libdw = ../libdw/libdw.so diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c b/libdwfl_stacktrace/dwflst_process_tracker.c new file mode 100644 index 00000000..057c9f7a --- /dev/null +++ b/libdwfl_stacktrace/dwflst_process_tracker.c @@ -0,0 +1,66 @@ +/* Track multiple Dwfl structs for multiple processes. + Copyright (C) 2025, Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwfl_stacktraceP.h" + +Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) +{ + Dwflst_Process_Tracker *tracker = calloc (1, sizeof *tracker); + if (tracker == NULL) + { + __libdwfl_seterrno (DWFL_E_NOMEM); + return tracker; + } + + tracker->callbacks = callbacks; + return tracker; +} + +Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) +{ + Dwfl *dwfl = INTUSE(dwfl_begin) (tracker->callbacks); + if (dwfl == NULL) + return dwfl; + + /* TODO: Could also share dwfl->debuginfod, but thread-safely? */ + dwfl->tracker = tracker; + return dwfl; +} + +void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) +{ + if (tracker == NULL) + return; + + /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */ + free (tracker); +} diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h index 564f504a..f3a82d18 100644 --- a/libdwfl_stacktrace/libdwfl_stacktrace.h +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -41,6 +41,26 @@ extern "C" { #endif +/* Keeps track of and caches Elf structs across multiple libdwfl + sessions corresponding to different processes. */ +typedef struct Dwflst_Process_Tracker Dwflst_Process_Tracker; + + +/* Initialize a new tracker for multiple libdwfl sessions. Since Elf + data will shared between the libdwfl sessions, each Dwfl must use + the same Dwfl_Callbacks CALLBACKS provided when the tracker is + created. */ +extern Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) + __nonnull_attribute__ (1); + +/* Create a new Dwfl linked to this tracker. */ +extern Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) + __nonnull_attribute__ (1); + +/* End all sessions with this tracker. */ +extern void dwflst_tracker_end (Dwflst_Process_Tracker *tracker); + + /* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ /* Returns the linux perf_events register mask describing a set of diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h index fe6945fc..9313176c 100644 --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h @@ -33,4 +33,10 @@ #include "libdwflP.h" +struct Dwflst_Process_Tracker +{ + const Dwfl_Callbacks *callbacks; + /* ... */ +}; + #endif /* libdwfl_stacktraceP.h */ From patchwork Thu Apr 24 21:47:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110971 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C71A23858C2A for ; Thu, 24 Apr 2025 21:53:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C71A23858C2A Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=hdn5ERQd; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=WVFU8F0M X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fout-b6-smtp.messagingengine.com (fout-b6-smtp.messagingengine.com [202.12.124.149]) by sourceware.org (Postfix) with ESMTPS id 66AB73858D35 for ; Thu, 24 Apr 2025 21:48:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 66AB73858D35 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 66AB73858D35 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.149 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531296; cv=none; b=qcq4Hkx5qtFQRTq0o7Kgic9T83okTe+/b2IOyUllBm51wwtQFA4ELEjAUVwfyOrJ3QGrnFXlcf3k5T+71NggSjVj268pO5fzrXkC+/mIwN0WuVtQO/KxG+POZ2GQTRADaZq3q8YHFJs9VmN9ZqrYUhlf2OamQ/xPMAKH7PzeUlo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531296; c=relaxed/simple; bh=64nIoBSQ8cX+J1IH+jHZJ233GaWfyDXGqRkxo4inVYs=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=Y/L6MBPZifZUn9pFialowAtCKC+enYUj89pfwNfvyoqG+oxDGCOd82YV/+8/HpPr3V7OVvsrR9r68h9ghCIeRwQ3W83alTR613Z9TbIE3TPo3a07Y4VEQ/fT3wiZmPvYiUDf2WW6GcwqwjLGtI65dfel+exqwyED9XmSH+7QGcA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 66AB73858D35 Received: from phl-compute-12.internal (phl-compute-12.phl.internal [10.202.2.52]) by mailfout.stl.internal (Postfix) with ESMTP id 0C60411401FA; Thu, 24 Apr 2025 17:48:16 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-12.internal (MEProxy); Thu, 24 Apr 2025 17:48:16 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531295; x= 1745617695; bh=Ad3GkRKGJN8Dgy0LMsQl2Fpx58U5aokv4gAk2ZYamLc=; b=h dn5ERQdk/Vb3tSXVdoPvDq7JHMOYoYcIT8zuW/1LNRV1SB2nmueF83f3/ZxNCaKq hgVIfhPr1/MGLiQHlfV+KUWXj7FgJmKj2VUTSKzOqzRwBsx30m9kR+rpywrG0qsU JuqNpGP1ssNXOCyErxs2TUUeyQM1s3dFuvFN0Npud9ZmCg0Kl3OzAT2ab7gMRmKC k+qXLJLQpQ+Y+SYWDRlkW/TuNN7X+BA6jvzR9bsKRFDDEqPjgomOaY9JT/Ur7PUn 9Bn6JzcYH32zqXBfJIU0POin+ZATcHbM9R0VPD5i4zp+WvswpUKg9t6pUGldItmM LxZ0xemnuFO4TYqfi0NZQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531295; x=1745617695; bh=A d3GkRKGJN8Dgy0LMsQl2Fpx58U5aokv4gAk2ZYamLc=; b=WVFU8F0MeJHD3IJXx hN8249/Hv2YF3QG370/ddtGd8qQozefCy6ZppQQio0zI2Br7fymMLNAU1VqHAMSH 5OJPOEXphwh+zOxQ8wVmLee9uMtjOLAPx+EdS7u5xq4YtErhAwoc2nYse12nXha3 tX6jK8RAMA5QUwsHEZvIynRs4fksV/DhZBku5G0DicWU4XqEZlSeyxm2Yj7tyLAa RcVpsocCmVWpcE+TfYoyeHbs8wIwqkHIpl4jNLrtHah0wvc8lBvosHGtOVhMpOu2 2wGcC5tKztp2dhIDmFTdF3v5R/RQnve54KgJUDYt/asBGKSxKXbWjKyKKYihzEAT UVPPw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd dtheeljefhjefggefgteegveetfeetudejkeduvdeigfffjeduveffgeeltddunecuffho mhgrihhnpehgnhhurdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpe hmrghilhhfrhhomhepshgvrhhhvghisehsvghrhhgvihdrihhopdhnsggprhgtphhtthho pedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopegvlhhfuhhtihhlshdquggvvh gvlhesshhouhhrtggvfigrrhgvrdhorhhgpdhrtghpthhtohepshgvrhhhvghisehsvghr hhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:15 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 06/12] libdwfl_stacktrace [6/12]: Elf* caching via dwflst_process_tracker Date: Thu, 24 Apr 2025 17:47:08 -0400 Message-ID: <20250424214715.306147-6-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_SHORT, PROLO_LEO1, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v5: - Bugfixes in dwflst_tracker_find_elf.c. Changes for v4: - Separate out libdwfl_stacktrace, as requested. - dwfl_module_getdwarf.c now uses the dwflst_tracker_cache_elf() interface instead of editing the elftab directly. Changes for v3: - Reworked elftab to incorporate dev/ino into the caching key (to allow caching modules from different filesystems e.g. a main filesystem and a container filesystem) and guard more carefully against collisions. - Add external API for implementing a custom find_elf callback that reads/writes the cache. Changes for v2: - Add locking for elftab. This is needed in addition to the intrinsic locking in dynamicsizehash_concurrent to avoid having cache_elf expose an incomplete dwfltracker_elf_info* entry to other threads while its data is being populated / replaced. - Tidy dwfl_process_tracker_find_elf.c into the main find_elf callback and two functions to consider (in future) making into a public api for custom cached callbacks. * * * The Dwflst_Process_Tracker includes a dynamicsizehash cache which maps file paths to Elf * (or rather, dwflst_tracker_elf_info * storing fd and Elf *). We provide a dwflst_tracker_linux_proc_find_elf callback which checks the cache for an already-loaded Elf * and, if missing, populates the cache with the fd returned by dwfl_linux_proc_find_elf. Later, open_elf updates the cache with the Elf * for that fd. The commented asserts in dwflst_tracker_cache_elf still catch some cases where a redundant Elf * is being created without checking the cache. Since the Elf * outlasts the Dwfl that created it, we use the (convenient, already-existing) reference count field in Elf * to retain the data in the table. Then dwfl_end calling elf_end will decrement the refcount cleanly, and dwflst_tracker_end will issue another elf_end call. * libdwfl_stacktrace/libdwfl_stacktrace.h (dwflst_tracker_find_cached_elf): New function. (dwflst_tracker_cache_elf): New function. (dwflst_module_gettracker): New function, gives external users a way to access Dwflst_Process_Tracker given a Dwfl_Module. (dwflst_tracker_linux_proc_find_elf): New function, serves as a cached version of the dwfl_linux_proc_find_elf callback. * libdwfl_stacktrace/libdwfl_stacktraceP.h (dwflst_tracker_elf_info): New struct typedef. (struct Dwflst_Process_Tracker): Add dynamicsizehash table of dwflst_tracker_elf_info structs + associated rwlock. (INTDECLs): Add INTDECL for dwflst_tracker_find_cached_elf, dwflst_tracker_cache_elf, dwflst_module_gettracker. * libdwfl_stacktrace/dwflst_tracker_elftab.c: New file, instantiates lib/dynamicsizehash_concurrent.c to store dwflst_tracker_elf_info structs. * libdwfl_stacktrace/dwflst_tracker_elftab.h: New file, ditto. * libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c: New file. * libdwfl_stacktrace/dwflst_process_tracker.c (dwflst_tracker_begin): Init elftab. (dwflst_tracker_end): Clean up elftab. Lock and iterate the hash to free tracker->elftab.table items. * libdwfl_stacktrace/dwflst_tracker_find_elf.c: New file, implements a find_elf callback that wraps dwfl_linux_proc_find_elf with additional caching logic, and an API to access the Dwflst_Process_Tracker Elf cache when implementing a custom find_elf callback. * libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): Add dwflst_tracker_find_elf.c, dwflst_tracker_elftab.c, libdwfl_stacktrace_next_prime.c. (noinst_HEADERS): Add dwflst_tracker_elftab.h. * libdw/libdw.map: Add dwflst_tracker_find_cached_elf, dwflst_tracker_cache_elf, dwflst_module_gettracker, dwflst_tracker_linux_proc_find_elf. * libdwfl/Makefile.am (AM_CPPFLAGS): Include headers from ../libdwfl_stacktrace. * libdwfl/dwfl_module_getdwarf.c (open_elf): Cache file->elf in Dwflst_Process_Tracker. Must be done here as dwfl_linux_proc_find_elf opens an fd but does not yet create the Elf *. --- libdw/libdw.map | 4 + libdwfl/Makefile.am | 5 +- libdwfl/dwfl_module_getdwarf.c | 10 +- libdwfl_stacktrace/Makefile.am | 5 +- libdwfl_stacktrace/dwflst_process_tracker.c | 26 +++ libdwfl_stacktrace/dwflst_tracker_elftab.c | 45 ++++ libdwfl_stacktrace/dwflst_tracker_elftab.h | 40 ++++ libdwfl_stacktrace/dwflst_tracker_find_elf.c | 218 ++++++++++++++++++ libdwfl_stacktrace/libdwfl_stacktrace.h | 37 ++- libdwfl_stacktrace/libdwfl_stacktraceP.h | 24 +- .../libdwfl_stacktrace_next_prime.c | 6 + 11 files changed, 414 insertions(+), 6 deletions(-) create mode 100644 libdwfl_stacktrace/dwflst_tracker_elftab.c create mode 100644 libdwfl_stacktrace/dwflst_tracker_elftab.h create mode 100644 libdwfl_stacktrace/dwflst_tracker_find_elf.c create mode 100644 libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c diff --git a/libdw/libdw.map b/libdw/libdw.map index fb69a62a..46d0878a 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -400,4 +400,8 @@ ELFUTILS_0.193_EXPERIMENTAL { dwflst_tracker_begin; dwflst_tracker_dwfl_begin; dwflst_tracker_end; + dwflst_tracker_find_cached_elf; + dwflst_tracker_cache_elf; + dwflst_module_gettracker; + dwflst_tracker_linux_proc_find_elf; }; diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index b30b86f0..6ad5ba10 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -2,7 +2,7 @@ ## ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2005-2010, 2013 Red Hat, Inc. +## Copyright (C) 2005-2010, 2013, 2025 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -31,7 +31,8 @@ ## include $(top_srcdir)/config/eu.am AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ - -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(builddir)/../debuginfod + -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(builddir)/../debuginfod \ + -I$(srcdir)/../libdwfl_stacktrace VERSION = 1 noinst_LIBRARIES = libdwfl.a diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 6f98c02b..7fd0d3aa 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2005-2012, 2014, 2015 Red Hat, Inc. + Copyright (C) 2005-2012, 2014, 2015, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include #include #include "libdwP.h" /* DWARF_E_* values are here. */ +#include "libdwfl_stacktraceP.h" /* want the INTDECLS */ #include "libelfP.h" #include "system.h" @@ -79,6 +80,13 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) if (error != DWFL_E_NOERROR) return error; + /* Cache file->elf in Dwflst_Process_Tracker if available: */ + if (mod->dwfl->tracker != NULL && file->name != NULL) + { + INTUSE(dwflst_tracker_cache_elf) (mod->dwfl->tracker, file->name, + file->name, file->elf, file->fd); + } + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); if (ehdr == NULL) { diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am index d57431c0..ffddec0c 100644 --- a/libdwfl_stacktrace/Makefile.am +++ b/libdwfl_stacktrace/Makefile.am @@ -41,6 +41,9 @@ pkginclude_HEADERS = libdwfl_stacktrace.h libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ + dwflst_tracker_find_elf.c \ + dwflst_tracker_elftab.c \ + libdwfl_stacktrace_next_prime.c \ dwflst_perf_frame.c libdwfl_stacktrace = $(libdw) @@ -52,7 +55,7 @@ libeu = ../lib/libeu.a libdwfl_stacktrace_pic_a_SOURCES = am_libdwfl_stacktrace_pic_a_OBJECTS = $(libdwfl_stacktrace_a_SOURCES:.c=.os) -noinst_HEADERS = libdwfl_stacktraceP.h +noinst_HEADERS = libdwfl_stacktraceP.h dwflst_tracker_elftab.h EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c b/libdwfl_stacktrace/dwflst_process_tracker.c index 057c9f7a..10cd9a7c 100644 --- a/libdwfl_stacktrace/dwflst_process_tracker.c +++ b/libdwfl_stacktrace/dwflst_process_tracker.c @@ -32,6 +32,8 @@ #include "libdwfl_stacktraceP.h" +#define HTAB_DEFAULT_SIZE 1021 + Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) { Dwflst_Process_Tracker *tracker = calloc (1, sizeof *tracker); @@ -41,6 +43,9 @@ Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) return tracker; } + dwflst_tracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE); + rwlock_init (tracker->elftab_lock); + tracker->callbacks = callbacks; return tracker; } @@ -61,6 +66,27 @@ void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) if (tracker == NULL) return; + /* HACK to allow iteration of dynamicsizehash_concurrent. */ + /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */ + rwlock_fini (tracker->elftab_lock); + pthread_rwlock_destroy(&tracker->elftab.resize_rwl); + for (size_t idx = 1; idx <= tracker->elftab.size; idx++) + { + dwflst_tracker_elftab_ent *ent = &tracker->elftab.table[idx]; + if (ent->hashval == 0) + continue; + dwflst_tracker_elf_info *t = + (dwflst_tracker_elf_info *) atomic_load_explicit (&ent->val_ptr, + memory_order_relaxed); + free(t->module_name); + if (t->fd >= 0) + close(t->fd); + if (t->elf != NULL) + elf_end(t->elf); + free(t); /* TODO: Check necessity. */ + } + free (tracker->elftab.table); + /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */ free (tracker); } diff --git a/libdwfl_stacktrace/dwflst_tracker_elftab.c b/libdwfl_stacktrace/dwflst_tracker_elftab.c new file mode 100644 index 00000000..a7ce452a --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_elftab.c @@ -0,0 +1,45 @@ +/* Dwflst_Process_Tracker Elf table implementation. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +/* Definitions for the Elf table. */ +#define TYPE dwflst_tracker_elf_info * +#define NAME dwflst_tracker_elftab +#define ITERATE 1 +#define COMPARE(a, b) \ + (strcmp ((a)->module_name, (b)->module_name) \ + && (a)->dev == (b)->dev && (a)->ino == (b)->ino) + +#include "../lib/dynamicsizehash_concurrent.c" diff --git a/libdwfl_stacktrace/dwflst_tracker_elftab.h b/libdwfl_stacktrace/dwflst_tracker_elftab.h new file mode 100644 index 00000000..c22ddb53 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_elftab.h @@ -0,0 +1,40 @@ +/* Dwflst_Process_Tracker Elf table. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef DWFLST_TRACKER_ELFTAB_H +#define DWFLST_TRACKER_ELFTAB_H 1 + +/* Definitions for the Elf table. */ +#define TYPE dwflst_tracker_elf_info * +#define NAME dwflst_tracker_elftab +#define ITERATE 1 +#define COMPARE(a, b) \ + strcmp ((a)->module_name, (b)->module_name) +#include + +#endif diff --git a/libdwfl_stacktrace/dwflst_tracker_find_elf.c b/libdwfl_stacktrace/dwflst_tracker_find_elf.c new file mode 100644 index 00000000..7f9eb569 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_find_elf.c @@ -0,0 +1,218 @@ +/* Find Elf file and cache via Dwflst_Process_Tracker. + Copyright (C) 2025, Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "../libelf/libelfP.h" +/* XXX: Private header needed for Elf * ref_count field. */ +/* TODO: Consider dup_elf() rather than direct ref_count access. */ + +#include "libdwfl_stacktraceP.h" + +unsigned long int +__libdwfl_stacktrace_elftab_hash_st (const char *module_name, + dev_t st_dev, + ino_t st_ino) +{ + unsigned long int hval = elf_hash(module_name); + hval ^= (unsigned long int)st_dev; + hval ^= (unsigned long int)st_ino; + return hval; +} + +unsigned long int +__libdwfl_stacktrace_elftab_hash (const char *module_name, + const char *module_path, + int fd) +{ + struct stat sb; + int rc = -1; + if (fd >= 0) + rc = fstat(fd, &sb); + else if (module_path != NULL) + rc = stat(module_path, &sb); + if (rc < 0) + return elf_hash(module_name); + return __libdwfl_stacktrace_elftab_hash_st + (module_name, sb.st_dev, sb.st_ino); +} + +int +dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *module_path, + char **file_name, Elf **elfp) +{ + dwflst_tracker_elf_info *ent = NULL; + int rc = -1; + struct stat sb; + + if (module_path == NULL) + module_path = module_name; + unsigned long int hval = + __libdwfl_stacktrace_elftab_hash (module_name, module_path, -1/* no fd */); + + rwlock_rdlock(tracker->elftab_lock); + ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); + rwlock_unlock(tracker->elftab_lock); + + /* Guard against collisions. + TODO: Need proper chaining, dynamicsizehash_concurrent isn't really + equipped for it. */ + if (ent != NULL) + rc = fstat(ent->fd, &sb); + if (rc < 0 || strcmp (module_name, ent->module_name) != 0 + || ent->dev != sb.st_dev || ent->ino != sb.st_ino) + return -1; + + /* Verify that ent->fd has not been updated: */ + if (rc < 0 || ent->dev != sb.st_dev || ent->ino != sb.st_ino + || ent->last_mtime != sb.st_mtime) + return -1; + + if (ent->elf != NULL) + ent->elf->ref_count++; + *elfp = ent->elf; + *file_name = strdup(ent->module_name); + return ent->fd; +} +INTDEF(dwflst_tracker_find_cached_elf) + +bool +dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *file_name __attribute__((unused)), + Elf *elf, int fd) +{ + dwflst_tracker_elf_info *ent = NULL; + int rc = -1; + struct stat sb; + + if (fd >= 0) + rc = fstat(fd, &sb); + if (rc < 0) + return false; + unsigned long int hval = + __libdwfl_stacktrace_elftab_hash_st (module_name, sb.st_dev, sb.st_ino); + + rwlock_wrlock(tracker->elftab_lock); + ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); + /* Guard against collisions. + TODO: Need proper chaining, dynamicsizehash_concurrent isn't really + equipped for it. */ + if (ent != NULL && (strcmp (module_name, ent->module_name) != 0 + || ent->dev != sb.st_dev || ent->ino != sb.st_ino)) + { + rwlock_unlock(tracker->elftab_lock); + return false; + } + if (ent == NULL) + { + ent = calloc (1, sizeof (dwflst_tracker_elf_info)); + ent->module_name = strdup(module_name); + + if (dwflst_tracker_elftab_insert(&tracker->elftab, hval, ent) != 0) + { + free(ent->module_name); + free(ent); + rwlock_unlock(tracker->elftab_lock); + assert(false); /* Should not occur due to the wrlock on elftab. */ + } + } + else + { + /* TODO: The following assertions are still triggered on certain + code paths that acquire fds or create Elf structs without + checking the caching mechanism first. This is not a serious + problem, and can be fixed incrementally. */ + + /* assert(ent->elf == NULL || ent->elf == elf); */ /* Guard against redundant/leaked Elf *. */ + /* assert(ent->fd == fd); */ /* Guard against redundant open. */ + + /* For now, correct behaviour (from dwfl_module_getdwarf.c open_elf) + is to replace the existing elf, keep module_name. */ + if (ent->elf != NULL && ent->elf != elf) + elf_end(ent->elf); + } + if (elf != NULL && ent->elf != elf) + elf->ref_count++; + ent->elf = elf; + ent->fd = fd; + if (rc == 0) /* TODO(REVIEW): Report rc != 0 via errno? */ + { + ent->dev = sb.st_dev; + ent->ino = sb.st_ino; + ent->last_mtime = sb.st_mtime; + } + rwlock_unlock(tracker->elftab_lock); + return true; +} +INTDEF(dwflst_tracker_cache_elf) + +Dwflst_Process_Tracker * +dwflst_module_gettracker (Dwfl_Module *mod) +{ + if (mod == NULL) + return NULL; + if (mod->dwfl == NULL) + return NULL; + return mod->dwfl->tracker; +} +INTDEF(dwflst_module_gettracker) + +int +dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, + void **userdata __attribute__ ((unused)), + const char *module_name, Dwarf_Addr base, + char **file_name, Elf **elfp) +{ + Dwflst_Process_Tracker *tracker = INTUSE(dwflst_module_gettracker) (mod); + int fd; + + if (tracker != NULL) + { + fd = INTUSE(dwflst_tracker_find_cached_elf) + (tracker, module_name, module_name, file_name, elfp); + if (fd >= 0) + return fd; + } + + fd = INTUSE(dwfl_linux_proc_find_elf) (mod, userdata, module_name, + base, file_name, elfp); + + if (tracker != NULL && fd >= 0 && *file_name != NULL) + { + INTUSE(dwflst_tracker_cache_elf) + (tracker, module_name, *file_name, *elfp, fd); + } + return fd; +} diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h index f3a82d18..d29dc640 100644 --- a/libdwfl_stacktrace/libdwfl_stacktrace.h +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -57,10 +57,45 @@ extern Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callb extern Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) __nonnull_attribute__ (1); -/* End all sessions with this tracker. */ +/* Try to find a cached Elf corresponding to MODULE_NAME. Verifies + that the cached Elf has dev/ino/mtime matching the file on disk. + Non-NULL MODULE_PATH specifies an alternate location for the module + e.g. /proc/PID/root/MODULE_NAME. Stores FILE_NAME and ELFP values. + Returns fd similar to the find_elf callbacks, or -1 if cached Elf + was not found. */ +extern int dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *module_path, + char **file_name, Elf **elfp) + __nonnull_attribute__ (1, 2, 4, 5); + +/* Store an Elf corresponding to MODULE_NAME in the tracker's cache. + FILE_NAME and FD values must be provided, similar to the output of + a find_elf callback. Returns TRUE iff the Elf was successfully + stored in the cache. */ +extern bool dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *file_name, + Elf *elf, int fd) + __nonnull_attribute__ (1, 2); + +/* For implementing a find_elf callback based on the prior two functions. + Returns the Dwflst_Process_Tracker corresponding to MOD. */ +extern Dwflst_Process_Tracker *dwflst_module_gettracker (Dwfl_Module *mod); + +/* End all sessions with this tracker. */ extern void dwflst_tracker_end (Dwflst_Process_Tracker *tracker); +/* Adaptation of the dwfl_linux_proc_find_elf callback from libdwfl, + except this first attempts to look up a cached Elf* and fd from the + Dwfl_Module's Dwflst_Process_Tracker (if any). If a new Elf* is + created, this callback saves it to the tracker's cache. */ +extern int dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, void **userdata, + const char *module_name, Dwarf_Addr base, + char **file_name, Elf **); + + /* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ /* Returns the linux perf_events register mask describing a set of diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h index 9313176c..e457d35a 100644 --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h @@ -33,10 +33,32 @@ #include "libdwflP.h" +/* Hash table for Elf *. */ +typedef struct +{ + char *module_name; /* dwfltracker_elftab_ent is used iff non-NULL. */ + int fd; + Elf *elf; + dev_t dev; + ino_t ino; + time_t last_mtime; +} dwflst_tracker_elf_info; +#include "dwflst_tracker_elftab.h" + struct Dwflst_Process_Tracker { const Dwfl_Callbacks *callbacks; - /* ... */ + + /* Table of cached Elf * including fd, path, fstat info. */ + dwflst_tracker_elftab elftab; + rwlock_define(, elftab_lock); }; + +/* Avoid PLT entries. */ +INTDECL (dwflst_module_gettracker) +INTDECL (dwflst_tracker_find_cached_elf) +INTDECL (dwflst_tracker_cache_elf) + + #endif /* libdwfl_stacktraceP.h */ diff --git a/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c b/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c new file mode 100644 index 00000000..0974ce68 --- /dev/null +++ b/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c @@ -0,0 +1,6 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#define next_prime attribute_hidden __libdwfl_stacktrace_next_prime +#include "../lib/next_prime.c" From patchwork Thu Apr 24 21:47:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110965 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 030363858CDA for ; Thu, 24 Apr 2025 21:50:07 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 030363858CDA Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=aBqsNwhm; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=MxS1FxKM X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id B2EF23858D3C for ; Thu, 24 Apr 2025 21:48:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B2EF23858D3C Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B2EF23858D3C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531302; cv=none; b=BkdwxBAruqrOEAASXesFZkJm9JAUNvzS/Re8ien9mW07FcIECaa7KVriaqJ6CFOFIFQaf0D3M91hdl7hXNy+qd4lpH21ELKh0P7y9ASnq/W16/G7WK/Ea+i1ZauIh7pq3Xw+vA/v/FycVHZepbpSQ8JhBohp5yfrOilBsiXYO8U= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531302; c=relaxed/simple; bh=slNZ/0SOFoQG/MrUmOfbzTrWbVzixZBZM9AXc9vG7S4=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=xJ7qtqrjj4bo7oOOe5/A59HHal4xp5/XoUw+5o8iy9s98fbleBnx73fL5I0jox2TqpObi0r0EdQE8h8/gGjij0A0uAuVr0rGGQyA5yDo1vJnZLxHKo/heZ1A0iOANW82FJwYXXhisKv3NkxriWGkTOioBNNDgVqYi01BB07+Euo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B2EF23858D3C Received: from phl-compute-11.internal (phl-compute-11.phl.internal [10.202.2.51]) by mailfhigh.stl.internal (Postfix) with ESMTP id 58671254022B; Thu, 24 Apr 2025 17:48:22 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-11.internal (MEProxy); Thu, 24 Apr 2025 17:48:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531302; x= 1745617702; bh=s9W3MuvLjmlj2MExKuahWJnrBbUKquzRa9RELBRX/EY=; b=a BqsNwhmth2KJAUD6mPdzfzJOl5B6avGUXc3hQCsUngCg7gxVz+Jn8j0ckv7jehnH 6ysooTubI568Ce3QRASbcFdE/eDG5IiQYRq+XrNMx+jxaUGYdhfQddxXwqNb2eIc RwlWcv26n32J0ZJW5osjAyaGFvQxAkp6bvNnzbh5tWZIU55IuyeIVTPPA0N+HPny DhYS8ANRrQzWafspgXmQRPDLQXwqBzONaLdF0E2QfxWmU624PM8ruTjmKvZC5d70 gQ9gnRDSl4wUPZiQsRJgKU2neT9sc4yGgGbjX6l/CxrEJUhAiHbgCzTovzAMDvxj Qyyv/q7NiovQwa80gaMcw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531302; x=1745617702; bh=s 9W3MuvLjmlj2MExKuahWJnrBbUKquzRa9RELBRX/EY=; b=MxS1FxKMPlBKB1nLB 2WVU4AYK8wpPMKSNaxv+qMEg/vNCDnmg9/YRVg2CvWET5fF+qKq6RV5XFjel9ZmL xsnweuhFAY6mwGKYY727FICTqh6wONpud9YGxXJly84mEjVpjn7E540AMLoBcoQU vwuCMM/vKyOwaTLbf7764ZZFw4lPGXVOddZjrJXAD4VSWhGAoMi0wsg6sG5s6V0T qHKDBGjdAMQwvrxH2IGA0YDCSqOo+qro1SUGa89OwtImn6EslxAznuO0X0MMVdrk 7OoFb7c3koYDtfn5G/CxeQthq/J0gXBAHjN/zwYaIGJYYDqmfA1LKmhed0aIviti svFqQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd dtheeljefhjefggefgteegveetfeetudejkeduvdeigfffjeduveffgeeltddunecuffho mhgrihhnpehgnhhurdhorhhgnecuvehluhhsthgvrhfuihiivgepudenucfrrghrrghmpe hmrghilhhfrhhomhepshgvrhhhvghisehsvghrhhgvihdrihhopdhnsggprhgtphhtthho pedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopegvlhhfuhhtihhlshdquggvvh gvlhesshhouhhrtggvfigrrhgvrdhorhhgpdhrtghpthhtohepshgvrhhhvghisehsvghr hhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:21 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 07/14] libdwfl_stacktrace [6/12]: Elf* caching via dwflst_process_tracker Date: Thu, 24 Apr 2025 17:47:10 -0400 Message-ID: <20250424214715.306147-8-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_SHORT, PROLO_LEO1, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes Changes for v4: - Separate out libdwfl_stacktrace, as requested. - dwfl_module_getdwarf.c now uses the dwflst_tracker_cache_elf() interface instead of editing the elftab directly. Changes for v3: - Reworked elftab to incorporate dev/ino into the caching key (to allow caching modules from different filesystems e.g. a main filesystem and a container filesystem) and guard more carefully against collisions. - Add external API for implementing a custom find_elf callback that reads/writes the cache. Changes for v2: - Add locking for elftab. This is needed in addition to the intrinsic locking in dynamicsizehash_concurrent to avoid having cache_elf expose an incomplete dwfltracker_elf_info* entry to other threads while its data is being populated / replaced. - Tidy dwfl_process_tracker_find_elf.c into the main find_elf callback and two functions to consider (in future) making into a public api for custom cached callbacks. * * * The Dwflst_Process_Tracker includes a dynamicsizehash cache which maps file paths to Elf * (or rather, dwflst_tracker_elf_info * storing fd and Elf *). We provide a dwflst_tracker_linux_proc_find_elf callback which checks the cache for an already-loaded Elf * and, if missing, populates the cache with the fd returned by dwfl_linux_proc_find_elf. Later, open_elf updates the cache with the Elf * for that fd. The commented asserts in dwflst_tracker_cache_elf still catch some cases where a redundant Elf * is being created without checking the cache. Since the Elf * outlasts the Dwfl that created it, we use the (convenient, already-existing) reference count field in Elf * to retain the data in the table. Then dwfl_end calling elf_end will decrement the refcount cleanly, and dwflst_tracker_end will issue another elf_end call. * libdwfl_stacktrace/libdwfl_stacktrace.h (dwflst_tracker_find_cached_elf): New function. (dwflst_tracker_cache_elf): New function. (dwflst_module_gettracker): New function, gives external users a way to access Dwflst_Process_Tracker given a Dwfl_Module. (dwflst_tracker_linux_proc_find_elf): New function, serves as a cached version of the dwfl_linux_proc_find_elf callback. * libdwfl_stacktrace/libdwfl_stacktraceP.h (dwflst_tracker_elf_info): New struct typedef. (struct Dwflst_Process_Tracker): Add dynamicsizehash table of dwflst_tracker_elf_info structs + associated rwlock. (INTDECLs): Add INTDECL for dwflst_tracker_find_cached_elf, dwflst_tracker_cache_elf, dwflst_module_gettracker. * libdwfl_stacktrace/dwflst_tracker_elftab.c: New file, instantiates lib/dynamicsizehash_concurrent.c to store dwflst_tracker_elf_info structs. * libdwfl_stacktrace/dwflst_tracker_elftab.h: New file, ditto. * libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c: New file. * libdwfl_stacktrace/dwflst_process_tracker.c (dwflst_tracker_begin): Init elftab. (dwflst_tracker_end): Clean up elftab. Lock and iterate the hash to free tracker->elftab.table items. * libdwfl_stacktrace/dwflst_tracker_find_elf.c: New file, implements a find_elf callback that wraps dwfl_linux_proc_find_elf with additional caching logic, and an API to access the Dwflst_Process_Tracker Elf cache when implementing a custom find_elf callback. * libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): Add dwflst_tracker_find_elf.c, dwflst_tracker_elftab.c, libdwfl_stacktrace_next_prime.c. (noinst_HEADERS): Add dwflst_tracker_elftab.h. * libdw/libdw.map: Add dwflst_tracker_find_cached_elf, dwflst_tracker_cache_elf, dwflst_module_gettracker, dwflst_tracker_linux_proc_find_elf. * libdwfl/Makefile.am (AM_CPPFLAGS): Include headers from ../libdwfl_stacktrace. * libdwfl/dwfl_module_getdwarf.c (open_elf): Cache file->elf in Dwflst_Process_Tracker. Must be done here as dwfl_linux_proc_find_elf opens an fd but does not yet create the Elf *. --- libdw/libdw.map | 4 + libdwfl/Makefile.am | 5 +- libdwfl/dwfl_module_getdwarf.c | 10 +- libdwfl_stacktrace/Makefile.am | 5 +- libdwfl_stacktrace/dwflst_process_tracker.c | 26 +++ libdwfl_stacktrace/dwflst_tracker_elftab.c | 45 ++++ libdwfl_stacktrace/dwflst_tracker_elftab.h | 40 ++++ libdwfl_stacktrace/dwflst_tracker_find_elf.c | 216 ++++++++++++++++++ libdwfl_stacktrace/libdwfl_stacktrace.h | 37 ++- libdwfl_stacktrace/libdwfl_stacktraceP.h | 24 +- .../libdwfl_stacktrace_next_prime.c | 6 + 11 files changed, 412 insertions(+), 6 deletions(-) create mode 100644 libdwfl_stacktrace/dwflst_tracker_elftab.c create mode 100644 libdwfl_stacktrace/dwflst_tracker_elftab.h create mode 100644 libdwfl_stacktrace/dwflst_tracker_find_elf.c create mode 100644 libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c diff --git a/libdw/libdw.map b/libdw/libdw.map index fb69a62a..46d0878a 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -400,4 +400,8 @@ ELFUTILS_0.193_EXPERIMENTAL { dwflst_tracker_begin; dwflst_tracker_dwfl_begin; dwflst_tracker_end; + dwflst_tracker_find_cached_elf; + dwflst_tracker_cache_elf; + dwflst_module_gettracker; + dwflst_tracker_linux_proc_find_elf; }; diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index b30b86f0..6ad5ba10 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -2,7 +2,7 @@ ## ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2005-2010, 2013 Red Hat, Inc. +## Copyright (C) 2005-2010, 2013, 2025 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -31,7 +31,8 @@ ## include $(top_srcdir)/config/eu.am AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ - -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(builddir)/../debuginfod + -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(builddir)/../debuginfod \ + -I$(srcdir)/../libdwfl_stacktrace VERSION = 1 noinst_LIBRARIES = libdwfl.a diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 6f98c02b..7fd0d3aa 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2005-2012, 2014, 2015 Red Hat, Inc. + Copyright (C) 2005-2012, 2014, 2015, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include #include #include "libdwP.h" /* DWARF_E_* values are here. */ +#include "libdwfl_stacktraceP.h" /* want the INTDECLS */ #include "libelfP.h" #include "system.h" @@ -79,6 +80,13 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) if (error != DWFL_E_NOERROR) return error; + /* Cache file->elf in Dwflst_Process_Tracker if available: */ + if (mod->dwfl->tracker != NULL && file->name != NULL) + { + INTUSE(dwflst_tracker_cache_elf) (mod->dwfl->tracker, file->name, + file->name, file->elf, file->fd); + } + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); if (ehdr == NULL) { diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am index d57431c0..ffddec0c 100644 --- a/libdwfl_stacktrace/Makefile.am +++ b/libdwfl_stacktrace/Makefile.am @@ -41,6 +41,9 @@ pkginclude_HEADERS = libdwfl_stacktrace.h libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ + dwflst_tracker_find_elf.c \ + dwflst_tracker_elftab.c \ + libdwfl_stacktrace_next_prime.c \ dwflst_perf_frame.c libdwfl_stacktrace = $(libdw) @@ -52,7 +55,7 @@ libeu = ../lib/libeu.a libdwfl_stacktrace_pic_a_SOURCES = am_libdwfl_stacktrace_pic_a_OBJECTS = $(libdwfl_stacktrace_a_SOURCES:.c=.os) -noinst_HEADERS = libdwfl_stacktraceP.h +noinst_HEADERS = libdwfl_stacktraceP.h dwflst_tracker_elftab.h EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c b/libdwfl_stacktrace/dwflst_process_tracker.c index 057c9f7a..10cd9a7c 100644 --- a/libdwfl_stacktrace/dwflst_process_tracker.c +++ b/libdwfl_stacktrace/dwflst_process_tracker.c @@ -32,6 +32,8 @@ #include "libdwfl_stacktraceP.h" +#define HTAB_DEFAULT_SIZE 1021 + Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) { Dwflst_Process_Tracker *tracker = calloc (1, sizeof *tracker); @@ -41,6 +43,9 @@ Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) return tracker; } + dwflst_tracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE); + rwlock_init (tracker->elftab_lock); + tracker->callbacks = callbacks; return tracker; } @@ -61,6 +66,27 @@ void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) if (tracker == NULL) return; + /* HACK to allow iteration of dynamicsizehash_concurrent. */ + /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */ + rwlock_fini (tracker->elftab_lock); + pthread_rwlock_destroy(&tracker->elftab.resize_rwl); + for (size_t idx = 1; idx <= tracker->elftab.size; idx++) + { + dwflst_tracker_elftab_ent *ent = &tracker->elftab.table[idx]; + if (ent->hashval == 0) + continue; + dwflst_tracker_elf_info *t = + (dwflst_tracker_elf_info *) atomic_load_explicit (&ent->val_ptr, + memory_order_relaxed); + free(t->module_name); + if (t->fd >= 0) + close(t->fd); + if (t->elf != NULL) + elf_end(t->elf); + free(t); /* TODO: Check necessity. */ + } + free (tracker->elftab.table); + /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */ free (tracker); } diff --git a/libdwfl_stacktrace/dwflst_tracker_elftab.c b/libdwfl_stacktrace/dwflst_tracker_elftab.c new file mode 100644 index 00000000..a7ce452a --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_elftab.c @@ -0,0 +1,45 @@ +/* Dwflst_Process_Tracker Elf table implementation. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +/* Definitions for the Elf table. */ +#define TYPE dwflst_tracker_elf_info * +#define NAME dwflst_tracker_elftab +#define ITERATE 1 +#define COMPARE(a, b) \ + (strcmp ((a)->module_name, (b)->module_name) \ + && (a)->dev == (b)->dev && (a)->ino == (b)->ino) + +#include "../lib/dynamicsizehash_concurrent.c" diff --git a/libdwfl_stacktrace/dwflst_tracker_elftab.h b/libdwfl_stacktrace/dwflst_tracker_elftab.h new file mode 100644 index 00000000..c22ddb53 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_elftab.h @@ -0,0 +1,40 @@ +/* Dwflst_Process_Tracker Elf table. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef DWFLST_TRACKER_ELFTAB_H +#define DWFLST_TRACKER_ELFTAB_H 1 + +/* Definitions for the Elf table. */ +#define TYPE dwflst_tracker_elf_info * +#define NAME dwflst_tracker_elftab +#define ITERATE 1 +#define COMPARE(a, b) \ + strcmp ((a)->module_name, (b)->module_name) +#include + +#endif diff --git a/libdwfl_stacktrace/dwflst_tracker_find_elf.c b/libdwfl_stacktrace/dwflst_tracker_find_elf.c new file mode 100644 index 00000000..6eee5057 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_find_elf.c @@ -0,0 +1,216 @@ +/* Find Elf file and cache via Dwflst_Process_Tracker. + Copyright (C) 2025, Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "../libelf/libelfP.h" +/* XXX: Private header needed for Elf * ref_count field. */ +/* TODO: Consider dup_elf() rather than direct ref_count access. */ + +#include "libdwfl_stacktraceP.h" + +unsigned long int +__libdwfl_stacktrace_elftab_hash_st (const char *module_name, + dev_t st_dev, + ino_t st_ino) +{ + unsigned long int hval = elf_hash(module_name); + hval ^= (unsigned long int)st_dev; + hval ^= (unsigned long int)st_ino; + return hval; +} + +unsigned long int +__libdwfl_stacktrace_elftab_hash (const char *module_name, + const char *module_path, + int fd) +{ + struct stat sb; + int rc = -1; + if (fd >= 0) + rc = fstat(fd, &sb); + else if (module_path != NULL) + rc = stat(module_path, &sb); + if (rc < 0) + return elf_hash(module_name); + return __libdwfl_stacktrace_elftab_hash_st + (module_name, sb.st_dev, sb.st_ino); +} + +int +dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *module_path, + char **file_name, Elf **elfp) +{ + dwflst_tracker_elf_info *ent = NULL; + int rc; + struct stat sb; + + if (module_path == NULL) + module_path = module_name; + unsigned long int hval = + __libdwfl_stacktrace_elftab_hash (module_name, module_path, -1/* no fd */); + + rwlock_rdlock(tracker->elftab_lock); + ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); + rwlock_unlock(tracker->elftab_lock); + + /* Guard against collisions. + TODO: Need proper chaining, dynamicsizehash_concurrent isn't really + equipped for it. */ + if (ent == NULL || !strcmp (module_name, ent->module_name) + || ent->dev != sb.st_dev || ent->ino != sb.st_ino) + return -1; + + /* Verify that ent->fd has not been updated: */ + rc = fstat(ent->fd, &sb); + if (rc < 0 || ent->dev != sb.st_dev || ent->ino != sb.st_ino + || ent->last_mtime != sb.st_mtime) + return -1; + + if (ent->elf != NULL) + ent->elf->ref_count++; + *elfp = ent->elf; + *file_name = strdup(ent->module_name); + return ent->fd; +} +INTDEF(dwflst_tracker_find_cached_elf) + +bool +dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *file_name __attribute__((unused)), + Elf *elf, int fd) +{ + dwflst_tracker_elf_info *ent = NULL; + int rc; + struct stat sb; + + rc = fstat(fd, &sb); + if (rc < 0) + return false; + unsigned long int hval = + __libdwfl_stacktrace_elftab_hash_st (module_name, sb.st_dev, sb.st_ino); + + rwlock_wrlock(tracker->elftab_lock); + ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); + /* Guard against collisions. + TODO: Need proper chaining, dynamicsizehash_concurrent isn't really + equipped for it. */ + if (ent != NULL && (!strcmp (module_name, ent->module_name) + || ent->dev != sb.st_dev || ent->ino != sb.st_ino)) + { + rwlock_unlock(tracker->elftab_lock); + return false; + } + if (ent == NULL) + { + ent = calloc (1, sizeof (dwflst_tracker_elf_info)); + ent->module_name = strdup(module_name); + + if (dwflst_tracker_elftab_insert(&tracker->elftab, hval, ent) != 0) + { + free(ent->module_name); + free(ent); + rwlock_unlock(tracker->elftab_lock); + assert(false); /* Should not occur due to the wrlock on elftab. */ + } + } + else + { + /* TODO: The following assertions are still triggered on certain + code paths that acquire fds or create Elf structs without + checking the caching mechanism first. This is not a serious + problem, and can be fixed incrementally. */ + + /* assert(ent->elf == NULL || ent->elf == elf); */ /* Guard against redundant/leaked Elf *. */ + /* assert(ent->fd == file->fd); */ /* Guard against redundant open. */ + + /* For now, correct behaviour (from dwfl_module_getdwarf.c open_elf) + is to replace the existing elf, keep module_name. */ + if (ent->elf != NULL) + elf_end(ent->elf); + } + if (elf != NULL) + elf->ref_count++; + ent->elf = elf; + ent->fd = fd; + if (rc == 0) /* TODO(REVIEW): Report rc != 0 via errno? */ + { + ent->dev = sb.st_dev; + ent->ino = sb.st_ino; + ent->last_mtime = sb.st_mtime; + } + rwlock_unlock(tracker->elftab_lock); + return true; +} +INTDEF(dwflst_tracker_cache_elf) + +Dwflst_Process_Tracker * +dwflst_module_gettracker (Dwfl_Module *mod) +{ + if (mod == NULL) + return NULL; + if (mod->dwfl == NULL) + return NULL; + return mod->dwfl->tracker; +} +INTDEF(dwflst_module_gettracker) + +int +dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, + void **userdata __attribute__ ((unused)), + const char *module_name, Dwarf_Addr base, + char **file_name, Elf **elfp) +{ + Dwflst_Process_Tracker *tracker = INTUSE(dwflst_module_gettracker) (mod); + int fd; + + if (tracker != NULL) + { + fd = INTUSE(dwflst_tracker_find_cached_elf) + (tracker, module_name, module_name, file_name, elfp); + if (fd >= 0) + return fd; + } + + fd = INTUSE(dwfl_linux_proc_find_elf) (mod, userdata, module_name, + base, file_name, elfp); + + if (tracker != NULL && fd >= 0 && *file_name != NULL) + { + INTUSE(dwflst_tracker_cache_elf) + (tracker, module_name, *file_name, *elfp, fd); + } + return fd; +} diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h index f3a82d18..d29dc640 100644 --- a/libdwfl_stacktrace/libdwfl_stacktrace.h +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -57,10 +57,45 @@ extern Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callb extern Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) __nonnull_attribute__ (1); -/* End all sessions with this tracker. */ +/* Try to find a cached Elf corresponding to MODULE_NAME. Verifies + that the cached Elf has dev/ino/mtime matching the file on disk. + Non-NULL MODULE_PATH specifies an alternate location for the module + e.g. /proc/PID/root/MODULE_NAME. Stores FILE_NAME and ELFP values. + Returns fd similar to the find_elf callbacks, or -1 if cached Elf + was not found. */ +extern int dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *module_path, + char **file_name, Elf **elfp) + __nonnull_attribute__ (1, 2, 4, 5); + +/* Store an Elf corresponding to MODULE_NAME in the tracker's cache. + FILE_NAME and FD values must be provided, similar to the output of + a find_elf callback. Returns TRUE iff the Elf was successfully + stored in the cache. */ +extern bool dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, + const char *module_name, + const char *file_name, + Elf *elf, int fd) + __nonnull_attribute__ (1, 2); + +/* For implementing a find_elf callback based on the prior two functions. + Returns the Dwflst_Process_Tracker corresponding to MOD. */ +extern Dwflst_Process_Tracker *dwflst_module_gettracker (Dwfl_Module *mod); + +/* End all sessions with this tracker. */ extern void dwflst_tracker_end (Dwflst_Process_Tracker *tracker); +/* Adaptation of the dwfl_linux_proc_find_elf callback from libdwfl, + except this first attempts to look up a cached Elf* and fd from the + Dwfl_Module's Dwflst_Process_Tracker (if any). If a new Elf* is + created, this callback saves it to the tracker's cache. */ +extern int dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, void **userdata, + const char *module_name, Dwarf_Addr base, + char **file_name, Elf **); + + /* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ /* Returns the linux perf_events register mask describing a set of diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h index 9313176c..e457d35a 100644 --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h @@ -33,10 +33,32 @@ #include "libdwflP.h" +/* Hash table for Elf *. */ +typedef struct +{ + char *module_name; /* dwfltracker_elftab_ent is used iff non-NULL. */ + int fd; + Elf *elf; + dev_t dev; + ino_t ino; + time_t last_mtime; +} dwflst_tracker_elf_info; +#include "dwflst_tracker_elftab.h" + struct Dwflst_Process_Tracker { const Dwfl_Callbacks *callbacks; - /* ... */ + + /* Table of cached Elf * including fd, path, fstat info. */ + dwflst_tracker_elftab elftab; + rwlock_define(, elftab_lock); }; + +/* Avoid PLT entries. */ +INTDECL (dwflst_module_gettracker) +INTDECL (dwflst_tracker_find_cached_elf) +INTDECL (dwflst_tracker_cache_elf) + + #endif /* libdwfl_stacktraceP.h */ diff --git a/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c b/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c new file mode 100644 index 00000000..0974ce68 --- /dev/null +++ b/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c @@ -0,0 +1,6 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#define next_prime attribute_hidden __libdwfl_stacktrace_next_prime +#include "../lib/next_prime.c" From patchwork Thu Apr 24 21:47:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110970 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0E3E63858D35 for ; Thu, 24 Apr 2025 21:52:27 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0E3E63858D35 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=r3/lbWBY; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=qEPbGjKr X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id 32F953858D3C for ; Thu, 24 Apr 2025 21:48:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 32F953858D3C Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 32F953858D3C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531322; cv=none; b=rImzyoblvOu4mkoljSs099LpxVusPPA0bJs2ZsZdieSEqNPqUjxWK6x1YTB2/mYSkrXKSxwc8FMr4Jrk6Au02y+2tTuT70SWVMIets35aAM8PS1BAh6pkNEHyJu/KieiCYORCuvyqunuRUtmgfboEXAAgZ/8cv/clYQE+X4JRBQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531322; c=relaxed/simple; bh=+Dka7mI3NsPphAbgyegFPNVTq/SWkFOtkRrywpvCRhU=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=XpyxIUmTBfPuJcjZSnf5CiPqiKlG/5yrjyv15RX2Ql3Tnx8RUddM9r4pTP+4+vUZ7l8B0+I3ZFXSRP0TYWM4FBNQ0txkQwKrI7WFiamoTzJe8pingZHGVIX4titISPSs7t+AILjO/djvVsgKMbSqxwiEkarNCjsoR1ZoIULVb/0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 32F953858D3C Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfhigh.stl.internal (Postfix) with ESMTP id CBE952540229; Thu, 24 Apr 2025 17:48:41 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-01.internal (MEProxy); Thu, 24 Apr 2025 17:48:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531321; x= 1745617721; bh=LrBRJq/+vh/8SNjoUriuqOTE9mjyDCRESuRHtUYC6yk=; b=r 3/lbWBY7im89MYAed6lIhChnE1RJj6QJoZIoLwmw3yM2n4G0/fmSUUo0H36Da1Jp FPjlc4B8TI6zLQxU35IrZEawTjUo6GBg+ERbQ9r1R1WUdigccvHAxtTHEuquOP1W l4JtBVO+trK2Lqz8lZyZW47f6W5y7DLcJKhSa0x4cxaCqDP6W0AxXR3FAgMbepQQ /QLi2vm0UbnTceSK+XY3QlDmthvq+mRI8ZN1j8Oh8Vm1dYNoiO9Hyz5q5nCxAmvx NF+axWXnMTkISJxegQFhQUKyD6ckbTVx+wMSAgdztMOBKkXUNYq/Htcvsp80Fhrc UVOOqqrFOpvZk2kWSmHRA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531321; x=1745617721; bh=L rBRJq/+vh/8SNjoUriuqOTE9mjyDCRESuRHtUYC6yk=; b=qEPbGjKrfrkwFQ6Rz k+f8lf/AOf7DpdCiS8OwOMVtAZapymsdvsUEtz4Kv6cL05DBEUIe9nmotTLVPwXz J1/3VoI9erGED2sLv6k09qcvEyTJ4NzqbingV+PjGqRtUK/gWjwhFS0M+vhfez1/ aNW6N+EzCiozbdvrz6v2DcPENL0BycvsP9Vn/z/CSOwdYHCU6reiqf9t7EwKuQ+8 T73dFEeGd2T1nnefZ3zGNA1ychYaEbqvSHspA5rS/LE9oK5gqk87LJ3LlS7Os56d IYchjv0B1iyma5p38XRgrJaTpr7+GcNrtl33BCu0GKlGtu1S3bZCcfKfHxx1SHd3 i7X+g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd dtheeljefhjefggefgteegveetfeetudejkeduvdeigfffjeduveffgeeltddunecuffho mhgrihhnpehgnhhurdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpe hmrghilhhfrhhomhepshgvrhhhvghisehsvghrhhgvihdrihhopdhnsggprhgtphhtthho pedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopegvlhhfuhhtihhlshdquggvvh gvlhesshhouhhrtggvfigrrhgvrdhorhhgpdhrtghpthhtohepshgvrhhhvghisehsvghr hhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:41 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 08/12] libdwfl_stacktrace [8/12]: Dwfl* caching via Dwflst_Process_Tracker Date: Thu, 24 Apr 2025 17:47:11 -0400 Message-ID: <20250424214715.306147-9-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Separate out libdwfl_stacktrace, as requested. Changes for v3: - Handle dwfl->process == NULL case in __libdwfl_remove_dwfl_from_tracker. * * * The Dwflst_Process_Tracker also includes a dynamicsizehash cache which maps process ids to Dwfl * (or rather, dwflst_tracker_dwfl_info * allowing the table entry to be replaced). Dwfls created from the tracker are automatically added to it, and removed on dwfl_end(). * libdwfl_stacktrace/libdwfl_stacktraceP.h (dwflst_tracker_dwfl_info): New typedef, provides indirection to allow a dwfltab entry to be invalidated. (struct Dwflst_Process_Tracker): add dwfltab. (__libdwfl_stacktrace_add_dwfl_to_tracker): New function. (__libdwfl_stacktrace_remove_dwfl_from_tracker): New function. * libdwfl_stacktrace/dwflst_process_tracker.c (dwflst_tracker_begin): Init dwfltab. (__libdwfl_stacktrace_add_dwfl_to_tracker): New function; add dwfl to dwfltab. (__libdwfl_stacktrace_remove_dwfl_from_tracker): New function; invalidate dwfl entry, since dynamicsizehash doesn't support outright deletion. (dwflst_tracker_end): Clean up dwfltab. Lock and iterate the table to free tracker->dwfltab.table items. * libdwfl_stacktrace/dwflst_tracker_dwfltab.c: New file, instantiates lib/dynamicsizehash_concurrent.c to store dwfltracker_dwfl_info structs. * libdwfl_stacktrace/dwflst_tracker_dwfltab.h: New file, ditto. * libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): Add dwflst_tracker_dwfltab.c. (noinst_HEADERS): Add dwflst_tracker_dwfltab.h. * libdwfl/dwfl_frame.c (dwfl_attach_state): Call __libdwfl_stacktrace_add_dwfl_to_tracker. * libdwfl/dwfl_end.c (dwfl_end): Add INTDEF. Call __libdwfl_stacktrace_remove_dwfl_from_tracker. * libdwfl/libdwflP.h (INTDECLs): Add dwfl_end. --- libdwfl/dwfl_end.c | 8 +- libdwfl/dwfl_frame.c | 7 +- libdwfl/libdwflP.h | 1 + libdwfl_stacktrace/Makefile.am | 4 +- libdwfl_stacktrace/dwflst_process_tracker.c | 87 ++++++++++++++++++++- libdwfl_stacktrace/dwflst_tracker_dwfltab.c | 46 +++++++++++ libdwfl_stacktrace/dwflst_tracker_dwfltab.h | 42 ++++++++++ libdwfl_stacktrace/libdwfl_stacktraceP.h | 23 ++++++ 8 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 libdwfl_stacktrace/dwflst_tracker_dwfltab.c create mode 100644 libdwfl_stacktrace/dwflst_tracker_dwfltab.h diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c index 7b5ac8a1..d9cf569b 100644 --- a/libdwfl/dwfl_end.c +++ b/libdwfl/dwfl_end.c @@ -1,5 +1,5 @@ /* Finish a session using libdwfl. - Copyright (C) 2005, 2008, 2012-2013, 2015 Red Hat, Inc. + Copyright (C) 2005, 2008, 2012-2013, 2015, 2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -31,6 +31,7 @@ #endif #include "libdwflP.h" +#include "libdwfl_stacktraceP.h" void dwfl_end (Dwfl *dwfl) @@ -42,6 +43,9 @@ dwfl_end (Dwfl *dwfl) __libdwfl_debuginfod_end (dwfl->debuginfod); #endif + if (dwfl->tracker != NULL) + __libdwfl_stacktrace_remove_dwfl_from_tracker (dwfl); + if (dwfl->process) __libdwfl_process_free (dwfl->process); @@ -68,3 +72,5 @@ dwfl_end (Dwfl *dwfl) } free (dwfl); } +INTDEF(dwfl_end) + diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c index 2e6c6de8..0d619887 100644 --- a/libdwfl/dwfl_frame.c +++ b/libdwfl/dwfl_frame.c @@ -1,5 +1,5 @@ /* Get Dwarf Frame state for target PID or core file. - Copyright (C) 2013, 2014, 2024 Red Hat, Inc. + Copyright (C) 2013, 2014, 2024-2025 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -33,6 +33,7 @@ #include #include "libdwflP.h" +#include "libdwfl_stacktraceP.h" /* Set STATE->pc_set from STATE->regs according to the backend. Return true on success, false on error. */ @@ -206,6 +207,10 @@ dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid, process->pid = pid; process->callbacks = thread_callbacks; process->callbacks_arg = arg; + + if (dwfl->tracker != NULL) + __libdwfl_stacktrace_add_dwfl_to_tracker (dwfl); + return true; } INTDEF(dwfl_attach_state) diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 57305f81..b622f8aa 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -757,6 +757,7 @@ extern int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, /* Avoid PLT entries. */ INTDECL (dwfl_begin) +INTDECL (dwfl_end) INTDECL (dwfl_errmsg) INTDECL (dwfl_errno) INTDECL (dwfl_addrmodule) diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am index ffddec0c..99a80b5c 100644 --- a/libdwfl_stacktrace/Makefile.am +++ b/libdwfl_stacktrace/Makefile.am @@ -43,6 +43,7 @@ pkginclude_HEADERS = libdwfl_stacktrace.h libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ dwflst_tracker_find_elf.c \ dwflst_tracker_elftab.c \ + dwflst_tracker_dwfltab.c \ libdwfl_stacktrace_next_prime.c \ dwflst_perf_frame.c @@ -55,7 +56,8 @@ libeu = ../lib/libeu.a libdwfl_stacktrace_pic_a_SOURCES = am_libdwfl_stacktrace_pic_a_OBJECTS = $(libdwfl_stacktrace_a_SOURCES:.c=.os) -noinst_HEADERS = libdwfl_stacktraceP.h dwflst_tracker_elftab.h +noinst_HEADERS = libdwfl_stacktraceP.h \ + dwflst_tracker_elftab.h dwflst_tracker_dwfltab.h EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c b/libdwfl_stacktrace/dwflst_process_tracker.c index 10cd9a7c..f72b72b0 100644 --- a/libdwfl_stacktrace/dwflst_process_tracker.c +++ b/libdwfl_stacktrace/dwflst_process_tracker.c @@ -45,6 +45,8 @@ Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks *callbacks) dwflst_tracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE); rwlock_init (tracker->elftab_lock); + dwflst_tracker_dwfltab_init (&tracker->dwfltab, HTAB_DEFAULT_SIZE); + rwlock_init (tracker->dwfltab_lock); tracker->callbacks = callbacks; return tracker; @@ -58,19 +60,84 @@ Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) /* TODO: Could also share dwfl->debuginfod, but thread-safely? */ dwfl->tracker = tracker; + + /* XXX: dwfl added to dwfltab when dwfl->process set in dwfl_attach_state. */ + /* XXX: dwfl removed from dwfltab in dwfl_end() */ + return dwfl; } + +void +internal_function +__libdwfl_stacktrace_add_dwfl_to_tracker (Dwfl *dwfl) { + Dwflst_Process_Tracker *tracker = dwfl->tracker; + assert (tracker != NULL); + + /* First try to find an existing entry to replace: */ + dwflst_tracker_dwfl_info *ent = NULL; + unsigned long int hval = dwfl->process->pid; + + rwlock_wrlock (tracker->dwfltab_lock); + ent = dwflst_tracker_dwfltab_find(&tracker->dwfltab, hval); + if (ent != NULL) + { + /* TODO: This is a bare-minimum solution. Ideally + we would clean up the existing ent->dwfl, but + this needs to be coordinated with any users of + the dwfl library that might still be holding it. */ + ent->dwfl = dwfl; + ent->invalid = false; + rwlock_unlock (tracker->dwfltab_lock); + return; + } + + /* Only otherwise try to insert an entry: */ + ent = calloc (1, sizeof(dwflst_tracker_dwfl_info)); + ent->dwfl = dwfl; + ent->invalid = false; + if (dwflst_tracker_dwfltab_insert(&tracker->dwfltab, hval, ent) != 0) + { + free(ent); + rwlock_unlock (tracker->dwfltab_lock); + assert(false); /* Should not occur due to the wrlock on dwfltab. */ + } + rwlock_unlock (tracker->dwfltab_lock); +} + +void +internal_function +__libdwfl_stacktrace_remove_dwfl_from_tracker (Dwfl *dwfl) { + if (dwfl->tracker == NULL) + return; + Dwflst_Process_Tracker *tracker = dwfl->tracker; + dwflst_tracker_dwfl_info *ent = NULL; + if (dwfl->process == NULL) + return; + unsigned long int hval = dwfl->process->pid; + + rwlock_wrlock (tracker->dwfltab_lock); + ent = dwflst_tracker_dwfltab_find(&tracker->dwfltab, hval); + if (ent != NULL && ent->dwfl == dwfl) + { + ent->dwfl = NULL; + ent->invalid = true; + } + rwlock_unlock (tracker->dwfltab_lock); +} + void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) { if (tracker == NULL) return; + size_t idx; + /* HACK to allow iteration of dynamicsizehash_concurrent. */ /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */ rwlock_fini (tracker->elftab_lock); pthread_rwlock_destroy(&tracker->elftab.resize_rwl); - for (size_t idx = 1; idx <= tracker->elftab.size; idx++) + for (idx = 1; idx <= tracker->elftab.size; idx++) { dwflst_tracker_elftab_ent *ent = &tracker->elftab.table[idx]; if (ent->hashval == 0) @@ -87,6 +154,22 @@ void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) } free (tracker->elftab.table); - /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */ + /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */ + rwlock_fini (tracker->dwfltab_lock); + pthread_rwlock_destroy(&tracker->dwfltab.resize_rwl); + for (idx = 1; idx <= tracker->dwfltab.size; idx++) + { + dwflst_tracker_dwfltab_ent *ent = &tracker->dwfltab.table[idx]; + if (ent->hashval == 0) + continue; + dwflst_tracker_dwfl_info *t = + (dwflst_tracker_dwfl_info *) atomic_load_explicit (&ent->val_ptr, + memory_order_relaxed); + if (t->dwfl != NULL) + INTUSE(dwfl_end) (t->dwfl); + free(t); + } + free (tracker->dwfltab.table); + free (tracker); } diff --git a/libdwfl_stacktrace/dwflst_tracker_dwfltab.c b/libdwfl_stacktrace/dwflst_tracker_dwfltab.c new file mode 100644 index 00000000..f4749e29 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_dwfltab.c @@ -0,0 +1,46 @@ +/* Dwflst_Process_Tracker Dwfl table implementation. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* Definitions for the Dwfl table. */ +#define TYPE dwflst_tracker_dwfl_info * +#define NAME dwflst_tracker_dwfltab +#define ITERATE 1 +/* TODO(REVIEW): Omit reverse? */ +#define REVERSE 1 +#define COMPARE(a, b) \ + ((a->invalid && b->invalid) || \ + (!a->invalid && !b->invalid && \ + (a)->dwfl->process->pid == (b)->dwfl->process->pid)) + +#include "../lib/dynamicsizehash_concurrent.c" diff --git a/libdwfl_stacktrace/dwflst_tracker_dwfltab.h b/libdwfl_stacktrace/dwflst_tracker_dwfltab.h new file mode 100644 index 00000000..fd687e61 --- /dev/null +++ b/libdwfl_stacktrace/dwflst_tracker_dwfltab.h @@ -0,0 +1,42 @@ +/* Dwflst_Process_Tracker Dwfl table. + Copyright (C) 2025 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef DWFLST_TRACKER_DWFLTAB_H +#define DWFLST_TRACKER_DWFLTAB_H 1 + +/* Definitions for the Dwfl table. */ +#define TYPE dwflst_tracker_dwfl_info * +#define NAME dwflst_tracker_dwfltab +#define ITERATE 1 +#define COMPARE(a, b) \ + ((a->invalid && b->invalid) || \ + (!a->invalid && !b->invalid && \ + (a)->dwfl->process->pid == (b)->dwfl->process->pid)) +#include + +#endif diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h index e457d35a..0bfc9c83 100644 --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h @@ -45,6 +45,14 @@ typedef struct } dwflst_tracker_elf_info; #include "dwflst_tracker_elftab.h" +/* Hash table for Dwfl *. */ +typedef struct +{ + Dwfl *dwfl; + bool invalid; /* Mark when the dwfl has been removed. */ +} dwflst_tracker_dwfl_info; +#include "dwflst_tracker_dwfltab.h" + struct Dwflst_Process_Tracker { const Dwfl_Callbacks *callbacks; @@ -52,9 +60,24 @@ struct Dwflst_Process_Tracker /* Table of cached Elf * including fd, path, fstat info. */ dwflst_tracker_elftab elftab; rwlock_define(, elftab_lock); + + /* Table of cached Dwfl * including pid. */ + dwflst_tracker_dwfltab dwfltab; + rwlock_define(, dwfltab_lock); }; +/* Called when dwfl->process->pid becomes known to add the dwfl to its + Dwflst_Process_Tracker's dwfltab: */ +extern void __libdwfl_stacktrace_add_dwfl_to_tracker (Dwfl *dwfl) + internal_function; + +/* Called from dwfl_end() to remove the dwfl from its + Dwfl_Process_Tracker's dwfltab: */ +extern void __libdwfl_stacktrace_remove_dwfl_from_tracker (Dwfl *dwfl) + internal_function; + + /* Avoid PLT entries. */ INTDECL (dwflst_module_gettracker) INTDECL (dwflst_tracker_find_cached_elf) From patchwork Thu Apr 24 21:47:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110966 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B7F193858D21 for ; Thu, 24 Apr 2025 21:50:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B7F193858D21 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=AAly14Y7; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=TR6Rnpkg X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id 766553858D35 for ; Thu, 24 Apr 2025 21:48:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 766553858D35 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 766553858D35 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531329; cv=none; b=pkwCYZLneHECQTZJDGNLe4S7KWEsVv32NtP8ndkdr0+XVRH8KfGV3dKTLbHCR1uhRplhP2Kgahl5ZIPfFyWl8LJSLRAdkIFj3IiQVPo0FUosF7oU6vkx1FtgAFAistu06dzdYdJp6xAppm955YZndx0ug3SAKaCrkPKGhioupy8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531329; c=relaxed/simple; bh=QJANPKDSbTfZEqjaCQm4cpxfGXRL/TUxFLOrE8UyZxU=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=uoxUyzGkw2EU/lhLhVeZ8n8q0VIapf87kbLCcgUEibdOVtmg9nXvBa2uuzNMQXNskVwGIALmtAIQ+4viOJ5ABGVGi6urXN3YH63kY/NqbLOxdaDcqgn7rq/+zMKlNfQW2Vb/a+QX0vz9WaTOTP7PUNucerNA55DxQ9OPM+q39Zk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 766553858D35 Received: from phl-compute-12.internal (phl-compute-12.phl.internal [10.202.2.52]) by mailfhigh.stl.internal (Postfix) with ESMTP id 1BA25254023C; Thu, 24 Apr 2025 17:48:49 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-12.internal (MEProxy); Thu, 24 Apr 2025 17:48:49 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531328; x= 1745617728; bh=+s2QhLtIiEUMcGn6vw9fMRNXOVdQjsKOMkSsk4wgyic=; b=A Aly14Y7T6u0H3eHsptI6RjAfGpuOt883n8xCx3rGwRXJcIuxHd1Haapm9erlycpJ pNRZLvR/4ZaG1e6iPanbO9jZE3QPPgAUHP+99yNEOnWuqcJHJs6ZnLKUyIUVYs2j 5lTZH0lmWXQABtX/7LZuJqg5CNziIBy4cDz/p97hsLYsYlabtxe1YWQzyo+swuS0 SBL745VvGUvDRki4rlfMc3/Ccb6efP3/G+42tnyFoRREqR7aLZ2a3ziiJnzSbMRr nrWXeeadHsM15TTMrhXN2BO+jilO7+ooQosWLzAQd4IqnTGOa0NyYeKyv3WeKDwb E91b+fXQyoICLePtaszig== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531328; x=1745617728; bh=+ s2QhLtIiEUMcGn6vw9fMRNXOVdQjsKOMkSsk4wgyic=; b=TR6RnpkgOO/WWJJew K4E3fW7ZONY7H+SlUNYWvT/qXxNWdZpeAgwnzFwlYowwr4CYILsbkMufE48iy2EP 0TtaanDpcAB/R5aR/UuBXSmaqmk84OD755UYlVFXKRq9ntUIAmceG6dVC1m4NeeY 3GCWEmfdT4Ma4sXV3Z7xttAAiP+vGQxCKI/tpW9lf4ta3zfd86OWP5RHqnEBDLtb 9MIQ/Pq13BZYYsDLbYkvTWHneOY9CxJgGTMCEVLMPMCocTU0NSrtJQ85eSPiMoCr 5vcoPrgM4GUD10P5kP13Va23RJEzA1QL3oN97Y77NAu5JhPt80Typw7kki08sjkw FO+PA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd euudfftdehgfetheehlefghefffeegieekheefgfdufefgudeifefhvefgudeknecuvehl uhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshgvrhhhvghise hsvghrhhgvihdrihhopdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdp rhgtphhtthhopegvlhhfuhhtihhlshdquggvvhgvlhesshhouhhrtggvfigrrhgvrdhorh hgpdhrtghpthhtohepshgvrhhhvghisehsvghrhhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:48 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 09/12] libdwfl_stacktrace [9/12]: add dwflst_tracker_find_pid Date: Thu, 24 Apr 2025 17:47:12 -0400 Message-ID: <20250424214715.306147-10-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Separate out libdwfl_stacktrace, as requested. Changes for v2: - Add locking for dwfltab. * * * New function that retrieves the Dwfl for a particular PID, or, if the Dwfl is absent, creates it via a provided callback and adds it to the table later, when the PID is confirmed via dwfl_attach_state. * libdwfl_stacktrace/libdwfl_stacktrace.h (dwflst_tracker_find_pid): New function. * libdwfl_stacktrace/dwfl_process_tracker.c (dwflst_tracker_find_pid): New function; find a Dwfl in the dwfltab or create one using the provided callback. The newly created Dwfl will be added to the dwfltab automatically when its pid is confirmed by a call to dwfl_attach_state. * libdw/libdw.map: Add dwflst_tracker_find_pid. --- libdw/libdw.map | 1 + libdwfl_stacktrace/dwflst_process_tracker.c | 26 +++++++++++++++++++++ libdwfl_stacktrace/libdwfl_stacktrace.h | 11 +++++++++ 3 files changed, 38 insertions(+) diff --git a/libdw/libdw.map b/libdw/libdw.map index 46d0878a..688e415c 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -404,4 +404,5 @@ ELFUTILS_0.193_EXPERIMENTAL { dwflst_tracker_cache_elf; dwflst_module_gettracker; dwflst_tracker_linux_proc_find_elf; + dwflst_tracker_find_pid; }; diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c b/libdwfl_stacktrace/dwflst_process_tracker.c index f72b72b0..fc019b23 100644 --- a/libdwfl_stacktrace/dwflst_process_tracker.c +++ b/libdwfl_stacktrace/dwflst_process_tracker.c @@ -67,6 +67,32 @@ Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) return dwfl; } +Dwfl *dwflst_tracker_find_pid (Dwflst_Process_Tracker *tracker, + pid_t pid, + Dwfl *(*callback) (Dwflst_Process_Tracker *, + pid_t, void *), + void *arg) +{ + Dwfl *dwfl = NULL; + + rwlock_rdlock (tracker->dwfltab_lock); + dwflst_tracker_dwfl_info *ent + = dwflst_tracker_dwfltab_find(&tracker->dwfltab, pid); + rwlock_unlock (tracker->dwfltab_lock); + + if (ent != NULL && !ent->invalid) + dwfl = ent->dwfl; + if (dwfl == NULL && callback != NULL) + dwfl = callback(tracker, pid, arg); + if (dwfl != NULL) + { + assert (dwfl->tracker == tracker); + /* XXX: dwfl added to dwfltab when dwfl->process set in dwfl_attach_state. + Prior to that, the pid is not confirmed. */ + } + + return dwfl; +} void internal_function diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h index d29dc640..ed6a6a5c 100644 --- a/libdwfl_stacktrace/libdwfl_stacktrace.h +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -79,6 +79,17 @@ extern bool dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, Elf *elf, int fd) __nonnull_attribute__ (1, 2); +/* Find the Dwfl corresponding to PID. If CALLBACK is non-NULL and + the Dwfl has not been created, invoke CALLBACK to create the Dwfl + and then store it in the tracker. */ +extern Dwfl *dwflst_tracker_find_pid (Dwflst_Process_Tracker *tracker, + pid_t pid, + Dwfl *(*callback) (Dwflst_Process_Tracker *tracker, + pid_t pid, + void *arg), + void *arg) + __nonnull_attribute__ (1); + /* For implementing a find_elf callback based on the prior two functions. Returns the Dwflst_Process_Tracker corresponding to MOD. */ extern Dwflst_Process_Tracker *dwflst_module_gettracker (Dwfl_Module *mod); From patchwork Thu Apr 24 21:47:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110969 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0A8CB3858D35 for ; Thu, 24 Apr 2025 21:51:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0A8CB3858D35 Authentication-Results: sourceware.org; dkim=temperror header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=mFrFc1L8; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=BIF7NeE4 X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fout-b6-smtp.messagingengine.com (fout-b6-smtp.messagingengine.com [202.12.124.149]) by sourceware.org (Postfix) with ESMTPS id A58643858D37 for ; Thu, 24 Apr 2025 21:48:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A58643858D37 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A58643858D37 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.149 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531335; cv=none; b=f3LCDQgMw5/nhKAzbADtzwWgior70TpESsblSo89fcnFPN9fQudHdkjE/ruChDGkjYcXTZxSS25hybEMZRkgoLe8JA4qqzIeaNs1kfS7jclvqBRCnTL7GaUz7YCwSa6pUTe+mY8P4/Fb4Il6jx20IgmDCQT29PPpZSdcftfxv1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531335; c=relaxed/simple; bh=u8VEBjo5VY4fWfws3/Pw7o0xsNlICzHxw6a3MriS1z8=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=bpXPOEvHLm6swzlFEBgVofWsDdkLum5Sa12/sXvVZM/nmDphA0SHYfSRPpM3m49st+hc8rqCCK+GNjs2N/fMF9hUVwpuyX8zdSXXnOCZIubtEfH6AAiOG7XNJJaYGnOsk4VnXBG5tGQUoyF4MeMIaT04sltCvQ6ZwwCYLbMSEU8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A58643858D37 Received: from phl-compute-07.internal (phl-compute-07.phl.internal [10.202.2.47]) by mailfout.stl.internal (Postfix) with ESMTP id 351C011401ED; Thu, 24 Apr 2025 17:48:55 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-07.internal (MEProxy); Thu, 24 Apr 2025 17:48:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531335; x= 1745617735; bh=0fIR9ko7AvCdSiOE3brmLQD/8LR1HtTRgBTO2CwlYKI=; b=m FrFc1L8aRsNwy2wh+VVHORY8Kxx8yXjxuahsX/oVzsljXD9mDbERDB0HFzcu5XYP 6Hqcm3OlfsY0i6yFJOwuAJz4byWMKYBBWnDsuyfBgNp2sgy+8rQaOLmbbyDlPD1Q LSVKbGlpmoj2SquyOIcepYsCyzTvoXy8lUUAzBkcYqLrP67Frj6RniapGfdf2lBd 2yrMGs/Dly+Qrs7gHumbOtCwdzjYrhkAyS6TVF78bUBgyLPnGpMsFDhwbFhzUpM/ vZW5iTOXn3ZDFt3Pw7CvYAk0S8KwAR5LCLdWexjD9ckTngIWKQc4U4zunGKqk8q5 gsZOL124m0pWf0Hwqmjfw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531335; x=1745617735; bh=0 fIR9ko7AvCdSiOE3brmLQD/8LR1HtTRgBTO2CwlYKI=; b=BIF7NeE4bbapU+Yp+ LFjUcWAaY3Z/mYpMrrIuJacCoUPFbXomA2KTxFQFoolRBrutBrFWKzM00Uxn3EGa xGtY7aE9eqlM3Wjp+f0pI6AYrHLlSoepxm/h+lo8IJSClLNiyE0Hy/QCcT2mQxHa YdMitxF81K1RauBq1bDoZz3aYOuuE+IAuasC9btbAJLB7NW1LjpYULSqWO/2B0h2 46ru9nJA+C6zX573O0In6XlHN/PjQrqBFXB5EVZMr/7BbogBGYNj9PSgDMImJiP/ dXNh9DeJOvHMdXe5A+hFuZ0WxcEdQhD8vYwQS52IggpWTk0BtEwahhdpok3FXEc7 yN4Tg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd euudfftdehgfetheehlefghefffeegieekheefgfdufefgudeifefhvefgudeknecuvehl uhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshgvrhhhvghise hsvghrhhgvihdrihhopdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdp rhgtphhtthhopegvlhhfuhhtihhlshdquggvvhgvlhesshhouhhrtggvfigrrhgvrdhorh hgpdhrtghpthhtohepshgvrhhhvghisehsvghrhhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:54 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 10/12] eu-stacktrace [10/12]: use dwflst_tracker_find_pid Date: Thu, 24 Apr 2025 17:47:13 -0400 Message-ID: <20250424214715.306147-11-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Separate out libdwfl_stacktrace, as requested. * * * Initial minimal change to ensure dwflst_tracker_find_pid is tested. For now, we keep the additional dwfltab implementation in stacktrace.c, since it's being used to track statistics. In future follow-ups, it will be good to switch to storing eu-stacktrace statistics e.g. in dwfl->process->callbacks_arg. This requires some additional design to keep the statistics from being lost when a pid is reused and the corresponding processtracker table entry is replaced. * src/stacktrace.c (sysprof_init_dwfl): New function. (sysprof_find_dwfl): Rename the existing sysprof_init_dwfl. Also use dwflst_tracker_find_pid with callback. (sysprof_unwind_cb): Rename the existing sysprof_init_dwfl. --- src/stacktrace.c | 63 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/src/stacktrace.c b/src/stacktrace.c index c0c9929d..dd58ef3b 100644 --- a/src/stacktrace.c +++ b/src/stacktrace.c @@ -96,7 +96,7 @@ #include ELFUTILS_HEADER(ebl) /* #include ELFUTILS_HEADER(dwfl) */ #include "../libdwfl/libdwflP.h" -/* XXX: Private header needed for find_procfile, sysprof_init_dwfl, +/* XXX: Private header needed for find_procfile, sysprof_find_dwfl, sample_set_initial_registers. */ #include ELFUTILS_HEADER(dwfl_stacktrace) @@ -1014,7 +1014,34 @@ find_procfile (Dwfl *dwfl, pid_t *pid, Elf **elf, int *elf_fd) } Dwfl * -sysprof_init_dwfl (struct sysprof_unwind_info *sui, +sysprof_init_dwfl (Dwflst_Process_Tracker *cb_tracker, + pid_t pid, + void *arg __attribute__ ((unused))) +{ + Dwfl *dwfl = dwflst_tracker_dwfl_begin (cb_tracker); + + int err = dwfl_linux_proc_report (dwfl, pid); + if (err < 0) + { + if (show_failures) + fprintf(stderr, "dwfl_linux_proc_report pid %lld: %s", + (long long) pid, dwfl_errmsg (-1)); + return NULL; + } + err = dwfl_report_end (dwfl, NULL, NULL); + if (err != 0) + { + if (show_failures) + fprintf(stderr, "dwfl_report_end pid %lld: %s", + (long long) pid, dwfl_errmsg (-1)); + return NULL; + } + + return dwfl; +} + +Dwfl * +sysprof_find_dwfl (struct sysprof_unwind_info *sui, SysprofCaptureStackUser *ev, SysprofCaptureUserRegs *regs) { @@ -1027,42 +1054,24 @@ sysprof_init_dwfl (struct sysprof_unwind_info *sui, if (regs->n_regs < EXPECTED_REGS) /* XXX expecting everything except FLAGS */ { if (show_failures) - fprintf(stderr, N_("sysprof_init_dwfl: n_regs=%d, expected %d\n"), + fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %d\n"), regs->n_regs, EXPECTED_REGS); return NULL; } - Dwfl *dwfl = pid_find_dwfl(pid); + Dwfl *dwfl = dwflst_tracker_find_pid (tracker, pid, sysprof_init_dwfl, NULL); struct __sample_arg *sample_arg; bool cached = false; - if (dwfl != NULL) + if (dwfl != NULL && dwfl->process != NULL) { sample_arg = dwfl->process->callbacks_arg; /* XXX requires libdwflP.h */ cached = true; goto reuse; } - dwfl = dwflst_tracker_dwfl_begin (tracker); - - int err = dwfl_linux_proc_report (dwfl, pid); - if (err < 0) - { - if (show_failures) - fprintf(stderr, "dwfl_linux_proc_report pid %lld: %s", - (long long) pid, dwfl_errmsg (-1)); - return NULL; - } - err = dwfl_report_end (dwfl, NULL, NULL); - if (err != 0) - { - if (show_failures) - fprintf(stderr, "dwfl_report_end pid %lld: %s", - (long long) pid, dwfl_errmsg (-1)); - return NULL; - } Elf *elf = NULL; int elf_fd = -1; - err = find_procfile (dwfl, &pid, &elf, &elf_fd); + int err = find_procfile (dwfl, &pid, &elf, &elf_fd); if (err < 0) { if (show_failures) @@ -1099,7 +1108,7 @@ sysprof_init_dwfl (struct sysprof_unwind_info *sui, if (show_frames) { bool is_abi32 = (sample_arg->abi == PERF_SAMPLE_REGS_ABI_32); - fprintf(stderr, "sysprof_init_dwfl pid %lld%s: size=%ld%s pc=%lx sp=%lx+(%lx)\n", + fprintf(stderr, "sysprof_find_dwfl pid %lld%s: size=%ld%s pc=%lx sp=%lx+(%lx)\n", (long long) pid, cached ? " (cached)" : "", sample_arg->size, is_abi32 ? " (32-bit)" : "", sample_arg->pc, sample_arg->base_addr, @@ -1260,7 +1269,7 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg) SysprofCaptureUserRegs *regs = (SysprofCaptureUserRegs *)tail_ptr; if (show_frames) fprintf(stderr, "\n"); /* extra newline for padding */ - Dwfl *dwfl = sysprof_init_dwfl (sui, ev, regs); + Dwfl *dwfl = sysprof_find_dwfl (sui, ev, regs); if (dwfl == NULL) { if (show_summary) @@ -1270,7 +1279,7 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg) dwfl_ent->lost_samples++; } if (show_failures) - fprintf(stderr, "sysprof_init_dwfl pid %lld (%s) (failed)\n", + fprintf(stderr, "sysprof_find_dwfl pid %lld (%s) (failed)\n", (long long)frame->pid, comm); return SYSPROF_CB_OK; } From patchwork Thu Apr 24 21:47:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110973 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5B1973858D39 for ; Thu, 24 Apr 2025 21:54:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5B1973858D39 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=W9krbEoH; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=ON4VfV9N X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) by sourceware.org (Postfix) with ESMTPS id 51AF83858D21 for ; Thu, 24 Apr 2025 21:48:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 51AF83858D21 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 51AF83858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.156 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531339; cv=none; b=ScHJIu5S/Hm54XmbxxznNOXwVxLKC51gSAPkyCiaYzj5CQ5sVt4eC2UmtbUu0N2CNlVN0EREaeRAp7KPxINNMSPZNTOUk7tEUNmbTs1FuV9dmBwyCknVs5tcdCd+pGZXo2ODqw9HUmfWu1VZ33kPUhXgNBz5d16y/JBi0XKdrrc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531339; c=relaxed/simple; bh=6+LjSbE0T0kmABEd+/FH9XY/tBKWZ8Qrr6v3mToqAFU=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=BRAxSPHYHazbdDFYe0sVlCSFZoDRMT0wSFDyc2hmIP958GPgPWx2VOptbnqhRF9cifNBNTFq3liVMAqYw5KY1XlaNy1+UlGRS9WkgmgUnU5bE9oB7GcdvlT88rT8q55QfTLsHYPtYt5ES/ExzLmrlvu0GhlxUR1lgDOci+n59po= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 51AF83858D21 Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfhigh.stl.internal (Postfix) with ESMTP id EBF15254023F; Thu, 24 Apr 2025 17:48:58 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-01.internal (MEProxy); Thu, 24 Apr 2025 17:48:59 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531338; x= 1745617738; bh=NSeHL6Dw5ztXgO8OoodrB0RQjN1Q76gPaIeHYKV57T8=; b=W 9krbEoHL/4zFGDCjvF6pOyko/8PWH9kMRDB/cgDda6dD86nFtfqxffy17key8zcZ bAuEyuNfJP6wAhzwcBkOKrH41a1pINAZE6yqCZSacI7IyfSdFwb/WjWjejwNapi/ 7IZhztSJuDXdt3s8KtLZKL0/IOB/Y7MeTnaYnuFmIcyCtnP98ow5vMQv3XtxuScX aGA1XGH1I78CtBreOUuj7+20ItqAXQmHOsqdjDGCHulG1cTIJX1va2TRixPahEUT 0xBw3sJnV9AuuGyqFKVES5Mb1HhlW/+x/TfQHatYbAbX/+dC3QmN7VqP+Es/rpGj r6nO97KmqVCyHXMJCyR3g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531338; x=1745617738; bh=N SeHL6Dw5ztXgO8OoodrB0RQjN1Q76gPaIeHYKV57T8=; b=ON4VfV9NKYlKLCA3D erOj4Q5+aFFwkoR7tviELOCwGJ9fkWEUcbKN9767KGmW25uyJGfKGyVHcf7hjSFR AxGk7vL6wO9fAoGK5G7DOwaX7zfgE3ct6OZU4q3l8QVsQihVtVjzAeCCWYPN0MwR 2yvYitKlg+mzQ65dzKZh+cBCDRUU8MNILbQD2JvitAcpSz31niGkGUZbhdU59M0I ukouR2D8xJ/UBRkSUQztGa6DfLuWb5a+QGmFEz0rpec6bsoaECSA773hYT0G4ju1 QFDjPyAa/29cAjhZhV6mmgBUOW7WLtWFfrE/uPLjNCHrYLPSvW6RpKbLKp0GL6IC 3IRLQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnheptd euudfftdehgfetheehlefghefffeegieekheefgfdufefgudeifefhvefgudeknecuvehl uhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshgvrhhhvghise hsvghrhhgvihdrihhopdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdp rhgtphhtthhopegvlhhfuhhtihhlshdquggvvhgvlhesshhouhhrtggvfigrrhgvrdhorh hgpdhrtghpthhtohepshgvrhhhvghisehsvghrhhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:48:58 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 11/12] libdwfl_stacktrace [11/12]: add dwflst_perf_sample_getframes Date: Thu, 24 Apr 2025 17:47:14 -0400 Message-ID: <20250424214715.306147-12-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Separate out libdwfl_stacktrace, as requested. Changes for v3: - use const void *stack, not void *, to allow users to pass a const stack sample Changes for v2: - use renamed __libdwfl_set_initial_registers_thread - oops, should use provided sample_arg->perf_regs_mask in sample_set_initial registers * * * This is a new interface for unwinding that doesn't require the Dwfl to be attached to a live process (via ptrace) or via corefile. Instead, data from a perf_events stack sample is provided along with an Elf struct used to identify the architecture. Based on code from eu-stacktrace. * libdwfl_stacktrace/libdwfl_stacktrace.h (dwflst_perf_sample_getframes): New function. * libdwfl_stacktrace/dwflst_perf_frame.c (struct __libdwfl_stacktrace_perf_sample_info): New struct, based on src/stacktrace.c struct sample_arg. (sample_next_thread): New function, based on src/stacktrace.c. (sample_getthread): Ditto. (copy_word_64): New macro, based on src/stacktrace.c. (copy_word_32): Ditto. (copy_word): Ditto. (elf_memory_read): New function, based on src/stacktrace.c. (sample_memory_read): Ditto. (sample_set_initial_registers): Ditto. (sample_detach): Ditto. (sample_thread_callbacks): New struct, set of callbacks based on src/stacktrace.c sample_thread_callbacks. (dwflst_perf_sample_getframes): New function, based on parts of src/stacktrace.c sysprof_find_dwfl. If the Dwfl is not attached, attaches it with sample_thread_callbacks and __libdwfl_stacktrace_perf_sample_info. Populates the __libdwfl_stacktrace_perf_sample_info with data from the stack sample and calls dwfl_getthread_frames to unwind it using the sample_thread_callbacks. * libdw/libdw.map (ELFUTILS_0.193): Add dwflst_perf_sample_getframes. --- libdw/libdw.map | 1 + libdwfl_stacktrace/dwflst_perf_frame.c | 193 +++++++++++++++++++++++- libdwfl_stacktrace/libdwfl_stacktrace.h | 15 +- 3 files changed, 207 insertions(+), 2 deletions(-) diff --git a/libdw/libdw.map b/libdw/libdw.map index 688e415c..137b5738 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -405,4 +405,5 @@ ELFUTILS_0.193_EXPERIMENTAL { dwflst_module_gettracker; dwflst_tracker_linux_proc_find_elf; dwflst_tracker_find_pid; + dwflst_perf_sample_getframes; }; diff --git a/libdwfl_stacktrace/dwflst_perf_frame.c b/libdwfl_stacktrace/dwflst_perf_frame.c index 79e8e482..591097e8 100644 --- a/libdwfl_stacktrace/dwflst_perf_frame.c +++ b/libdwfl_stacktrace/dwflst_perf_frame.c @@ -60,4 +60,195 @@ uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine) return 0; } -/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ +struct __libdwfl_stacktrace_perf_sample_info { + pid_t pid; + pid_t tid; + Dwarf_Addr base_addr; + const uint8_t *stack; + size_t stack_size; + const Dwarf_Word *regs; + uint n_regs; + uint64_t perf_regs_mask; + uint abi; + Dwarf_Addr pc; +}; + +/* The next few functions imitate the corefile interface for a single + stack sample, with very restricted access to registers and memory. */ + +/* Just yield the single thread id matching the sample. */ +static pid_t +sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg, + void **thread_argp) +{ + struct __libdwfl_stacktrace_perf_sample_info *sample_arg = + (struct __libdwfl_stacktrace_perf_sample_info *)dwfl_arg; + if (*thread_argp == NULL) + { + *thread_argp = (void *)0xea7b3375; + return sample_arg->tid; + } + else + return 0; +} + +/* Just check that the thread id matches the sample. */ +static bool +sample_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid, + void *dwfl_arg, void **thread_argp) +{ + struct __libdwfl_stacktrace_perf_sample_info *sample_arg = + (struct __libdwfl_stacktrace_perf_sample_info *)dwfl_arg; + *thread_argp = (void *)sample_arg; + if (sample_arg->tid != tid) + { + __libdwfl_seterrno(DWFL_E_INVALID_ARGUMENT); + return false; + } + return true; +} + +#define copy_word_64(result, d) \ + if ((((uintptr_t) (d)) & (sizeof (uint64_t) - 1)) == 0) \ + *(result) = *(uint64_t *)(d); \ + else \ + memcpy ((result), (d), sizeof (uint64_t)); + +#define copy_word_32(result, d) \ + if ((((uintptr_t) (d)) & (sizeof (uint32_t) - 1)) == 0) \ + *(result) = *(uint32_t *)(d); \ + else \ + memcpy ((result), (d), sizeof (uint32_t)); + +#define copy_word(result, d, abi) \ + if ((abi) == PERF_SAMPLE_REGS_ABI_64) \ + { copy_word_64((result), (d)); } \ + else if ((abi) == PERF_SAMPLE_REGS_ABI_32) \ + { copy_word_32((result), (d)); } \ + else \ + *(result) = 0; + +static bool +elf_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) +{ + struct __libdwfl_stacktrace_perf_sample_info *sample_arg = + (struct __libdwfl_stacktrace_perf_sample_info *)arg; + Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, addr); + Dwarf_Addr bias; + Elf_Scn *section = INTUSE(dwfl_module_address_section) (mod, &addr, &bias); + + if (!section) + { + __libdwfl_seterrno(DWFL_E_ADDR_OUTOFRANGE); + return false; + } + + Elf_Data *data = elf_getdata(section, NULL); + if (data && data->d_buf && data->d_size > addr) { + uint8_t *d = ((uint8_t *)data->d_buf) + addr; + copy_word(result, d, sample_arg->abi); + return true; + } + __libdwfl_seterrno(DWFL_E_ADDR_OUTOFRANGE); + return false; +} + +static bool +sample_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) +{ + struct __libdwfl_stacktrace_perf_sample_info *sample_arg = + (struct __libdwfl_stacktrace_perf_sample_info *)arg; + /* Imitate read_cached_memory() with the stack sample data as the cache. */ + if (addr < sample_arg->base_addr || + addr - sample_arg->base_addr >= sample_arg->stack_size) + return elf_memory_read(dwfl, addr, result, arg); + const uint8_t *d = &sample_arg->stack[addr - sample_arg->base_addr]; + copy_word(result, d, sample_arg->abi); + return true; +} + +static bool +sample_set_initial_registers (Dwfl_Thread *thread, void *arg) +{ + struct __libdwfl_stacktrace_perf_sample_info *sample_arg = + (struct __libdwfl_stacktrace_perf_sample_info *)arg; + INTUSE(dwfl_thread_state_register_pc) (thread, sample_arg->pc); + Dwfl_Process *process = thread->process; + Ebl *ebl = process->ebl; + return ebl_set_initial_registers_sample + (ebl, sample_arg->regs, sample_arg->n_regs, + sample_arg->perf_regs_mask, sample_arg->abi, + __libdwfl_set_initial_registers_thread, thread); +} + +static void +sample_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg) +{ + struct __libdwfl_stacktrace_perf_sample_info *sample_arg = + (struct __libdwfl_stacktrace_perf_sample_info *)dwfl_arg; + free (sample_arg); +} + +static const Dwfl_Thread_Callbacks sample_thread_callbacks = + { + sample_next_thread, + sample_getthread, + sample_memory_read, + sample_set_initial_registers, + sample_detach, + NULL, /* sample_thread_detach */ + }; + +int +dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf, + pid_t pid, pid_t tid, + const void *stack, size_t stack_size, + const Dwarf_Word *regs, uint n_regs, + uint64_t perf_regs_mask, uint abi, + int (*callback) (Dwfl_Frame *state, void *arg), + void *arg) +{ + /* TODO: Lock the dwfl to ensure attach_state does not interfere + with other dwfl_perf_sample_getframes calls. */ + + struct __libdwfl_stacktrace_perf_sample_info *sample_arg; + bool attached = false; + if (dwfl->process != NULL) + { + sample_arg = dwfl->process->callbacks_arg; + attached = true; + } + else + { + sample_arg = malloc (sizeof *sample_arg); + if (sample_arg == NULL) + { + __libdwfl_seterrno(DWFL_E_NOMEM); + return -1; + } + } + + sample_arg->pid = pid; + sample_arg->tid = tid; + sample_arg->stack = (const uint8_t *)stack; + sample_arg->stack_size = stack_size; + sample_arg->regs = regs; + sample_arg->n_regs = n_regs; + sample_arg->perf_regs_mask = perf_regs_mask; + sample_arg->abi = abi; + + if (! attached + && ! INTUSE(dwfl_attach_state) (dwfl, elf, pid, + &sample_thread_callbacks, sample_arg)) + return -1; + + /* Now that Dwfl is attached, we can access its Ebl: */ + Dwfl_Process *process = dwfl->process; + Ebl *ebl = process->ebl; + sample_arg->base_addr = ebl_sample_base_addr(ebl, regs, n_regs, + perf_regs_mask, abi); + sample_arg->pc = ebl_sample_pc(ebl, regs, n_regs, + perf_regs_mask, abi); + + return INTUSE(dwfl_getthread_frames) (dwfl, tid, callback, arg); +} diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h index ed6a6a5c..ed0b612f 100644 --- a/libdwfl_stacktrace/libdwfl_stacktrace.h +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -107,7 +107,20 @@ extern int dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, void **userdata char **file_name, Elf **); -/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ +/* Like dwfl_thread_getframes, but iterates through the frames for a + linux perf_events stack sample rather than a live thread. Calls + dwfl_attach_state on DWFL, with architecture specified by ELF, ELF + must remain valid during Dwfl lifetime. 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 set dwfl_errno (). */ +int dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf, pid_t pid, pid_t tid, + const void *stack, size_t stack_size, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t perf_regs_mask, uint32_t abi, + int (*callback) (Dwfl_Frame *state, void *arg), + void *arg) + __nonnull_attribute__ (1, 5, 7, 11); /* Returns the linux perf_events register mask describing a set of registers sufficient for unwinding on MACHINE, or 0 if libdwfl does From patchwork Thu Apr 24 21:47:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serhei Makarov X-Patchwork-Id: 110972 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 94D5C3858D29 for ; Thu, 24 Apr 2025 21:53:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 94D5C3858D29 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=serhei.io header.i=@serhei.io header.a=rsa-sha256 header.s=fm3 header.b=rgX3Edwy; dkim=pass (2048-bit key, unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.a=rsa-sha256 header.s=fm3 header.b=R3hpk7rj X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from fout-b6-smtp.messagingengine.com (fout-b6-smtp.messagingengine.com [202.12.124.149]) by sourceware.org (Postfix) with ESMTPS id CB8533858D21 for ; Thu, 24 Apr 2025 21:49:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CB8533858D21 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=serhei.io Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=serhei.io ARC-Filter: OpenARC Filter v1.0.0 sourceware.org CB8533858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=202.12.124.149 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531349; cv=none; b=TFcVFGmvkfvlxzojDikbjRzTTuETb4WkYNbuly1Tuo2OA1l7h5Lo7C53o6GvFt1pTt+Q50C8wWt2Q/d+mHeH7dlbz2+Dxs67bonF019UqM5Ua3zrNQbxkZNyE0/gURppuywHeUi9CV8IAORg1y3/SqXT5Wjuyb9zJNmYNMCX86E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1745531349; c=relaxed/simple; bh=fhBIJMfFgE9jLYPyEIaQRBPATIKkC+Mfv925+r4qog4=; h=DKIM-Signature:DKIM-Signature:From:To:Subject:Date:Message-ID: MIME-Version; b=Kt9szHDzeF00GFo6klNiKfGIDSJHvEVC9eYOcyzIda684NoJ2DoprLVdDsH7r2B9u52E4iaWV41RRSXRaZUzDTJ+uIoTOUlxIF7vw/X7inDG79nFaswNUvQB9QKfW+Ls8milUDLsJJImjvf1gFAPSCcqsekJwUVeqtglKUUuTZI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CB8533858D21 Received: from phl-compute-07.internal (phl-compute-07.phl.internal [10.202.2.47]) by mailfout.stl.internal (Postfix) with ESMTP id 709D71140209; Thu, 24 Apr 2025 17:49:09 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-07.internal (MEProxy); Thu, 24 Apr 2025 17:49:09 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=serhei.io; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1745531349; x= 1745617749; bh=+rwLJE1pLHwLn/CNfZrtCFiAvWyJOeY3KPBpilR0Z5Y=; b=r gX3EdwyRhYcgKgq05cyUA0pzd1T2u27ScsYRrgTJ0jWrUOMyooXLeZnPwYTy33OM gbZUi1aoKWXrYA5d7tcZhZgZB4N2T5xP8904y8p2FqOkQ4WMpt40wPLJge0JbLCo U40RvA1sAI8bqyAhKSXV2t/ssy6/pZMdzdxBByo7R3GthgEzu0tjk3X9nTnTaf18 DLd6h+vvmR629TKmC64KDWnBkWuwru/r9BfiVrL58ux3Hjw8dHyVHSu0Ck1n+Z1G kIbfHfe+jUC4xZPaJKzSuiKhgporrH+wApa/c3ZkmR8QespJay2bSCa2TwlezLbz wGiYodzubmkI2VA6CC16g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1745531349; x=1745617749; bh=+ rwLJE1pLHwLn/CNfZrtCFiAvWyJOeY3KPBpilR0Z5Y=; b=R3hpk7rj2Lw9fAgSb wVYEMlPFfarPnqYVD+gKRQPuXG2jeOOG8Y2AF1CcbyNZDvycUVpHys6jMohdI4lx 7aIZ03AOPpdtgDlk727NcfePleYqUy0TNU4CHFlbblhu880kR4wzmK0ZI2vUVGGK 1CRjbGAqqdyXLtsRN+vAHG20OCT3yIl7v34Q7P/uPrGeD5vYESx+YRcdIY6k7iv0 91mDiFABKYQ4RV5+DF/fIjh1sDmsWspmgyqMzZLsygW5L1Z+RCSkG7/VhqGkn7Ei MxMEw0WQz7fT5tErepLhmOOIKZNemxv7LXcVSSvkUMIi8uXi5FMm/C7qIll5lS0k Dw4+g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvhedtheelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefuvghrhhgvihcuofgrkhgr rhhovhcuoehsvghrhhgvihesshgvrhhhvghirdhioheqnecuggftrfgrthhtvghrnhepue dufedvudfhheetkeeilefhgeeuleefteeukefgkeejveefjeetkeevfeegtdeinecuffho mhgrihhnpehqthdqphhrohhjvggtthdrohhrghdpshhouhhrtggvfigrrhgvrdhorhhgne cuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshgvrhhh vghisehsvghrhhgvihdrihhopdhnsggprhgtphhtthhopedvpdhmohguvgepshhmthhpoh huthdprhgtphhtthhopegvlhhfuhhtihhlshdquggvvhgvlhesshhouhhrtggvfigrrhgv rdhorhhgpdhrtghpthhtohepshgvrhhhvghisehsvghrhhgvihdrihho X-ME-Proxy: Feedback-ID: i572946fc:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 24 Apr 2025 17:49:08 -0400 (EDT) From: Serhei Makarov To: elfutils-devel@sourceware.org Cc: Serhei Makarov Subject: [PATCH v5 12/12] eu-stacktrace [12/12]: use dwflst_perf_sample_getframes Date: Thu, 24 Apr 2025 17:47:15 -0400 Message-ID: <20250424214715.306147-13-serhei@serhei.io> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250424214715.306147-1-serhei@serhei.io> References: <20250424214715.306147-1-serhei@serhei.io> MIME-Version: 1.0 X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, JMQ_SPF_NEUTRAL, PROLO_LEO2, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org Changes for v4: - Separate out libdwfl_stacktrace, as requested. Changes for v3: - Fix initialization of elf in sysprof_init_dwfl (previously triggered -Wmaybe-uninitialized). * * * Remove the code from src/stacktrace.c that is now covered by libdwfl_stacktrace/dwflst_perf_frame.c and dwflst_perf_sample_getframes. * src/stacktrace.c (show_memory_reads): Remove this verbose option as the relevant code is inside libdwfl_stacktrace now. (struct __sample_arg): Remove, handled by libdwfl_stacktrace/dwflst_perf_frame.c. (sample_next_thread): Ditto. (sample_getthread): Ditto. (copy_word_64): Ditto. (copy_word_32): Ditto. (elf_memory_read): Ditto. (sample_memory_read): Ditto. (sample_set_initial_registers): Ditto. (sample_detach): Ditto. (sample_thread_callbacks): Ditto. (sysprof_find_dwfl): Now also return the Elf* so that it can be passed to dwflst_perf_sample_getframes. Don't create sample_arg. Do record sp in sui->last_sp. Don't dwfl_attach_state, dwflst_perf_sample_getframes handles that now. (sysprof_unwind_cb): Adapt to sysprof_find_dwfl changes, now invoke dwflst_perf_sample_getframes instead of dwfl_getthread_frames. (parse_opt): Remove show_memory_reads. (main): Remove show_memory_reads. --- src/stacktrace.c | 276 ++++++----------------------------------------- 1 file changed, 30 insertions(+), 246 deletions(-) diff --git a/src/stacktrace.c b/src/stacktrace.c index dd58ef3b..43b091b7 100644 --- a/src/stacktrace.c +++ b/src/stacktrace.c @@ -96,8 +96,7 @@ #include ELFUTILS_HEADER(ebl) /* #include ELFUTILS_HEADER(dwfl) */ #include "../libdwfl/libdwflP.h" -/* XXX: Private header needed for find_procfile, sysprof_find_dwfl, - sample_set_initial_registers. */ +/* XXX: Private header needed for find_procfile. */ #include ELFUTILS_HEADER(dwfl_stacktrace) /************************************* @@ -177,11 +176,13 @@ static int processing_mode = MODE_NAIVE; static int input_format; static int output_format = FORMAT_SYSPROF; +/* XXX Used to decide regs_mask for dwflst_perf_sample_getframes. */ +Ebl *default_ebl = NULL; + /* non-printable argp options. */ #define OPT_DEBUG 0x100 /* Diagnostic options. */ -static bool show_memory_reads = false; static bool show_frames = false; static bool show_samples = false; static bool show_failures = false; @@ -191,9 +192,6 @@ static bool show_summary = true; #define ELFUTILS_STACKTRACE_VERBOSE_ENV_VAR "ELFUTILS_STACKTRACE_VERBOSE" /* Valid values that turn on diagnostics: 'true', 'verbose', 'debug', '1', '2'. */ -/* Enables detailed tracing of simulated memory reads: */ -/* #define DEBUG_MEMORY_READ */ - /* Enables even more diagnostics on modules: */ /* #define DEBUG_MODULES */ @@ -470,190 +468,6 @@ sysprof_reader_getframes (SysprofReader *reader, #endif /* HAVE_SYSPROF_HEADERS */ -/******************************************* - * Memory read interface for stack samples * - *******************************************/ - -/* TODO: elfutils (internal) libraries use libNN_set_errno and DWFL_E_WHATEVER; - this code fails silently in sample_getthread. */ - -struct __sample_arg -{ - int tid; - Dwarf_Addr base_addr; - uint64_t size; - uint8_t *data; - uint64_t n_regs; - uint64_t abi; /* PERF_SAMPLE_REGS_ABI_{32,64} */ - Dwarf_Addr pc; - Dwarf_Addr sp; - Dwarf_Addr *regs; -}; - -/* The next few functions imitate the corefile interface for a single - stack sample, with very restricted access to registers and memory. */ - -/* Just yield the single thread id matching the sample. */ -static pid_t -sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg, - void **thread_argp) -{ - struct __sample_arg *sample_arg = (struct __sample_arg *)dwfl_arg; - if (*thread_argp == NULL) - { - *thread_argp = (void *)0xea7b3375; - return sample_arg->tid; - } - else - return 0; -} - -/* Just check that the thread id matches the sample. */ -static bool -sample_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid, - void *dwfl_arg, void **thread_argp) -{ - struct __sample_arg *sample_arg = (struct __sample_arg *)dwfl_arg; - *thread_argp = (void *)sample_arg; - if (sample_arg->tid != tid) - { - return false; - } - return true; -} - -#define copy_word_64(result, d) \ - if ((((uintptr_t) (d)) & (sizeof (uint64_t) - 1)) == 0) \ - *(result) = *(uint64_t *)(d); \ - else \ - memcpy ((result), (d), sizeof (uint64_t)); - -#define copy_word_32(result, d) \ - if ((((uintptr_t) (d)) & (sizeof (uint32_t) - 1)) == 0) \ - *(result) = *(uint32_t *)(d); \ - else \ - memcpy ((result), (d), sizeof (uint32_t)); - -#define copy_word(result, d, abi) \ - if ((abi) == PERF_SAMPLE_REGS_ABI_64) \ - { copy_word_64((result), (d)); } \ - else if ((abi) == PERF_SAMPLE_REGS_ABI_32) \ - { copy_word_32((result), (d)); } \ - else \ - *(result) = 0; - -static bool -elf_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) -{ - struct __sample_arg *sample_arg = (struct __sample_arg *)arg; - Dwfl_Module *mod = dwfl_addrmodule(dwfl, addr); - Dwarf_Addr bias; - Elf_Scn *section = dwfl_module_address_section(mod, &addr, &bias); - - if (!section) - return false; - - Elf_Data *data = elf_getdata(section, NULL); - if (data && data->d_buf && data->d_size > addr) { - uint8_t *d = ((uint8_t *)data->d_buf) + addr; - copy_word(result, d, sample_arg->abi); - return true; - } - return false; -} - -static bool -sample_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) -{ - struct __sample_arg *sample_arg = (struct __sample_arg *)arg; - if (show_memory_reads) - fprintf(stderr, "* memory_read addr=%lx+(%lx) size=%lx\n", - sample_arg->base_addr, addr - sample_arg->base_addr, sample_arg->size); - /* Imitate read_cached_memory() with the stack sample data as the cache. */ - if (addr < sample_arg->base_addr || addr - sample_arg->base_addr >= sample_arg->size) - return elf_memory_read(dwfl, addr, result, arg); - uint8_t *d = &sample_arg->data[addr - sample_arg->base_addr]; - copy_word(result, d, sample_arg->abi); - return true; -} - -static bool -sample_set_initial_registers (Dwfl_Thread *thread, void *arg) -{ -#if 0 - /* TODO: __libdwfl_set_initial_registers_thread not exported from libdwfl, - after it was decided to be unsuitable for a public API. - - A subsequent patch in the series removes sample_set_initial_registers, - replacing it with code in libdwfl_stacktrace/dwflst_perf_frame.c. - Keeping this code commented-out for the record, cf how we would - implement if the set_initial_registers utility func was public. - - To *actually* make this work, would need to copy the set_initial_registers - implementation into stacktrace.c; not worth doing since the later patch - overrides this code. */ - struct __sample_arg *sample_arg = (struct __sample_arg *)arg; - dwfl_thread_state_register_pc (thread, sample_arg->pc); - Dwfl_Process *process = thread->process; - Ebl *ebl = process->ebl; - /* XXX Sysprof provides exactly the required registers for unwinding: */ - uint64_t regs_mask = ebl_perf_frame_regs_mask (ebl); - return ebl_set_initial_registers_sample - (ebl, sample_arg->regs, sample_arg->n_regs, regs_mask, sample_arg->abi, - __libdwfl_set_initial_registers_thread, thread); -#else - /* The following facts are needed to translate x86 registers correctly: - - perf register order seen in linux arch/x86/include/uapi/asm/perf_regs.h - The registers array is built in the same order as the enum! - (See the code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs().) - - sysprof libsysprof/perf-event-stream-private.h records all registers - except segment and flags. - - TODO: Should include the perf regs mask in sysprof data and - translate registers in fully-general fashion, removing this assumption. - - dwarf register order seen in elfutils backends/{x86_64,i386}_initreg.c; - and it's a fairly different register order! - - For comparison, you can study codereview.qt-project.org/gitweb?p=qt-creator/perfparser.git;a=blob;f=app/perfregisterinfo.cpp;hb=HEAD - and follow the code which uses those tables of magic numbers. - But it's better to follow original sources of truth for this. */ - struct __sample_arg *sample_arg = (struct __sample_arg *)arg; - bool is_abi32 = (sample_arg->abi == PERF_SAMPLE_REGS_ABI_32); - static const int regs_i386[] = {0, 2, 3, 1, 7/*sp*/, 6, 4, 5, 8/*ip*/}; - static const int regs_x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7/*sp*/, 9, 10, 11, 12, 13, 14, 15, 16, 8/*ip*/}; - const int *reg_xlat = is_abi32 ? regs_i386 : regs_x86_64; - int n_regs = is_abi32 ? 9 : 17; - dwfl_thread_state_register_pc (thread, sample_arg->pc); - if (sample_arg->n_regs < (uint64_t)n_regs && show_failures) - fprintf(stderr, N_("sample_set_initial_regs: n_regs=%ld, expected %d\n"), - sample_arg->n_regs, n_regs); - for (int i = 0; i < n_regs; i++) - { - int j = reg_xlat[i]; - if (j < 0) continue; - if (sample_arg->n_regs <= (uint64_t)j) continue; - dwfl_thread_state_registers (thread, i, 1, &sample_arg->regs[j]); - } - return true; -#endif -} - -static void -sample_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg) -{ - struct __sample_arg *sample_arg = (struct __sample_arg *)dwfl_arg; - free (sample_arg); -} - -static const Dwfl_Thread_Callbacks sample_thread_callbacks = -{ - sample_next_thread, - sample_getthread, - sample_memory_read, - sample_set_initial_registers, - sample_detach, - NULL, /* sample_thread_detach */ -}; - /**************************************************** * Dwfl and statistics table for multiple processes * ****************************************************/ @@ -1043,33 +857,30 @@ sysprof_init_dwfl (Dwflst_Process_Tracker *cb_tracker, Dwfl * sysprof_find_dwfl (struct sysprof_unwind_info *sui, SysprofCaptureStackUser *ev, - SysprofCaptureUserRegs *regs) + SysprofCaptureUserRegs *regs, + Elf **out_elf) { pid_t pid = ev->frame.pid; - /* TODO: Need to generalize this code beyond x86 architectures. */ /* XXX: Note that sysprof requesting the x86_64 register file from perf_events will result in an array of 17 regs even for 32-bit applications. */ -#define EXPECTED_REGS 17 - if (regs->n_regs < EXPECTED_REGS) /* XXX expecting everything except FLAGS */ + if (regs->n_regs < ebl_frame_nregs(default_ebl)) /* XXX expecting everything except FLAGS */ { if (show_failures) - fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %d\n"), - regs->n_regs, EXPECTED_REGS); + fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %ld\n"), + regs->n_regs, ebl_frame_nregs(default_ebl)); return NULL; } + Elf *elf = NULL; Dwfl *dwfl = dwflst_tracker_find_pid (tracker, pid, sysprof_init_dwfl, NULL); - struct __sample_arg *sample_arg; bool cached = false; if (dwfl != NULL && dwfl->process != NULL) { - sample_arg = dwfl->process->callbacks_arg; /* XXX requires libdwflP.h */ cached = true; goto reuse; } - Elf *elf = NULL; int elf_fd = -1; int err = find_procfile (dwfl, &pid, &elf, &elf_fd); if (err < 0) @@ -1080,53 +891,22 @@ sysprof_find_dwfl (struct sysprof_unwind_info *sui, return NULL; } - sample_arg = malloc (sizeof *sample_arg); - if (sample_arg == NULL) - { - if (elf != NULL) { - elf_end (elf); - close (elf_fd); - } - free (sample_arg); - return NULL; - } - reuse: - sample_arg->tid = ev->tid; - sample_arg->size = ev->size; - sample_arg->data = (uint8_t *)&ev->data; - sample_arg->regs = regs->regs; - sample_arg->n_regs = (uint64_t)regs->n_regs; - sample_arg->abi = (uint64_t)regs->abi; - /* TODO: Need to generalize this code beyond x86 architectures. */ - /* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h enum perf_event_x86_regs: */ - sample_arg->sp = regs->regs[7]; - sample_arg->pc = regs->regs[8]; - sample_arg->base_addr = sample_arg->sp; - sui->last_sp = sample_arg->base_addr; - sui->last_base = sample_arg->base_addr; + /* TODO: Generalize to other architectures than x86_64. */ + sui->last_sp = regs->regs[7]; + sui->last_base = sui->last_sp; if (show_frames) { - bool is_abi32 = (sample_arg->abi == PERF_SAMPLE_REGS_ABI_32); + bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32); fprintf(stderr, "sysprof_find_dwfl pid %lld%s: size=%ld%s pc=%lx sp=%lx+(%lx)\n", (long long) pid, cached ? " (cached)" : "", - sample_arg->size, is_abi32 ? " (32-bit)" : "", - sample_arg->pc, sample_arg->base_addr, - sample_arg->sp - sample_arg->base_addr); + ev->size, is_abi32 ? " (32-bit)" : "", + regs->regs[8], sui->last_base, (long)0); } - if (!cached && ! dwfl_attach_state (dwfl, elf, pid, &sample_thread_callbacks, sample_arg)) - { - if (show_failures) - fprintf(stderr, "dwfl_attach_state pid %lld: %s\n", - (long long)pid, dwfl_errmsg(-1)); - elf_end (elf); - close (elf_fd); - free (sample_arg); - return NULL; - } if (!cached) pid_store_dwfl (pid, dwfl); + *out_elf = elf; return dwfl; } @@ -1269,7 +1049,8 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg) SysprofCaptureUserRegs *regs = (SysprofCaptureUserRegs *)tail_ptr; if (show_frames) fprintf(stderr, "\n"); /* extra newline for padding */ - Dwfl *dwfl = sysprof_find_dwfl (sui, ev, regs); + Elf *elf = NULL; + Dwfl *dwfl = sysprof_find_dwfl (sui, ev, regs, &elf); if (dwfl == NULL) { if (show_summary) @@ -1289,11 +1070,16 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg) sui->last_dwfl = dwfl; #endif sui->last_pid = frame->pid; - int rc = dwfl_getthread_frames (dwfl, ev->tid, sysprof_unwind_frame_cb, sui); + uint64_t regs_mask = ebl_perf_frame_regs_mask (default_ebl); + int rc = dwflst_perf_sample_getframes (dwfl, elf, frame->pid, ev->tid, + (uint8_t *)&ev->data, ev->size, + regs->regs, regs->n_regs, + regs_mask, regs->abi, + sysprof_unwind_frame_cb, sui); if (rc < 0) { if (show_failures) - fprintf(stderr, "dwfl_getthread_frames pid %lld: %s\n", + fprintf(stderr, "dwflst_perf_sample_getframes pid %lld: %s\n", (long long)frame->pid, dwfl_errmsg(-1)); } if (show_samples) @@ -1409,9 +1195,6 @@ parse_opt (int key, char *arg __attribute__ ((unused)), break; case OPT_DEBUG: -#ifdef DEBUG_MEMORY_READ - show_memory_reads = true; -#endif show_frames = true; FALLTHROUGH; case 'v': @@ -1503,9 +1286,6 @@ https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu else if (strcmp(env_verbose, "debug") == 0 || strcmp(env_verbose, "2") == 0) { -#ifdef DEBUG_MEMORY_READ - show_memory_reads = true; -#endif show_frames = true; show_samples = true; show_failures = true; @@ -1571,6 +1351,10 @@ https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu { if (!dwfltab_init()) error (EXIT_BAD, errno, N_("Could not initialize Dwfl table")); + + /* TODO: Generalize to other architectures. */ + default_ebl = ebl_openbackend_machine(EM_X86_64); + struct sysprof_unwind_info sui; sui.output_fd = output_fd; sui.reader = reader;