3955 Commits
Author SHA1 Message Date
TCMalloc TeamandChris Kennelly c737c113dc Verify that MallocExtension::GetStats and GetStatsInPbTxt complete within 1 second.
Add `GetStatsTest.Latency` to guard against regressions where `DumpStats` or `DumpStatsInPbtxt` blocks for seconds (such as slow `/proc` or `/sys` reads under `pageheap_lock`).

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1008685385
2026-10-28 00:17:47 -07:00
TCMalloc Team 0d083638de No public description
PiperOrigin-RevId: 1008659490
2026-10-27 23:23:34 -07:00
TCMalloc Team 4e19be7421 No public description
PiperOrigin-RevId: 1008656155
2026-10-27 23:16:31 -07:00
Chris Kennelly CA a2a13b5390 In TransferCache::TryPlunder, return immediately after releasing the final
batch to `freelist()` when `to_return == 0` instead of re-acquiring `lock_`
only to unlock and exit. In `ShardedTransferCacheManager::Plunder`, skip size
classes where `!should_use(size_class)`.

Preserves legacy behavior under `-DTCMALLOC_INTERNAL_LEGACY_LOCKING2`.

PiperOrigin-RevId: 1008616070
2026-10-27 21:49:05 -07:00
Vaibhav Gogte CA 7c38292288 Add object bounds, uniqueness, and canary checks to FuzzSpanInstructions
When `use_bitmap_for_small = true`, free objects are tracked in an outline
bitmap at the end of the span rather than an inline linked list, so neither
popping nor pushing touches object memory unless explicitly checked.

- Poison backing span memory with `0xA5` before `BuildFreelist` in `State` so
  uninitialized outline bitmap words surface immediately.
- Track `allocated_idx` in `State` to verify that popped and pushed objects are
  aligned, lie within `[0, objects_per_span * object_size)` (not overlapping
  the outline tail bitmap), and are never allocated twice concurrently.
- Write per-object canary bytes on pop and verify them on push.

PiperOrigin-RevId: 1008456250
2026-10-27 16:28:22 -07:00
Nuthatch AgentandChris Kennelly e18f88dca6 Fix modulo-4 low-bit sampling bias in HugePageFiller::Contribute.
When contributing a hugepage tracker, the filler steps a 48-bit linear congruential generator via `ExponentialBiased::NextRandom` and samples 1% of trackers for VMA tagging. Taking the remainder of the raw generator state modulo 100 depends on its low two bits, which cycle with period 4 because 100 is a multiple of 4. Starting from a zero seed, only contributions at index 3 modulo 4 could ever be sampled, which excluded three quarters of contributed hugepages.

Extract high-order bits via `ExponentialBiased::GetRandom` before taking the remainder modulo 100 so all modulo-4 contribution indices are sampled uniformly, and initialize the generator state to 42.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1008455755
2026-10-27 16:27:56 -07:00
Chris Kennelly CA 0aed5651ce Use read-only CpuCacheActive() in global_stats.cc and initialize tc_globals.
Root Cause: ExtractStats, DumpStats, and DumpStatsInPbtxt called
UsePerCpuCache(tc_globals), which registers rseq on the calling thread and calls
MallocExtension_Internal_MarkThreadIdle() (acquiring pageheap_lock while
ExtractStats already holds pageheap_lock), and ExtractStats and
GetNumericProperty read tc_globals before calling tc_globals.InitIfNecessary().

PiperOrigin-RevId: 1008451909
2026-10-27 16:20:12 -07:00
Chris Kennelly CA 4482254884 Track whether a non-default region factory has ever been installed via has_custom_region_factory_ in SystemAllocator. When only the default MmapRegion anonymous memory factory (MAP_ANONYMOUS) has been used, madvise(..., MADV_REMOVE) always fails in the Linux kernel (vma_is_anonymous(vma) -> -EINVAL) after acquiring mmap_read_lock, walking the VMA tree, and checking VMA flags. Skip the guaranteed-failing MADV_REMOVE syscall on default anonymous mappings and issue MADV_DONTNEED directly.
When compiled with `-DTCMALLOC_INTERNAL_LEGACY_LOCKING2`, `MADV_REMOVE` is attempted before `MADV_DONTNEED` on all mappings.

