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"