From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.167.199 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794590331; b=qpBy142aav0gIk4BhMetdFmxwC3t6uRblPPXM2KJpTQJEt88q7eZI3Jy4+bc9T0XplH/ xy7Vht3fmBNlKTbtiwaKjnvQy7Lfsa3QOHUTkQNmfGyh0PG3f+Bbt4wngMB+wEONuuaa8 JOYXTijSIbPbU1nfB8TwJBj5WWeuXvH5aCGTOWM/7WWCqKiSFxLYUSBroqr+SPIxfXaSQ EOOHr9eZJ01AVI5Ls4+Ybeyfxo+jetjZHZeoOOjDnU7QrltEO4tATxlRG7MLsk/5L77p7 7atHQD4zLMS/mSPiO6mO45jptdhI2rT12IdTWcLLp/nM77GdA82g5mU2iU6seNjDHSw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794590331; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Received:Date:In-Reply-To: Mime-Version:References:X-Mailer:Message-ID:Subject:From:To:Cc: Content-Type; bh=nj6A0Vd1gQuHBHZzzD40YQZo6hLqtvz4jv5Hz+liCDk=; b=33uaVyXtiJqMxLfMpU1bprMNXugrMZ6jan8Z2UUBjoU1Ia8IU9Abkg9lZxRRz1QiL73u CTR3ZBTUq8/V1iKr8Uipk009ldky6/gOmzNfzPGYJRDdyNDgFbV2tzsA50/V/rLHawQvJ SdxX4pmtYbqH9ewoW4fQvSO5ZndN90HkO7SLMgCxoLyRGDtsdmc4A/1Yb4gQPFe9QSfEY iNw9vzr7XCMGaXMHEUvRg4eIC6BLk0kUG93an5rUqgEwAV3Q4H8wZ9LPBJABLQA0GxNay K6G85Ihf9gyaCgDSmJ2IV8lKQXb81k1zeXicajnET6mQof1+JX+EPj4aiVtk75cO5cg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20260707 header.b=km76vkwg; arc=none smtp.remote-ip=209.85.167.199 Received: from mail-oi1-f199.google.com (mail-oi1-f199.google.com [209.85.167.199]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF30D606D848 for ; Fri, 13 Nov 2026 17:18:50 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org DF30D606D848 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20260707 header.b=km76vkwg Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-5384e381fbbso787729b6e.2 for ; Fri, 13 Nov 2026 09:18:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20260707; t=1794590330; x=1795195130; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=nj6A0Vd1gQuHBHZzzD40YQZo6hLqtvz4jv5Hz+liCDk=; b=km76vkwgecQcBBNif/rr9kVdDYkM+vAOaFrlLy4JduijtXrllhMQ/Ixfok+7HuYSNe z5V7x+ZSAM63a1WiCksvEeSHBI9Nu2f7QLDv5DfG6x9xJSgwuYiOtFyPUBH66OpnyP2j jJmsQ0iGqShYoLZchL6kgDmqz16bRK548qEJxNwTf9phZTFM8W9eRYW6+0qFTobDqDKW CybNUAys/Uw0yi8cE6H30PsAui28xedWcwKNeB+8EbqeGWEtqPtrF/xv9XScyY/b1wR8 ZEzeeCIebb6L4cpOBVPKHt+fKeTX2mlCdhuvl56VByTAt91jM4OEWeBYWfGSkCG4CRoI 4rrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794590330; x=1795195130; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=nj6A0Vd1gQuHBHZzzD40YQZo6hLqtvz4jv5Hz+liCDk=; b=ONEySLD04QCfroxP6gHzSvpNib7Ghv/OrWCv4LRh02Fu+eVA8kdCRd7ZaT8A1RJ7FT J4sMLRPxDjSq5GqRVG2fZ0a7+jxrXpjCCkT1JWAjBQyjtRaE0WgeW5GYeRF9mQNM0kR2 T0BB0ebZ4QAzwOtRj+RD45gJffEjCuDJRIP2c7T9uEQ2GVQNymKe/gdBC7Lq3A19lu51 81cxA6lDqBsFyOOslad7K/+hljTscs5jF3ufySmi19KbzZoBi6wulDaaSAwxk4x5cxB1 jM6zRsZyv+zLXMQNQriovCAShBM4qmI8lhLsUMo4RZfVlwxbe1Sx/0Bdute8ahy8WRu/ SCWg== X-Forwarded-Encrypted: i=1; AL+hErHJhQvY8j7dMW3UL/9fDsu3rzs6dAa/8Aeh3y0FjYvncz8ur/wuNwgD8Ik5T/ks+zj0zW2KdB57kDsJBu8=@vger.kernel.org X-Gm-Message-State: APuU4Z/IGf/pkXRZtAKDCitCyuBXfweFZXLmE7guvAvojA32s95JhHQf 4t6QUHkdAiMbL4RC0fv6eLNSEelxzFbJndvCdX1ZEJD0myV2NGe0x8yP8vymPK/aeOATg0gbT2m fANpNyPgD7w== X-Received: from oibkc21-n2.prod.google.com ([2002:a05:6808:6555:20b0:4f4:549a:d85d]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:238a:b0:4f1:6ab6:8fc0 with SMTP id 5614622812f47-56e642d0ae8mr2531513b6e.15.1794590329531; Fri, 13 Nov 2026 09:18:49 -0800 (PST) Date: Fri, 13 Nov 2026 09:17:31 -0800 In-Reply-To: <20261113171734.2188066-1-irogers@google.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list Mime-Version: 1.0 References: <20261110220803.2503416-1-irogers@google.com> <20261113171734.2188066-1-irogers@google.com> X-Mailer: git-send-email 2.56.1.703.gf848ed807c-goog Message-ID: <20261113171734.2188066-21-irogers@google.com> Subject: [PATCH v4 20/23] perf symbol: Add memory shrinking for symbols, DSOs, and machines From: Ian Rogers To: irogers@google.com, acme@kernel.org, haghdoost@uber.com, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Type: text/plain; charset="UTF-8" When profiling large workloads or systems with thousands of processes and shared libraries, perf can accumulate significant memory in loaded DSO symbol tables, secondary name-sorted symbol indices, cached DSO file data, and DWARF/addr2line state. Because the lifetime of struct symbol is controlled by its owning struct dso, symbols in a referenced DSO (refcnt > 1) must remain valid for the lifetime of the DSO reference. However, auxiliary per-DSO allocations and unreferenced DSOs can still be reclaimed under memory pressure: 1. symbols__shrink(): Reclaims the secondary name-sorted index (symbols_by_name, which is rebuilt on demand by dso__sort_by_name()) and trims unused over-allocated tail capacity in the symbols array, using down_write_trylock() so shrinking never deadlocks if invoked while a symbol table lock is already held on the current stack. 2. dso__shrink(): Releases cached DSO resources (open file descriptor, cached 4KB data pages, addr2line handles, and libdw DWARF contexts). When a DSO in a dsos collection has no external references (refcnt == 1), dso__shrink() also evicts its unannotated symbol table via symbols__try_delete_unannotated() and marks the DSO unloaded so it can be reloaded on demand if mapped again; otherwise it calls symbols__shrink() to reclaim auxiliary symbol arrays. 3. dsos__shrink(), machine__shrink(), machines__shrink(), and perf_session__shrink(): Walk the session's host and guest machines to shrink DSOs and evict unreferenced user DSOs (refcnt == 1, not kernel, not vDSO, not hit, and not carrying header build-ids), followed by malloc_trim(0) on glibc to return freed heap pages to the OS. Signed-off-by: Ian Rogers --- tools/perf/util/dso.c | 198 ++++++++++++++++++++++++++++++++++---- tools/perf/util/dso.h | 13 ++- tools/perf/util/dsos.c | 69 +++++++++++++ tools/perf/util/dsos.h | 2 + tools/perf/util/machine.c | 32 ++++++ tools/perf/util/machine.h | 3 + tools/perf/util/session.c | 8 ++ tools/perf/util/session.h | 2 + tools/perf/util/symbol.c | 73 +++++++++++++- tools/perf/util/symbol.h | 3 + 10 files changed, 381 insertions(+), 22 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 35a5c65030af..184d49935bfd 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -2,6 +2,7 @@ #include "dso.h" #include +#include #include #include @@ -1075,12 +1076,12 @@ static int bpf_size(struct dso *dso) #endif // HAVE_LIBBPF_SUPPORT static void -dso_cache__free(struct dso *dso) +__dso_cache__free(struct dso *dso) + EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso)) { struct rb_root *root = &dso__data(dso)->cache; struct rb_node *next = rb_first(root); - mutex_lock(dso__lock(dso)); while (next) { struct dso_cache *cache; @@ -1089,6 +1090,13 @@ dso_cache__free(struct dso *dso) rb_erase(&cache->rb_node, root); free(cache); } +} + +static void +dso_cache__free(struct dso *dso) +{ + mutex_lock(dso__lock(dso)); + __dso_cache__free(dso); mutex_unlock(dso__lock(dso)); } @@ -1243,27 +1251,30 @@ static struct dso_cache *dso_cache__populate(struct dso *dso, return cache; } -static struct dso_cache *dso_cache__find(struct dso *dso, - struct machine *machine, - u64 offset, - ssize_t *ret) -{ - struct dso_cache *cache = __dso_cache__find(dso, offset); - - return cache ? cache : dso_cache__populate(dso, machine, offset, ret); -} - static ssize_t dso_cache_io(struct dso *dso, struct machine *machine, u64 offset, u8 *data, ssize_t size, bool out) { struct dso_cache *cache; ssize_t ret = 0; - cache = dso_cache__find(dso, machine, offset, &ret); - if (!cache) - return ret; + mutex_lock(dso__lock(dso)); + cache = __dso_cache__find(dso, offset); + while (!cache) { + mutex_unlock(dso__lock(dso)); + cache = dso_cache__populate(dso, machine, offset, &ret); + if (!cache) + return ret; + mutex_lock(dso__lock(dso)); + /* + * Re-lookup under dso__lock(dso) in case dso__shrink() freed + * the cache while the lock was dropped. + */ + cache = __dso_cache__find(dso, offset); + } - return dso_cache__memcpy(cache, offset, data, size, out); + ret = dso_cache__memcpy(cache, offset, data, size, out); + mutex_unlock(dso__lock(dso)); + return ret; } /* @@ -1803,7 +1814,7 @@ int dso__name_len(const struct dso *dso) bool dso__loaded(const struct dso *dso) { - return RC_CHK_ACCESS(dso)->loaded; + return atomic_load_explicit(&RC_CHK_ACCESS(dso)->loaded, memory_order_acquire); } bool dso__sorted_by_name(const struct dso *dso) @@ -1835,7 +1846,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id) dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->dbginfo_type = DSO_BINARY_TYPE__NOT_FOUND; dso->is_64_bit = (sizeof(void *) == 8); - dso->loaded = 0; + atomic_init(&dso->loaded, false); dso->rel = 0; dso->has_srcline = 1; dso->a2l_fails = 1; @@ -1896,6 +1907,154 @@ void dso__delete(struct dso *dso) RC_CHK_FREE(dso); } +bool dso__can_shrink_symbols(const struct dso *dso) +{ + const char *name; + + if (!dso || !dso__loaded(dso)) + return false; + + if (dso__kernel(dso) || dso__is_kmod(dso)) + return false; + + if (dso__is_vdso(dso)) + return false; + + switch (dso__binary_type(dso)) { + case DSO_BINARY_TYPE__KALLSYMS: + case DSO_BINARY_TYPE__GUEST_KALLSYMS: + case DSO_BINARY_TYPE__VMLINUX: + case DSO_BINARY_TYPE__GUEST_VMLINUX: + case DSO_BINARY_TYPE__KCORE: + case DSO_BINARY_TYPE__GUEST_KCORE: + case DSO_BINARY_TYPE__BPF_PROG_INFO: + case DSO_BINARY_TYPE__BPF_IMAGE: + case DSO_BINARY_TYPE__OOL: + case DSO_BINARY_TYPE__JAVA_JIT: + case DSO_BINARY_TYPE__GUEST_KMODULE: + case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: + case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: + case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: + return false; + case DSO_BINARY_TYPE__DEBUGLINK: + case DSO_BINARY_TYPE__BUILD_ID_CACHE: + case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO: + case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: + case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: + case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO: + case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: + case DSO_BINARY_TYPE__GNU_DEBUGDATA: + case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: + case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: + case DSO_BINARY_TYPE__NOT_FOUND: + default: + break; + } + + switch (dso__symtab_type(dso)) { + case DSO_BINARY_TYPE__KALLSYMS: + case DSO_BINARY_TYPE__GUEST_KALLSYMS: + case DSO_BINARY_TYPE__VMLINUX: + case DSO_BINARY_TYPE__GUEST_VMLINUX: + case DSO_BINARY_TYPE__KCORE: + case DSO_BINARY_TYPE__GUEST_KCORE: + case DSO_BINARY_TYPE__BPF_PROG_INFO: + case DSO_BINARY_TYPE__BPF_IMAGE: + case DSO_BINARY_TYPE__OOL: + case DSO_BINARY_TYPE__JAVA_JIT: + case DSO_BINARY_TYPE__GUEST_KMODULE: + case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: + case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: + case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: + case DSO_BINARY_TYPE__NOT_FOUND: + return false; + case DSO_BINARY_TYPE__DEBUGLINK: + case DSO_BINARY_TYPE__BUILD_ID_CACHE: + case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO: + case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: + case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: + case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO: + case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: + case DSO_BINARY_TYPE__GNU_DEBUGDATA: + case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: + case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: + default: + break; + } + + name = dso__long_name(dso); + if (!name || name[0] == '[' || is_perf_pid_map_name(name) || + strstr(name, " (deleted)") != NULL) + return false; + + return true; +} + +static void dso__delete_inlines_and_srclines(void *ctx) +{ + struct dso *dso = ctx; + + /* Free inlines before symbols, as inline frames reference symbols. */ + inlines__tree_delete(dso__inlined_nodes(dso)); + srcline__tree_delete(dso__srclines(dso)); +} + +size_t dso__shrink(struct dso *dso) + NO_THREAD_SAFETY_ANALYSIS +{ + size_t freed = 0; + + if (!dso) + return 0; + + if (mutex_trylock(dso__data_open_lock())) { + if (dso__data(dso)->fd >= 0) { + close_dso(dso); + freed++; + } + mutex_unlock(dso__data_open_lock()); + } + + if (!mutex_trylock(dso__lock(dso))) + return freed; + + if (!dso__kernel(dso) && dso__binary_type(dso) != DSO_BINARY_TYPE__OOL && + !RB_EMPTY_ROOT(&dso__data(dso)->cache)) { + __dso_cache__free(dso); + freed++; + } + if (dso__a2l(dso)) { + dso__free_a2l(dso); + freed++; + } +#ifdef HAVE_LIBBFD_SUPPORT + if (dso__a2l_libbfd(dso)) { + dso__free_a2l_libbfd(dso); + freed++; + } +#endif +#ifdef HAVE_LIBDW_SUPPORT + if (dso__libdw(dso)) { + dso__free_libdw(dso); + freed++; + } +#endif + + if (dso__dsos(dso) && refcount_read(dso__refcnt(dso)) == 1 && + dso__can_shrink_symbols(dso) && dso__has_symbols(dso) && + symbols__try_delete_unannotated(dso__symbols(dso), + dso__delete_inlines_and_srclines, dso)) { + atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, false, + memory_order_release); + freed++; + } else { + freed += symbols__shrink(dso__symbols(dso)); + } + + mutex_unlock(dso__lock(dso)); + return freed; +} + struct dso *dso__get(struct dso *dso) { struct dso *result; @@ -1909,7 +2068,8 @@ struct dso *dso__get(struct dso *dso) void dso__put(struct dso *dso) { #ifdef REFCNT_CHECKING - if (dso && dso__data(dso) && refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2) + if (dso && dso__data(dso) && dso__data(dso)->fd >= 0 && + refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2) dso__data_close(dso); #endif if (dso && refcount_dec_and_test(&RC_CHK_ACCESS(dso)->refcnt)) diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index f23cf9957aec..7f217815ec07 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -346,7 +347,7 @@ DECLARE_RC_STRUCT(dso) { u8 is_64_bit:1; u8 bintype_searched:1; u8 debuginfo_searched:1; - bool loaded; + _Atomic bool loaded; u8 rel; char name[]; }; @@ -602,7 +603,7 @@ static inline enum dso_load_errno *dso__load_errno(struct dso *dso) static inline void dso__set_loaded(struct dso *dso) { - RC_CHK_ACCESS(dso)->loaded = true; + atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, true, memory_order_release); } static inline struct mutex *dso__lock(struct dso *dso) @@ -783,6 +784,14 @@ static inline bool dso__has_symbols(const struct dso *dso) return RC_CHK_ACCESS(dso)->symbols.cnt > 0; } +static inline refcount_t *dso__refcnt(struct dso *dso) +{ + return &RC_CHK_ACCESS(dso)->refcnt; +} + +bool dso__can_shrink_symbols(const struct dso *dso); +size_t dso__shrink(struct dso *dso); + char *dso__filename_with_chroot(const struct dso *dso, const char *filename); bool dso__sorted_by_name(const struct dso *dso); diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index e927e707abac..f6868ffcc3e1 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -6,6 +6,9 @@ #include "vdso.h" #include "namespaces.h" #include +#ifdef __GLIBC__ +#include +#endif #include #include #include // filename__read_build_id @@ -493,3 +496,69 @@ int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data) up_read(&dsos->lock); return err; } + +size_t dsos__shrink(struct dsos *dsos) + NO_THREAD_SAFETY_ANALYSIS +{ + unsigned int write_idx = 0; + size_t freed = 0; + bool any_removed = false; + + if (!down_write_trylock(&dsos->lock)) + return 0; + + for (unsigned int read_idx = 0; read_idx < dsos->cnt; read_idx++) { + struct dso *dso = dsos->dsos[read_idx]; + + freed += dso__shrink(dso); + + if (!dso__has_symbols(dso) && + !dso__header_build_id(dso) && + !dso__hit(dso) && + !dso__kernel(dso) && + !dso__is_vdso(dso) && + refcount_read(dso__refcnt(dso)) == 1 && + mutex_trylock(&_dso__data_open_lock)) { + bool can_evict = dso__data(dso)->fd < 0 && + list_empty(&dso__data(dso)->open_entry) && + mutex_trylock(dso__lock(dso)); + + if (can_evict) + mutex_unlock(dso__lock(dso)); + mutex_unlock(&_dso__data_open_lock); + if (can_evict) { + dso__set_dsos(dso, NULL); + dso__put(dso); + any_removed = true; + freed++; + continue; + } + } + dsos->dsos[write_idx++] = dso; + } + + dsos->cnt = write_idx; + if (any_removed) { + if (write_idx == 0) { + zfree(&dsos->dsos); + dsos->allocated = 0; + dsos->sorted = true; + } else if (write_idx < dsos->allocated / 2) { + struct dso **shrunk = realloc(dsos->dsos, + write_idx * sizeof(struct dso *)); + if (shrunk) { + dsos->dsos = shrunk; + dsos->allocated = write_idx; + } + } + } + + up_write(&dsos->lock); + +#ifdef __GLIBC__ + if (freed > 0) + malloc_trim(0); +#endif + + return freed; +} diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h index a26774950866..151efdba2aa4 100644 --- a/tools/perf/util/dsos.h +++ b/tools/perf/util/dsos.h @@ -49,4 +49,6 @@ struct dso *dsos__find_kernel_dso(struct dsos *dsos); int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data), void *data); +size_t dsos__shrink(struct dsos *dsos); + #endif /* __PERF_DSOS */ diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 40fda6100740..4d5bac589aaf 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -3441,3 +3441,35 @@ int machine__hit_all_dsos(struct machine *machine) { return dsos__hit_all(&machine->dsos); } + +size_t machine__shrink(struct machine *machine) +{ + if (!machine) + return 0; + + return dsos__shrink(&machine->dsos); +} + +size_t machines__shrink(struct machines *machines) + NO_THREAD_SAFETY_ANALYSIS +{ + struct rb_node *nd; + size_t freed; + + if (!machines) + return 0; + + freed = machine__shrink(&machines->host); + + if (!down_write_trylock(&machines->lock)) + return freed; + + for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + + freed += machine__shrink(pos); + } + up_write(&machines->lock); + + return freed; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 80191545ee8a..0f4c0ae5b56d 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -338,4 +338,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al, int machine__hit_all_dsos(struct machine *machine); +size_t machine__shrink(struct machine *machine); +size_t machines__shrink(struct machines *machines); + #endif /* __PERF_MACHINE_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 56a5e57ec512..3f35c7affc29 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -4266,3 +4266,11 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags */ return perf_env__e_machine_nocache(env, e_flags); } + +size_t perf_session__shrink(struct perf_session *session) +{ + if (!session) + return 0; + + return machines__shrink(&session->machines); +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 05ea2c1ee8c9..cbea6792a008 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -211,4 +211,6 @@ int perf_event__process_finished_round(const struct perf_tool *tool, struct perf_env *perf_session__env(struct perf_session *session); uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags); +size_t perf_session__shrink(struct perf_session *session); + #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 0ef4d82cc842..ce6596de49a4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -254,6 +255,74 @@ void symbols__exit(struct symbols *symbols) exit_rwsem(&symbols->lock); } +bool symbols__try_delete_unannotated(struct symbols *symbols, + void (*pre_delete)(void *ctx), void *ctx) +{ + if (!down_write_trylock(&symbols->lock)) + return false; + + for (unsigned int i = 0; i < symbols->cnt; i++) { + if (symbol__annotated(symbols->symbols[i])) { + up_write(&symbols->lock); + return false; + } + } + + if (pre_delete) + pre_delete(ctx); + + symbols__delete_locked(symbols); + up_write(&symbols->lock); + return true; +} + +/* + * Reclaim auxiliary memory from @symbols without freeing live symbol + * entries (whose lifetimes are owned by the containing struct dso): + * - Free the secondary name-sorted index (@symbols_by_name), which is + * rebuilt on demand by dso__sort_by_name(). + * - Trim unused over-allocated tail capacity in @symbols->symbols. + * + * Uses down_write_trylock() so this is safe to call from OOM recovery + * paths where the current thread might already hold a lock on @symbols. + * + * Returns the number of buffers reclaimed/shrunk. + */ +size_t symbols__shrink(struct symbols *symbols) +{ + size_t freed = 0; + + if (!down_write_trylock(&symbols->lock)) + return 0; + + if (symbols->symbols_by_name) { + zfree(&symbols->symbols_by_name); + symbols->num_by_name = 0; + symbols->sorted_by_name = false; + freed++; + } + + if (symbols->cnt == 0) { + if (symbols->allocated > 0) { + zfree(&symbols->symbols); + symbols->allocated = 0; + symbols->sorted = true; + freed++; + } + } else if (symbols->cnt < symbols->allocated) { + struct symbol **shrunk = realloc(symbols->symbols, + symbols->cnt * sizeof(struct symbol *)); + if (shrunk) { + symbols->symbols = shrunk; + symbols->allocated = symbols->cnt; + freed++; + } + } + + up_write(&symbols->lock); + return freed; +} + /* * Order symbols by start address only, preserving insertion order among * symbols sharing the same start address (matching the previous rbtree @@ -2442,8 +2511,9 @@ int dso__load(struct dso *dso, struct map *map) /* check again under the dso->lock */ if (dso__loaded(dso)) { ret = 1; - goto out; + goto out_unlock; } + atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, false, memory_order_release); kmod = dso__is_kmod(dso); @@ -2601,6 +2671,7 @@ int dso__load(struct dso *dso, struct map *map) ret = 0; out: dso__set_loaded(dso); +out_unlock: mutex_unlock(dso__lock(dso)); nsinfo__mountns_exit(&nsc); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 97cdc2e962c6..e68248be38c5 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -140,6 +140,9 @@ void symbol__delete(struct symbol *sym); void symbols__init(struct symbols *symbols); void symbols__exit(struct symbols *symbols); void symbols__delete(struct symbols *symbols); +bool symbols__try_delete_unannotated(struct symbols *symbols, + void (*pre_delete)(void *ctx), void *ctx); +size_t symbols__shrink(struct symbols *symbols); void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock); static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym) -- 2.56.1.703.gf848ed807c-goog