PiperOrigin-RevId: 1008399804
2026-10-27 14:59:52 -07:00
Vaibhav Gogte CA 99fe125393 Exercise multi-object batch pushes in FuzzSpan
`FuzzSpan` previously pushed freed objects back to the span one at a time
(`batch.size() == 1`), leaving the multi-object coalescing loop in
`Span::DynamicBitmapPush` unexercised by `FuzzSpan`.

- Push objects in batches up to `num_to_move` in `FuzzSpan` and verify
  `span->Allocated()` after each non-terminal push.

PiperOrigin-RevId: 1008371700
2026-10-27 14:20:13 -07:00
Nuthatch AgentandChris Kennelly 24306ca913 Add ScopedParameter RAII template to restore parameters in tests.
Replaces repetitive hand-written `Scoped*` classes in `testutil.h` with a two-argument template `ScopedParameter<Getter, Setter>` that records the current value on construction and restores it on destruction. Adds `ScopedMaxPerCpuCacheSize` and `ScopedMaxTotalThreadCacheBytes`, and converts manual parameter restore logic in `profile_test.cc`, `threadcachesize_test.cc`, and `hooks_test.cc`.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1008370660
2026-10-27 14:16:23 -07:00
Vaibhav Gogte CA 8b5f5a1fda Snapshot page allocator limit stats in ExtractStats under pageheap_lock
In DumpStats() and DumpStatsInPbtxt(), limit(kSoft), limit(kHard), limit_hits(kSoft), limit_hits(kHard), successful_shrinks_after_limit_hit(kSoft), and successful_shrinks_after_limit_hit(kHard) were queried sequentially, acquiring and releasing pageheap_lock 6 separate times.

Add PageAllocatorImpl::GetLimitStats() (ABSL_EXCLUSIVE_LOCKS_REQUIRED(pageheap_lock)) and snapshot LimitStats inside ExtractStats() alongside GetReleaseStats() under the existing PageHeapSpinLockHolder scope, eliminating 6 pageheap_lock acquisitions per DumpStats / DumpStatsInPbtxt call.

PiperOrigin-RevId: 1008281099
2026-10-27 12:08:24 -07:00
Chris Kennelly 87ce233e31 Fix dead hpaa_subrelease checks in GetStatsTest.
The `using_hpaa` guard looked for "HugePageAwareAllocator", which the stats
never print, so its checks never ran (one searched the text output for a pbtxt
field).  HPAA is the only page allocator, so check unconditionally.  Sampled
and cold allocators always subrelease, so the enabled case checks that no
allocator reports it disabled.

PiperOrigin-RevId: 1008278009
2026-10-27 12:03:07 -07:00
Vaibhav Gogte CA 35aab08a20 Rebuild span state after being fully freed in fuzz test.
In `Dealloc::Perform`, `Span::FreelistPushBatch` returns `false` without
pushing objects or decrementing `allocated_` when all live objects in the span
are freed (`n == state.live_ptrs.size()`). Previously, `Dealloc::Perform`
ignored this return value and cleared `state.live_ptrs`, desynchronizing
`state.span->Allocated()` from `state.live_ptrs.size()` and permanently losing
those objects across subsequent instructions.

- Check the return value of `FreelistPushBatch` in `Dealloc::Perform` and
  rebuild the span freelist when the span becomes completely free.
- Check `state.span->Allocated() == state.live_ptrs.size()` after each
  instruction in `FuzzSpanInstructions`.
- Add `FuzzSpanInstructionsFullDealloc` regression test.

PiperOrigin-RevId: 1008265820
2026-10-27 11:46:55 -07:00
Vaibhav Gogte CA ef274ad3be Remove wider slabs support from CpuCache.
`UseWiderSlabs()` only enabled 512 KiB per-CPU slabs when `active_partitions() == 1`. With light security heap partitioning enabled by default in builds with alloc-token support (`active_partitions() == 2`), `UseWiderSlabs()` is no longer active.

PiperOrigin-RevId: 1008221735
2026-10-27 10:50:45 -07:00
Vaibhav Gogte CA be996a32ff Check HugeRegion invariants during unback in huge_region_fuzz.
Releasing from HugeRegion without pageheap_lock lets other operations run
while an unback is in flight. Strengthen the fuzzer's reentrant unback
callback and invariants so it catches a hugepage being unbacked twice or
stats drifting out of agreement:
- Assert that an in-flight unback range does not overlap another in-flight
  range and contains no already-released page, before and after the reentrant
  subprogram runs.
- Check `AddSpanStats` output against `free_pages()`/`unmapped_pages()` and
  the mock's released-page set in `CheckInvariants`.
- Track released state at whole-hugepage granularity on allocation and check
  `from_released` in both directions.
- Add `HugeRegionTest.UnbackPendingCandidateDuringRelease`: a candidate that
  Release selected up front is unbacked by a concurrent Release or by
  Allocate + Deallocate(release=true) while an earlier range is in flight.

PiperOrigin-RevId: 1008202931
2026-10-27 10:26:57 -07:00
TCMalloc Team 8cddfe342c Don't count zero-page PTEs as resident in ResidencyPageMap.
A PTE mapping the shared zero page reads PM_PRESENT with neither
PM_MMAP_EXCLUSIVE nor PM_FILE set. The kernel does not count it in Rss, but
ResidencyPageMap::Update() did, overstating resident bytes. Only count present
pages that also have PM_MMAP_EXCLUSIVE or PM_FILE set.

Anonymous pages shared after fork() and KSM pages are no longer counted
either; an exact zero-page check would need PFNs, which require
CAP_SYS_ADMIN. Huge zero page PMD mappings report PM_FILE and are still
counted. GetUnbackedAndSwappedBitmaps() is unchanged.

New tests cross-check ResidencyPageMap against /proc/self/smaps Rss for real
mappings: touched and read-faulted anonymous memory, a THP before and after a
split by MADV_COLD, and a file mapped twice.
PiperOrigin-RevId: 1008179989
2026-10-27 09:56:24 -07:00
Dmitry Vyukov c400e6afcb Internal change.
PiperOrigin-RevId: 1008123676
2026-10-27 08:28:40 -07:00
Dmitry Vyukov 35718d3852 Optimize Span dynamic bitmap with two-level bitmaps.
Large sparse bitmaps are slow, since we need to scan lots of empty words.
The worst case is 8b objects in 256K page config with 512 uint64_t words for bitmap.

Introduce two-level bitmap tracking for Span dynamic bitmaps across five specializations:
- `<= 64` objects (`kInline64`): single-level bitmap in `Span::raw_bitmap_[0]`.
- `65..256` objects (`kInline256`): 1st-level bitmap in `Span::raw_bitmap_[0..3]`
  and a 4-bit 2nd-level summary bitmap in a new `level2_bitmap_ : kRawBitmapSize`
  bitfield placed before `first_page_` (preserving `sizeof(Span)`).
- `> 256` objects: 1st-level bitmap at the end of the 1-page span and 2nd-level
  summary bitmap in `Span::raw_bitmap_[0..3]`, split into:
  - `kOutlineL2_1`: 1st-level bitmap has `<= 64` words (1 L2 word).
  - `kOutlineL2_4`: 1st-level bitmap has `65..256` words (up to 4 L2 words,
    compiled only when `kPageSize == 256 KiB`).
  - `kOutlineL2_8`: 1st-level bitmap has `257..512` words (8-byte objects in
    the 256 KiB page configuration, where each L2 bit summarizes a pair of L1
    words; compiled only when `kPageSize == 256 KiB`).

Template `DynamicBitmapPushImpl`, `DynamicBitmapPopWord`, and
`DynamicBitmapPopBitmap` on `Span::BitmapMode` so inner loops are branch-free
with respect to the configuration.

This prevents steep drop in single-threaded performance esp for 256K page config.

PiperOrigin-RevId: 1008101179
2026-10-27 07:54:50 -07:00
Dmitry Vyukov e60f645ebe Index Span bitmap backwards.
Index dynamic Span bitmap backwards. This allows to locate the bitmap without offset.
Bitmap at the end of the span is used only for spans with 1 page, so we can locate
"beginning" of the bitmap by adding kPageSize to span start, and then index backwards.

This is needed in preparation for the next change that adds 2-level bitmaps,
and multiple "modes". Without this change we would need to pass both
bitmap_offset and bitmap_mode around through all methods, which increases
register pressure.
If we index the bitmap backwards, we only need to pass bitmap_mode around.

PiperOrigin-RevId: 1008055986
2026-10-27 06:34:14 -07:00
Chris Kennelly 99341e5d5c Enable per-CPU caches on the first allocation.
`Static::SlowInitIfNecessary` activates `cpu_cache_` after dropping
`pageheap_lock`, still holding `init_lock_`, when
`Parameters::per_cpu_caches()` and `subtle::percpu::IsFast()` hold. `inited_`
is published only after activation, so a thread racing to make its first
allocation waits on `init_lock_` instead of starting on a per-thread cache.
Setting `TCMALLOC_DEFER_PER_CPU_CACHES=1` defers activation as before; the
`defer_per_cpu_caches` test variant keeps that path covered.

PiperOrigin-RevId: 1007839812
2026-10-26 23:16:05 -07:00
Chris Kennelly 56a8e1f295 Remove dead TCMalloc parameter accessor declarations.
Nothing defines or calls the weak
`TCMalloc_Internal_{Get,Set}PrioritizeSpansEnabled` and
`TCMalloc_Internal_{Get,Set}ReleaseFreeSwapped` accessors.

Drop `Parameters` friend declarations for
`TCMalloc_Internal_SetLifetimeAllocatorOptions` (never defined),
`TCMalloc_Internal_SetReleaseFreeSwapped`, and four setters that don't touch
`Parameters` internals (`SetHPAASubrelease`, `SetMadvise`,
`SetMaxPerCpuCacheSize`, `SetPerCpuCachesEnabled`).

PiperOrigin-RevId: 1007828919
2026-10-26 22:53:38 -07:00
Chris Kennelly CA f15f9edb24 Reject metadata and unassigned memory tags in IsNormalMemory and deallocation masks.
Root Cause: IsNormalMemory and kNormalOrBadDeallocationMask masked only bit 0
of the pointer tag, accepting kMetadata (0x3) in IsNormalMemory and unassigned
tags (0x5, 0x7) in both IsNormalMemory and kNormalOrBadDeallocationMask.

PiperOrigin-RevId: 1007828882
2026-10-26 22:53:02 -07:00
Nuthatch AgentandChris Kennelly f9b3af78a3 Remove undefined MallocExtension_Internal_{Get,Set}SkipSubreleaseInterval declarations.
These weak shims have had no definitions since the subrelease interval split into short and long intervals, and have no callers.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1007827280
2026-10-26 22:50:22 -07:00
Chris Kennelly CA 5ead08fbc9 Normalize and validate PT_NOTE p_align in GetBuildId.
Per the ELF specification, p_align of 0 or 1 specifies default 4-byte note alignment rather than 0- or 1-byte alignment. Normalize p_align <= 1 to alignof(ElfW(Nhdr)) and require power-of-2 alignment >= alignof(ElfW(Nhdr)) before dereferencing note headers.

PiperOrigin-RevId: 1007825808
2026-10-26 22:47:13 -07:00
Chris Kennelly 128c732fc2 Move drained slab metadata ablation to InitializeFromExperiments.
`Parameters::release_drained_slab_metadata()` checked a `LowLevelCallOnce`
flag on every read to apply `TCMALLOC_RELEASE_DRAINED_METADATA_ABLATION`.
Apply the experiment in `Parameters::InitializeFromExperiments()` with the
other experiment-defaulted parameters, and make the getter a plain load.

`TCMalloc_Internal_{Get,Set}ReleaseDrainedSlabMetadata` call
`InitIfNecessary()` first, like the other parameters initialized there, so an
explicit setting still wins over the experiment.

PiperOrigin-RevId: 1007825324
2026-10-26 22:44:33 -07:00
Nuthatch AgentandChris Kennelly 3b5165b616 Avoid straddling the 9984-byte size class in TcmallocTest.Realloc.
`TcmallocTest` realloc tests allocate buffers at several starting sizes and check that small positive and negative size deltas stay in place without reallocating. Under the 256 KiB page flexible size-class tables, a 9984-byte size class sits between 9968 and 10000, so shrinking a 10000-byte allocation by 32 bytes crosses into the smaller size class and forces `do_realloc` to allocate a new buffer.

Use 9900 instead of 10000 as the starting size so the 9868-to-9916 byte range stays within a single size class across all page sizes and size-class tables.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1007817945
2026-10-26 22:29:03 -07:00
Chris Kennelly 8c72461e68 Remove retired tcmalloc_dense_trackers_sorted_on_spans_allocated stat.
PiperOrigin-RevId: 1007804018
2026-10-26 22:00:17 -07:00
Chris Kennelly CA 62664af9c6 Return a const reference from PageTracker::GetHugePageResidencyState()
(annotated `ABSL_EXCLUSIVE_LOCKS_REQUIRED(pageheap_lock)` and
`ABSL_ATTRIBUTE_LIFETIME_BOUND`) to avoid copying the 112-byte
`HugePageResidencyState` (including three 32-byte `PageBitmap`s) by value
when `SelectEligibleTrackers` and `UsageInfo::Record` inspect tracker
metadata under `pageheap_lock`.

Preserves legacy behavior under `-DTCMALLOC_INTERNAL_LEGACY_LOCKING2`.

PiperOrigin-RevId: 1007801966
2026-10-26 21:57:38 -07:00
Chris Kennelly 2e7c37f44f Make tcmalloc::MallocHook publicly visible (#44).
PiperOrigin-RevId: 1007800961
2026-10-26 21:53:49 -07:00
Chris Kennelly f3641ec121 Fix TestStaticForwarder::AllocCpuCache alignment on aarch64.
AllocCpuCache only padded its mapping when the requested alignment exceeded
EXEC_PAGESIZE, assuming mmap returns EXEC_PAGESIZE-aligned memory.  On aarch64,
EXEC_PAGESIZE is 64 KiB, but kernels commonly use 4 KiB pages, so slabs
requesting up to 64 KiB alignment came back 4 KiB-aligned and larger alignments
were under-padded.  Pad relative to GetPageSize() instead.

PiperOrigin-RevId: 1007780831
2026-10-26 21:10:00 -07:00
Chris Kennelly b5ebaabede Fix parameter leaks in GetStatsTest.Parameters.
Root Cause: The guarded sampling interval was saved in a `bool`, so the test
restored an interval of 1, and `min_hot_access_hint` was never restored after
being set to 3.

Use the existing `ScopedGuardedSamplingInterval` and
`ScopedSkipSubrelease{Short,Long}Interval` helpers, and restore
`min_hot_access_hint`.

PiperOrigin-RevId: 1007780601
2026-10-26 21:06:50 -07:00
Chris Kennelly 1bd58f7c66 Acquire sample lock before graveyard lock in SampleRecorder::PushDead.
In `SampleRecorder::PushDead`, acquire `sample->lock` before `graveyard_.lock` (returning early if `sample->dead != nullptr`) so `graveyard_.lock` is held only across the two-pointer dead-list splice (`Register` already pops `graveyard_.dead` before acquiring `sample->lock`), and delegate `UnregisterAll` to `PushDead(sample)` so both paths share the `sample->lock` -> `graveyard_.lock` acquisition order (`ABSL_ACQUIRED_AFTER(T::lock)`).

PiperOrigin-RevId: 1007780388
2026-10-26 21:03:29 -07:00
Nuthatch AgentandChris Kennelly 3524a2f5f2 Turn on TCMALLOC_SONIC_PREFER_BACKED_FILLER_PAGES at 1%.
Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1007714584
2026-10-26 18:33:57 -07:00
Nuthatch AgentandChris Kennelly eeddd26506 Wire prefer_backed_filler_pages to TCMALLOC_SONIC_PREFER_BACKED_FILLER_PAGES.
Actuate the backed-pages filler parameter from the experiment and the `--tcmalloc_prefer_backed_filler_pages` flag, and export it in allocator statistics and test variants.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1007657956
2026-10-26 16:52:49 -07:00
Nuthatch AgentandChris Kennelly b3596d551b Prefer backed free pages in partially released filler hugepages.
When allocating spans from a partially released hugepage, standard best-fit selection chooses the shortest free range across all free pages regardless of whether the pages are currently backed or released to the OS. When an unbacked free hole happens to be a tighter size match than an already-backed free hole on the same hugepage, allocating the unbacked hole faults in previously unbacked pages while leaving resident free pages unused.

Gate backed-first allocation within a hugepage behind `Parameters::prefer_backed_filler_pages()`. When enabled on a tracker with released pages, `PageTracker::Get()` scans the combined allocated and released page bitmaps to pick the shortest backed free range that fits the request, and falls back to unbacked holes only when no backed range is large enough.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1007593493
2026-10-26 15:18:12 -07:00
Steinar H. Gunderson fdb5ce5429 Make an experiment for rolling out flexible sizeclasses.
PiperOrigin-RevId: 1007097504
2026-10-26 01:39:47 -07:00
Chris Kennelly 32efe41d78 Remove orphaned want_legacy_size_classes_helper.cc.
PiperOrigin-RevId: 1007073110
2026-10-26 00:47:43 -07:00
Chris Kennelly de96aa9fa7 Add cpu_cache_fuzz, a FuzzTest harness for CpuCache.
cpu_cache_test exercises each CpuCache path in isolation.  The new FuzzTest
harness drives a fuzzer-chosen sequence of Allocate, Deallocate, Churn,
Drain, Reclaim, ReleaseDrainedMetadata, TryDrainingCaches, ShuffleCpuCaches,
ResizeSizeClasses, ResizeSizeClassMaxCapacities, ResizeSlabIfNeeded,
SetCacheLimit, Print, PrintInPbtxt, and stats reads against a single-threaded
CpuCache<TestStaticForwarder> from up to four faked CPUs, under every
combination of dynamic slab, drained-slab release, real or three-class SizeMap,
sharded transfer cache backing (none, large size classes only, or generic),
and six per-CPU cache limits.  After each step it checks
that Allocated + Unallocated == Capacity per CPU, that total capacity is
conserved across shuffles and stealing and follows SetCacheLimit (growing adds
the difference to every CPU, shrinking resets every CPU to the limit), that
TotalUsedBytes agrees with both the per-CPU and per-size-class views, that
size-class capacities stay within GetMaxCapacity for the current shift, that
unpopulated CPUs hold no capacity, and that ResizeSlabIfNeeded lands on the
shift predicted from the interval miss counters.

Reclaim and ReleaseDrainedMetadata (CpuCachePeer::Reclaim and
CpuCachePeer::ReleaseDrainedSlabMetadata) unpopulate CPUs and release their
slab metadata without waiting for two idle drain intervals, so Populate runs
again on CPUs whose slab was madvised away and ResizeSlabs copies around
unpopulated CPUs.  The sharded transfer cache mode is part of the fuzzed
configuration rather than an instruction: ShardedTransferCacheManagerBase::Init
latches it once at startup and nothing changes it afterwards.  The harness owns
that manager so that it is backed by FakeCentralFreeList, whose objects can be
dereferenced and leak-checked, and carves its metadata from a rewindable arena
because the manager never frees its shards.

In production every remote slab operation stops and fences the CPUs it
touches, and the kernel's rseq notify-resume invalidates each thread's cached
slab pointer.  With rseq unregistered by ScopedFakeCpuId that never happens,
so the harness re-registers rseq after each such instruction; otherwise the
thread would keep using a slab that had been drained, unpopulated, or
madvised away.  TestStaticForwarder::AllocReportedImpending now allocates
through AllocCpuCache so that resized slabs carry the alignment
ReleaseSlabMetadataForDrainedCpus checks for.

The harness records every mapping handed out through the forwarder and
unmaps the survivors at teardown, since CpuCache retains one slab per shift
for reuse and only Deactivate() returns the current one.

PiperOrigin-RevId: 1005924977
2026-10-23 15:52:49 -07:00
Chris Kennelly 9570647419 Check inverted rollouts on a shared salt at compile time.
PiperOrigin-RevId: 1005875799
2026-10-23 14:39:27 -07:00
Chris Kennelly CA 8109de422e In HugePageAwareAllocator::AllocSmall, pass /*may_have_grown=*/false to Finalize(Range(page, n), /*may_have_grown=*/false) after RefillFiller, because RefillFiller (AllocAndContribute) already invoked ShrinkToUsageLimit for the newly backed or released hugepage before donating it to filler_.
Ensure `PageAllocatorImpl::ShrinkToUsageLimitSlow` preserves `over_limit_ = backed > limits_[kSoft]` before returning early when `limits_[kHard]` is configured alongside `limits_[kSoft]`, so subsequent `Finalize(..., /*may_have_grown=*/false)` calls still enforce `limits_[kSoft]` after `AllocAndContribute` adds the new hugepage to `filler_`. Also accumulate `last_may_have_grown_ |= may_have_grown` in `FakeHugePageStaticForwarder::ShrinkToUsageLimit` across calls within a single allocation.

When compiled with `-DTCMALLOC_INTERNAL_LEGACY_LOCKING2`, `*from_released` is passed to `Finalize` as before.

PiperOrigin-RevId: 1005872181
2026-10-23 14:34:18 -07:00
Chris Kennelly 313ea47a5b Move CpuCachePeer and TestStaticForwarder into mock_cpu_cache.h.
cpu_cache_fuzz will drive CpuCache<TestStaticForwarder> with the same
forwarder and white-box accessors as cpu_cache_test, so hoist CpuCachePeer,
DynamicSlab, and TestStaticForwarder verbatim from cpu_cache_test.cc into a
testonly mock_cpu_cache library.

PiperOrigin-RevId: 1005826410
2026-10-23 13:32:04 -07:00
Chris Kennelly 7aaa4776b4 Signal sampler initialization via Sampler::RecordedAllocationFast.
The first allocation in a process looks up its size class before `SizeMap` is
initialized, so `fast_alloc` passes `size_class == 0`. Today the
`!UsePerCpuCache()` check routes it to
`alloc_small_sampled_hooks_or_perthread`, which recomputes the size class.
Have `Sampler::RecordAllocationSlow` return `-1` when initializing the sampler
for an unsampled allocation so `weight != 0` in `slow_alloc_small` routes it to
`alloc_small_sampled_hooks_or_perthread` without adding a branch once
initialization activates per-CPU caches.

PiperOrigin-RevId: 1005680372
2026-10-23 10:03:02 -07:00
Chris Kennelly 7d358d5fb4 Add thread-safety lock-order annotations to SampleRecorder.
Give `SampleRecorder::graveyard_` its own `SpinLock` and `dead` member declarations (via `struct Graveyard : public T`) annotated with `ABSL_ACQUIRED_BEFORE(T::lock)` and `ABSL_GUARDED_BY(lock)`, and add `ABSL_LOCKS_EXCLUDED` annotations to `SampleRecorder` methods so Clang `-Wthread-safety-analysis` distinguishes `graveyard_.lock` from per-sample `sample->lock` and statically enforces their acquisition order.

PiperOrigin-RevId: 1005677913
2026-10-23 09:59:12 -07:00
Chris Kennelly b911292890 Serialize per-CPU cache activation with Static::init_lock_.
Concurrent `TCMalloc_Internal_ForceCpuCacheActivation` calls could each see
`!CpuCacheActive()` and call `CpuCache::Activate` twice.
`Static::ActivateCpuCacheIfNecessary` rechecks under `init_lock_`, which
`SlowInitIfNecessary` also holds, so initialization can later activate the
cache after dropping `pageheap_lock`.

PiperOrigin-RevId: 1005667748
2026-10-23 09:45:11 -07:00
Chris Kennelly 12aaa545e4 Consolidate want_disable_* tests into want_disable_test.
Replace the three duplicate
want_disable_{huge_region_more_often,cfl_subbucket_prioritization,subrelease_unbacked_hugepages}_test{,_helper}
pairs with want_disable_test and a shared want_disable_test_helper that writes
MallocExtension::GetStats() to stdout, and exclude the helper source from
open-source exports.
PiperOrigin-RevId: 1005598420
2026-10-23 07:51:53 -07:00
Chris Kennelly 75c9768ef7 Assert stats against Parameters getters in get_stats_test.
Assert reported stats against Parameters getters instead of re-checking
IsExperimentActive in get_stats_test and drop the experiment dependency.

PiperOrigin-RevId: 1005591448
2026-10-23 07:41:02 -07:00
Chris Kennelly CA ae25417842 Check size class in do_realloc before returning old_ptr in-place.
Over-aligned allocations with size <= kMaxSize have sizeclass == 0 in PageMap. Checking only actual_new_size != old_size returned old_ptr in-place when new_size mapped to a small size class of the same byte size, failing CorrectSize and routing subsequent free_sized calls to FreeSmall on a PageHeap large page.

PiperOrigin-RevId: 1005244547
2026-10-22 19:51:37 -07:00
Nuthatch AgentandChris Kennelly 7632d46c5f Run scheduled GitHub Actions CI every 4 hours at minute 17.
Scheduled runs in the GitHub Actions CI workflow pass `--cache_test_results=no` across all 12 matrix jobs to re-run the full test suite without cached test results. Running that uncached 12-job wave every hour creates recurring contention on the remote execution pool.

Change the `schedule` cron expression from `0 * * * *` to `17 */4 * * *` so scheduled test runs execute every 4 hours, and offset the trigger to minute 17 to avoid top-of-the-hour cron traffic spikes.

Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1005214566
2026-10-22 18:44:29 -07:00
Nuthatch AgentandChris Kennelly da0471c9e3 Enable 1% rollout for TCMALLOC_SONIC_COLLAPSE_RELEASED_HUGEPAGES.
Co-authored-by: Chris Kennelly <ckennelly@google.com>
PiperOrigin-RevId: 1005192513
2026-10-22 17:59:24 -07:00
Chris Kennelly 80dcca7a36 Share one Arena across FakeHugePageStaticForwarder instances.
Each forwarder owned an Arena, and Arena never returns its blocks: the first allocation takes a hugepage from the system allocator and keeps it for the life of the process.  huge_page_aware_allocator_fuzz builds a HugePageAwareAllocator, and so a forwarder, per input, leaking about a hugepage of resident memory per execution.  Under Centipede's persistent runner that reached 11 GiB after a minute of fuzzing and the run was killed.

Hand out one process-wide Arena instead.  Metadata an allocator took from it stays allocated once the allocator is gone, which is a few KiB per instance.

PiperOrigin-RevId: 1005182717
2026-10-22 17:40:54 -07:00