Bug Summary

File:root/firefox-clang/memory/build/mozjemalloc.cpp
Warning:line 2019, column 12
Value stored to 'now' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_memory_build0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/build -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D MOZ_MEMORY_IMPL -D MOZ_SUPPORT_LEAKCHECKING -D MOZ_PHC -D MOZ_REPLACE_MALLOC_STATIC -D NON_RANDOM_ARENA_IDS -D MOZJEMALLOC_PROFILING_CALLBACKS -D MOZ_HAS_MOZGLUE -D IMPL_MFBT -I /root/firefox-clang/memory/build -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/build -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ Unified_cpp_memory_build0.cpp
1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7// Portions of this file were originally under the following license:
8//
9// Copyright (C) 2006-2008 Jason Evans <jasone@FreeBSD.org>.
10// All rights reserved.
11// Copyright (C) 2007-2017 Mozilla Foundation.
12//
13// Redistribution and use in source and binary forms, with or without
14// modification, are permitted provided that the following conditions
15// are met:
16// 1. Redistributions of source code must retain the above copyright
17// notice(s), this list of conditions and the following disclaimer as
18// the first lines of this file unmodified other than the possible
19// addition of one or more copyright notices.
20// 2. Redistributions in binary form must reproduce the above copyright
21// notice(s), this list of conditions and the following disclaimer in
22// the documentation and/or other materials provided with the
23// distribution.
24//
25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
26// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
29// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
32// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
34// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36//
37// *****************************************************************************
38//
39// This allocator implementation is designed to provide scalable performance
40// for multi-threaded programs on multi-processor systems. The following
41// features are included for this purpose:
42//
43// + Multiple arenas are used if there are multiple CPUs, which reduces lock
44// contention and cache sloshing.
45//
46// + Cache line sharing between arenas is avoided for internal data
47// structures.
48//
49// + Memory is managed in chunks and runs (chunks can be split into runs),
50// rather than as individual pages. This provides a constant-time
51// mechanism for associating allocations with particular arenas.
52//
53// Allocation requests are rounded up to the nearest size class, and no record
54// of the original request size is maintained. Allocations are broken into
55// categories according to size class. Assuming runtime defaults, the size
56// classes in each category are as follows (for x86, x86_64 and Apple Silicon):
57//
58// |=========================================================|
59// | Category | Subcategory | x86 | x86_64 | Mac ARM |
60// |---------------------------+---------+---------+---------|
61// | Word size | 32 bit | 64 bit | 64 bit |
62// | Page size | 4 Kb | 4 Kb | 16 Kb |
63// |=========================================================|
64// | Small | Tiny | 4/-w | -w | - |
65// | | | 8 | 8/-w | 8 |
66// | |----------------+---------|---------|---------|
67// | | Quantum-spaced | 16 | 16 | 16 |
68// | | | 32 | 32 | 32 |
69// | | | 48 | 48 | 48 |
70// | | | ... | ... | ... |
71// | | | 480 | 480 | 480 |
72// | | | 496 | 496 | 496 |
73// | |----------------+---------|---------|---------|
74// | | Quantum-wide- | 512 | 512 | 512 |
75// | | spaced | 768 | 768 | 768 |
76// | | | ... | ... | ... |
77// | | | 3584 | 3584 | 3584 |
78// | | | 3840 | 3840 | 3840 |
79// | |----------------+---------|---------|---------|
80// | | Sub-page | - | - | 4096 |
81// | | | - | - | 8 kB |
82// |=========================================================|
83// | Large | 4 kB | 4 kB | - |
84// | | 8 kB | 8 kB | - |
85// | | 12 kB | 12 kB | - |
86// | | 16 kB | 16 kB | 16 kB |
87// | | ... | ... | - |
88// | | 32 kB | 32 kB | 32 kB |
89// | | ... | ... | ... |
90// | | 1008 kB | 1008 kB | 1008 kB |
91// | | 1012 kB | 1012 kB | - |
92// | | 1016 kB | 1016 kB | - |
93// | | 1020 kB | 1020 kB | - |
94// |=========================================================|
95// | Huge | 1 MB | 1 MB | 1 MB |
96// | | 2 MB | 2 MB | 2 MB |
97// | | 3 MB | 3 MB | 3 MB |
98// | | ... | ... | ... |
99// |=========================================================|
100//
101// Legend:
102// n: Size class exists for this platform.
103// n/-w: This size class doesn't exist on Windows (see kMinTinyClass).
104// -: This size class doesn't exist for this platform.
105// ...: Size classes follow a pattern here.
106//
107// NOTE: Due to Mozilla bug 691003, we cannot reserve less than one word for an
108// allocation on Linux or Mac. So on 32-bit *nix, the smallest bucket size is
109// 4 bytes, and on 64-bit, the smallest bucket size is 8 bytes.
110//
111// A different mechanism is used for each category:
112//
113// Small : Each size class is segregated into its own set of runs. Each run
114// maintains a bitmap of which regions are free/allocated.
115//
116// Large : Each allocation is backed by a dedicated run. Metadata are stored
117// in the associated arena chunk header maps.
118//
119// Huge : Each allocation is backed by a dedicated contiguous set of chunks.
120// Metadata are stored in a separate red-black tree.
121//
122// *****************************************************************************
123
124#include "mozmemory_wrap.h"
125#include "mozjemalloc.h"
126#include "mozjemalloc_types.h"
127#include "mozjemalloc_profiling.h"
128
129#include <cstring>
130#include <cerrno>
131#include <chrono>
132#include <optional>
133#include <type_traits>
134#ifdef XP_WIN
135# include <io.h>
136# include <windows.h>
137#else
138# include <sys/mman.h>
139# include <unistd.h>
140#endif
141#ifdef XP_DARWIN
142# include <libkern/OSAtomic.h>
143# include <mach/mach_init.h>
144# include <mach/vm_map.h>
145#endif
146
147#include "mozilla/Atomics.h"
148#include "mozilla/Alignment.h"
149#include "mozilla/ArrayUtils.h"
150#include "mozilla/Assertions.h"
151#include "mozilla/CheckedInt.h"
152#include "mozilla/DebugOnly.h"
153#include "mozilla/DoublyLinkedList.h"
154#include "mozilla/HelperMacros.h"
155#include "mozilla/Likely.h"
156#include "mozilla/Literals.h"
157#include "mozilla/MathAlgorithms.h"
158#include "mozilla/RandomNum.h"
159#include "mozilla/RefPtr.h"
160// Note: MozTaggedAnonymousMmap() could call an LD_PRELOADed mmap
161// instead of the one defined here; use only MozTagAnonymousMemory().
162#include "mozilla/TaggedAnonymousMemory.h"
163#include "mozilla/ThreadLocal.h"
164#include "mozilla/UniquePtr.h"
165#include "mozilla/Unused.h"
166#include "mozilla/XorShift128PlusRNG.h"
167#include "mozilla/fallible.h"
168#include "RadixTree.h"
169#include "BaseAlloc.h"
170#include "Chunk.h"
171#include "Constants.h"
172#include "Extent.h"
173#include "Globals.h"
174#include "Mutex.h"
175#include "PHC.h"
176#include "RedBlackTree.h"
177#include "Utils.h"
178#include "Zero.h"
179
180#if defined(XP_WIN)
181# include "mozmemory_stall.h"
182#endif
183
184using namespace mozilla;
185
186#ifdef MOZJEMALLOC_PROFILING_CALLBACKS1
187// MallocProfilerCallbacks is refcounted so that one thread cannot destroy it
188// while another thread accesses it. This means that clearing this value or
189// otherwise dropping a reference to it must not be done while holding an
190// arena's lock.
191MOZ_CONSTINIT[[clang::require_constant_initialization]] static RefPtr<MallocProfilerCallbacks> sCallbacks;
192#endif
193
194// ***************************************************************************
195// MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive.
196#if defined(MALLOC_DECOMMIT) && defined(MALLOC_DOUBLE_PURGE)
197# error MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive.
198#endif
199
200// Set to true once the allocator has been initialized.
201#if defined(_MSC_VER) && !defined(__clang__1)
202// MSVC may create a static initializer for an Atomic<bool>, which may actually
203// run after `malloc_init` has been called once, which triggers multiple
204// initializations.
205// We work around the problem by not using an Atomic<bool> at all. There is a
206// theoretical problem with using `malloc_initialized` non-atomically, but
207// practically, this is only true if `malloc_init` is never called before
208// threads are created.
209static bool malloc_initialized;
210#else
211// We can rely on Relaxed here because this variable is only ever set when
212// holding gInitLock. A thread that still sees it false while another sets it
213// true will enter the same lock, synchronize with the former and check the
214// flag again under the lock.
215static Atomic<bool, MemoryOrdering::Relaxed> malloc_initialized;
216#endif
217
218// This lock must be held while bootstrapping us.
219StaticMutex gInitLock MOZ_UNANNOTATED = {STATIC_MUTEX_INIT{ { 0, 0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, 0, 0, { 0, 0 } } }};
220
221// ***************************************************************************
222// Statistics data structures.
223
224struct arena_stats_t {
225 // Number of bytes currently mapped.
226 size_t mapped;
227
228 // Current number of committed pages (non madvised/decommitted)
229 size_t committed;
230
231 // Per-size-category statistics.
232 size_t allocated_small;
233
234 size_t allocated_large;
235
236 // The number of "memory operations" aka mallocs/frees.
237 uint64_t operations;
238};
239
240// Describe size classes to which allocations are rounded up to.
241// TODO: add large and huge types when the arena allocation code
242// changes in a way that allows it to be beneficial.
243class SizeClass {
244 public:
245 enum ClassType {
246 Tiny,
247 Quantum,
248 QuantumWide,
249 SubPage,
250 Large,
251 };
252
253 explicit inline SizeClass(size_t aSize) {
254 if (aSize <= kMaxTinyClass) {
255 mType = Tiny;
256 mSize = std::max(RoundUpPow2(aSize), kMinTinyClass);
257 } else if (aSize <= kMaxQuantumClass) {
258 mType = Quantum;
259 mSize = QUANTUM_CEILING(aSize)(((aSize) + (kQuantumMask)) & ~(kQuantumMask));
260 } else if (aSize <= kMaxQuantumWideClass) {
261 mType = QuantumWide;
262 mSize = QUANTUM_WIDE_CEILING(aSize)(((aSize) + (kQuantumWideMask)) & ~(kQuantumWideMask));
263 } else if (aSize <= gMaxSubPageClass) {
264 mType = SubPage;
265 mSize = SUBPAGE_CEILING(aSize)(RoundUpPow2(aSize));
266 } else if (aSize <= gMaxLargeClass) {
267 mType = Large;
268 mSize = PAGE_CEILING(aSize)(((aSize) + gPageSizeMask) & ~gPageSizeMask);
269 } else {
270 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid size")do { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Invalid size" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 270); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Invalid size" ")"); do { MOZ_CrashSequence
(__null, 270); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false); __builtin_unreachable(); } while (false
)
;
271 }
272 }
273
274 SizeClass& operator=(const SizeClass& aOther) = default;
275
276 bool operator==(const SizeClass& aOther) { return aOther.mSize == mSize; }
277
278 size_t Size() { return mSize; }
279
280 ClassType Type() { return mType; }
281
282 SizeClass Next() { return SizeClass(mSize + 1); }
283
284 private:
285 ClassType mType;
286 size_t mSize;
287};
288
289// ***************************************************************************
290// Arena data structures.
291
292struct arena_bin_t;
293
294struct ArenaChunkMapLink {
295 static RedBlackTreeNode<arena_chunk_map_t>& GetTreeNode(
296 arena_chunk_map_t* aThis) {
297 return aThis->link;
298 }
299};
300
301struct ArenaAvailTreeTrait : public ArenaChunkMapLink {
302 static inline Order Compare(arena_chunk_map_t* aNode,
303 arena_chunk_map_t* aOther) {
304 size_t size1 = aNode->bits & ~gPageSizeMask;
305 size_t size2 = aOther->bits & ~gPageSizeMask;
306 Order ret = CompareInt(size1, size2);
307 return (ret != Order::eEqual)
308 ? ret
309 : CompareAddr((aNode->bits & CHUNK_MAP_KEY((size_t)0x10U)) ? nullptr : aNode,
310 aOther);
311 }
312};
313
314struct ArenaDirtyChunkTrait {
315 static RedBlackTreeNode<arena_chunk_t>& GetTreeNode(arena_chunk_t* aThis) {
316 return aThis->link_dirty;
317 }
318
319 static inline Order Compare(arena_chunk_t* aNode, arena_chunk_t* aOther) {
320 MOZ_ASSERT(aNode)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNode)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(aNode))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("aNode", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNode" ")");
do { MOZ_CrashSequence(__null, 320); __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
;
321 MOZ_ASSERT(aOther)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOther))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOther", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 321); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOther" ")")
; do { MOZ_CrashSequence(__null, 321); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
322 return CompareAddr(aNode, aOther);
323 }
324};
325
326#ifdef MALLOC_DOUBLE_PURGE
327namespace mozilla {
328
329template <>
330struct GetDoublyLinkedListElement<arena_chunk_t> {
331 static DoublyLinkedListElement<arena_chunk_t>& Get(arena_chunk_t* aThis) {
332 return aThis->chunks_madvised_elem;
333 }
334};
335} // namespace mozilla
336#endif
337
338enum class purge_action_t {
339 None,
340 PurgeNow,
341 Queue,
342};
343
344struct arena_run_t {
345#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED1)
346 uint32_t mMagic;
347# define ARENA_RUN_MAGIC0x384adf93 0x384adf93
348
349 // On 64-bit platforms, having the arena_bin_t pointer following
350 // the mMagic field means there's padding between both fields, making
351 // the run header larger than necessary.
352 // But when MOZ_DIAGNOSTIC_ASSERT_ENABLED is not set, starting the
353 // header with this field followed by the arena_bin_t pointer yields
354 // the same padding. We do want the mMagic field to appear first, so
355 // depending whether MOZ_DIAGNOSTIC_ASSERT_ENABLED is set or not, we
356 // move some field to avoid padding.
357
358 // Number of free regions in run.
359 unsigned mNumFree;
360#endif
361
362 // Used by arena_bin_t::mNonFullRuns.
363 DoublyLinkedListElement<arena_run_t> mRunListElem;
364
365 // Bin this run is associated with.
366 arena_bin_t* mBin;
367
368 // Index of first element that might have a free region.
369 unsigned mRegionsMinElement;
370
371#if !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED1)
372 // Number of free regions in run.
373 unsigned mNumFree;
374#endif
375
376 // Bitmask of in-use regions (0: in use, 1: free).
377 unsigned mRegionsMask[]; // Dynamically sized.
378};
379
380namespace mozilla {
381
382template <>
383struct GetDoublyLinkedListElement<arena_run_t> {
384 static DoublyLinkedListElement<arena_run_t>& Get(arena_run_t* aThis) {
385 return aThis->mRunListElem;
386 }
387};
388
389} // namespace mozilla
390
391struct arena_bin_t {
392 // We use a LIFO ("last-in-first-out") policy to refill non-full runs.
393 //
394 // This has the following reasons:
395 // 1. It is cheap, as all our non-full-runs' book-keeping is O(1), no
396 // tree-balancing or walking is needed.
397 // 2. It also helps to increase the probability for CPU cache hits for the
398 // book-keeping and the reused slots themselves, as the same memory was
399 // most recently touched during free, especially when used from the same
400 // core (or via the same shared cache, depending on the architecture).
401 DoublyLinkedList<arena_run_t> mNonFullRuns;
402
403 // Bin's size class.
404 size_t mSizeClass;
405
406 // Total number of regions in a run for this bin's size class.
407 uint32_t mRunNumRegions;
408
409 // Number of elements in a run's mRegionsMask for this bin's size class.
410 uint32_t mRunNumRegionsMask;
411
412 // Offset of first region in a run for this bin's size class.
413 uint32_t mRunFirstRegionOffset;
414
415 // Current number of runs in this bin, full or otherwise.
416 uint32_t mNumRuns;
417
418 // A constant for fast division by size class. This value is 16 bits wide so
419 // it is placed last.
420 FastDivisor<uint16_t> mSizeDivisor;
421
422 // Total number of pages in a run for this bin's size class.
423 uint8_t mRunSizePages;
424
425 // Amount of overhead runs are allowed to have.
426 static constexpr double kRunOverhead = 1.6_percent;
427 static constexpr double kRunRelaxedOverhead = 2.4_percent;
428
429 // Initialize a bin for the given size class.
430 // The generated run sizes, for a page size of 4 KiB, are:
431 // size|run size|run size|run size|run
432 // class|size class|size class|size class|size
433 // 4 4 KiB 8 4 KiB 16 4 KiB 32 4 KiB
434 // 48 4 KiB 64 4 KiB 80 4 KiB 96 4 KiB
435 // 112 4 KiB 128 8 KiB 144 4 KiB 160 8 KiB
436 // 176 4 KiB 192 4 KiB 208 8 KiB 224 4 KiB
437 // 240 8 KiB 256 16 KiB 272 8 KiB 288 4 KiB
438 // 304 12 KiB 320 12 KiB 336 4 KiB 352 8 KiB
439 // 368 4 KiB 384 8 KiB 400 20 KiB 416 16 KiB
440 // 432 12 KiB 448 4 KiB 464 16 KiB 480 8 KiB
441 // 496 20 KiB 512 32 KiB 768 16 KiB 1024 64 KiB
442 // 1280 24 KiB 1536 32 KiB 1792 16 KiB 2048 128 KiB
443 // 2304 16 KiB 2560 48 KiB 2816 36 KiB 3072 64 KiB
444 // 3328 36 KiB 3584 32 KiB 3840 64 KiB
445 inline void Init(SizeClass aSizeClass);
446};
447
448// We try to keep the above structure aligned with common cache lines sizes,
449// often that's 64 bytes on x86 and ARM, we don't make assumptions for other
450// architectures.
451#if defined(__x86_64__1) || defined(__aarch64__)
452// On 64bit platforms this structure is often 48 bytes
453// long, which means every other array element will be properly aligned.
454static_assert(sizeof(arena_bin_t) == 48);
455#elif defined(__x86__) || defined(__arm__)
456static_assert(sizeof(arena_bin_t) == 32);
457#endif
458
459// We cannot instantiate
460// Atomic<std::chrono::time_point<std::chrono::steady_clock>>
461// so we explicitly force timestamps to be uint64_t in ns.
462uint64_t GetTimestampNS() {
463 // On most if not all systems we care about the conversion to ns is a no-op,
464 // so we prefer to keep the precision here for performance, but let's be
465 // explicit about it.
466 return std::chrono::floor<std::chrono::nanoseconds>(
467 std::chrono::steady_clock::now())
468 .time_since_epoch()
469 .count();
470}
471
472enum PurgeCondition { PurgeIfThreshold, PurgeUnconditional };
473
474struct arena_t {
475#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED1)
476 uint32_t mMagic;
477# define ARENA_MAGIC0x947d3d24 0x947d3d24
478#endif
479
480 // Linkage for the tree of arenas by id.
481 // This just provides the memory to be used by the collection tree
482 // and thus needs no arena_t::mLock.
483 RedBlackTreeNode<arena_t> mLink;
484
485 // Arena id, that we keep away from the beginning of the struct so that
486 // free list pointers in TypedBaseAlloc<arena_t> don't overflow in it,
487 // and it keeps the value it had after the destructor.
488 arena_id_t mId;
489
490 // Operations on this arena require that lock be locked. The MaybeMutex
491 // class will elude locking if the arena is accessed from a single thread
492 // only (currently only the main thread can be used like this).
493 // Can be acquired while holding gArenas.mLock, but must not be acquired or
494 // held while holding or acquiring gArenas.mPurgeListLock.
495 MaybeMutex mLock MOZ_UNANNOTATED;
496
497 // The lock is required to write to fields of mStats, but it is not needed to
498 // read them, so long as inconsistents reads are okay (fields might not make
499 // sense together).
500 arena_stats_t mStats MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
501
502 // We can read the allocated counts from mStats without a lock:
503 size_t AllocatedBytes() const MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) {
504 return mStats.allocated_small + mStats.allocated_large;
505 }
506
507 // We can read the operations field from mStats without a lock:
508 uint64_t Operations() const MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) {
509 return mStats.operations;
510 }
511
512 private:
513 // Tree of dirty-page-containing chunks this arena manages.
514 RedBlackTree<arena_chunk_t, ArenaDirtyChunkTrait> mChunksDirty
515 MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
516
517#ifdef MALLOC_DOUBLE_PURGE
518 // Head of a linked list of MADV_FREE'd-page-containing chunks this
519 // arena manages.
520 DoublyLinkedList<arena_chunk_t> mChunksMAdvised MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
521#endif
522
523 // In order to avoid rapid chunk allocation/deallocation when an arena
524 // oscillates right on the cusp of needing a new chunk, cache the most
525 // recently freed chunk. The spare is left in the arena's chunk trees
526 // until it is deleted.
527 //
528 // There is one spare chunk per arena, rather than one spare total, in
529 // order to avoid interactions between multiple threads that could make
530 // a single spare inadequate.
531 arena_chunk_t* mSpare MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
532
533 // A per-arena opt-in to randomize the offset of small allocations
534 // Needs no lock, read-only.
535 bool mRandomizeSmallAllocations;
536
537 // A pseudorandom number generator. Initially null, it gets initialized
538 // on first use to avoid recursive malloc initialization (e.g. on OSX
539 // arc4random allocates memory).
540 mozilla::non_crypto::XorShift128PlusRNG* mPRNG MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
541 bool mIsPRNGInitializing MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
542
543 public:
544 // Whether this is a private arena. Multiple public arenas are just a
545 // performance optimization and not a safety feature.
546 //
547 // Since, for example, we don't want thread-local arenas to grow too much, we
548 // use the default arena for bigger allocations. We use this member to allow
549 // realloc() to switch out of our arena if needed (which is not allowed for
550 // private arenas for security).
551 // Needs no lock, read-only.
552 bool mIsPrivate;
553
554 // Current count of pages within unused runs that are potentially
555 // dirty, and for which madvise(... MADV_FREE) has not been called. By
556 // tracking this, we can institute a limit on how much dirty unused
557 // memory is mapped for each arena.
558 size_t mNumDirty MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
559
560 // Precalculated value for faster checks.
561 size_t mMaxDirty MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
562
563 // The current number of pages that are available without a system call (but
564 // probably a page fault).
565 size_t mNumMAdvised MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
566 size_t mNumFresh MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
567
568 // Maximum value allowed for mNumDirty.
569 // Needs no lock, read-only.
570 size_t mMaxDirtyBase;
571
572 // Needs no lock, read-only.
573 int32_t mMaxDirtyIncreaseOverride;
574 int32_t mMaxDirtyDecreaseOverride;
575
576 // The link to gArenas.mOutstandingPurges.
577 // Note that this must only be accessed while holding gArenas.mPurgeListLock
578 // (but not arena_t.mLock !) through gArenas.mOutstandingPurges.
579 DoublyLinkedListElement<arena_t> mPurgeListElem;
580
581 // A "significant reuse" is when a dirty page is used for a new allocation,
582 // it has the CHUNK_MAP_DIRTY bit cleared and CHUNK_MAP_ALLOCATED set.
583 //
584 // Timestamp of the last time we saw a significant reuse (in ns).
585 // Note that this variable is written very often from many threads and read
586 // only sparsely on the main thread, but when we read it we need to see the
587 // chronologically latest write asap (so we cannot use Relaxed).
588 Atomic<uint64_t> mLastSignificantReuseNS;
589
590 public:
591 // A flag that indicates if arena will be Purge()'d.
592 //
593 // It is set either when a thread commits to adding it to mOutstandingPurges
594 // or when imitating a Purge. Cleared only by Purge when we know we are
595 // completely done. This is used to avoid accessing the list (and list lock)
596 // on every call to ShouldStartPurge() and to avoid deleting arenas that
597 // another thread is purging.
598 bool mIsPurgePending MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
599
600 // A mirror of ArenaCollection::mIsDeferredPurgeEnabled, here only to
601 // optimize memory reads in ShouldStartPurge().
602 bool mIsDeferredPurgeEnabled MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
603
604 // True if the arena is in the process of being destroyed, and needs to be
605 // released after a concurrent purge completes.
606 bool mMustDeleteAfterPurge MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock))) = false;
607
608 // mLabel describes the label for the firefox profiler. It's stored in a
609 // fixed size area including a null terminating byte. The actual maximum
610 // length of the string is one less than LABEL_MAX_CAPACITY;
611 static constexpr size_t LABEL_MAX_CAPACITY = 128;
612 char mLabel[LABEL_MAX_CAPACITY];
613
614 private:
615 // Size/address-ordered tree of this arena's available runs. This tree
616 // is used for first-best-fit run allocation.
617 RedBlackTree<arena_chunk_map_t, ArenaAvailTreeTrait> mRunsAvail
618 MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
619
620 public:
621 // mBins is used to store rings of free regions of the following sizes,
622 // assuming a 16-byte quantum, 4kB pagesize, and default MALLOC_OPTIONS.
623 //
624 // | mBins[i] | size |
625 // +----------+------+
626 // | 0 | 2 |
627 // | 1 | 4 |
628 // | 2 | 8 |
629 // +----------+------+
630 // | 3 | 16 |
631 // | 4 | 32 |
632 // | 5 | 48 |
633 // | 6 | 64 |
634 // | : :
635 // | : :
636 // | 33 | 496 |
637 // | 34 | 512 |
638 // +----------+------+
639 // | 35 | 768 |
640 // | 36 | 1024 |
641 // | : :
642 // | : :
643 // | 46 | 3584 |
644 // | 47 | 3840 |
645 // +----------+------+
646 arena_bin_t mBins[] MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock))); // Dynamically sized.
647
648 explicit arena_t(arena_params_t* aParams, bool aIsPrivate);
649 ~arena_t();
650
651 void ResetSmallAllocRandomization();
652
653 void InitPRNG() MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
654
655 private:
656 void InitChunk(arena_chunk_t* aChunk, size_t aMinCommittedPages)
657 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
658
659 // Remove the chunk from the arena. This removes it from all the page counts.
660 // It assumes its run has already been removed and lets the caller clear
661 // mSpare as necessary.
662 bool RemoveChunk(arena_chunk_t* aChunk) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
663
664 // This may return a chunk that should be destroyed with chunk_dealloc outside
665 // of the arena lock. It is not the same chunk as was passed in (since that
666 // chunk now becomes mSpare).
667 [[nodiscard]] arena_chunk_t* DemoteChunkToSpare(arena_chunk_t* aChunk)
668 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
669
670 // Try to merge the run with its neighbours. Returns the new index of the run
671 // (since it may have merged with an earlier one).
672 size_t TryCoalesce(arena_chunk_t* aChunk, size_t run_ind, size_t run_pages,
673 size_t size) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
674
675 arena_run_t* AllocRun(size_t aSize, bool aLarge, bool aZero)
676 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
677
678 arena_chunk_t* DallocRun(arena_run_t* aRun, bool aDirty) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
679
680 [[nodiscard]] bool SplitRun(arena_run_t* aRun, size_t aSize, bool aLarge,
681 bool aZero) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
682
683 void TrimRunHead(arena_chunk_t* aChunk, arena_run_t* aRun, size_t aOldSize,
684 size_t aNewSize) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
685
686 void TrimRunTail(arena_chunk_t* aChunk, arena_run_t* aRun, size_t aOldSize,
687 size_t aNewSize, bool dirty) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
688
689 arena_run_t* GetNewEmptyBinRun(arena_bin_t* aBin) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
690
691 inline arena_run_t* GetNonFullBinRun(arena_bin_t* aBin) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
692
693 inline uint8_t FindFreeBitInMask(uint32_t aMask, uint32_t& aRng)
694 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
695
696 inline void* ArenaRunRegAlloc(arena_run_t* aRun, arena_bin_t* aBin)
697 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
698
699 inline void* MallocSmall(size_t aSize, bool aZero) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
700
701 void* MallocLarge(size_t aSize, bool aZero) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
702
703 void* MallocHuge(size_t aSize, bool aZero) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
704
705 void* PallocLarge(size_t aAlignment, size_t aSize, size_t aAllocSize)
706 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
707
708 void* PallocHuge(size_t aSize, size_t aAlignment, bool aZero)
709 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
710
711 void RallocShrinkLarge(arena_chunk_t* aChunk, void* aPtr, size_t aSize,
712 size_t aOldSize) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
713
714 bool RallocGrowLarge(arena_chunk_t* aChunk, void* aPtr, size_t aSize,
715 size_t aOldSize) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
716
717 void* RallocSmallOrLarge(void* aPtr, size_t aSize, size_t aOldSize)
718 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
719
720 void* RallocHuge(void* aPtr, size_t aSize, size_t aOldSize)
721 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
722
723 public:
724 inline void* Malloc(size_t aSize, bool aZero) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
725
726 void* Palloc(size_t aAlignment, size_t aSize) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
727
728 // This may return a chunk that should be destroyed with chunk_dealloc outside
729 // of the arena lock. It is not the same chunk as was passed in (since that
730 // chunk now becomes mSpare).
731 [[nodiscard]] inline arena_chunk_t* DallocSmall(arena_chunk_t* aChunk,
732 void* aPtr,
733 arena_chunk_map_t* aMapElm)
734 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
735
736 [[nodiscard]] arena_chunk_t* DallocLarge(arena_chunk_t* aChunk, void* aPtr)
737 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
738
739 void* Ralloc(void* aPtr, size_t aSize, size_t aOldSize) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
740
741 void UpdateMaxDirty() MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
742
743#ifdef MALLOC_DECOMMIT
744 // During a commit operation (for aReqPages) we have the opportunity of
745 // commiting at most aRemPages additional pages. How many should we commit to
746 // amortise system calls?
747 size_t ExtraCommitPages(size_t aReqPages, size_t aRemainingPages)
748 MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
749#endif
750
751 // Purge some dirty pages.
752 //
753 // When this is called the caller has already tested ShouldStartPurge()
754 // (possibly on another thread asychronously) or is passing
755 // PurgeUnconditional. However because it's called without the lock it will
756 // recheck ShouldContinuePurge() before doing any work.
757 //
758 // It may purge a number of runs within a single chunk before returning. It
759 // will return Continue if there's more work to do in other chunks
760 // (ShouldContinuePurge()).
761 //
762 // To release more pages from other chunks then it's best to call Purge
763 // in a loop, looping when it returns Continue.
764 //
765 // This must be called without the mLock held (it'll take the lock).
766 //
767 ArenaPurgeResult Purge(PurgeCondition aCond, PurgeStats& aStats)
768 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
769
770 // Run Purge() in a loop. If sCallback is non-null then collect statistics and
771 // publish them through the callback, aCaller should be used to identify the
772 // caller in the profiling data.
773 //
774 // aCond - when to stop purging
775 // aCaller - a string representing the caller, this is used for
776 // profiling
777 // aReuseGraceMS - Stop purging the arena if it was used within this many
778 // milliseconds. Or 0 to ignore recent reuse.
779 // aKeepGoing - Optional function to implement a time budget.
780 //
781 ArenaPurgeResult PurgeLoop(
782 PurgeCondition aCond, const char* aCaller, uint32_t aReuseGraceMS = 0,
783 Maybe<std::function<bool()>> aKeepGoing = Nothing()) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
784
785 class PurgeInfo {
786 private:
787 size_t mDirtyInd = 0;
788 size_t mDirtyNPages = 0;
789 size_t mFreeRunInd = 0;
790 size_t mFreeRunLen = 0;
791
792 public:
793 arena_t& mArena;
794
795 arena_chunk_t* mChunk = nullptr;
796
797 private:
798 PurgeStats& mPurgeStats;
799
800 public:
801 size_t FreeRunLenBytes() const { return mFreeRunLen << gPageSize2Pow; }
802
803 // The last index of the free run.
804 size_t FreeRunLastInd() const { return mFreeRunInd + mFreeRunLen - 1; }
805
806 void* DirtyPtr() const {
807 return (void*)(uintptr_t(mChunk) + (mDirtyInd << gPageSize2Pow));
808 }
809
810 size_t DirtyLenBytes() const { return mDirtyNPages << gPageSize2Pow; }
811
812 // Purging memory is seperated into 3 phases.
813 // * FindDirtyPages() which find the dirty pages in a chunk and marks the
814 // run and chunk as busy while holding the lock.
815 // * Release the pages (without the lock)
816 // * UpdatePagesAndCounts() which marks the dirty pages as not-dirty and
817 // updates other counters (while holding the lock).
818 //
819 // FindDirtyPages() will return false purging should not continue purging in
820 // this chunk. Either because it has no dirty pages or is dying.
821 bool FindDirtyPages(bool aPurgedOnce) MOZ_REQUIRES(mArena.mLock)__attribute__((exclusive_locks_required(mArena.mLock)));
822
823 // Returns a pair, the first field indicates if there are more dirty pages
824 // remaining in the current chunk. The second field if non-null points to a
825 // chunk that must be released by the caller.
826 std::pair<bool, arena_chunk_t*> UpdatePagesAndCounts()
827 MOZ_REQUIRES(mArena.mLock)__attribute__((exclusive_locks_required(mArena.mLock)));
828
829 // FinishPurgingInChunk() is used whenever we decide to stop purging in a
830 // chunk, This could be because there are no more dirty pages, or the chunk
831 // is dying, or we hit the arena-level threshold.
832 void FinishPurgingInChunk(bool aAddToMAdvised) MOZ_REQUIRES(mArena.mLock)__attribute__((exclusive_locks_required(mArena.mLock)));
833
834 explicit PurgeInfo(arena_t& arena, arena_chunk_t* chunk, PurgeStats& stats)
835 : mArena(arena), mChunk(chunk), mPurgeStats(stats) {}
836 };
837
838 void HardPurge();
839
840 // Check mNumDirty against EffectiveMaxDirty and return the appropriate
841 // action to be taken by MayDoOrQueuePurge (outside mLock's scope).
842 //
843 // None: Nothing to do.
844 // PurgeNow: Immediate synchronous purge.
845 // Queue: Add a new purge request.
846 //
847 // Note that in the case of deferred purge this function takes into account
848 // mIsDeferredPurgeNeeded to avoid useless operations on the purge list
849 // that would require gArenas.mPurgeListLock.
850 inline purge_action_t ShouldStartPurge() MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
851
852 // Take action according to ShouldStartPurge.
853 inline void MayDoOrQueuePurge(purge_action_t aAction, const char* aCaller)
854 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
855
856 // Check the EffectiveHalfMaxDirty threshold to decide if we continue purge.
857 // This threshold is lower than ShouldStartPurge to have some hysteresis.
858 bool ShouldContinuePurge(PurgeCondition aCond) MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock))) {
859 return (mNumDirty > ((aCond == PurgeUnconditional) ? 0 : mMaxDirty >> 1));
860 }
861
862 // Update the last significant reuse timestamp.
863 void NotifySignificantReuse() MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
864
865 bool IsMainThreadOnly() const { return !mLock.LockIsEnabled(); }
866
867 void* operator new(size_t aCount) = delete;
868
869 void* operator new(size_t aCount, const fallible_t&) noexcept;
870
871 void operator delete(void*);
872};
873
874namespace mozilla {
875
876template <>
877struct GetDoublyLinkedListElement<arena_t> {
878 static DoublyLinkedListElement<arena_t>& Get(arena_t* aThis) {
879 return aThis->mPurgeListElem;
880 }
881};
882
883} // namespace mozilla
884
885struct ArenaTreeTrait {
886 static RedBlackTreeNode<arena_t>& GetTreeNode(arena_t* aThis) {
887 return aThis->mLink;
888 }
889
890 static inline Order Compare(arena_t* aNode, arena_t* aOther) {
891 MOZ_ASSERT(aNode)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNode)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(aNode))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("aNode", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNode" ")");
do { MOZ_CrashSequence(__null, 891); __attribute__((nomerge)
) ::abort(); } while (false); } } while (false)
;
892 MOZ_ASSERT(aOther)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOther))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOther", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOther" ")")
; do { MOZ_CrashSequence(__null, 892); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
893 return CompareInt(aNode->mId, aOther->mId);
894 }
895};
896
897// Bookkeeping for all the arenas used by the allocator.
898// Arenas are separated in two categories:
899// - "private" arenas, used through the moz_arena_* API
900// - all the other arenas: the default arena, and thread-local arenas,
901// used by the standard API.
902class ArenaCollection {
903 public:
904 bool Init() MOZ_REQUIRES(gInitLock)__attribute__((exclusive_locks_required(gInitLock))) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock))) {
905 MOZ_PUSH_IGNORE_THREAD_SAFETYGCC diagnostic push GCC diagnostic ignored "-Wthread-safety"
906 mArenas.Init();
907 mPrivateArenas.Init();
908#ifndef NON_RANDOM_ARENA_IDS1
909 mMainThreadArenas.Init();
910#endif
911 MOZ_POP_THREAD_SAFETYGCC diagnostic pop
912 arena_params_t params;
913 // The main arena allows more dirty pages than the default for other arenas.
914 params.mMaxDirty = opt_dirty_max;
915 params.mLabel = "Default";
916 mDefaultArena =
917 mLock.Init() ? CreateArena(/* aIsPrivate = */ false, &params) : nullptr;
918 mPurgeListLock.Init();
919 mIsDeferredPurgeEnabled = false;
920 return bool(mDefaultArena);
921 }
922
923 // The requested arena must exist.
924 inline arena_t* GetById(arena_id_t aArenaId, bool aIsPrivate)
925 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
926
927 arena_t* CreateArena(bool aIsPrivate, arena_params_t* aParams)
928 MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock)));
929
930 void DisposeArena(arena_t* aArena) MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock))) {
931 // This will not call MayPurge but only unlink the element in case.
932 // It returns true if we successfully removed the item from the list,
933 // meaning we have exclusive access to it and can delete it.
934 bool delete_now = RemoveFromOutstandingPurges(aArena);
935
936 {
937 MutexAutoLock lock(mLock);
938 Tree& tree =
939#ifndef NON_RANDOM_ARENA_IDS1
940 aArena->IsMainThreadOnly() ? mMainThreadArenas :
941#endif
942 mPrivateArenas;
943
944 MOZ_RELEASE_ASSERT(tree.Search(aArena), "Arena not in tree")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(tree.Search(aArena))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(tree.Search(aArena)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("tree.Search(aArena)"
" (" "Arena not in tree" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 944); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "tree.Search(aArena)"
") (" "Arena not in tree" ")"); do { MOZ_CrashSequence(__null
, 944); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
;
945 tree.Remove(aArena);
946 mNumOperationsDisposedArenas += aArena->Operations();
947 }
948 {
949 MutexAutoLock lock(aArena->mLock);
950 if (!aArena->mIsPurgePending) {
951 // If no purge was pending then we have exclusive access to the
952 // arena and must delete it.
953 delete_now = true;
954 } else if (!delete_now) {
955 // The remaining possibility, when we failed to remove the arena from
956 // the list (because a purging thread alredy did so) then that thread
957 // will be the last thread holding the arena and is now responsible for
958 // deleting it.
959 aArena->mMustDeleteAfterPurge = true;
960
961 // Not that it's not possible to have checked the list of pending purges
962 // BEFORE the arena was added to the list because that would mean that
963 // an operation on the arena (free or realloc) was running concurrently
964 // with deletion, which would be a memory error and the assertions in
965 // the destructor help check for that.
966 }
967 }
968
969 if (delete_now) {
970 delete aArena;
971 }
972 }
973
974 void SetDefaultMaxDirtyPageModifier(int32_t aModifier) {
975 {
976 MutexAutoLock lock(mLock);
977 mDefaultMaxDirtyPageModifier = aModifier;
978 for (auto* arena : iter()) {
979 // We can only update max-dirty for main-thread-only arenas from the
980 // main thread.
981 if (!arena->IsMainThreadOnly() || IsOnMainThreadWeak()) {
982 arena->UpdateMaxDirty();
983 }
984 }
985 }
986 }
987
988 int32_t DefaultMaxDirtyPageModifier() { return mDefaultMaxDirtyPageModifier; }
989
990 using Tree = RedBlackTree<arena_t, ArenaTreeTrait>;
991
992 struct Iterator : Tree::Iterator {
993 explicit Iterator(Tree* aTree, Tree* aSecondTree,
994 Tree* aThirdTree = nullptr)
995 : Tree::Iterator(aTree),
996 mSecondTree(aSecondTree),
997 mThirdTree(aThirdTree) {}
998
999 Item<Iterator> begin() {
1000 return Item<Iterator>(this, *Tree::Iterator::begin());
1001 }
1002
1003 Item<Iterator> end() { return Item<Iterator>(this, nullptr); }
1004
1005 arena_t* Next() {
1006 arena_t* result = Tree::Iterator::Next();
1007 if (!result && mSecondTree) {
1008 new (this) Iterator(mSecondTree, mThirdTree);
1009 result = *Tree::Iterator::begin();
1010 }
1011 return result;
1012 }
1013
1014 private:
1015 Tree* mSecondTree;
1016 Tree* mThirdTree;
1017 };
1018
1019 Iterator iter() MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock))) {
1020#ifdef NON_RANDOM_ARENA_IDS1
1021 return Iterator(&mArenas, &mPrivateArenas);
1022#else
1023 return Iterator(&mArenas, &mPrivateArenas, &mMainThreadArenas);
1024#endif
1025 }
1026
1027 inline arena_t* GetDefault() { return mDefaultArena; }
1028
1029 // Guards the collection of arenas. Must not be acquired while holding
1030 // a single arena's lock or mPurgeListLock.
1031 Mutex mLock MOZ_UNANNOTATED;
1032
1033 // Guards only the list of outstanding purge requests. Can be acquired
1034 // while holding gArenas.mLock, but must not be acquired or held while
1035 // holding or acquiring a single arena's lock.
1036 Mutex mPurgeListLock;
1037
1038 // We're running on the main thread which is set by a call to SetMainThread().
1039 bool IsOnMainThread() const {
1040 return mMainThreadId.isSome() &&
1041 ThreadIdEqual(mMainThreadId.value(), GetThreadId());
1042 }
1043
1044 // We're running on the main thread or SetMainThread() has never been called.
1045 bool IsOnMainThreadWeak() const {
1046 return mMainThreadId.isNothing() || IsOnMainThread();
1047 }
1048
1049 // After a fork set the new thread ID in the child.
1050 // This is done as the first thing after a fork, before mLock even re-inits.
1051 void ResetMainThread() MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock))) {
1052 // The post fork handler in the child can run from a MacOS worker thread,
1053 // so we can't set our main thread to it here. Instead we have to clear it.
1054 mMainThreadId = Nothing();
1055 }
1056
1057 void SetMainThread() MOZ_EXCLUDES(mLock)__attribute__((locks_excluded(mLock))) {
1058 MutexAutoLock lock(mLock);
1059 MOZ_ASSERT(mMainThreadId.isNothing())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMainThreadId.isNothing())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMainThreadId.isNothing())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("mMainThreadId.isNothing()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1059); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mMainThreadId.isNothing()" ")"); do { MOZ_CrashSequence
(__null, 1059); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1060 mMainThreadId = Some(GetThreadId());
1061 }
1062
1063 // This requires the lock to get a consistent count across all the active
1064 // + disposed arenas.
1065 uint64_t OperationsDisposedArenas() MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock))) {
1066 return mNumOperationsDisposedArenas;
1067 }
1068
1069 // Enable or disable the lazy purge.
1070 //
1071 // Returns the former state of enablement.
1072 // This is a global setting for all arenas. Changing it may cause an
1073 // immediate purge for all arenas.
1074 bool SetDeferredPurge(bool aEnable) {
1075 MOZ_ASSERT(IsOnMainThreadWeak())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsOnMainThreadWeak())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsOnMainThreadWeak()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsOnMainThreadWeak()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1075); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsOnMainThreadWeak()" ")"); do { MOZ_CrashSequence
(__null, 1075); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1076
1077 bool ret = mIsDeferredPurgeEnabled;
1078 {
1079 MutexAutoLock lock(mLock);
1080 mIsDeferredPurgeEnabled = aEnable;
1081 for (auto* arena : iter()) {
1082 MaybeMutexAutoLock lock(arena->mLock);
1083 arena->mIsDeferredPurgeEnabled = aEnable;
1084 }
1085 }
1086 if (ret != aEnable) {
1087 MayPurgeAll(PurgeIfThreshold, __func__);
1088 }
1089 return ret;
1090 }
1091
1092 bool IsDeferredPurgeEnabled() { return mIsDeferredPurgeEnabled; }
1093
1094 // Set aside a new purge request for aArena.
1095 void AddToOutstandingPurges(arena_t* aArena) MOZ_EXCLUDES(mPurgeListLock)__attribute__((locks_excluded(mPurgeListLock)));
1096
1097 // Remove an unhandled purge request for aArena. Returns true if the arena
1098 // was in the list.
1099 bool RemoveFromOutstandingPurges(arena_t* aArena)
1100 MOZ_EXCLUDES(mPurgeListLock)__attribute__((locks_excluded(mPurgeListLock)));
1101
1102 // Execute all outstanding purge requests, if any.
1103 void MayPurgeAll(PurgeCondition aCond, const char* aCaller);
1104
1105 // Purge some dirty memory, based on purge requests, returns true if there are
1106 // more to process.
1107 //
1108 // Returns a may_purge_now_result_t with the following meaning:
1109 // Done: Purge has completed for all arenas.
1110 // NeedsMore: There may be some arenas that needs to be purged now.
1111 // WantsLater: There is at least one arena that might want a purge later,
1112 // according to aReuseGraceMS.
1113 //
1114 // Parameters:
1115 // aPeekOnly: If true, check only if there is work to do without doing it.
1116 // aReuseGraceMS: The time to wait with purge after a
1117 // significant re-use happened for an arena.
1118 // aKeepGoing: If this returns false purging will cease.
1119 //
1120 // This could exit for 3 different reasons.
1121 // - There are no more requests (it returns false)
1122 // - There are more requests but aKeepGoing() returned false. (returns true)
1123 // - One arena is completely purged, (returns true).
1124 //
1125 may_purge_now_result_t MayPurgeSteps(
1126 bool aPeekOnly, uint32_t aReuseGraceMS,
1127 const Maybe<std::function<bool()>>& aKeepGoing);
1128
1129 private:
1130 const static arena_id_t MAIN_THREAD_ARENA_BIT = 0x1;
1131
1132#ifndef NON_RANDOM_ARENA_IDS1
1133 // Can be called with or without lock, depending on aTree.
1134 inline arena_t* GetByIdInternal(Tree& aTree, arena_id_t aArenaId);
1135
1136 arena_id_t MakeRandArenaId(bool aIsMainThreadOnly) const MOZ_REQUIRES(mLock)__attribute__((exclusive_locks_required(mLock)));
1137#endif
1138 static bool ArenaIdIsMainThreadOnly(arena_id_t aArenaId) {
1139 return aArenaId & MAIN_THREAD_ARENA_BIT;
1140 }
1141
1142 arena_t* mDefaultArena;
1143 arena_id_t mLastPublicArenaId MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
1144
1145 // Accessing mArenas and mPrivateArenas can only be done while holding mLock.
1146 Tree mArenas MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
1147 Tree mPrivateArenas MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
1148
1149#ifdef NON_RANDOM_ARENA_IDS1
1150 // Arena ids are pseudo-obfuscated/deobfuscated based on these values randomly
1151 // initialized on first use.
1152 arena_id_t mArenaIdKey = 0;
1153 int8_t mArenaIdRotation = 0;
1154#else
1155 // Some mMainThreadArenas accesses to mMainThreadArenas can (and should) elude
1156 // the lock, see GetById().
1157 Tree mMainThreadArenas MOZ_GUARDED_BY(mLock)__attribute__((guarded_by(mLock)));
1158#endif
1159
1160 // Set only rarely and then propagated on the same thread to all arenas via
1161 // UpdateMaxDirty(). But also read in ExtraCommitPages on arbitrary threads.
1162 // TODO: Could ExtraCommitPages use arena_t::mMaxDirty instead ?
1163 Atomic<int32_t> mDefaultMaxDirtyPageModifier;
1164 // This is never changed except for forking, and it does not need mLock.
1165 Maybe<ThreadId> mMainThreadId;
1166
1167 // The number of operations that happened in arenas that have since been
1168 // destroyed.
1169 uint64_t mNumOperationsDisposedArenas = 0;
1170
1171 // Linked list of outstanding purges. This list has no particular order.
1172 // It is ok for an arena to be in this list even if mIsPurgePending is false,
1173 // it will just cause an extra round of a (most likely no-op) purge.
1174 // It is not ok to not be in this list but have mIsPurgePending set to true,
1175 // as this would prevent any future purges for this arena (except for during
1176 // MayPurgeStep or Purge).
1177 DoublyLinkedList<arena_t> mOutstandingPurges MOZ_GUARDED_BY(mPurgeListLock)__attribute__((guarded_by(mPurgeListLock)));
1178 // Flag if we should defer purge to later. Only ever set when holding the
1179 // collection lock. Read only during arena_t ctor.
1180 Atomic<bool> mIsDeferredPurgeEnabled;
1181};
1182
1183MOZ_RUNINIT static ArenaCollection gArenas;
1184
1185// Protects huge allocation-related data structures.
1186static Mutex huge_mtx;
1187
1188// Tree of chunks that are stand-alone huge allocations.
1189static RedBlackTree<extent_node_t, ExtentTreeTrait> huge
1190 MOZ_GUARDED_BY(huge_mtx)__attribute__((guarded_by(huge_mtx)));
1191
1192// Huge allocation statistics.
1193static size_t huge_allocated MOZ_GUARDED_BY(huge_mtx)__attribute__((guarded_by(huge_mtx)));
1194static size_t huge_mapped MOZ_GUARDED_BY(huge_mtx)__attribute__((guarded_by(huge_mtx)));
1195static uint64_t huge_operations MOZ_GUARDED_BY(huge_mtx)__attribute__((guarded_by(huge_mtx)));
1196
1197// ******
1198// Arenas.
1199
1200// The arena associated with the current thread (per
1201// jemalloc_thread_local_arena) On OSX, __thread/thread_local circles back
1202// calling malloc to allocate storage on first access on each thread, which
1203// leads to an infinite loop, but pthread-based TLS somehow doesn't have this
1204// problem.
1205#if !defined(XP_DARWIN)
1206static MOZ_THREAD_LOCAL(arena_t*)__thread ::mozilla::detail::ThreadLocal< arena_t*, ::mozilla
::detail::ThreadLocalNativeStorage>
thread_arena;
1207#else
1208static detail::ThreadLocal<arena_t*, detail::ThreadLocalKeyStorage>
1209 thread_arena;
1210#endif
1211
1212// ***************************************************************************
1213// Begin forward declarations.
1214
1215static void huge_dalloc(void* aPtr, arena_t* aArena);
1216static bool malloc_init_hard();
1217
1218#ifndef XP_WIN
1219# ifdef XP_DARWIN
1220# define FORK_HOOKstatic extern "C"
1221# else
1222# define FORK_HOOKstatic static
1223# endif
1224FORK_HOOKstatic void _malloc_prefork(void);
1225FORK_HOOKstatic void _malloc_postfork_parent(void);
1226FORK_HOOKstatic void _malloc_postfork_child(void);
1227# ifdef XP_DARWIN
1228FORK_HOOKstatic void _malloc_postfork(void);
1229# endif
1230#endif
1231
1232// End forward declarations.
1233// ***************************************************************************
1234
1235// FreeBSD's pthreads implementation calls malloc(3), so the malloc
1236// implementation has to take pains to avoid infinite recursion during
1237// initialization.
1238// Returns whether the allocator was successfully initialized.
1239static inline bool malloc_init() {
1240 if (!malloc_initialized) {
1241 return malloc_init_hard();
1242 }
1243 return true;
1244}
1245
1246#ifdef ANDROID
1247// Android's pthread.h does not declare pthread_atfork() until SDK 21.
1248extern "C" MOZ_EXPORT__attribute__((visibility("default"))) int pthread_atfork(void (*)(void), void (*)(void),
1249 void (*)(void));
1250#endif
1251
1252// ***************************************************************************
1253// Begin Utility functions/macros.
1254
1255#ifdef MOZJEMALLOC_PROFILING_CALLBACKS1
1256namespace mozilla {
1257
1258void jemalloc_set_profiler_callbacks(
1259 RefPtr<MallocProfilerCallbacks>&& aCallbacks) {
1260 sCallbacks = aCallbacks;
1261}
1262
1263} // namespace mozilla
1264#endif
1265
1266template <>
1267arena_t* TypedBaseAlloc<arena_t>::sFirstFree = nullptr;
1268
1269template <>
1270size_t TypedBaseAlloc<arena_t>::size_of() {
1271 // Allocate enough space for trailing bins.
1272 return sizeof(arena_t) + (sizeof(arena_bin_t) * NUM_SMALL_CLASSES(kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses
+ gNumSubPageClasses)
);
1273}
1274
1275// End Utility functions/macros.
1276// ***************************************************************************
1277// Begin arena.
1278
1279static inline arena_t* thread_local_arena(bool enabled) {
1280 arena_t* arena;
1281
1282 if (enabled) {
1283 // The arena will essentially be leaked if this function is
1284 // called with `false`, but it doesn't matter at the moment.
1285 // because in practice nothing actually calls this function
1286 // with `false`, except maybe at shutdown.
1287 arena_params_t params;
1288 params.mLabel = "Thread local";
1289 arena = gArenas.CreateArena(/* aIsPrivate = */ false, &params);
1290 } else {
1291 arena = gArenas.GetDefault();
1292 }
1293 thread_arena.set(arena);
1294 return arena;
1295}
1296
1297inline void MozJemalloc::jemalloc_thread_local_arena(bool aEnabled) {
1298 if (malloc_init()) {
1299 thread_local_arena(aEnabled);
1300 }
1301}
1302
1303// Choose an arena based on a per-thread value.
1304static inline arena_t* choose_arena(size_t size) {
1305 arena_t* ret = nullptr;
1306
1307 // We can only use TLS if this is a PIC library, since for the static
1308 // library version, libc's malloc is used by TLS allocation, which
1309 // introduces a bootstrapping issue.
1310
1311 if (size > kMaxQuantumClass) {
1312 // Force the default arena for larger allocations.
1313 ret = gArenas.GetDefault();
1314 } else {
1315 // Check TLS to see if our thread has requested a pinned arena.
1316 ret = thread_arena.get();
1317 // If ret is non-null, it must not be in the first page.
1318 MOZ_DIAGNOSTIC_ASSERT_IF(ret, (size_t)ret >= gPageSize)do { if (ret) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype((size_t)ret >= gPageSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((size_t)ret >= gPageSize)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(size_t)ret >= gPageSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1318); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(size_t)ret >= gPageSize" ")"
); do { MOZ_CrashSequence(__null, 1318); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
1319 if (!ret) {
1320 // Nothing in TLS. Pin this thread to the default arena.
1321 ret = thread_local_arena(false);
1322 }
1323 }
1324
1325 MOZ_DIAGNOSTIC_ASSERT(ret)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ret)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(ret))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("ret", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1325); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "ret"
")"); do { MOZ_CrashSequence(__null, 1325); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1326 return ret;
1327}
1328
1329inline uint8_t arena_t::FindFreeBitInMask(uint32_t aMask, uint32_t& aRng) {
1330 if (mPRNG != nullptr) {
1331 if (aRng == UINT_MAX(2147483647 *2U +1U)) {
1332 aRng = mPRNG->next() % 32;
1333 }
1334 uint8_t bitIndex;
1335 // RotateRight asserts when provided bad input.
1336 aMask = aRng ? RotateRight(aMask, aRng)
1337 : aMask; // Rotate the mask a random number of slots
1338 bitIndex = CountTrailingZeroes32(aMask);
1339 return (bitIndex + aRng) % 32;
1340 }
1341 return CountTrailingZeroes32(aMask);
1342}
1343
1344inline void* arena_t::ArenaRunRegAlloc(arena_run_t* aRun, arena_bin_t* aBin) {
1345 void* ret;
1346 unsigned i, mask, bit, regind;
1347 uint32_t rndPos = UINT_MAX(2147483647 *2U +1U);
1348
1349 MOZ_DIAGNOSTIC_ASSERT(aRun->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRun->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aRun->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aRun->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1349); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aRun->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 1349); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1350 MOZ_ASSERT(aRun->mRegionsMinElement < aBin->mRunNumRegionsMask)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRun->mRegionsMinElement < aBin->mRunNumRegionsMask
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aRun->mRegionsMinElement < aBin->mRunNumRegionsMask
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aRun->mRegionsMinElement < aBin->mRunNumRegionsMask"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1350); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aRun->mRegionsMinElement < aBin->mRunNumRegionsMask"
")"); do { MOZ_CrashSequence(__null, 1350); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1351
1352 // Move the first check outside the loop, so that aRun->mRegionsMinElement can
1353 // be updated unconditionally, without the possibility of updating it
1354 // multiple times.
1355 i = aRun->mRegionsMinElement;
1356 mask = aRun->mRegionsMask[i];
1357 if (mask != 0) {
1358 bit = FindFreeBitInMask(mask, rndPos);
1359
1360 regind = ((i << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3)) + bit);
1361 MOZ_ASSERT(regind < aBin->mRunNumRegions)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(regind < aBin->mRunNumRegions)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(regind < aBin->mRunNumRegions
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"regind < aBin->mRunNumRegions", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "regind < aBin->mRunNumRegions"
")"); do { MOZ_CrashSequence(__null, 1361); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1362 ret = (void*)(((uintptr_t)aRun) + aBin->mRunFirstRegionOffset +
1363 (aBin->mSizeClass * regind));
1364
1365 // Clear bit.
1366 mask ^= (1U << bit);
1367 aRun->mRegionsMask[i] = mask;
1368
1369 return ret;
1370 }
1371
1372 for (i++; i < aBin->mRunNumRegionsMask; i++) {
1373 mask = aRun->mRegionsMask[i];
1374 if (mask != 0) {
1375 bit = FindFreeBitInMask(mask, rndPos);
1376
1377 regind = ((i << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3)) + bit);
1378 MOZ_ASSERT(regind < aBin->mRunNumRegions)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(regind < aBin->mRunNumRegions)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(regind < aBin->mRunNumRegions
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"regind < aBin->mRunNumRegions", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "regind < aBin->mRunNumRegions"
")"); do { MOZ_CrashSequence(__null, 1378); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1379 ret = (void*)(((uintptr_t)aRun) + aBin->mRunFirstRegionOffset +
1380 (aBin->mSizeClass * regind));
1381
1382 // Clear bit.
1383 mask ^= (1U << bit);
1384 aRun->mRegionsMask[i] = mask;
1385
1386 // Make a note that nothing before this element
1387 // contains a free region.
1388 aRun->mRegionsMinElement = i; // Low payoff: + (mask == 0);
1389
1390 return ret;
1391 }
1392 }
1393 // Not reached.
1394 MOZ_DIAGNOSTIC_ASSERT(0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("0", "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1394
); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "0" ")"
); do { MOZ_CrashSequence(__null, 1394); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1395 return nullptr;
1396}
1397
1398static inline void arena_run_reg_dalloc(arena_run_t* run, arena_bin_t* bin,
1399 void* ptr, size_t size) {
1400 uint32_t diff, regind;
1401 unsigned elm, bit;
1402
1403 MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1403); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 1403); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1404
1405 // Avoid doing division with a variable divisor if possible. Using
1406 // actual division here can reduce allocator throughput by over 20%!
1407 diff =
1408 (uint32_t)((uintptr_t)ptr - (uintptr_t)run - bin->mRunFirstRegionOffset);
1409
1410 MOZ_ASSERT(diff <=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(diff <= (static_cast<unsigned>(bin->mRunSizePages
) << gPageSize2Pow))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(diff <= (static_cast<unsigned
>(bin->mRunSizePages) << gPageSize2Pow)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("diff <= (static_cast<unsigned>(bin->mRunSizePages) << gPageSize2Pow)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1411); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "diff <= (static_cast<unsigned>(bin->mRunSizePages) << gPageSize2Pow)"
")"); do { MOZ_CrashSequence(__null, 1411); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1411 (static_cast<unsigned>(bin->mRunSizePages) << gPageSize2Pow))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(diff <= (static_cast<unsigned>(bin->mRunSizePages
) << gPageSize2Pow))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(diff <= (static_cast<unsigned
>(bin->mRunSizePages) << gPageSize2Pow)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("diff <= (static_cast<unsigned>(bin->mRunSizePages) << gPageSize2Pow)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1411); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "diff <= (static_cast<unsigned>(bin->mRunSizePages) << gPageSize2Pow)"
")"); do { MOZ_CrashSequence(__null, 1411); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1412 regind = diff / bin->mSizeDivisor;
1413
1414 MOZ_DIAGNOSTIC_ASSERT(diff == regind * size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(diff == regind * size)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(diff == regind * size))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("diff == regind * size"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1414); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "diff == regind * size" ")"); do
{ MOZ_CrashSequence(__null, 1414); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1415 MOZ_DIAGNOSTIC_ASSERT(regind < bin->mRunNumRegions)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(regind < bin->mRunNumRegions)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(regind < bin->mRunNumRegions
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"regind < bin->mRunNumRegions", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1415); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "regind < bin->mRunNumRegions"
")"); do { MOZ_CrashSequence(__null, 1415); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1416
1417 elm = regind >> (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3);
1418 if (elm < run->mRegionsMinElement) {
1419 run->mRegionsMinElement = elm;
1420 }
1421 bit = regind - (elm << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3));
1422 MOZ_RELEASE_ASSERT((run->mRegionsMask[elm] & (1U << bit)) == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype((run->mRegionsMask[elm] & (1U << bit)) ==
0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((run->mRegionsMask[elm] & (1U << bit)) ==
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(run->mRegionsMask[elm] & (1U << bit)) == 0" " ("
"Double-free?" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1423); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(run->mRegionsMask[elm] & (1U << bit)) == 0"
") (" "Double-free?" ")"); do { MOZ_CrashSequence(__null, 1423
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
1423 "Double-free?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype((run->mRegionsMask[elm] & (1U << bit)) ==
0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((run->mRegionsMask[elm] & (1U << bit)) ==
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(run->mRegionsMask[elm] & (1U << bit)) == 0" " ("
"Double-free?" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1423); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(run->mRegionsMask[elm] & (1U << bit)) == 0"
") (" "Double-free?" ")"); do { MOZ_CrashSequence(__null, 1423
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
1424 run->mRegionsMask[elm] |= (1U << bit);
1425}
1426
1427bool arena_t::SplitRun(arena_run_t* aRun, size_t aSize, bool aLarge,
1428 bool aZero) {
1429 arena_chunk_t* chunk = GetChunkForPtr(aRun);
1430 size_t old_ndirty = chunk->ndirty;
1431 size_t run_ind =
1432 (unsigned)((uintptr_t(aRun) - uintptr_t(chunk)) >> gPageSize2Pow);
1433 size_t total_pages =
1434 (chunk->map[run_ind].bits & ~gPageSizeMask) >> gPageSize2Pow;
1435 size_t need_pages = (aSize >> gPageSize2Pow);
1436 MOZ_ASSERT(need_pages > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(need_pages > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(need_pages > 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("need_pages > 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1436); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "need_pages > 0" ")"); do { MOZ_CrashSequence
(__null, 1436); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1437 MOZ_ASSERT(need_pages <= total_pages)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(need_pages <= total_pages)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(need_pages <= total_pages
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"need_pages <= total_pages", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1437); AnnotateMozCrashReason("MOZ_ASSERT" "(" "need_pages <= total_pages"
")"); do { MOZ_CrashSequence(__null, 1437); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1438 size_t rem_pages = total_pages - need_pages;
1439
1440 MOZ_ASSERT((chunk->map[run_ind].bits & CHUNK_MAP_BUSY) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind].bits & ((size_t)0x100U))
== 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((chunk->map[run_ind].bits & ((size_t)0x100U))
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(chunk->map[run_ind].bits & ((size_t)0x100U)) == 0",
"/root/firefox-clang/memory/build/mozjemalloc.cpp", 1440); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[run_ind].bits & ((size_t)0x100U)) == 0"
")"); do { MOZ_CrashSequence(__null, 1440); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1441
1442#ifdef MALLOC_DECOMMIT
1443 size_t i = 0;
1444 while (i < need_pages) {
1445 MOZ_ASSERT((chunk->map[run_ind + i].bits & CHUNK_MAP_BUSY) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i].bits & ((size_t)0x100U
)) == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((chunk->map[run_ind + i].bits & ((size_t)0x100U
)) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(chunk->map[run_ind + i].bits & ((size_t)0x100U)) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1445); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[run_ind + i].bits & ((size_t)0x100U)) == 0"
")"); do { MOZ_CrashSequence(__null, 1445); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1446
1447 // Commit decommitted pages if necessary. If a decommitted
1448 // page is encountered, commit all needed adjacent decommitted
1449 // pages in one operation, in order to reduce system call
1450 // overhead.
1451 if (chunk->map[run_ind + i].bits & CHUNK_MAP_DECOMMITTED((size_t)0x20U)) {
1452 // Advance i+j to just past the index of the last page
1453 // to commit. Clear CHUNK_MAP_DECOMMITTED along the way.
1454 size_t j;
1455 for (j = 0; i + j < need_pages &&
1456 (chunk->map[run_ind + i + j].bits & CHUNK_MAP_DECOMMITTED((size_t)0x20U));
1457 j++) {
1458 // DECOMMITTED, MADVISED and FRESH are mutually exclusive.
1459 MOZ_ASSERT((chunk->map[run_ind + i + j].bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i + j].bits & (((size_t
)0x80U) | ((size_t)0x40U))) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((chunk->map[run_ind + i +
j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1460); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
")"); do { MOZ_CrashSequence(__null, 1460); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1460 (CHUNK_MAP_FRESH | CHUNK_MAP_MADVISED)) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i + j].bits & (((size_t
)0x80U) | ((size_t)0x40U))) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((chunk->map[run_ind + i +
j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1460); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
")"); do { MOZ_CrashSequence(__null, 1460); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1461 }
1462
1463 // Consider committing more pages to amortise calls to VirtualAlloc.
1464 // This only makes sense at the edge of our run hence the if condition
1465 // here.
1466 if (i + j == need_pages) {
1467 size_t extra_commit = ExtraCommitPages(j, rem_pages);
1468 for (; i + j < need_pages + extra_commit &&
1469 (chunk->map[run_ind + i + j].bits &
1470 CHUNK_MAP_MADVISED_OR_DECOMMITTED(((size_t)0x40U) | ((size_t)0x20U)));
1471 j++) {
1472 MOZ_ASSERT((chunk->map[run_ind + i + j].bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i + j].bits & (((size_t
)0x80U) | ((size_t)0x40U))) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((chunk->map[run_ind + i +
j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1473); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
")"); do { MOZ_CrashSequence(__null, 1473); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1473 (CHUNK_MAP_FRESH | CHUNK_MAP_MADVISED)) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i + j].bits & (((size_t
)0x80U) | ((size_t)0x40U))) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((chunk->map[run_ind + i +
j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1473); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[run_ind + i + j].bits & (((size_t)0x80U) | ((size_t)0x40U))) == 0"
")"); do { MOZ_CrashSequence(__null, 1473); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1474 }
1475 }
1476
1477 if (!pages_commit(
1478 (void*)(uintptr_t(chunk) + ((run_ind + i) << gPageSize2Pow)),
1479 j << gPageSize2Pow)) {
1480 return false;
1481 }
1482
1483 // pages_commit zeroes pages, so mark them as such if it succeeded.
1484 // That's checked further below to avoid manually zeroing the pages.
1485 for (size_t k = 0; k < j; k++) {
1486 chunk->map[run_ind + i + k].bits =
1487 (chunk->map[run_ind + i + k].bits & ~CHUNK_MAP_DECOMMITTED((size_t)0x20U)) |
1488 CHUNK_MAP_ZEROED((size_t)0x04U) | CHUNK_MAP_FRESH((size_t)0x80U);
1489 }
1490
1491 mNumFresh += j;
1492 i += j;
1493 } else {
1494 i++;
1495 }
1496 }
1497#endif
1498
1499 mRunsAvail.Remove(&chunk->map[run_ind]);
1500
1501 // Keep track of trailing unused pages for later use.
1502 if (rem_pages > 0) {
1503 chunk->map[run_ind + need_pages].bits =
1504 (rem_pages << gPageSize2Pow) |
1505 (chunk->map[run_ind + need_pages].bits & gPageSizeMask);
1506 chunk->map[run_ind + total_pages - 1].bits =
1507 (rem_pages << gPageSize2Pow) |
1508 (chunk->map[run_ind + total_pages - 1].bits & gPageSizeMask);
1509 mRunsAvail.Insert(&chunk->map[run_ind + need_pages]);
1510 }
1511
1512 for (size_t i = 0; i < need_pages; i++) {
1513 // Zero if necessary.
1514 if (aZero) {
1515 if ((chunk->map[run_ind + i].bits & CHUNK_MAP_ZEROED((size_t)0x04U)) == 0) {
1516 memset((void*)(uintptr_t(chunk) + ((run_ind + i) << gPageSize2Pow)), 0,
1517 gPageSize);
1518 // CHUNK_MAP_ZEROED is cleared below.
1519 }
1520 }
1521
1522 // Update dirty page accounting.
1523 if (chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY((size_t)0x08U)) {
1524 chunk->ndirty--;
1525 mNumDirty--;
1526 // CHUNK_MAP_DIRTY is cleared below.
1527 } else if (chunk->map[run_ind + i].bits & CHUNK_MAP_MADVISED((size_t)0x40U)) {
1528 mStats.committed++;
1529 mNumMAdvised--;
1530 }
1531
1532 if (chunk->map[run_ind + i].bits & CHUNK_MAP_FRESH((size_t)0x80U)) {
1533 mStats.committed++;
1534 mNumFresh--;
1535 }
1536
1537 // This bit has already been cleared
1538 MOZ_ASSERT(!(chunk->map[run_ind + i].bits & CHUNK_MAP_DECOMMITTED))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(chunk->map[run_ind + i].bits & ((size_t)0x20U
)))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!(chunk->map[run_ind + i].bits & ((size_t)0x20U
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(chunk->map[run_ind + i].bits & ((size_t)0x20U))", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1538); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!(chunk->map[run_ind + i].bits & ((size_t)0x20U))"
")"); do { MOZ_CrashSequence(__null, 1538); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1539
1540 // Initialize the chunk map. This clears the dirty, zeroed and madvised
1541 // bits, decommitted is cleared above.
1542 if (aLarge) {
1543 chunk->map[run_ind + i].bits = CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
1544 } else {
1545 chunk->map[run_ind + i].bits = size_t(aRun) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
1546 }
1547 }
1548
1549 // Set the run size only in the first element for large runs. This is
1550 // primarily a debugging aid, since the lack of size info for trailing
1551 // pages only matters if the application tries to operate on an
1552 // interior pointer.
1553 if (aLarge) {
1554 chunk->map[run_ind].bits |= aSize;
1555 }
1556
1557 if (chunk->ndirty == 0 && old_ndirty > 0 && !chunk->mIsPurging) {
1558 mChunksDirty.Remove(chunk);
1559 }
1560 return true;
1561}
1562
1563void arena_t::InitChunk(arena_chunk_t* aChunk, size_t aMinCommittedPages) {
1564 mStats.mapped += kChunkSize;
1565
1566 aChunk->arena = this;
1567
1568 // Claim that no pages are in use, since the header is merely overhead.
1569 aChunk->ndirty = 0;
1570
1571 aChunk->mIsPurging = false;
1572 aChunk->mDying = false;
1573
1574 // Setup the chunk's pages in two phases. First we mark which pages are
1575 // committed & decommitted and perform the decommit. Then we update the map
1576 // to create the runs.
1577
1578 // Clear the bits for the real header pages.
1579 size_t i;
1580 for (i = 0; i < gChunkHeaderNumPages - 1; i++) {
1581 aChunk->map[i].bits = 0;
1582 }
1583 mStats.committed += gChunkHeaderNumPages - 1;
1584
1585 // Decommit the last header page (=leading page) as a guard.
1586 pages_decommit((void*)(uintptr_t(aChunk) + (i << gPageSize2Pow)), gPageSize);
1587 aChunk->map[i++].bits = CHUNK_MAP_DECOMMITTED((size_t)0x20U);
1588
1589 // If MALLOC_DECOMMIT is enabled then commit only the pages we're about to
1590 // use. Otherwise commit all of them.
1591#ifdef MALLOC_DECOMMIT
1592 size_t n_fresh_pages =
1593 aMinCommittedPages +
1594 ExtraCommitPages(
1595 aMinCommittedPages,
1596 gChunkNumPages - gChunkHeaderNumPages - aMinCommittedPages - 1);
1597#else
1598 size_t n_fresh_pages = gChunkNumPages - 1 - gChunkHeaderNumPages;
1599#endif
1600
1601 // The committed pages are marked as Fresh. Our caller, SplitRun will update
1602 // this when it uses them.
1603 for (size_t j = 0; j < n_fresh_pages; j++) {
1604 aChunk->map[i + j].bits = CHUNK_MAP_ZEROED((size_t)0x04U) | CHUNK_MAP_FRESH((size_t)0x80U);
1605 }
1606 i += n_fresh_pages;
1607 mNumFresh += n_fresh_pages;
1608
1609#ifndef MALLOC_DECOMMIT
1610 // If MALLOC_DECOMMIT isn't defined then all the pages are fresh and setup in
1611 // the loop above.
1612 MOZ_ASSERT(i == gChunkNumPages - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(i == gChunkNumPages - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(i == gChunkNumPages - 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("i == gChunkNumPages - 1"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1612); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "i == gChunkNumPages - 1" ")"); do { MOZ_CrashSequence
(__null, 1612); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1613#endif
1614
1615 // If MALLOC_DECOMMIT is defined, then this will decommit the remainder of the
1616 // chunk plus the last page which is a guard page, if it is not defined it
1617 // will only decommit the guard page.
1618 pages_decommit((void*)(uintptr_t(aChunk) + (i << gPageSize2Pow)),
1619 (gChunkNumPages - i) << gPageSize2Pow);
1620 for (; i < gChunkNumPages; i++) {
1621 aChunk->map[i].bits = CHUNK_MAP_DECOMMITTED((size_t)0x20U);
1622 }
1623
1624 // aMinCommittedPages will create a valid run.
1625 MOZ_ASSERT(aMinCommittedPages > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMinCommittedPages > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aMinCommittedPages > 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aMinCommittedPages > 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1625); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aMinCommittedPages > 0" ")"); do { MOZ_CrashSequence
(__null, 1625); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1626 MOZ_ASSERT(aMinCommittedPages <= gChunkNumPages - gChunkHeaderNumPages - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMinCommittedPages <= gChunkNumPages - gChunkHeaderNumPages
- 1)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aMinCommittedPages <= gChunkNumPages - gChunkHeaderNumPages
- 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aMinCommittedPages <= gChunkNumPages - gChunkHeaderNumPages - 1"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1626); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aMinCommittedPages <= gChunkNumPages - gChunkHeaderNumPages - 1"
")"); do { MOZ_CrashSequence(__null, 1626); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1627
1628 // Create the run.
1629 aChunk->map[gChunkHeaderNumPages].bits |= gMaxLargeClass;
1630 aChunk->map[gChunkNumPages - 2].bits |= gMaxLargeClass;
1631 mRunsAvail.Insert(&aChunk->map[gChunkHeaderNumPages]);
1632
1633#ifdef MALLOC_DOUBLE_PURGE
1634 new (&aChunk->chunks_madvised_elem) DoublyLinkedListElement<arena_chunk_t>();
1635#endif
1636}
1637
1638bool arena_t::RemoveChunk(arena_chunk_t* aChunk) {
1639 aChunk->mDying = true;
1640
1641 // If the chunk has busy pages that means that a Purge() is in progress.
1642 // We can't remove the chunk now, instead Purge() will do it.
1643 if (aChunk->mIsPurging) {
1644 return false;
1645 }
1646
1647 if (aChunk->ndirty > 0) {
1648 MOZ_ASSERT(aChunk->arena == this)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aChunk->arena == this)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aChunk->arena == this))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("aChunk->arena == this"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1648); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aChunk->arena == this" ")"); do { MOZ_CrashSequence
(__null, 1648); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1649 mChunksDirty.Remove(aChunk);
1650 mNumDirty -= aChunk->ndirty;
1651 mStats.committed -= aChunk->ndirty;
1652 }
1653
1654 // Count the number of madvised/fresh pages and update the stats.
1655 size_t madvised = 0;
1656 size_t fresh = 0;
1657 for (size_t i = gChunkHeaderNumPages; i < gChunkNumPages - 1; i++) {
1658 // There must not be any pages that are not fresh, madvised, decommitted
1659 // or dirty.
1660 MOZ_ASSERT(aChunk->map[i].bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aChunk->map[i].bits & ((((size_t)0x80U) | ((size_t
)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aChunk->map[i].bits &
((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t
)0x08U))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aChunk->map[i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1661); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aChunk->map[i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))"
")"); do { MOZ_CrashSequence(__null, 1661); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1661 (CHUNK_MAP_FRESH_MADVISED_OR_DECOMMITTED | CHUNK_MAP_DIRTY))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aChunk->map[i].bits & ((((size_t)0x80U) | ((size_t
)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aChunk->map[i].bits &
((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t
)0x08U))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aChunk->map[i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1661); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aChunk->map[i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))"
")"); do { MOZ_CrashSequence(__null, 1661); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1662 MOZ_ASSERT((aChunk->map[i].bits & CHUNK_MAP_BUSY) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aChunk->map[i].bits & ((size_t)0x100U)) == 0
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((aChunk->map[i].bits & ((size_t)0x100U)) == 0
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"(aChunk->map[i].bits & ((size_t)0x100U)) == 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1662); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(aChunk->map[i].bits & ((size_t)0x100U)) == 0"
")"); do { MOZ_CrashSequence(__null, 1662); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1663
1664 if (aChunk->map[i].bits & CHUNK_MAP_MADVISED((size_t)0x40U)) {
1665 madvised++;
1666 } else if (aChunk->map[i].bits & CHUNK_MAP_FRESH((size_t)0x80U)) {
1667 fresh++;
1668 }
1669 }
1670
1671 mNumMAdvised -= madvised;
1672 mNumFresh -= fresh;
1673
1674#ifdef MALLOC_DOUBLE_PURGE
1675 if (mChunksMAdvised.ElementProbablyInList(aChunk)) {
1676 mChunksMAdvised.remove(aChunk);
1677 }
1678#endif
1679
1680 mStats.mapped -= kChunkSize;
1681 mStats.committed -= gChunkHeaderNumPages - 1;
1682
1683 return true;
1684}
1685
1686bool arena_chunk_t::IsEmpty() {
1687 return (map[gChunkHeaderNumPages].bits &
1688 (~gPageSizeMask | CHUNK_MAP_ALLOCATED((size_t)0x01U))) == gMaxLargeClass;
1689}
1690
1691arena_chunk_t* arena_t::DemoteChunkToSpare(arena_chunk_t* aChunk) {
1692 if (mSpare) {
1693 if (!RemoveChunk(mSpare)) {
1694 // If we can't remove the spare chunk now purge will finish removing it
1695 // later. Set it to null so that the return below will return null and
1696 // our caller won't delete the chunk before Purge() is finished.
1697 mSpare = nullptr;
1698 }
1699 }
1700
1701 arena_chunk_t* chunk_dealloc = mSpare;
1702 mSpare = aChunk;
1703 return chunk_dealloc;
1704}
1705
1706arena_run_t* arena_t::AllocRun(size_t aSize, bool aLarge, bool aZero) {
1707 arena_run_t* run;
1708 arena_chunk_map_t* mapelm;
1709 arena_chunk_map_t key;
1710
1711 MOZ_ASSERT(aSize <= gMaxLargeClass)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize <= gMaxLargeClass)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize <= gMaxLargeClass))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aSize <= gMaxLargeClass"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1711); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSize <= gMaxLargeClass" ")"); do { MOZ_CrashSequence
(__null, 1711); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1712 MOZ_ASSERT((aSize & gPageSizeMask) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aSize & gPageSizeMask) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((aSize & gPageSizeMask) ==
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(aSize & gPageSizeMask) == 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 1712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(aSize & gPageSizeMask) == 0"
")"); do { MOZ_CrashSequence(__null, 1712); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1713
1714 // Search the arena's chunks for the lowest best fit.
1715 key.bits = aSize | CHUNK_MAP_KEY((size_t)0x10U);
1716 mapelm = mRunsAvail.SearchOrNext(&key);
1717 if (mapelm) {
1718 arena_chunk_t* chunk = GetChunkForPtr(mapelm);
1719 size_t pageind =
1720 (uintptr_t(mapelm) - uintptr_t(chunk->map)) / sizeof(arena_chunk_map_t);
1721
1722 MOZ_ASSERT((chunk->map[pageind].bits & CHUNK_MAP_BUSY) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[pageind].bits & ((size_t)0x100U))
== 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((chunk->map[pageind].bits & ((size_t)0x100U))
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(chunk->map[pageind].bits & ((size_t)0x100U)) == 0",
"/root/firefox-clang/memory/build/mozjemalloc.cpp", 1722); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[pageind].bits & ((size_t)0x100U)) == 0"
")"); do { MOZ_CrashSequence(__null, 1722); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1723 run = (arena_run_t*)(uintptr_t(chunk) + (pageind << gPageSize2Pow));
1724 } else if (mSpare && !mSpare->mIsPurging) {
1725 // Use the spare.
1726 arena_chunk_t* chunk = mSpare;
1727 mSpare = nullptr;
1728 run = (arena_run_t*)(uintptr_t(chunk) +
1729 (gChunkHeaderNumPages << gPageSize2Pow));
1730 // Insert the run into the tree of available runs.
1731 MOZ_ASSERT((chunk->map[gChunkHeaderNumPages].bits & CHUNK_MAP_BUSY) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[gChunkHeaderNumPages].bits & ((size_t
)0x100U)) == 0)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!((chunk->map[gChunkHeaderNumPages
].bits & ((size_t)0x100U)) == 0))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("(chunk->map[gChunkHeaderNumPages].bits & ((size_t)0x100U)) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1731); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(chunk->map[gChunkHeaderNumPages].bits & ((size_t)0x100U)) == 0"
")"); do { MOZ_CrashSequence(__null, 1731); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1732 mRunsAvail.Insert(&chunk->map[gChunkHeaderNumPages]);
1733 } else {
1734 // No usable runs. Create a new chunk from which to allocate
1735 // the run.
1736 arena_chunk_t* chunk =
1737 (arena_chunk_t*)chunk_alloc(kChunkSize, kChunkSize, false);
1738 if (!chunk) {
1739 return nullptr;
1740 }
1741
1742 InitChunk(chunk, aSize >> gPageSize2Pow);
1743 run = (arena_run_t*)(uintptr_t(chunk) +
1744 (gChunkHeaderNumPages << gPageSize2Pow));
1745 }
1746 // Update page map.
1747 return SplitRun(run, aSize, aLarge, aZero) ? run : nullptr;
1748}
1749
1750void arena_t::UpdateMaxDirty() {
1751 MaybeMutexAutoLock lock(mLock);
1752 int32_t modifier = gArenas.DefaultMaxDirtyPageModifier();
1753 if (modifier) {
1754 int32_t arenaOverride =
1755 modifier > 0 ? mMaxDirtyIncreaseOverride : mMaxDirtyDecreaseOverride;
1756 if (arenaOverride) {
1757 modifier = arenaOverride;
1758 }
1759 }
1760
1761 mMaxDirty =
1762 modifier >= 0 ? mMaxDirtyBase << modifier : mMaxDirtyBase >> -modifier;
1763}
1764
1765#ifdef MALLOC_DECOMMIT
1766
1767size_t arena_t::ExtraCommitPages(size_t aReqPages, size_t aRemainingPages) {
1768 const int32_t modifier = gArenas.DefaultMaxDirtyPageModifier();
1769 if (modifier < 0) {
1770 return 0;
1771 }
1772
1773 // The maximum size of the page cache
1774 const size_t max_page_cache = mMaxDirty;
1775
1776 // The current size of the page cache, note that we use mNumFresh +
1777 // mNumMAdvised here but Purge() does not.
1778 const size_t page_cache = mNumDirty + mNumFresh + mNumMAdvised;
1779
1780 if (page_cache > max_page_cache) {
1781 // We're already exceeding our dirty page count even though we're trying
1782 // to allocate. This can happen due to fragmentation. Don't commit
1783 // excess memory since we're probably here due to a larger allocation and
1784 // small amounts of memory are certainly available in the page cache.
1785 return 0;
1786 }
1787 if (modifier > 0) {
1788 // If modifier is > 0 then we want to keep all the pages we can, but don't
1789 // exceed the size of the page cache. The subtraction cannot underflow
1790 // because of the condition above.
1791 return std::min(aRemainingPages, max_page_cache - page_cache);
1792 }
1793
1794 // The rest is arbitrary and involves a some assumptions. I've broken it down
1795 // into simple expressions to document them more clearly.
1796
1797 // Assumption 1: a quarter of mMaxDirty is a sensible "minimum
1798 // target" for the dirty page cache. Likewise 3 quarters is a sensible
1799 // "maximum target". Note that for the maximum we avoid using the whole page
1800 // cache now so that a free that follows this allocation doesn't immeidatly
1801 // call Purge (churning memory).
1802 const size_t min = max_page_cache / 4;
1803 const size_t max = 3 * max_page_cache / 4;
1804
1805 // Assumption 2: Committing 32 pages at a time is sufficient to amortise
1806 // VirtualAlloc costs.
1807 size_t amortisation_threshold = 32;
1808
1809 // extra_pages is the number of additional pages needed to meet
1810 // amortisation_threshold.
1811 size_t extra_pages = aReqPages < amortisation_threshold
1812 ? amortisation_threshold - aReqPages
1813 : 0;
1814
1815 // If committing extra_pages isn't enough to hit the minimum target then
1816 // increase it.
1817 if (page_cache + extra_pages < min) {
1818 extra_pages = min - page_cache;
1819 } else if (page_cache + extra_pages > max) {
1820 // If committing extra_pages would exceed our maximum target then it may
1821 // still be useful to allocate extra pages. One of the reasons this can
1822 // happen could be fragmentation of the cache,
1823
1824 // Therefore reduce the amortisation threshold so that we might allocate
1825 // some extra pages but avoid exceeding the dirty page cache.
1826 amortisation_threshold /= 2;
1827 extra_pages = std::min(aReqPages < amortisation_threshold
1828 ? amortisation_threshold - aReqPages
1829 : 0,
1830 max_page_cache - page_cache);
1831 }
1832
1833 // Cap extra_pages to aRemainingPages and adjust aRemainingPages. We will
1834 // commit at least this many extra pages.
1835 extra_pages = std::min(extra_pages, aRemainingPages);
1836
1837 // Finally if commiting a small number of additional pages now can prevent
1838 // a small commit later then try to commit a little more now, provided we
1839 // don't exceed max_page_cache.
1840 if ((aRemainingPages - extra_pages) < amortisation_threshold / 2 &&
1841 (page_cache + aRemainingPages) < max_page_cache) {
1842 return aRemainingPages;
1843 }
1844
1845 return extra_pages;
1846}
1847#endif
1848
1849ArenaPurgeResult arena_t::Purge(PurgeCondition aCond, PurgeStats& aStats) {
1850 arena_chunk_t* chunk;
1851
1852 // The first critical section will find a chunk and mark dirty pages in it as
1853 // busy.
1854 {
1855 MaybeMutexAutoLock lock(mLock);
1856
1857 if (mMustDeleteAfterPurge) {
1858 mIsPurgePending = false;
1859 return Dying;
1860 }
1861
1862#ifdef MOZ_DEBUG1
1863 size_t ndirty = 0;
1864 for (auto* chunk : mChunksDirty.iter()) {
1865 ndirty += chunk->ndirty;
1866 }
1867 // Not all dirty chunks are in mChunksDirty as others might be being Purged.
1868 MOZ_ASSERT(ndirty <= mNumDirty)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ndirty <= mNumDirty)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ndirty <= mNumDirty))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("ndirty <= mNumDirty"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1868); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ndirty <= mNumDirty" ")"); do { MOZ_CrashSequence
(__null, 1868); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1869#endif
1870
1871 if (!ShouldContinuePurge(aCond)) {
1872 mIsPurgePending = false;
1873 return ReachedThreshold;
1874 }
1875
1876 // Take a single chunk and attempt to purge some of its dirty pages. The
1877 // loop below will purge memory from the chunk until either:
1878 // * The dirty page count for the arena hits its target,
1879 // * Another thread attempts to delete this chunk, or
1880 // * The chunk has no more dirty pages.
1881 // In any of these cases the loop will break and Purge() will return, which
1882 // means it may return before the arena meets its dirty page count target,
1883 // the return value is used by the caller to call Purge() again where it
1884 // will take the next chunk with dirty pages.
1885 chunk = mChunksDirty.Last();
1886 if (!chunk) {
1887 // There are chunks with dirty pages (because mNumDirty > 0 above) but
1888 // they're not in mChunksDirty. That can happen if they're busy being
1889 // purged by other threads.
1890 // We have to clear the flag to preserve the invariant that if Purge()
1891 // returns false the flag is clear, if there's more purging work to do in
1892 // other chunks then either other calls to Purge() (in other threads) will
1893 // handle it or we rely on ShouldStartPurge() returning true at some point
1894 // in the future.
1895 mIsPurgePending = false;
1896 return Busy;
1897 }
1898 MOZ_ASSERT(chunk->ndirty > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chunk->ndirty > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(chunk->ndirty > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("chunk->ndirty > 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1898); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "chunk->ndirty > 0" ")"); do { MOZ_CrashSequence
(__null, 1898); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1899
1900 // Mark the chunk as busy so it won't be deleted and remove it from
1901 // mChunksDirty so we're the only thread purging it.
1902 MOZ_ASSERT(!chunk->mIsPurging)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!chunk->mIsPurging)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!chunk->mIsPurging))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!chunk->mIsPurging"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1902); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!chunk->mIsPurging" ")"); do { MOZ_CrashSequence
(__null, 1902); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1903 mChunksDirty.Remove(chunk);
1904 chunk->mIsPurging = true;
1905 aStats.chunks++;
1906 } // MaybeMutexAutoLock
1907
1908 // True if we should continue purging memory from this arena.
1909 bool continue_purge_arena = true;
1910
1911 // True if we should continue purging memory in this chunk.
1912 bool continue_purge_chunk = true;
1913
1914 // True if at least one Purge operation has occured and therefore we need to
1915 // call FinishPurgingInChunk() before returning.
1916 bool purged_once = false;
1917
1918 while (continue_purge_chunk && continue_purge_arena) {
1919 // This structure is used to communicate between the two PurgePhase
1920 // functions.
1921 PurgeInfo purge_info(*this, chunk, aStats);
1922
1923 {
1924 // Phase 1: Find pages that need purging.
1925 MaybeMutexAutoLock lock(purge_info.mArena.mLock);
1926 MOZ_ASSERT(chunk->mIsPurging)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chunk->mIsPurging)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(chunk->mIsPurging))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("chunk->mIsPurging"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1926); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "chunk->mIsPurging" ")"); do { MOZ_CrashSequence
(__null, 1926); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1927
1928 if (purge_info.mArena.mMustDeleteAfterPurge) {
1929 chunk->mIsPurging = false;
1930 purge_info.mArena.mIsPurgePending = false;
1931 return Dying;
1932 }
1933
1934 continue_purge_chunk = purge_info.FindDirtyPages(purged_once);
1935 continue_purge_arena = purge_info.mArena.ShouldContinuePurge(aCond);
1936
1937 // The code below will exit returning false if these are both false, so
1938 // clear mIsDeferredPurgeNeeded while we still hold the lock.
1939 if (!continue_purge_chunk && !continue_purge_arena) {
1940 purge_info.mArena.mIsPurgePending = false;
1941 }
1942 }
1943 if (!continue_purge_chunk) {
1944 if (chunk->mDying) {
1945 // Phase one already unlinked the chunk from structures, we just need to
1946 // release the memory.
1947 chunk_dealloc((void*)chunk, kChunkSize, ARENA_CHUNK);
1948 }
1949 // There's nothing else to do here, our caller may execute Purge() again
1950 // if continue_purge_arena is true.
1951 return continue_purge_arena ? NotDone : ReachedThreshold;
1952 }
1953
1954#ifdef MALLOC_DECOMMIT
1955 pages_decommit(purge_info.DirtyPtr(), purge_info.DirtyLenBytes());
1956#else
1957# ifdef XP_SOLARIS
1958 posix_madvise(purge_info.DirtyPtr(), purge_info.DirtyLenBytes(), MADV_FREE8);
1959# else
1960 madvise(purge_info.DirtyPtr(), purge_info.DirtyLenBytes(), MADV_FREE8);
1961# endif
1962#endif
1963
1964 arena_chunk_t* chunk_to_release = nullptr;
1965 bool is_dying;
1966 {
1967 // Phase 2: Mark the pages with their final state (madvised or
1968 // decommitted) and fix up any other bookkeeping.
1969 MaybeMutexAutoLock lock(purge_info.mArena.mLock);
1970 MOZ_ASSERT(chunk->mIsPurging)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chunk->mIsPurging)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(chunk->mIsPurging))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("chunk->mIsPurging"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 1970); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "chunk->mIsPurging" ")"); do { MOZ_CrashSequence
(__null, 1970); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1971
1972 // We can't early exit if the arena is dying, we have to finish the purge
1973 // (which restores the state so the destructor will check it) and maybe
1974 // release the old spare arena.
1975 is_dying = purge_info.mArena.mMustDeleteAfterPurge;
1976
1977 auto [cpc, ctr] = purge_info.UpdatePagesAndCounts();
1978 continue_purge_chunk = cpc;
1979 chunk_to_release = ctr;
1980 continue_purge_arena = purge_info.mArena.ShouldContinuePurge(aCond);
1981
1982 if (!continue_purge_chunk || !continue_purge_arena) {
1983 // We're going to stop purging here so update the chunk's bookkeeping.
1984 purge_info.FinishPurgingInChunk(true);
1985 purge_info.mArena.mIsPurgePending = false;
1986 }
1987 } // MaybeMutexAutoLock
1988
1989 // Phase 2 can release the spare chunk (not always == chunk) so an extra
1990 // parameter is used to return that chunk.
1991 if (chunk_to_release) {
1992 chunk_dealloc((void*)chunk_to_release, kChunkSize, ARENA_CHUNK);
1993 }
1994 if (is_dying) {
1995 return Dying;
1996 }
1997 purged_once = true;
1998 }
1999
2000 return continue_purge_arena ? NotDone : ReachedThreshold;
2001}
2002
2003ArenaPurgeResult arena_t::PurgeLoop(PurgeCondition aCond, const char* aCaller,
2004 uint32_t aReuseGraceMS,
2005 Maybe<std::function<bool()>> aKeepGoing) {
2006 PurgeStats purge_stats(mId, mLabel, aCaller);
2007
2008#ifdef MOZJEMALLOC_PROFILING_CALLBACKS1
2009 // We hold our own reference to callbacks for the duration of PurgeLoop to
2010 // make sure it's not released during purging.
2011 RefPtr<MallocProfilerCallbacks> callbacks = sCallbacks;
2012 TimeStamp start;
2013 if (callbacks) {
2014 start = TimeStamp::Now();
2015 }
2016#endif
2017
2018 uint64_t reuseGraceNS = (uint64_t)aReuseGraceMS * 1000 * 1000;
2019 uint64_t now = aReuseGraceMS ? 0 : GetTimestampNS();
Value stored to 'now' during its initialization is never read
2020 ArenaPurgeResult pr;
2021 do {
2022 pr = Purge(aCond, purge_stats);
2023 now = aReuseGraceMS ? 0 : GetTimestampNS();
2024 } while (
2025 pr == NotDone &&
2026 (!aReuseGraceMS || (now - mLastSignificantReuseNS >= reuseGraceNS)) &&
2027 (!aKeepGoing || (*aKeepGoing)()));
2028
2029#ifdef MOZJEMALLOC_PROFILING_CALLBACKS1
2030 if (callbacks) {
2031 TimeStamp end = TimeStamp::Now();
2032 // We can't hold an arena lock while committing profiler markers.
2033 callbacks->OnPurge(start, end, purge_stats, pr);
2034 }
2035#endif
2036
2037 return pr;
2038}
2039
2040bool arena_t::PurgeInfo::FindDirtyPages(bool aPurgedOnce) {
2041 // It's possible that the previously dirty pages have now been
2042 // allocated or the chunk is dying.
2043 if (mChunk->ndirty == 0 || mChunk->mDying) {
2044 // Add the chunk to the mChunksMAdvised list if it's had at least one
2045 // madvise.
2046 FinishPurgingInChunk(aPurgedOnce);
2047 return false;
2048 }
2049
2050 // Look for the first dirty page, as we do record the beginning of the run
2051 // that contains the dirty page.
2052 bool previous_page_is_allocated = true;
2053 for (size_t i = gChunkHeaderNumPages; i < gChunkNumPages - 1; i++) {
2054 size_t bits = mChunk->map[i].bits;
2055
2056 // We must not find any busy pages because this chunk shouldn't be in
2057 // the dirty list.
2058 MOZ_ASSERT((bits & CHUNK_MAP_BUSY) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((bits & ((size_t)0x100U)) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((bits & ((size_t)0x100U)
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(bits & ((size_t)0x100U)) == 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(bits & ((size_t)0x100U)) == 0"
")"); do { MOZ_CrashSequence(__null, 2058); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2059
2060 // The first page belonging to a free run has the allocated bit clear
2061 // and a non-zero size. To distinguish it from the last page of a free
2062 // run we track the allocated bit of the previous page, if it's set
2063 // then this is the first.
2064 if ((bits & CHUNK_MAP_ALLOCATED((size_t)0x01U)) == 0 && (bits & ~gPageSizeMask) != 0 &&
2065 previous_page_is_allocated) {
2066 mFreeRunInd = i;
2067 mFreeRunLen = bits >> gPageSize2Pow;
2068 }
2069
2070 if (bits & CHUNK_MAP_DIRTY((size_t)0x08U)) {
2071 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mChunk->map[i].bits & (((size_t)0x80U) | ((size_t
)0x40U) | ((size_t)0x20U))) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((mChunk->map[i].bits &
(((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(mChunk->map[i].bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2072); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(mChunk->map[i].bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0"
")"); do { MOZ_CrashSequence(__null, 2072); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2072 (mChunk->map[i].bits & CHUNK_MAP_FRESH_MADVISED_OR_DECOMMITTED) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mChunk->map[i].bits & (((size_t)0x80U) | ((size_t
)0x40U) | ((size_t)0x20U))) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((mChunk->map[i].bits &
(((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(mChunk->map[i].bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2072); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(mChunk->map[i].bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0"
")"); do { MOZ_CrashSequence(__null, 2072); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2073 mDirtyInd = i;
2074 break;
2075 }
2076
2077 previous_page_is_allocated = bits & CHUNK_MAP_ALLOCATED((size_t)0x01U);
2078 }
2079 MOZ_ASSERT(mDirtyInd != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mDirtyInd != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mDirtyInd != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("mDirtyInd != 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2079); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mDirtyInd != 0" ")"); do { MOZ_CrashSequence
(__null, 2079); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2080 MOZ_ASSERT(mFreeRunInd >= gChunkHeaderNumPages)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFreeRunInd >= gChunkHeaderNumPages)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(mFreeRunInd >= gChunkHeaderNumPages))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mFreeRunInd >= gChunkHeaderNumPages"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2080); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFreeRunInd >= gChunkHeaderNumPages" ")"
); do { MOZ_CrashSequence(__null, 2080); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2081 MOZ_ASSERT(mFreeRunInd <= mDirtyInd)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFreeRunInd <= mDirtyInd)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mFreeRunInd <= mDirtyInd)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mFreeRunInd <= mDirtyInd"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2081); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFreeRunInd <= mDirtyInd" ")"); do { MOZ_CrashSequence
(__null, 2081); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2082 MOZ_ASSERT(mFreeRunLen > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFreeRunLen > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mFreeRunLen > 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mFreeRunLen > 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2082); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFreeRunLen > 0" ")"); do { MOZ_CrashSequence
(__null, 2082); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2083
2084 // Look for the next not-dirty page, it could be the guard page at the end
2085 // of the chunk.
2086 for (size_t i = 0; mDirtyInd + i < gChunkNumPages; i++) {
2087 size_t& bits = mChunk->map[mDirtyInd + i].bits;
2088
2089 // We must not find any busy pages because this chunk shouldn't be in the
2090 // dirty list.
2091 MOZ_ASSERT(!(bits & CHUNK_MAP_BUSY))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(bits & ((size_t)0x100U)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(bits & ((size_t)0x100U
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!(bits & ((size_t)0x100U))", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!(bits & ((size_t)0x100U))"
")"); do { MOZ_CrashSequence(__null, 2091); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2092
2093 if (!(bits & CHUNK_MAP_DIRTY((size_t)0x08U))) {
2094 mDirtyNPages = i;
2095 break;
2096 }
2097 MOZ_ASSERT((bits & CHUNK_MAP_FRESH_MADVISED_OR_DECOMMITTED) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t
)0x20U))) == 0)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!((bits & (((size_t)0x80U) |
((size_t)0x40U) | ((size_t)0x20U))) == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("(bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2097); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(bits & (((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U))) == 0"
")"); do { MOZ_CrashSequence(__null, 2097); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2098 bits ^= CHUNK_MAP_DIRTY((size_t)0x08U);
2099 }
2100 MOZ_ASSERT(mDirtyNPages > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mDirtyNPages > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mDirtyNPages > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("mDirtyNPages > 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2100); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mDirtyNPages > 0" ")"); do { MOZ_CrashSequence
(__null, 2100); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2101 MOZ_ASSERT(mDirtyNPages <= mChunk->ndirty)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mDirtyNPages <= mChunk->ndirty)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mDirtyNPages <= mChunk->
ndirty))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mDirtyNPages <= mChunk->ndirty", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2101); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mDirtyNPages <= mChunk->ndirty"
")"); do { MOZ_CrashSequence(__null, 2101); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2102 MOZ_ASSERT(mFreeRunInd + mFreeRunLen >= mDirtyInd + mDirtyNPages)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFreeRunInd + mFreeRunLen >= mDirtyInd + mDirtyNPages
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mFreeRunInd + mFreeRunLen >= mDirtyInd + mDirtyNPages
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mFreeRunInd + mFreeRunLen >= mDirtyInd + mDirtyNPages", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2102); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mFreeRunInd + mFreeRunLen >= mDirtyInd + mDirtyNPages"
")"); do { MOZ_CrashSequence(__null, 2102); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2103
2104 // Mark the run as busy so that another thread freeing memory won't try to
2105 // coalesce it.
2106 mChunk->map[mFreeRunInd].bits |= CHUNK_MAP_BUSY((size_t)0x100U);
2107 mChunk->map[FreeRunLastInd()].bits |= CHUNK_MAP_BUSY((size_t)0x100U);
2108
2109 mChunk->ndirty -= mDirtyNPages;
2110 mArena.mNumDirty -= mDirtyNPages;
2111
2112 // Before we unlock ensure that no other thread can allocate from these
2113 // pages.
2114 if (mArena.mSpare != mChunk) {
2115 mArena.mRunsAvail.Remove(&mChunk->map[mFreeRunInd]);
2116 }
2117 return true;
2118}
2119
2120std::pair<bool, arena_chunk_t*> arena_t::PurgeInfo::UpdatePagesAndCounts() {
2121 for (size_t i = 0; i < mDirtyNPages; i++) {
2122 // The page must not have any of the madvised, decommited or dirty bits
2123 // set.
2124 MOZ_ASSERT((mChunk->map[mDirtyInd + i].bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mChunk->map[mDirtyInd + i].bits & ((((size_t
)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U
))) == 0)>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!((mChunk->map[mDirtyInd + i].bits &
((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t
)0x08U))) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(mChunk->map[mDirtyInd + i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2126); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(mChunk->map[mDirtyInd + i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))) == 0"
")"); do { MOZ_CrashSequence(__null, 2126); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2125 (CHUNK_MAP_FRESH_MADVISED_OR_DECOMMITTED | CHUNK_MAP_DIRTY)) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mChunk->map[mDirtyInd + i].bits & ((((size_t
)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U
))) == 0)>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!((mChunk->map[mDirtyInd + i].bits &
((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t
)0x08U))) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(mChunk->map[mDirtyInd + i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2126); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(mChunk->map[mDirtyInd + i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))) == 0"
")"); do { MOZ_CrashSequence(__null, 2126); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2126 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mChunk->map[mDirtyInd + i].bits & ((((size_t
)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U
))) == 0)>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!((mChunk->map[mDirtyInd + i].bits &
((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t
)0x08U))) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(mChunk->map[mDirtyInd + i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2126); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(mChunk->map[mDirtyInd + i].bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x08U))) == 0"
")"); do { MOZ_CrashSequence(__null, 2126); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2127#ifdef MALLOC_DECOMMIT
2128 const size_t free_operation = CHUNK_MAP_DECOMMITTED((size_t)0x20U);
2129#else
2130 const size_t free_operation = CHUNK_MAP_MADVISED((size_t)0x40U);
2131#endif
2132 mChunk->map[mDirtyInd + i].bits ^= free_operation;
2133 }
2134
2135 // Remove the CHUNK_MAP_BUSY marks from the run.
2136#ifdef MOZ_DEBUG1
2137 MOZ_ASSERT(mChunk->map[mFreeRunInd].bits & CHUNK_MAP_BUSY)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mChunk->map[mFreeRunInd].bits & ((size_t)0x100U
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mChunk->map[mFreeRunInd].bits & ((size_t)0x100U
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mChunk->map[mFreeRunInd].bits & ((size_t)0x100U)", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mChunk->map[mFreeRunInd].bits & ((size_t)0x100U)"
")"); do { MOZ_CrashSequence(__null, 2137); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2138 MOZ_ASSERT(mChunk->map[FreeRunLastInd()].bits & CHUNK_MAP_BUSY)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mChunk->map[FreeRunLastInd()].bits & ((size_t
)0x100U))>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!(mChunk->map[FreeRunLastInd()].bits
& ((size_t)0x100U)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mChunk->map[FreeRunLastInd()].bits & ((size_t)0x100U)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2138); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mChunk->map[FreeRunLastInd()].bits & ((size_t)0x100U)"
")"); do { MOZ_CrashSequence(__null, 2138); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2139#endif
2140 mChunk->map[mFreeRunInd].bits &= ~CHUNK_MAP_BUSY((size_t)0x100U);
2141 mChunk->map[FreeRunLastInd()].bits &= ~CHUNK_MAP_BUSY((size_t)0x100U);
2142
2143#ifndef MALLOC_DECOMMIT
2144 mArena.mNumMAdvised += mDirtyNPages;
2145#endif
2146
2147 mArena.mStats.committed -= mDirtyNPages;
2148 mPurgeStats.pages += mDirtyNPages;
2149 mPurgeStats.system_calls++;
2150
2151 if (mChunk->mDying) {
2152 // A dying chunk doesn't need to be coaleased, it will already have one
2153 // large run.
2154 MOZ_ASSERT(mFreeRunInd == gChunkHeaderNumPages &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFreeRunInd == gChunkHeaderNumPages && mFreeRunLen
== gChunkNumPages - gChunkHeaderNumPages - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mFreeRunInd == gChunkHeaderNumPages
&& mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages
- 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mFreeRunInd == gChunkHeaderNumPages && mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages - 1"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2155); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFreeRunInd == gChunkHeaderNumPages && mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages - 1"
")"); do { MOZ_CrashSequence(__null, 2155); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2155 mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFreeRunInd == gChunkHeaderNumPages && mFreeRunLen
== gChunkNumPages - gChunkHeaderNumPages - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mFreeRunInd == gChunkHeaderNumPages
&& mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages
- 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mFreeRunInd == gChunkHeaderNumPages && mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages - 1"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2155); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFreeRunInd == gChunkHeaderNumPages && mFreeRunLen == gChunkNumPages - gChunkHeaderNumPages - 1"
")"); do { MOZ_CrashSequence(__null, 2155); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2156
2157 return std::make_pair(false, mChunk);
2158 }
2159
2160 bool was_empty = mChunk->IsEmpty();
2161 mFreeRunInd =
2162 mArena.TryCoalesce(mChunk, mFreeRunInd, mFreeRunLen, FreeRunLenBytes());
2163
2164 arena_chunk_t* chunk_to_release = nullptr;
2165 if (!was_empty && mChunk->IsEmpty()) {
2166 // This now-empty chunk will become the spare chunk and the spare
2167 // chunk will be returned for deletion.
2168 chunk_to_release = mArena.DemoteChunkToSpare(mChunk);
2169 }
2170
2171 if (mChunk != mArena.mSpare) {
2172 mArena.mRunsAvail.Insert(&mChunk->map[mFreeRunInd]);
2173 }
2174
2175 return std::make_pair(mChunk->ndirty != 0, chunk_to_release);
2176}
2177
2178void arena_t::PurgeInfo::FinishPurgingInChunk(bool aAddToMAdvised) {
2179 // If there's no more purge activity for this chunk then finish up while
2180 // we still have the lock.
2181 MOZ_ASSERT(mChunk->mIsPurging)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mChunk->mIsPurging)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mChunk->mIsPurging))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("mChunk->mIsPurging"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2181); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mChunk->mIsPurging" ")"); do { MOZ_CrashSequence
(__null, 2181); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2182 mChunk->mIsPurging = false;
2183
2184 if (mChunk->mDying) {
2185 // Another thread tried to delete this chunk while we weren't holding
2186 // the lock. Now it's our responsibility to finish deleting it. First
2187 // clear its dirty pages so that RemoveChunk() doesn't try to remove it
2188 // from mChunksDirty because it won't be there.
2189 mArena.mNumDirty -= mChunk->ndirty;
2190 mArena.mStats.committed -= mChunk->ndirty;
2191 mChunk->ndirty = 0;
2192
2193 DebugOnly<bool> release_chunk = mArena.RemoveChunk(mChunk);
2194 // RemoveChunk() can't return false because mIsPurging was false
2195 // during the call.
2196 MOZ_ASSERT(release_chunk)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(release_chunk)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(release_chunk))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("release_chunk",
"/root/firefox-clang/memory/build/mozjemalloc.cpp", 2196); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "release_chunk" ")"); do { MOZ_CrashSequence
(__null, 2196); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2197 return;
2198 }
2199
2200 if (mChunk->ndirty != 0) {
2201 mArena.mChunksDirty.Insert(mChunk);
2202 }
2203
2204#ifdef MALLOC_DOUBLE_PURGE
2205 if (aAddToMAdvised) {
2206 // The chunk might already be in the list, but this
2207 // makes sure it's at the front.
2208 if (mArena.mChunksMAdvised.ElementProbablyInList(mChunk)) {
2209 mArena.mChunksMAdvised.remove(mChunk);
2210 }
2211 mArena.mChunksMAdvised.pushFront(mChunk);
2212 }
2213#endif
2214}
2215
2216// run_pages and size make each-other redundant. But we use them both and the
2217// caller computes both so this function requires both and will assert if they
2218// are inconsistent.
2219size_t arena_t::TryCoalesce(arena_chunk_t* aChunk, size_t run_ind,
2220 size_t run_pages, size_t size) {
2221 // Copy in/out parameters to local variables so that we don't need '*'
2222 // operators throughout this code but also so that type checking is stricter
2223 // (references are too easily coerced).
2224 MOZ_ASSERT(size == run_pages << gPageSize2Pow)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(size == run_pages << gPageSize2Pow)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(size == run_pages << gPageSize2Pow))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("size == run_pages << gPageSize2Pow"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2224); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "size == run_pages << gPageSize2Pow" ")"
); do { MOZ_CrashSequence(__null, 2224); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2225
2226 // Try to coalesce forward.
2227 if (run_ind + run_pages < gChunkNumPages - 1 &&
2228 (aChunk->map[run_ind + run_pages].bits &
2229 (CHUNK_MAP_ALLOCATED((size_t)0x01U) | CHUNK_MAP_BUSY((size_t)0x100U))) == 0) {
2230 size_t nrun_size = aChunk->map[run_ind + run_pages].bits & ~gPageSizeMask;
2231
2232 // Remove successor from tree of available runs; the coalesced run is
2233 // inserted later.
2234 mRunsAvail.Remove(&aChunk->map[run_ind + run_pages]);
2235
2236 size += nrun_size;
2237 run_pages = size >> gPageSize2Pow;
2238
2239 MOZ_DIAGNOSTIC_ASSERT((aChunk->map[run_ind + run_pages - 1].bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aChunk->map[run_ind + run_pages - 1].bits & ~
gPageSizeMask) == nrun_size)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((aChunk->map[run_ind + run_pages
- 1].bits & ~gPageSizeMask) == nrun_size))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("(aChunk->map[run_ind + run_pages - 1].bits & ~gPageSizeMask) == nrun_size"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2240); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(aChunk->map[run_ind + run_pages - 1].bits & ~gPageSizeMask) == nrun_size"
")"); do { MOZ_CrashSequence(__null, 2240); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2240 ~gPageSizeMask) == nrun_size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aChunk->map[run_ind + run_pages - 1].bits & ~
gPageSizeMask) == nrun_size)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((aChunk->map[run_ind + run_pages
- 1].bits & ~gPageSizeMask) == nrun_size))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("(aChunk->map[run_ind + run_pages - 1].bits & ~gPageSizeMask) == nrun_size"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2240); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(aChunk->map[run_ind + run_pages - 1].bits & ~gPageSizeMask) == nrun_size"
")"); do { MOZ_CrashSequence(__null, 2240); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2241 aChunk->map[run_ind].bits =
2242 size | (aChunk->map[run_ind].bits & gPageSizeMask);
2243 aChunk->map[run_ind + run_pages - 1].bits =
2244 size | (aChunk->map[run_ind + run_pages - 1].bits & gPageSizeMask);
2245 }
2246
2247 // Try to coalesce backward.
2248 if (run_ind > gChunkHeaderNumPages &&
2249 (aChunk->map[run_ind - 1].bits &
2250 (CHUNK_MAP_ALLOCATED((size_t)0x01U) | CHUNK_MAP_BUSY((size_t)0x100U))) == 0) {
2251 size_t prun_size = aChunk->map[run_ind - 1].bits & ~gPageSizeMask;
2252
2253 run_ind -= prun_size >> gPageSize2Pow;
2254
2255 // Remove predecessor from tree of available runs; the coalesced run is
2256 // inserted later.
2257 mRunsAvail.Remove(&aChunk->map[run_ind]);
2258
2259 size += prun_size;
2260 run_pages = size >> gPageSize2Pow;
2261
2262 MOZ_DIAGNOSTIC_ASSERT((aChunk->map[run_ind].bits & ~gPageSizeMask) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aChunk->map[run_ind].bits & ~gPageSizeMask) ==
prun_size)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!((aChunk->map[run_ind].bits &
~gPageSizeMask) == prun_size))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("(aChunk->map[run_ind].bits & ~gPageSizeMask) == prun_size"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2263); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(aChunk->map[run_ind].bits & ~gPageSizeMask) == prun_size"
")"); do { MOZ_CrashSequence(__null, 2263); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2263 prun_size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aChunk->map[run_ind].bits & ~gPageSizeMask) ==
prun_size)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!((aChunk->map[run_ind].bits &
~gPageSizeMask) == prun_size))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("(aChunk->map[run_ind].bits & ~gPageSizeMask) == prun_size"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2263); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(aChunk->map[run_ind].bits & ~gPageSizeMask) == prun_size"
")"); do { MOZ_CrashSequence(__null, 2263); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2264 aChunk->map[run_ind].bits =
2265 size | (aChunk->map[run_ind].bits & gPageSizeMask);
2266 aChunk->map[run_ind + run_pages - 1].bits =
2267 size | (aChunk->map[run_ind + run_pages - 1].bits & gPageSizeMask);
2268 }
2269
2270 return run_ind;
2271}
2272
2273arena_chunk_t* arena_t::DallocRun(arena_run_t* aRun, bool aDirty) {
2274 arena_chunk_t* chunk;
2275 size_t size, run_ind, run_pages;
2276
2277 chunk = GetChunkForPtr(aRun);
2278 run_ind = (size_t)((uintptr_t(aRun) - uintptr_t(chunk)) >> gPageSize2Pow);
2279 MOZ_DIAGNOSTIC_ASSERT(run_ind >= gChunkHeaderNumPages)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run_ind >= gChunkHeaderNumPages)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run_ind >= gChunkHeaderNumPages
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run_ind >= gChunkHeaderNumPages", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2279); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run_ind >= gChunkHeaderNumPages"
")"); do { MOZ_CrashSequence(__null, 2279); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2280 MOZ_RELEASE_ASSERT(run_ind < gChunkNumPages - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run_ind < gChunkNumPages - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run_ind < gChunkNumPages -
1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("run_ind < gChunkNumPages - 1", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2280); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "run_ind < gChunkNumPages - 1"
")"); do { MOZ_CrashSequence(__null, 2280); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2281 if ((chunk->map[run_ind].bits & CHUNK_MAP_LARGE((size_t)0x02U)) != 0) {
2282 size = chunk->map[run_ind].bits & ~gPageSizeMask;
2283 run_pages = (size >> gPageSize2Pow);
2284 } else {
2285 run_pages = aRun->mBin->mRunSizePages;
2286 size = run_pages << gPageSize2Pow;
2287 }
2288
2289 // Mark pages as unallocated in the chunk map.
2290 if (aDirty) {
2291 size_t i;
2292
2293 for (i = 0; i < run_pages; i++) {
2294 MOZ_DIAGNOSTIC_ASSERT((chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i].bits & ((size_t)0x08U
)) == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((chunk->map[run_ind + i].bits & ((size_t)0x08U
)) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(chunk->map[run_ind + i].bits & ((size_t)0x08U)) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2295); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(chunk->map[run_ind + i].bits & ((size_t)0x08U)) == 0"
")"); do { MOZ_CrashSequence(__null, 2295); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2295 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((chunk->map[run_ind + i].bits & ((size_t)0x08U
)) == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((chunk->map[run_ind + i].bits & ((size_t)0x08U
)) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(chunk->map[run_ind + i].bits & ((size_t)0x08U)) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2295); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(chunk->map[run_ind + i].bits & ((size_t)0x08U)) == 0"
")"); do { MOZ_CrashSequence(__null, 2295); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2296 chunk->map[run_ind + i].bits = CHUNK_MAP_DIRTY((size_t)0x08U);
2297 }
2298
2299 if (chunk->ndirty == 0 && !chunk->mIsPurging) {
2300 mChunksDirty.Insert(chunk);
2301 }
2302 chunk->ndirty += run_pages;
2303 mNumDirty += run_pages;
2304 } else {
2305 size_t i;
2306
2307 for (i = 0; i < run_pages; i++) {
2308 chunk->map[run_ind + i].bits &= ~(CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U));
2309 }
2310 }
2311 chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & gPageSizeMask);
2312 chunk->map[run_ind + run_pages - 1].bits =
2313 size | (chunk->map[run_ind + run_pages - 1].bits & gPageSizeMask);
2314
2315 run_ind = TryCoalesce(chunk, run_ind, run_pages, size);
2316
2317 // Deallocate chunk if it is now completely unused.
2318 arena_chunk_t* chunk_dealloc = nullptr;
2319 if (chunk->IsEmpty()) {
2320 chunk_dealloc = DemoteChunkToSpare(chunk);
2321 } else {
2322 // Insert into tree of available runs, now that coalescing is complete.
2323 mRunsAvail.Insert(&chunk->map[run_ind]);
2324 }
2325
2326 return chunk_dealloc;
2327}
2328
2329void arena_t::TrimRunHead(arena_chunk_t* aChunk, arena_run_t* aRun,
2330 size_t aOldSize, size_t aNewSize) {
2331 size_t pageind = (uintptr_t(aRun) - uintptr_t(aChunk)) >> gPageSize2Pow;
2332 size_t head_npages = (aOldSize - aNewSize) >> gPageSize2Pow;
2333
2334 MOZ_ASSERT(aOldSize > aNewSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOldSize > aNewSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aOldSize > aNewSize))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aOldSize > aNewSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2334); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOldSize > aNewSize" ")"); do { MOZ_CrashSequence
(__null, 2334); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2335
2336 // Update the chunk map so that arena_t::RunDalloc() can treat the
2337 // leading run as separately allocated.
2338 aChunk->map[pageind].bits =
2339 (aOldSize - aNewSize) | CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
2340 aChunk->map[pageind + head_npages].bits =
2341 aNewSize | CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
2342
2343 DebugOnly<arena_chunk_t*> no_chunk = DallocRun(aRun, false);
2344 // This will never release a chunk as there's still at least one allocated
2345 // run.
2346 MOZ_ASSERT(!no_chunk)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!no_chunk)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!no_chunk))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!no_chunk", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2346); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!no_chunk" ")"
); do { MOZ_CrashSequence(__null, 2346); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2347}
2348
2349void arena_t::TrimRunTail(arena_chunk_t* aChunk, arena_run_t* aRun,
2350 size_t aOldSize, size_t aNewSize, bool aDirty) {
2351 size_t pageind = (uintptr_t(aRun) - uintptr_t(aChunk)) >> gPageSize2Pow;
2352 size_t npages = aNewSize >> gPageSize2Pow;
2353
2354 MOZ_ASSERT(aOldSize > aNewSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOldSize > aNewSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aOldSize > aNewSize))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aOldSize > aNewSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2354); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOldSize > aNewSize" ")"); do { MOZ_CrashSequence
(__null, 2354); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2355
2356 // Update the chunk map so that arena_t::RunDalloc() can treat the
2357 // trailing run as separately allocated.
2358 aChunk->map[pageind].bits = aNewSize | CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
2359 aChunk->map[pageind + npages].bits =
2360 (aOldSize - aNewSize) | CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
2361
2362 DebugOnly<arena_chunk_t*> no_chunk =
2363 DallocRun((arena_run_t*)(uintptr_t(aRun) + aNewSize), aDirty);
2364
2365 // This will never release a chunk as there's still at least one allocated
2366 // run.
2367 MOZ_ASSERT(!no_chunk)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!no_chunk)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!no_chunk))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!no_chunk", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2367); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!no_chunk" ")"
); do { MOZ_CrashSequence(__null, 2367); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2368}
2369
2370arena_run_t* arena_t::GetNewEmptyBinRun(arena_bin_t* aBin) {
2371 arena_run_t* run;
2372 unsigned i, remainder;
2373
2374 // Allocate a new run.
2375 run = AllocRun(static_cast<size_t>(aBin->mRunSizePages) << gPageSize2Pow,
2376 false, false);
2377 if (!run) {
2378 return nullptr;
2379 }
2380
2381 // Initialize run internals.
2382 run->mBin = aBin;
2383
2384 for (i = 0; i < aBin->mRunNumRegionsMask - 1; i++) {
2385 run->mRegionsMask[i] = UINT_MAX(2147483647 *2U +1U);
2386 }
2387 remainder = aBin->mRunNumRegions & ((1U << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3)) - 1);
2388 if (remainder == 0) {
2389 run->mRegionsMask[i] = UINT_MAX(2147483647 *2U +1U);
2390 } else {
2391 // The last element has spare bits that need to be unset.
2392 run->mRegionsMask[i] =
2393 (UINT_MAX(2147483647 *2U +1U) >> ((1U << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3)) - remainder));
2394 }
2395
2396 run->mRegionsMinElement = 0;
2397
2398 run->mNumFree = aBin->mRunNumRegions;
2399#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED1)
2400 run->mMagic = ARENA_RUN_MAGIC0x384adf93;
2401#endif
2402
2403 // Make sure we continue to use this run for subsequent allocations.
2404 new (&run->mRunListElem) DoublyLinkedListElement<arena_run_t>();
2405 aBin->mNonFullRuns.pushFront(run);
2406
2407 aBin->mNumRuns++;
2408 return run;
2409}
2410
2411arena_run_t* arena_t::GetNonFullBinRun(arena_bin_t* aBin) {
2412 auto mrf_head = aBin->mNonFullRuns.begin();
2413 if (mrf_head) {
2414 // Take the head and if we are going to fill it, remove it from our list.
2415 arena_run_t* run = &(*mrf_head);
2416 MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2416); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 2416); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2417 if (run->mNumFree == 1) {
2418 aBin->mNonFullRuns.remove(run);
2419 }
2420 return run;
2421 }
2422 return GetNewEmptyBinRun(aBin);
2423}
2424
2425void arena_bin_t::Init(SizeClass aSizeClass) {
2426 size_t try_run_size;
2427 unsigned try_nregs, try_mask_nelms, try_reg0_offset;
2428 // Size of the run header, excluding mRegionsMask.
2429 static const size_t kFixedHeaderSize = offsetof(arena_run_t, mRegionsMask)__builtin_offsetof(arena_run_t, mRegionsMask);
2430
2431 MOZ_ASSERT(aSizeClass.Size() <= gMaxBinClass)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSizeClass.Size() <= (gMaxSubPageClass ? gMaxSubPageClass
: kMaxQuantumWideClass))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSizeClass.Size() <= (gMaxSubPageClass
? gMaxSubPageClass : kMaxQuantumWideClass)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aSizeClass.Size() <= (gMaxSubPageClass ? gMaxSubPageClass : kMaxQuantumWideClass)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2431); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSizeClass.Size() <= (gMaxSubPageClass ? gMaxSubPageClass : kMaxQuantumWideClass)"
")"); do { MOZ_CrashSequence(__null, 2431); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2432
2433 try_run_size = gPageSize;
2434
2435 mSizeClass = aSizeClass.Size();
2436 mNumRuns = 0;
2437
2438 // Run size expansion loop.
2439 while (true) {
2440 try_nregs = ((try_run_size - kFixedHeaderSize) / mSizeClass) +
2441 1; // Counter-act try_nregs-- in loop.
2442
2443 // The do..while loop iteratively reduces the number of regions until
2444 // the run header and the regions no longer overlap. A closed formula
2445 // would be quite messy, since there is an interdependency between the
2446 // header's mask length and the number of regions.
2447 do {
2448 try_nregs--;
2449 try_mask_nelms =
2450 (try_nregs >> (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3)) +
2451 ((try_nregs & ((1U << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3)) - 1)) ? 1 : 0);
2452 try_reg0_offset = try_run_size - (try_nregs * mSizeClass);
2453 } while (kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) >
2454 try_reg0_offset);
2455
2456 // Try to keep the run overhead below kRunOverhead.
2457 if (Fraction(try_reg0_offset, try_run_size) <= kRunOverhead) {
2458 break;
2459 }
2460
2461 // If the overhead is larger than the size class, it means the size class
2462 // is small and doesn't align very well with the header. It's desirable to
2463 // have smaller run sizes for them, so relax the overhead requirement.
2464 if (try_reg0_offset > mSizeClass) {
2465 if (Fraction(try_reg0_offset, try_run_size) <= kRunRelaxedOverhead) {
2466 break;
2467 }
2468 }
2469
2470 // The run header includes one bit per region of the given size. For sizes
2471 // small enough, the number of regions is large enough that growing the run
2472 // size barely moves the needle for the overhead because of all those bits.
2473 // For example, for a size of 8 bytes, adding 4KiB to the run size adds
2474 // close to 512 bits to the header, which is 64 bytes.
2475 // With such overhead, there is no way to get to the wanted overhead above,
2476 // so we give up if the required size for mRegionsMask more than doubles the
2477 // size of the run header.
2478 if (try_mask_nelms * sizeof(unsigned) >= kFixedHeaderSize) {
2479 break;
2480 }
2481
2482 // If next iteration is going to be larger than the largest possible large
2483 // size class, then we didn't find a setup where the overhead is small
2484 // enough, and we can't do better than the current settings, so just use
2485 // that.
2486 if (try_run_size + gPageSize > gMaxLargeClass) {
2487 break;
2488 }
2489
2490 // Try more aggressive settings.
2491 try_run_size += gPageSize;
2492 }
2493
2494 MOZ_ASSERT(kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) <=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms
) <= try_reg0_offset)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(kFixedHeaderSize + (sizeof(unsigned
) * try_mask_nelms) <= try_reg0_offset))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) <= try_reg0_offset"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) <= try_reg0_offset"
")"); do { MOZ_CrashSequence(__null, 2495); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2495 try_reg0_offset)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms
) <= try_reg0_offset)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(kFixedHeaderSize + (sizeof(unsigned
) * try_mask_nelms) <= try_reg0_offset))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) <= try_reg0_offset"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "kFixedHeaderSize + (sizeof(unsigned) * try_mask_nelms) <= try_reg0_offset"
")"); do { MOZ_CrashSequence(__null, 2495); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2496 MOZ_ASSERT((try_mask_nelms << (LOG2(sizeof(int)) + 3)) >= try_nregs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((try_mask_nelms << (Log2<sizeof(int)>::value
+ 3)) >= try_nregs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((try_mask_nelms << (Log2
<sizeof(int)>::value + 3)) >= try_nregs))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(try_mask_nelms << (Log2<sizeof(int)>::value + 3)) >= try_nregs"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2496); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(try_mask_nelms << (Log2<sizeof(int)>::value + 3)) >= try_nregs"
")"); do { MOZ_CrashSequence(__null, 2496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2497
2498 // Our list management would break if mRunNumRegions == 1 and we should use
2499 // a large size class instead, anyways.
2500 MOZ_ASSERT(try_nregs > 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(try_nregs > 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(try_nregs > 1))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("try_nregs > 1"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2500); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "try_nregs > 1" ")"); do { MOZ_CrashSequence
(__null, 2500); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2501
2502 // Copy final settings.
2503 MOZ_ASSERT((try_run_size >> gPageSize2Pow) <= UINT8_MAX)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((try_run_size >> gPageSize2Pow) <= (255))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((try_run_size >> gPageSize2Pow) <= (255))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("(try_run_size >> gPageSize2Pow) <= (255)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2503); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(try_run_size >> gPageSize2Pow) <= (255)"
")"); do { MOZ_CrashSequence(__null, 2503); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2504 mRunSizePages = static_cast<uint8_t>(try_run_size >> gPageSize2Pow);
2505 mRunNumRegions = try_nregs;
2506 mRunNumRegionsMask = try_mask_nelms;
2507 mRunFirstRegionOffset = try_reg0_offset;
2508 mSizeDivisor = FastDivisor<uint16_t>(aSizeClass.Size(), try_run_size);
2509}
2510
2511void arena_t::ResetSmallAllocRandomization() {
2512 if (MOZ_UNLIKELY(opt_randomize_small)(__builtin_expect(!!(opt_randomize_small), 0))) {
2513 MaybeMutexAutoLock lock(mLock);
2514 InitPRNG();
2515 }
2516 mRandomizeSmallAllocations = opt_randomize_small;
2517}
2518
2519void arena_t::InitPRNG() {
2520 // Both another thread could race and the code backing RandomUint64
2521 // (arc4random for example) may allocate memory while here, so we must
2522 // ensure to start the mPRNG initialization only once and to not hold
2523 // the lock while initializing.
2524 mIsPRNGInitializing = true;
2525 {
2526 mLock.Unlock();
2527 mozilla::Maybe<uint64_t> prngState1 = mozilla::RandomUint64();
2528 mozilla::Maybe<uint64_t> prngState2 = mozilla::RandomUint64();
2529 mLock.Lock();
2530
2531 mozilla::non_crypto::XorShift128PlusRNG prng(prngState1.valueOr(0),
2532 prngState2.valueOr(0));
2533 if (mPRNG) {
2534 *mPRNG = prng;
2535 } else {
2536 void* backing =
2537 base_alloc(sizeof(mozilla::non_crypto::XorShift128PlusRNG));
2538 mPRNG = new (backing)
2539 mozilla::non_crypto::XorShift128PlusRNG(std::move(prng));
2540 }
2541 }
2542 mIsPRNGInitializing = false;
2543}
2544
2545void* arena_t::MallocSmall(size_t aSize, bool aZero) {
2546 void* ret;
2547 arena_bin_t* bin;
2548 arena_run_t* run;
2549 SizeClass sizeClass(aSize);
2550 aSize = sizeClass.Size();
2551
2552 switch (sizeClass.Type()) {
2553 case SizeClass::Tiny:
2554 bin = &mBins[FloorLog2(aSize / kMinTinyClass)];
2555 break;
2556 case SizeClass::Quantum:
2557 // Although we divide 2 things by kQuantum, the compiler will
2558 // reduce `kMinQuantumClass / kQuantum` and `kNumTinyClasses` to a
2559 // single constant.
2560 bin = &mBins[kNumTinyClasses + (aSize / kQuantum) -
2561 (kMinQuantumClass / kQuantum)];
2562 break;
2563 case SizeClass::QuantumWide:
2564 bin =
2565 &mBins[kNumTinyClasses + kNumQuantumClasses + (aSize / kQuantumWide) -
2566 (kMinQuantumWideClass / kQuantumWide)];
2567 break;
2568 case SizeClass::SubPage:
2569 bin =
2570 &mBins[kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses +
2571 (FloorLog2(aSize) - LOG2(kMinSubPageClass)Log2<kMinSubPageClass>::value)];
2572 break;
2573 default:
2574 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected size class type")do { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unexpected size class type" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2574); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unexpected size class type" ")")
; do { MOZ_CrashSequence(__null, 2574); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); __builtin_unreachable
(); } while (false)
;
2575 }
2576 MOZ_DIAGNOSTIC_ASSERT(aSize == bin->mSizeClass)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize == bin->mSizeClass)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize == bin->mSizeClass)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aSize == bin->mSizeClass"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2576); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aSize == bin->mSizeClass" ")"
); do { MOZ_CrashSequence(__null, 2576); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2577
2578 size_t num_dirty_before, num_dirty_after;
2579 {
2580 MaybeMutexAutoLock lock(mLock);
2581
2582#ifdef MOZ_DEBUG1
2583 bool isInitializingThread(false);
2584#endif
2585
2586 if (MOZ_UNLIKELY(mRandomizeSmallAllocations && mPRNG == nullptr &&(__builtin_expect(!!(mRandomizeSmallAllocations && mPRNG
== nullptr && !mIsPRNGInitializing), 0))
2587 !mIsPRNGInitializing)(__builtin_expect(!!(mRandomizeSmallAllocations && mPRNG
== nullptr && !mIsPRNGInitializing), 0))
) {
2588#ifdef MOZ_DEBUG1
2589 isInitializingThread = true;
2590#endif
2591 InitPRNG();
2592 }
2593
2594 MOZ_ASSERT(!mRandomizeSmallAllocations || mPRNG ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mRandomizeSmallAllocations || mPRNG || (mIsPRNGInitializing
&& !isInitializingThread))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mRandomizeSmallAllocations ||
mPRNG || (mIsPRNGInitializing && !isInitializingThread
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mRandomizeSmallAllocations || mPRNG || (mIsPRNGInitializing && !isInitializingThread)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2595); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mRandomizeSmallAllocations || mPRNG || (mIsPRNGInitializing && !isInitializingThread)"
")"); do { MOZ_CrashSequence(__null, 2595); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2595 (mIsPRNGInitializing && !isInitializingThread))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mRandomizeSmallAllocations || mPRNG || (mIsPRNGInitializing
&& !isInitializingThread))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mRandomizeSmallAllocations ||
mPRNG || (mIsPRNGInitializing && !isInitializingThread
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mRandomizeSmallAllocations || mPRNG || (mIsPRNGInitializing && !isInitializingThread)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2595); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mRandomizeSmallAllocations || mPRNG || (mIsPRNGInitializing && !isInitializingThread)"
")"); do { MOZ_CrashSequence(__null, 2595); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2596
2597 num_dirty_before = mNumDirty;
2598 run = GetNonFullBinRun(bin);
2599 num_dirty_after = mNumDirty;
2600 if (MOZ_UNLIKELY(!run)(__builtin_expect(!!(!run), 0))) {
2601 return nullptr;
2602 }
2603 MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2603); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 2603); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2604 MOZ_DIAGNOSTIC_ASSERT(run->mNumFree > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mNumFree > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mNumFree > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("run->mNumFree > 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2604); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mNumFree > 0" ")"); do
{ MOZ_CrashSequence(__null, 2604); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2605 ret = ArenaRunRegAlloc(run, bin);
2606 MOZ_DIAGNOSTIC_ASSERT(ret)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ret)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(ret))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("ret", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2606); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "ret"
")"); do { MOZ_CrashSequence(__null, 2606); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2607 run->mNumFree--;
2608 if (!ret) {
2609 return nullptr;
2610 }
2611
2612 mStats.allocated_small += aSize;
2613 mStats.operations++;
2614 }
2615 if (num_dirty_after < num_dirty_before) {
2616 NotifySignificantReuse();
2617 }
2618 if (!aZero) {
2619 ApplyZeroOrJunk(ret, aSize);
2620 } else {
2621 memset(ret, 0, aSize);
2622 }
2623
2624 return ret;
2625}
2626
2627void* arena_t::MallocLarge(size_t aSize, bool aZero) {
2628 void* ret;
2629
2630 // Large allocation.
2631 aSize = PAGE_CEILING(aSize)(((aSize) + gPageSizeMask) & ~gPageSizeMask);
2632
2633 size_t num_dirty_before, num_dirty_after;
2634 {
2635 MaybeMutexAutoLock lock(mLock);
2636 num_dirty_before = mNumDirty;
2637 ret = AllocRun(aSize, true, aZero);
2638 num_dirty_after = mNumDirty;
2639 if (!ret) {
2640 return nullptr;
2641 }
2642 mStats.allocated_large += aSize;
2643 mStats.operations++;
2644 }
2645 if (num_dirty_after < num_dirty_before) {
2646 NotifySignificantReuse();
2647 }
2648
2649 if (!aZero) {
2650 ApplyZeroOrJunk(ret, aSize);
2651 }
2652
2653 return ret;
2654}
2655
2656void* arena_t::Malloc(size_t aSize, bool aZero) {
2657 MOZ_DIAGNOSTIC_ASSERT(mMagic == ARENA_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMagic == 0x947d3d24)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMagic == 0x947d3d24))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("mMagic == 0x947d3d24"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2657); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "mMagic == 0x947d3d24" ")"); do {
MOZ_CrashSequence(__null, 2657); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2658 MOZ_ASSERT(aSize != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize != 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aSize != 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2658); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSize != 0"
")"); do { MOZ_CrashSequence(__null, 2658); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2659
2660 if (aSize <= gMaxBinClass(gMaxSubPageClass ? gMaxSubPageClass : kMaxQuantumWideClass)) {
2661 return MallocSmall(aSize, aZero);
2662 }
2663 if (aSize <= gMaxLargeClass) {
2664 return MallocLarge(aSize, aZero);
2665 }
2666 return MallocHuge(aSize, aZero);
2667}
2668
2669// Only handles large allocations that require more than page alignment.
2670void* arena_t::PallocLarge(size_t aAlignment, size_t aSize, size_t aAllocSize) {
2671 void* ret;
2672 size_t offset;
2673 arena_chunk_t* chunk;
2674
2675 MOZ_ASSERT((aSize & gPageSizeMask) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aSize & gPageSizeMask) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((aSize & gPageSizeMask) ==
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(aSize & gPageSizeMask) == 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2675); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(aSize & gPageSizeMask) == 0"
")"); do { MOZ_CrashSequence(__null, 2675); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2676 MOZ_ASSERT((aAlignment & gPageSizeMask) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((aAlignment & gPageSizeMask) == 0)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!((aAlignment & gPageSizeMask) == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("(aAlignment & gPageSizeMask) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2676); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(aAlignment & gPageSizeMask) == 0" ")"
); do { MOZ_CrashSequence(__null, 2676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2677
2678 size_t num_dirty_before, num_dirty_after;
2679 {
2680 MaybeMutexAutoLock lock(mLock);
2681 num_dirty_before = mNumDirty;
2682 ret = AllocRun(aAllocSize, true, false);
2683 if (!ret) {
2684 return nullptr;
2685 }
2686
2687 chunk = GetChunkForPtr(ret);
2688
2689 offset = uintptr_t(ret) & (aAlignment - 1);
2690 MOZ_ASSERT((offset & gPageSizeMask) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((offset & gPageSizeMask) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((offset & gPageSizeMask)
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(offset & gPageSizeMask) == 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2690); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(offset & gPageSizeMask) == 0"
")"); do { MOZ_CrashSequence(__null, 2690); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2691 MOZ_ASSERT(offset < aAllocSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offset < aAllocSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(offset < aAllocSize))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("offset < aAllocSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2691); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "offset < aAllocSize" ")"); do { MOZ_CrashSequence
(__null, 2691); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2692 if (offset == 0) {
2693 TrimRunTail(chunk, (arena_run_t*)ret, aAllocSize, aSize, false);
2694 } else {
2695 size_t leadsize, trailsize;
2696
2697 leadsize = aAlignment - offset;
2698 if (leadsize > 0) {
2699 TrimRunHead(chunk, (arena_run_t*)ret, aAllocSize,
2700 aAllocSize - leadsize);
2701 ret = (void*)(uintptr_t(ret) + leadsize);
2702 }
2703
2704 trailsize = aAllocSize - leadsize - aSize;
2705 if (trailsize != 0) {
2706 // Trim trailing space.
2707 MOZ_ASSERT(trailsize < aAllocSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(trailsize < aAllocSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(trailsize < aAllocSize)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("trailsize < aAllocSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2707); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "trailsize < aAllocSize" ")"); do { MOZ_CrashSequence
(__null, 2707); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2708 TrimRunTail(chunk, (arena_run_t*)ret, aSize + trailsize, aSize, false);
2709 }
2710 }
2711 num_dirty_after = mNumDirty;
2712
2713 mStats.allocated_large += aSize;
2714 mStats.operations++;
2715 }
2716 if (num_dirty_after < num_dirty_before) {
2717 NotifySignificantReuse();
2718 }
2719 // Note that since Bug 1488780we don't attempt purge dirty memory on this code
2720 // path. In general there won't be dirty memory above the threshold after an
2721 // allocation, only after free. The exception is if the dirty page threshold
2722 // has changed.
2723
2724 ApplyZeroOrJunk(ret, aSize);
2725 return ret;
2726}
2727
2728void* arena_t::Palloc(size_t aAlignment, size_t aSize) {
2729 void* ret;
2730 size_t ceil_size;
2731
2732 // Round size up to the nearest multiple of alignment.
2733 //
2734 // This done, we can take advantage of the fact that for each small
2735 // size class, every object is aligned at the smallest power of two
2736 // that is non-zero in the base two representation of the size. For
2737 // example:
2738 //
2739 // Size | Base 2 | Minimum alignment
2740 // -----+----------+------------------
2741 // 96 | 1100000 | 32
2742 // 144 | 10100000 | 32
2743 // 192 | 11000000 | 64
2744 //
2745 // Depending on runtime settings, it is possible that arena_malloc()
2746 // will further round up to a power of two, but that never causes
2747 // correctness issues.
2748 ceil_size = ALIGNMENT_CEILING(aSize, aAlignment)(((aSize) + ((aAlignment) - 1)) & (~((aAlignment) - 1)));
2749
2750 // (ceil_size < aSize) protects against the combination of maximal
2751 // alignment and size greater than maximal alignment.
2752 if (ceil_size < aSize) {
2753 // size_t overflow.
2754 return nullptr;
2755 }
2756
2757 if (ceil_size <= gPageSize ||
2758 (aAlignment <= gPageSize && ceil_size <= gMaxLargeClass)) {
2759 ret = Malloc(ceil_size, false);
2760 } else {
2761 size_t run_size;
2762
2763 // We can't achieve sub-page alignment, so round up alignment
2764 // permanently; it makes later calculations simpler.
2765 aAlignment = PAGE_CEILING(aAlignment)(((aAlignment) + gPageSizeMask) & ~gPageSizeMask);
2766 ceil_size = PAGE_CEILING(aSize)(((aSize) + gPageSizeMask) & ~gPageSizeMask);
2767
2768 // (ceil_size < aSize) protects against very large sizes within
2769 // pagesize of SIZE_T_MAX.
2770 //
2771 // (ceil_size + aAlignment < ceil_size) protects against the
2772 // combination of maximal alignment and ceil_size large enough
2773 // to cause overflow. This is similar to the first overflow
2774 // check above, but it needs to be repeated due to the new
2775 // ceil_size value, which may now be *equal* to maximal
2776 // alignment, whereas before we only detected overflow if the
2777 // original size was *greater* than maximal alignment.
2778 if (ceil_size < aSize || ceil_size + aAlignment < ceil_size) {
2779 // size_t overflow.
2780 return nullptr;
2781 }
2782
2783 // Calculate the size of the over-size run that arena_palloc()
2784 // would need to allocate in order to guarantee the alignment.
2785 if (ceil_size >= aAlignment) {
2786 run_size = ceil_size + aAlignment - gPageSize;
2787 } else {
2788 // It is possible that (aAlignment << 1) will cause
2789 // overflow, but it doesn't matter because we also
2790 // subtract pagesize, which in the case of overflow
2791 // leaves us with a very large run_size. That causes
2792 // the first conditional below to fail, which means
2793 // that the bogus run_size value never gets used for
2794 // anything important.
2795 run_size = (aAlignment << 1) - gPageSize;
2796 }
2797
2798 if (run_size <= gMaxLargeClass) {
2799 ret = PallocLarge(aAlignment, ceil_size, run_size);
2800 } else if (aAlignment <= kChunkSize) {
2801 ret = MallocHuge(ceil_size, false);
2802 } else {
2803 ret = PallocHuge(ceil_size, aAlignment, false);
2804 }
2805 }
2806
2807 MOZ_ASSERT((uintptr_t(ret) & (aAlignment - 1)) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((uintptr_t(ret) & (aAlignment - 1)) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((uintptr_t(ret) & (aAlignment - 1)) == 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(uintptr_t(ret) & (aAlignment - 1)) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2807); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(uintptr_t(ret) & (aAlignment - 1)) == 0"
")"); do { MOZ_CrashSequence(__null, 2807); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2808 return ret;
2809}
2810
2811class AllocInfo {
2812 public:
2813 template <bool Validate = false>
2814 static inline AllocInfo Get(const void* aPtr) {
2815 // If the allocator is not initialized, the pointer can't belong to it.
2816 if (Validate && !malloc_initialized) {
2817 return AllocInfo();
2818 }
2819
2820 auto chunk = GetChunkForPtr(aPtr);
2821 if (Validate) {
2822 if (!chunk || !gChunkRTree.Get(chunk)) {
2823 return AllocInfo();
2824 }
2825 }
2826
2827 if (chunk != aPtr) {
2828 MOZ_DIAGNOSTIC_ASSERT(chunk->arena->mMagic == ARENA_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chunk->arena->mMagic == 0x947d3d24)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(chunk->arena->mMagic == 0x947d3d24))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("chunk->arena->mMagic == 0x947d3d24"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2828); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "chunk->arena->mMagic == 0x947d3d24"
")"); do { MOZ_CrashSequence(__null, 2828); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2829 size_t pageind = (((uintptr_t)aPtr - (uintptr_t)chunk) >> gPageSize2Pow);
2830 return GetInChunk(aPtr, chunk, pageind);
2831 }
2832
2833 extent_node_t key;
2834
2835 // Huge allocation
2836 key.mAddr = chunk;
2837 MutexAutoLock lock(huge_mtx);
2838 extent_node_t* node = huge.Search(&key);
2839 if (Validate && !node) {
2840 return AllocInfo();
2841 }
2842 return AllocInfo(node->mSize, node);
2843 }
2844
2845 // Get the allocation information for a pointer we know is within a chunk
2846 // (Small or large, not huge).
2847 static inline AllocInfo GetInChunk(const void* aPtr, arena_chunk_t* aChunk,
2848 size_t pageind) {
2849 size_t mapbits = aChunk->map[pageind].bits;
2850 MOZ_DIAGNOSTIC_ASSERT((mapbits & CHUNK_MAP_ALLOCATED) != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapbits & ((size_t)0x01U)) != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((mapbits & ((size_t)0x01U
)) != 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(mapbits & ((size_t)0x01U)) != 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2850); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "(mapbits & ((size_t)0x01U)) != 0"
")"); do { MOZ_CrashSequence(__null, 2850); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2851
2852 size_t size;
2853 if ((mapbits & CHUNK_MAP_LARGE((size_t)0x02U)) == 0) {
2854 arena_run_t* run = (arena_run_t*)(mapbits & ~gPageSizeMask);
2855 MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2855); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 2855); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2856 size = run->mBin->mSizeClass;
2857 } else {
2858 size = mapbits & ~gPageSizeMask;
2859 MOZ_DIAGNOSTIC_ASSERT(size != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(size != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(size != 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("size != 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2859); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "size != 0"
")"); do { MOZ_CrashSequence(__null, 2859); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2860 }
2861
2862 return AllocInfo(size, aChunk);
2863 }
2864
2865 // Validate ptr before assuming that it points to an allocation. Currently,
2866 // the following validation is performed:
2867 //
2868 // + Check that ptr is not nullptr.
2869 //
2870 // + Check that ptr lies within a mapped chunk.
2871 static inline AllocInfo GetValidated(const void* aPtr) {
2872 return Get<true>(aPtr);
2873 }
2874
2875 AllocInfo() : mSize(0), mChunk(nullptr) {}
2876
2877 explicit AllocInfo(size_t aSize, arena_chunk_t* aChunk)
2878 : mSize(aSize), mChunk(aChunk) {
2879 MOZ_ASSERT(mSize <= gMaxLargeClass)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mSize <= gMaxLargeClass)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mSize <= gMaxLargeClass))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mSize <= gMaxLargeClass"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2879); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mSize <= gMaxLargeClass" ")"); do { MOZ_CrashSequence
(__null, 2879); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2880 }
2881
2882 explicit AllocInfo(size_t aSize, extent_node_t* aNode)
2883 : mSize(aSize), mNode(aNode) {
2884 MOZ_ASSERT(mSize > gMaxLargeClass)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mSize > gMaxLargeClass)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mSize > gMaxLargeClass)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("mSize > gMaxLargeClass"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2884); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mSize > gMaxLargeClass" ")"); do { MOZ_CrashSequence
(__null, 2884); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2885 }
2886
2887 size_t Size() { return mSize; }
2888
2889 arena_t* Arena() {
2890 if (mSize <= gMaxLargeClass) {
2891 return mChunk->arena;
2892 }
2893 // Best effort detection that we're not trying to access an already
2894 // disposed arena. In the case of a disposed arena, the memory location
2895 // pointed by mNode->mArena is either free (but still a valid memory
2896 // region, per TypedBaseAlloc<arena_t>), in which case its id was reset,
2897 // or has been reallocated for a new region, and its id is very likely
2898 // different (per randomness). In both cases, the id is unlikely to
2899 // match what it was for the disposed arena.
2900 MOZ_RELEASE_ASSERT(mNode->mArenaId == mNode->mArena->mId)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mNode->mArenaId == mNode->mArena->mId)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mNode->mArenaId == mNode->mArena->mId))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mNode->mArenaId == mNode->mArena->mId"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2900); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mNode->mArenaId == mNode->mArena->mId"
")"); do { MOZ_CrashSequence(__null, 2900); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2901 return mNode->mArena;
2902 }
2903
2904 bool IsValid() const { return !!mSize; }
2905
2906 private:
2907 size_t mSize;
2908 union {
2909 // Pointer to the chunk associated with the allocation for small
2910 // and large allocations.
2911 arena_chunk_t* mChunk;
2912
2913 // Pointer to the extent node for huge allocations.
2914 extent_node_t* mNode;
2915 };
2916};
2917
2918inline void MozJemalloc::jemalloc_ptr_info(const void* aPtr,
2919 jemalloc_ptr_info_t* aInfo) {
2920 arena_chunk_t* chunk = GetChunkForPtr(aPtr);
2921
2922 // Is the pointer null, or within one chunk's size of null?
2923 // Alternatively, if the allocator is not initialized yet, the pointer
2924 // can't be known.
2925 if (!chunk || !malloc_initialized) {
2926 *aInfo = {TagUnknown, nullptr, 0, 0};
2927 return;
2928 }
2929
2930 // Look for huge allocations before looking for |chunk| in gChunkRTree.
2931 // This is necessary because |chunk| won't be in gChunkRTree if it's
2932 // the second or subsequent chunk in a huge allocation.
2933 extent_node_t* node;
2934 extent_node_t key;
2935 {
2936 MutexAutoLock lock(huge_mtx);
2937 key.mAddr = const_cast<void*>(aPtr);
2938 node =
2939 reinterpret_cast<RedBlackTree<extent_node_t, ExtentTreeBoundsTrait>*>(
2940 &huge)
2941 ->Search(&key);
2942 if (node) {
2943 *aInfo = {TagLiveAlloc, node->mAddr, node->mSize, node->mArena->mId};
2944 return;
2945 }
2946 }
2947
2948 // It's not a huge allocation. Check if we have a known chunk.
2949 if (!gChunkRTree.Get(chunk)) {
2950 *aInfo = {TagUnknown, nullptr, 0, 0};
2951 return;
2952 }
2953
2954 MOZ_DIAGNOSTIC_ASSERT(chunk->arena->mMagic == ARENA_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chunk->arena->mMagic == 0x947d3d24)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(chunk->arena->mMagic == 0x947d3d24))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("chunk->arena->mMagic == 0x947d3d24"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 2954); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "chunk->arena->mMagic == 0x947d3d24"
")"); do { MOZ_CrashSequence(__null, 2954); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2955
2956 // Get the page number within the chunk.
2957 size_t pageind = (((uintptr_t)aPtr - (uintptr_t)chunk) >> gPageSize2Pow);
2958 if (pageind < gChunkHeaderNumPages) {
2959 // Within the chunk header.
2960 *aInfo = {TagUnknown, nullptr, 0, 0};
2961 return;
2962 }
2963
2964 size_t mapbits = chunk->map[pageind].bits;
2965
2966 if (!(mapbits & CHUNK_MAP_ALLOCATED((size_t)0x01U))) {
2967 void* pageaddr = (void*)(uintptr_t(aPtr) & ~gPageSizeMask);
2968 *aInfo = {TagFreedPage, pageaddr, gPageSize, chunk->arena->mId};
2969 return;
2970 }
2971
2972 if (mapbits & CHUNK_MAP_LARGE((size_t)0x02U)) {
2973 // It's a large allocation. Only the first page of a large
2974 // allocation contains its size, so if the address is not in
2975 // the first page, scan back to find the allocation size.
2976 size_t size;
2977 while (true) {
2978 size = mapbits & ~gPageSizeMask;
2979 if (size != 0) {
2980 break;
2981 }
2982
2983 // The following two return paths shouldn't occur in
2984 // practice unless there is heap corruption.
2985 pageind--;
2986 MOZ_DIAGNOSTIC_ASSERT(pageind >= gChunkHeaderNumPages)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pageind >= gChunkHeaderNumPages)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pageind >= gChunkHeaderNumPages
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pageind >= gChunkHeaderNumPages", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2986); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "pageind >= gChunkHeaderNumPages"
")"); do { MOZ_CrashSequence(__null, 2986); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2987 if (pageind < gChunkHeaderNumPages) {
2988 *aInfo = {TagUnknown, nullptr, 0, 0};
2989 return;
2990 }
2991
2992 mapbits = chunk->map[pageind].bits;
2993 MOZ_DIAGNOSTIC_ASSERT(mapbits & CHUNK_MAP_LARGE)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mapbits & ((size_t)0x02U))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mapbits & ((size_t)0x02U
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mapbits & ((size_t)0x02U)", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 2993); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "mapbits & ((size_t)0x02U)"
")"); do { MOZ_CrashSequence(__null, 2993); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2994 if (!(mapbits & CHUNK_MAP_LARGE((size_t)0x02U))) {
2995 *aInfo = {TagUnknown, nullptr, 0, 0};
2996 return;
2997 }
2998 }
2999
3000 void* addr = ((char*)chunk) + (pageind << gPageSize2Pow);
3001 *aInfo = {TagLiveAlloc, addr, size, chunk->arena->mId};
3002 return;
3003 }
3004
3005 // It must be a small allocation.
3006 auto run = (arena_run_t*)(mapbits & ~gPageSizeMask);
3007 MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3007); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 3007); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3008
3009 // The allocation size is stored in the run metadata.
3010 size_t size = run->mBin->mSizeClass;
3011
3012 // Address of the first possible pointer in the run after its headers.
3013 uintptr_t reg0_addr = (uintptr_t)run + run->mBin->mRunFirstRegionOffset;
3014 if (aPtr < (void*)reg0_addr) {
3015 // In the run header.
3016 *aInfo = {TagUnknown, nullptr, 0, 0};
3017 return;
3018 }
3019
3020 // Position in the run.
3021 unsigned regind = ((uintptr_t)aPtr - reg0_addr) / size;
3022
3023 // Pointer to the allocation's base address.
3024 void* addr = (void*)(reg0_addr + regind * size);
3025
3026 // Check if the allocation has been freed.
3027 unsigned elm = regind >> (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3);
3028 unsigned bit = regind - (elm << (LOG2(sizeof(int))Log2<sizeof(int)>::value + 3));
3029 PtrInfoTag tag =
3030 ((run->mRegionsMask[elm] & (1U << bit))) ? TagFreedAlloc : TagLiveAlloc;
3031
3032 *aInfo = {tag, addr, size, chunk->arena->mId};
3033}
3034
3035namespace Debug {
3036// Helper for debuggers. We don't want it to be inlined and optimized out.
3037MOZ_NEVER_INLINE__attribute__((noinline)) jemalloc_ptr_info_t* jemalloc_ptr_info(const void* aPtr) {
3038 static jemalloc_ptr_info_t info;
3039 MozJemalloc::jemalloc_ptr_info(aPtr, &info);
3040 return &info;
3041}
3042} // namespace Debug
3043
3044arena_chunk_t* arena_t::DallocSmall(arena_chunk_t* aChunk, void* aPtr,
3045 arena_chunk_map_t* aMapElm) {
3046 arena_run_t* run;
3047 arena_bin_t* bin;
3048 size_t size;
3049
3050 run = (arena_run_t*)(aMapElm->bits & ~gPageSizeMask);
3051 MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run->mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run->mMagic == 0x384adf93
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"run->mMagic == 0x384adf93", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3051); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "run->mMagic == 0x384adf93"
")"); do { MOZ_CrashSequence(__null, 3051); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3052 bin = run->mBin;
3053 size = bin->mSizeClass;
3054 MOZ_DIAGNOSTIC_ASSERT(uintptr_t(aPtr) >=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3055); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset"
")"); do { MOZ_CrashSequence(__null, 3055); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3055 uintptr_t(run) + bin->mRunFirstRegionOffset)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3055); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "uintptr_t(aPtr) >= uintptr_t(run) + bin->mRunFirstRegionOffset"
")"); do { MOZ_CrashSequence(__null, 3055); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3056
3057 arena_run_reg_dalloc(run, bin, aPtr, size);
3058 run->mNumFree++;
3059 arena_chunk_t* dealloc_chunk = nullptr;
3060
3061 if (run->mNumFree == bin->mRunNumRegions) {
3062 // This run is entirely freed, remove it from our bin.
3063#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED1)
3064 run->mMagic = 0;
3065#endif
3066 MOZ_ASSERT(bin->mNonFullRuns.ElementProbablyInList(run))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bin->mNonFullRuns.ElementProbablyInList(run))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(bin->mNonFullRuns.ElementProbablyInList(run)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("bin->mNonFullRuns.ElementProbablyInList(run)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3066); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bin->mNonFullRuns.ElementProbablyInList(run)"
")"); do { MOZ_CrashSequence(__null, 3066); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3067 bin->mNonFullRuns.remove(run);
3068 dealloc_chunk = DallocRun(run, true);
3069 bin->mNumRuns--;
3070 } else if (run->mNumFree == 1) {
3071 // This is first slot we freed from this run, start tracking.
3072 MOZ_ASSERT(!bin->mNonFullRuns.ElementProbablyInList(run))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!bin->mNonFullRuns.ElementProbablyInList(run))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!bin->mNonFullRuns.ElementProbablyInList(run)))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!bin->mNonFullRuns.ElementProbablyInList(run)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3072); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!bin->mNonFullRuns.ElementProbablyInList(run)"
")"); do { MOZ_CrashSequence(__null, 3072); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3073 bin->mNonFullRuns.pushFront(run);
3074 }
3075 // else we just keep the run in mNonFullRuns where it is.
3076 // Note that we could move it to the head of the list here to get a strict
3077 // "most-recently-freed" order, but some of our benchmarks seem to be more
3078 // sensible to the increased overhead that this brings than to the order
3079 // supposedly slightly better for keeping CPU caches warm if we do.
3080 // In general we cannot foresee the future, so any order we choose might
3081 // perform different for different use cases and needs to be balanced with
3082 // the book-keeping overhead via measurements.
3083
3084 mStats.allocated_small -= size;
3085 mStats.operations++;
3086
3087 return dealloc_chunk;
3088}
3089
3090arena_chunk_t* arena_t::DallocLarge(arena_chunk_t* aChunk, void* aPtr) {
3091 MOZ_DIAGNOSTIC_ASSERT((uintptr_t(aPtr) & gPageSizeMask) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((uintptr_t(aPtr) & gPageSizeMask) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((uintptr_t(aPtr) & gPageSizeMask) == 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("(uintptr_t(aPtr) & gPageSizeMask) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3091); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "(uintptr_t(aPtr) & gPageSizeMask) == 0"
")"); do { MOZ_CrashSequence(__null, 3091); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3092 size_t pageind = (uintptr_t(aPtr) - uintptr_t(aChunk)) >> gPageSize2Pow;
3093 size_t size = aChunk->map[pageind].bits & ~gPageSizeMask;
3094
3095 mStats.allocated_large -= size;
3096 mStats.operations++;
3097
3098 return DallocRun((arena_run_t*)aPtr, true);
3099}
3100
3101static inline void arena_dalloc(void* aPtr, size_t aOffset, arena_t* aArena) {
3102 MOZ_ASSERT(aPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aPtr)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aPtr))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aPtr", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3102); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPtr" ")");
do { MOZ_CrashSequence(__null, 3102); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3103 MOZ_ASSERT(aOffset != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOffset != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aOffset != 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aOffset != 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3103); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOffset != 0"
")"); do { MOZ_CrashSequence(__null, 3103); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3104 MOZ_ASSERT(GetChunkOffsetForPtr(aPtr) == aOffset)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetChunkOffsetForPtr(aPtr) == aOffset)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(GetChunkOffsetForPtr(aPtr) == aOffset))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("GetChunkOffsetForPtr(aPtr) == aOffset"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3104); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetChunkOffsetForPtr(aPtr) == aOffset" ")"
); do { MOZ_CrashSequence(__null, 3104); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3105
3106 auto chunk = (arena_chunk_t*)((uintptr_t)aPtr - aOffset);
3107 auto arena = chunk->arena;
3108 MOZ_ASSERT(arena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(arena))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("arena", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arena" ")")
; do { MOZ_CrashSequence(__null, 3108); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3109 MOZ_DIAGNOSTIC_ASSERT(arena->mMagic == ARENA_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena->mMagic == 0x947d3d24)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arena->mMagic == 0x947d3d24
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"arena->mMagic == 0x947d3d24", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3109); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "arena->mMagic == 0x947d3d24"
")"); do { MOZ_CrashSequence(__null, 3109); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3110 MOZ_RELEASE_ASSERT(!aArena || arena == aArena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aArena || arena == aArena)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aArena || arena == aArena))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aArena || arena == aArena"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3110); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "!aArena || arena == aArena" ")"); do
{ MOZ_CrashSequence(__null, 3110); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3111
3112 size_t pageind = aOffset >> gPageSize2Pow;
3113 if (opt_poison) {
3114 AllocInfo info = AllocInfo::GetInChunk(aPtr, chunk, pageind);
3115 MOZ_ASSERT(info.IsValid())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(info.IsValid())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(info.IsValid()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("info.IsValid()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3115); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "info.IsValid()" ")"); do { MOZ_CrashSequence
(__null, 3115); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3116 MaybePoison(aPtr, info.Size());
3117 }
3118
3119 arena_chunk_t* chunk_dealloc_delay = nullptr;
3120 purge_action_t purge_action;
3121 {
3122 MOZ_DIAGNOSTIC_ASSERT(arena->mLock.SafeOnThisThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena->mLock.SafeOnThisThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arena->mLock.SafeOnThisThread
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("arena->mLock.SafeOnThisThread()", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3122); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "arena->mLock.SafeOnThisThread()"
")"); do { MOZ_CrashSequence(__null, 3122); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3123 MaybeMutexAutoLock lock(arena->mLock);
3124 arena_chunk_map_t* mapelm = &chunk->map[pageind];
3125 MOZ_RELEASE_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapelm->bits & ((((size_t)0x80U) | ((size_t)
0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U)
| ((size_t)0x20U)) | ((size_t)0x04U))) == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
" (" "Freeing in a page with bad bits." ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3128); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
") (" "Freeing in a page with bad bits." ")"); do { MOZ_CrashSequence
(__null, 3128); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3126 (mapelm->bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapelm->bits & ((((size_t)0x80U) | ((size_t)
0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U)
| ((size_t)0x20U)) | ((size_t)0x04U))) == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
" (" "Freeing in a page with bad bits." ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3128); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
") (" "Freeing in a page with bad bits." ")"); do { MOZ_CrashSequence
(__null, 3128); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3127 (CHUNK_MAP_FRESH_MADVISED_OR_DECOMMITTED | CHUNK_MAP_ZEROED)) == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapelm->bits & ((((size_t)0x80U) | ((size_t)
0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U)
| ((size_t)0x20U)) | ((size_t)0x04U))) == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
" (" "Freeing in a page with bad bits." ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3128); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
") (" "Freeing in a page with bad bits." ")"); do { MOZ_CrashSequence
(__null, 3128); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3128 "Freeing in a page with bad bits.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapelm->bits & ((((size_t)0x80U) | ((size_t)
0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U)
| ((size_t)0x20U)) | ((size_t)0x04U))) == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
" (" "Freeing in a page with bad bits." ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3128); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(mapelm->bits & ((((size_t)0x80U) | ((size_t)0x40U) | ((size_t)0x20U)) | ((size_t)0x04U))) == 0"
") (" "Freeing in a page with bad bits." ")"); do { MOZ_CrashSequence
(__null, 3128); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3129 MOZ_RELEASE_ASSERT((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapelm->bits & ((size_t)0x01U)) != 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((mapelm->bits & ((size_t)0x01U)) != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(mapelm->bits & ((size_t)0x01U)) != 0"
" (" "Double-free?" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3130); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(mapelm->bits & ((size_t)0x01U)) != 0"
") (" "Double-free?" ")"); do { MOZ_CrashSequence(__null, 3130
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
3130 "Double-free?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mapelm->bits & ((size_t)0x01U)) != 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((mapelm->bits & ((size_t)0x01U)) != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(mapelm->bits & ((size_t)0x01U)) != 0"
" (" "Double-free?" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3130); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "(mapelm->bits & ((size_t)0x01U)) != 0"
") (" "Double-free?" ")"); do { MOZ_CrashSequence(__null, 3130
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
3131 if ((mapelm->bits & CHUNK_MAP_LARGE((size_t)0x02U)) == 0) {
3132 // Small allocation.
3133 chunk_dealloc_delay = arena->DallocSmall(chunk, aPtr, mapelm);
3134 } else {
3135 // Large allocation.
3136 chunk_dealloc_delay = arena->DallocLarge(chunk, aPtr);
3137 }
3138
3139 purge_action = arena->ShouldStartPurge();
3140 }
3141
3142 if (chunk_dealloc_delay) {
3143 chunk_dealloc((void*)chunk_dealloc_delay, kChunkSize, ARENA_CHUNK);
3144 }
3145
3146 arena->MayDoOrQueuePurge(purge_action, "arena_dalloc");
3147}
3148
3149static inline void idalloc(void* ptr, arena_t* aArena) {
3150 size_t offset;
3151
3152 MOZ_ASSERT(ptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ptr)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(ptr))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("ptr", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ptr" ")"); do
{ MOZ_CrashSequence(__null, 3152); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3153
3154 offset = GetChunkOffsetForPtr(ptr);
3155 if (offset != 0) {
3156 arena_dalloc(ptr, offset, aArena);
3157 } else {
3158 huge_dalloc(ptr, aArena);
3159 }
3160}
3161
3162inline purge_action_t arena_t::ShouldStartPurge() {
3163 if (mNumDirty > mMaxDirty) {
3164 if (!mIsDeferredPurgeEnabled) {
3165 return purge_action_t::PurgeNow;
3166 }
3167 if (mIsPurgePending) {
3168 return purge_action_t::None;
3169 }
3170 mIsPurgePending = true;
3171 return purge_action_t::Queue;
3172 }
3173 return purge_action_t::None;
3174}
3175
3176inline void arena_t::MayDoOrQueuePurge(purge_action_t aAction,
3177 const char* aCaller) {
3178 switch (aAction) {
3179 case purge_action_t::Queue:
3180 // Note that this thread committed earlier by setting
3181 // mIsDeferredPurgePending to add us to the list. There is a low
3182 // chance that in the meantime another thread ran Purge() and cleared
3183 // the flag, but that is fine, we'll adjust our bookkeeping when calling
3184 // ShouldStartPurge() or Purge() next time.
3185 gArenas.AddToOutstandingPurges(this);
3186 break;
3187 case purge_action_t::PurgeNow: {
3188 ArenaPurgeResult pr = PurgeLoop(PurgeIfThreshold, aCaller);
3189 // Arenas cannot die here because the caller is still using the arena, if
3190 // they did it'd be a use-after-free: the arena is destroyed but then used
3191 // afterwards.
3192 MOZ_RELEASE_ASSERT(pr != ArenaPurgeResult::Dying)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pr != ArenaPurgeResult::Dying)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pr != ArenaPurgeResult::Dying
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pr != ArenaPurgeResult::Dying", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3192); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "pr != ArenaPurgeResult::Dying"
")"); do { MOZ_CrashSequence(__null, 3192); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3193 break;
3194 }
3195 case purge_action_t::None:
3196 // do nothing.
3197 break;
3198 }
3199}
3200
3201inline void arena_t::NotifySignificantReuse() {
3202 // Note that there is a chance here for a race between threads calling
3203 // GetTimeStampNS in a different order than writing it to the Atomic,
3204 // resulting in mLastSignificantReuseNS going potentially backwards.
3205 // Our use case is not sensitive to small deviations, the worse that can
3206 // happen is a slightly earlier purge.
3207 mLastSignificantReuseNS = GetTimestampNS();
3208}
3209
3210void arena_t::RallocShrinkLarge(arena_chunk_t* aChunk, void* aPtr, size_t aSize,
3211 size_t aOldSize) {
3212 MOZ_ASSERT(aSize < aOldSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize < aOldSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize < aOldSize))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("aSize < aOldSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3212); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSize < aOldSize" ")"); do { MOZ_CrashSequence
(__null, 3212); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3213
3214 // Shrink the run, and make trailing pages available for other
3215 // allocations.
3216 purge_action_t purge_action;
3217 {
3218 MaybeMutexAutoLock lock(mLock);
3219 TrimRunTail(aChunk, (arena_run_t*)aPtr, aOldSize, aSize, true);
3220 mStats.allocated_large -= aOldSize - aSize;
3221 mStats.operations++;
3222
3223 purge_action = ShouldStartPurge();
3224 }
3225 MayDoOrQueuePurge(purge_action, "RallocShrinkLarge");
3226}
3227
3228// Returns whether reallocation was successful.
3229bool arena_t::RallocGrowLarge(arena_chunk_t* aChunk, void* aPtr, size_t aSize,
3230 size_t aOldSize) {
3231 size_t pageind = (uintptr_t(aPtr) - uintptr_t(aChunk)) >> gPageSize2Pow;
3232 size_t npages = aOldSize >> gPageSize2Pow;
3233
3234 size_t num_dirty_before, num_dirty_after;
3235 {
3236 MaybeMutexAutoLock lock(mLock);
3237 MOZ_DIAGNOSTIC_ASSERT(aOldSize ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3238); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask)"
")"); do { MOZ_CrashSequence(__null, 3238); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3238 (aChunk->map[pageind].bits & ~gPageSizeMask))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3238); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aOldSize == (aChunk->map[pageind].bits & ~gPageSizeMask)"
")"); do { MOZ_CrashSequence(__null, 3238); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3239
3240 // Try to extend the run.
3241 MOZ_ASSERT(aSize > aOldSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize > aOldSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize > aOldSize))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("aSize > aOldSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3241); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSize > aOldSize" ")"); do { MOZ_CrashSequence
(__null, 3241); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3242 if (pageind + npages < gChunkNumPages - 1 &&
3243 (aChunk->map[pageind + npages].bits &
3244 (CHUNK_MAP_ALLOCATED((size_t)0x01U) | CHUNK_MAP_BUSY((size_t)0x100U))) == 0 &&
3245 (aChunk->map[pageind + npages].bits & ~gPageSizeMask) >=
3246 aSize - aOldSize) {
3247 num_dirty_before = mNumDirty;
3248 // The next run is available and sufficiently large. Split the
3249 // following run, then merge the first part with the existing
3250 // allocation.
3251 if (!SplitRun((arena_run_t*)(uintptr_t(aChunk) +
3252 ((pageind + npages) << gPageSize2Pow)),
3253 aSize - aOldSize, true, false)) {
3254 return false;
3255 }
3256
3257 aChunk->map[pageind].bits = aSize | CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
3258 aChunk->map[pageind + npages].bits =
3259 CHUNK_MAP_LARGE((size_t)0x02U) | CHUNK_MAP_ALLOCATED((size_t)0x01U);
3260
3261 mStats.allocated_large += aSize - aOldSize;
3262 mStats.operations++;
3263 num_dirty_after = mNumDirty;
3264 } else {
3265 return false;
3266 }
3267 }
3268 if (num_dirty_after < num_dirty_before) {
3269 NotifySignificantReuse();
3270 }
3271 return true;
3272}
3273
3274#ifdef XP_DARWIN
3275# define VM_COPY_MIN kChunkSize
3276static inline void pages_copy(void* dest, const void* src, size_t n) {
3277 MOZ_ASSERT((void*)((uintptr_t)dest & ~gPageSizeMask) == dest)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((void*)((uintptr_t)dest & ~gPageSizeMask) == dest
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((void*)((uintptr_t)dest & ~gPageSizeMask) == dest
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"(void*)((uintptr_t)dest & ~gPageSizeMask) == dest", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(void*)((uintptr_t)dest & ~gPageSizeMask) == dest"
")"); do { MOZ_CrashSequence(__null, 3277); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3278 MOZ_ASSERT(n >= VM_COPY_MIN)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(n >= VM_COPY_MIN)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(n >= VM_COPY_MIN))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("n >= VM_COPY_MIN"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3278); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n >= VM_COPY_MIN" ")"); do { MOZ_CrashSequence
(__null, 3278); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3279 MOZ_ASSERT((void*)((uintptr_t)src & ~gPageSizeMask) == src)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((void*)((uintptr_t)src & ~gPageSizeMask) == src)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((void*)((uintptr_t)src & ~gPageSizeMask) == src)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(void*)((uintptr_t)src & ~gPageSizeMask) == src"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3279); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(void*)((uintptr_t)src & ~gPageSizeMask) == src"
")"); do { MOZ_CrashSequence(__null, 3279); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3280
3281 kern_return_t r = vm_copy(mach_task_self(), (vm_address_t)src, (vm_size_t)n,
3282 (vm_address_t)dest);
3283 if (r != KERN_SUCCESS) {
3284 MOZ_CRASH("vm_copy() failed")do { do { } while (false); MOZ_ReportCrash("" "vm_copy() failed"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3284); AnnotateMozCrashReason
("MOZ_CRASH(" "vm_copy() failed" ")"); do { MOZ_CrashSequence
(__null, 3284); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
3285 }
3286}
3287#endif
3288
3289void* arena_t::RallocSmallOrLarge(void* aPtr, size_t aSize, size_t aOldSize) {
3290 void* ret;
3291 size_t copysize;
3292 SizeClass sizeClass(aSize);
3293
3294 // Try to avoid moving the allocation.
3295 if (aOldSize <= gMaxLargeClass && sizeClass.Size() == aOldSize) {
3296 if (aSize < aOldSize) {
3297 MaybePoison((void*)(uintptr_t(aPtr) + aSize), aOldSize - aSize);
3298 }
3299 return aPtr;
3300 }
3301 if (sizeClass.Type() == SizeClass::Large && aOldSize > gMaxBinClass(gMaxSubPageClass ? gMaxSubPageClass : kMaxQuantumWideClass) &&
3302 aOldSize <= gMaxLargeClass) {
3303 arena_chunk_t* chunk = GetChunkForPtr(aPtr);
3304 if (sizeClass.Size() < aOldSize) {
3305 // Fill before shrinking in order to avoid a race.
3306 MaybePoison((void*)((uintptr_t)aPtr + aSize), aOldSize - aSize);
3307 RallocShrinkLarge(chunk, aPtr, sizeClass.Size(), aOldSize);
3308 return aPtr;
3309 }
3310 if (RallocGrowLarge(chunk, aPtr, sizeClass.Size(), aOldSize)) {
3311 ApplyZeroOrJunk((void*)((uintptr_t)aPtr + aOldSize), aSize - aOldSize);
3312 return aPtr;
3313 }
3314 }
3315
3316 // If we get here, then aSize and aOldSize are different enough that we
3317 // need to move the object or the run can't be expanded because the memory
3318 // after it is allocated or busy. In that case, fall back to allocating new
3319 // space and copying. Allow non-private arenas to switch arenas.
3320 ret = (mIsPrivate ? this : choose_arena(aSize))->Malloc(aSize, false);
3321 if (!ret) {
3322 return nullptr;
3323 }
3324
3325 // Junk/zero-filling were already done by arena_t::Malloc().
3326 copysize = (aSize < aOldSize) ? aSize : aOldSize;
3327#ifdef VM_COPY_MIN
3328 if (copysize >= VM_COPY_MIN) {
3329 pages_copy(ret, aPtr, copysize);
3330 } else
3331#endif
3332 {
3333 memcpy(ret, aPtr, copysize);
3334 }
3335 idalloc(aPtr, this);
3336 return ret;
3337}
3338
3339void* arena_t::Ralloc(void* aPtr, size_t aSize, size_t aOldSize) {
3340 MOZ_DIAGNOSTIC_ASSERT(mMagic == ARENA_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMagic == 0x947d3d24)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMagic == 0x947d3d24))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("mMagic == 0x947d3d24"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3340); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "mMagic == 0x947d3d24" ")"); do {
MOZ_CrashSequence(__null, 3340); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3341 MOZ_ASSERT(aPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aPtr)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aPtr))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aPtr", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3341); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPtr" ")");
do { MOZ_CrashSequence(__null, 3341); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3342 MOZ_ASSERT(aSize != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize != 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aSize != 0", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3342); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSize != 0"
")"); do { MOZ_CrashSequence(__null, 3342); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3343
3344 return (aSize <= gMaxLargeClass) ? RallocSmallOrLarge(aPtr, aSize, aOldSize)
3345 : RallocHuge(aPtr, aSize, aOldSize);
3346}
3347
3348void* arena_t::operator new(size_t aCount, const fallible_t&) noexcept {
3349 MOZ_ASSERT(aCount == sizeof(arena_t))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aCount == sizeof(arena_t))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aCount == sizeof(arena_t))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aCount == sizeof(arena_t)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3349); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aCount == sizeof(arena_t)" ")"); do { MOZ_CrashSequence
(__null, 3349); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3350 return TypedBaseAlloc<arena_t>::alloc();
3351}
3352
3353void arena_t::operator delete(void* aPtr) {
3354 TypedBaseAlloc<arena_t>::dealloc((arena_t*)aPtr);
3355}
3356
3357arena_t::arena_t(arena_params_t* aParams, bool aIsPrivate) {
3358 unsigned i;
3359
3360 memset(&mLink, 0, sizeof(mLink));
3361 memset(&mStats, 0, sizeof(arena_stats_t));
3362 mId = 0;
3363
3364 // Initialize chunks.
3365 mChunksDirty.Init();
3366#ifdef MALLOC_DOUBLE_PURGE
3367 new (&mChunksMAdvised) DoublyLinkedList<arena_chunk_t>();
3368#endif
3369 mSpare = nullptr;
3370
3371 mRandomizeSmallAllocations = opt_randomize_small;
3372 MaybeMutex::DoLock doLock = MaybeMutex::MUST_LOCK;
3373 if (aParams) {
3374 uint32_t randFlags = aParams->mFlags & ARENA_FLAG_RANDOMIZE_SMALL_MASK0x3;
3375 switch (randFlags) {
3376 case ARENA_FLAG_RANDOMIZE_SMALL_ENABLED1:
3377 mRandomizeSmallAllocations = true;
3378 break;
3379 case ARENA_FLAG_RANDOMIZE_SMALL_DISABLED2:
3380 mRandomizeSmallAllocations = false;
3381 break;
3382 case ARENA_FLAG_RANDOMIZE_SMALL_DEFAULT0:
3383 default:
3384 break;
3385 }
3386
3387 uint32_t threadFlags = aParams->mFlags & ARENA_FLAG_THREAD_MASK0x4;
3388 if (threadFlags == ARENA_FLAG_THREAD_MAIN_THREAD_ONLY0x4) {
3389 // At the moment we require that any ARENA_FLAG_THREAD_MAIN_THREAD_ONLY
3390 // arenas are created and therefore always accessed by the main thread.
3391 // This is for two reasons:
3392 // * it allows jemalloc_stats to read their statistics (we also require
3393 // that jemalloc_stats is only used on the main thread).
3394 // * Only main-thread or threadsafe arenas can be guanteed to be in a
3395 // consistent state after a fork() from the main thread. If fork()
3396 // occurs off-thread then the new child process cannot use these arenas
3397 // (new children should usually exec() or exit() since other data may
3398 // also be inconsistent).
3399 MOZ_ASSERT(gArenas.IsOnMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gArenas.IsOnMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gArenas.IsOnMainThread()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("gArenas.IsOnMainThread()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3399); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gArenas.IsOnMainThread()" ")"); do { MOZ_CrashSequence
(__null, 3399); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3400 MOZ_ASSERT(aIsPrivate)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aIsPrivate)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aIsPrivate))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aIsPrivate", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3400); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aIsPrivate"
")"); do { MOZ_CrashSequence(__null, 3400); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3401 doLock = MaybeMutex::AVOID_LOCK_UNSAFE;
3402 }
3403
3404 mMaxDirtyIncreaseOverride = aParams->mMaxDirtyIncreaseOverride;
3405 mMaxDirtyDecreaseOverride = aParams->mMaxDirtyDecreaseOverride;
3406
3407 if (aParams->mLabel) {
3408 // The string may be truncated so always place a null-byte in the last
3409 // position.
3410 strncpy(mLabel, aParams->mLabel, LABEL_MAX_CAPACITY - 1);
3411 mLabel[LABEL_MAX_CAPACITY - 1] = 0;
3412
3413 // If the string was trucated, then replace it's end with "..."
3414 if (strlen(aParams->mLabel) >= LABEL_MAX_CAPACITY) {
3415 for (int i = 0; i < 3; i++) {
3416 mLabel[LABEL_MAX_CAPACITY - 2 - i] = '.';
3417 }
3418 }
3419 } else {
3420 mLabel[0] = 0;
3421 }
3422 } else {
3423 mMaxDirtyIncreaseOverride = 0;
3424 mMaxDirtyDecreaseOverride = 0;
3425
3426 mLabel[0] = 0;
3427 }
3428
3429 mLastSignificantReuseNS = GetTimestampNS();
3430 mIsPurgePending = false;
3431 mIsDeferredPurgeEnabled = gArenas.IsDeferredPurgeEnabled();
3432
3433 MOZ_RELEASE_ASSERT(mLock.Init(doLock))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mLock.Init(doLock))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mLock.Init(doLock)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mLock.Init(doLock)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3433); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mLock.Init(doLock)" ")"); do { MOZ_CrashSequence
(__null, 3433); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3434
3435 mPRNG = nullptr;
3436 mIsPRNGInitializing = false;
3437
3438 mIsPrivate = aIsPrivate;
3439
3440 mNumDirty = 0;
3441 mNumFresh = 0;
3442 mNumMAdvised = 0;
3443 // The default maximum amount of dirty pages allowed on arenas is a fraction
3444 // of opt_dirty_max.
3445 mMaxDirtyBase = (aParams && aParams->mMaxDirty) ? aParams->mMaxDirty
3446 : (opt_dirty_max / 8);
3447 UpdateMaxDirty();
3448
3449 mRunsAvail.Init();
3450
3451 // Initialize bins.
3452 SizeClass sizeClass(1);
3453
3454 for (i = 0;; i++) {
3455 arena_bin_t& bin = mBins[i];
3456 bin.Init(sizeClass);
3457
3458 // SizeClass doesn't want sizes larger than gMaxBinClass for now.
3459 if (sizeClass.Size() == gMaxBinClass(gMaxSubPageClass ? gMaxSubPageClass : kMaxQuantumWideClass)) {
3460 break;
3461 }
3462 sizeClass = sizeClass.Next();
3463 }
3464 MOZ_ASSERT(i == NUM_SMALL_CLASSES - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(i == (kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses
+ gNumSubPageClasses) - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(i == (kNumTinyClasses + kNumQuantumClasses
+ kNumQuantumWideClasses + gNumSubPageClasses) - 1))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("i == (kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses + gNumSubPageClasses) - 1"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3464); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "i == (kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses + gNumSubPageClasses) - 1"
")"); do { MOZ_CrashSequence(__null, 3464); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3465
3466#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED1)
3467 mMagic = ARENA_MAGIC0x947d3d24;
3468#endif
3469}
3470
3471arena_t::~arena_t() {
3472 size_t i;
3473 MaybeMutexAutoLock lock(mLock);
3474
3475 MOZ_RELEASE_ASSERT(!mLink.Left() && !mLink.Right(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mLink.Left() && !mLink.Right())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!mLink.Left() && !mLink.Right()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!mLink.Left() && !mLink.Right()"
" (" "Arena is still registered" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3476); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!mLink.Left() && !mLink.Right()"
") (" "Arena is still registered" ")"); do { MOZ_CrashSequence
(__null, 3476); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3476 "Arena is still registered")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mLink.Left() && !mLink.Right())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!mLink.Left() && !mLink.Right()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!mLink.Left() && !mLink.Right()"
" (" "Arena is still registered" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3476); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!mLink.Left() && !mLink.Right()"
") (" "Arena is still registered" ")"); do { MOZ_CrashSequence
(__null, 3476); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3477 MOZ_RELEASE_ASSERT(!mStats.allocated_small && !mStats.allocated_large,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mStats.allocated_small && !mStats.allocated_large
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mStats.allocated_small && !mStats.allocated_large
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!mStats.allocated_small && !mStats.allocated_large" " ("
"Arena is not empty" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3478); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!mStats.allocated_small && !mStats.allocated_large"
") (" "Arena is not empty" ")"); do { MOZ_CrashSequence(__null
, 3478); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
3478 "Arena is not empty")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mStats.allocated_small && !mStats.allocated_large
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mStats.allocated_small && !mStats.allocated_large
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!mStats.allocated_small && !mStats.allocated_large" " ("
"Arena is not empty" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3478); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!mStats.allocated_small && !mStats.allocated_large"
") (" "Arena is not empty" ")"); do { MOZ_CrashSequence(__null
, 3478); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
3479 if (mSpare) {
3480 chunk_dealloc(mSpare, kChunkSize, ARENA_CHUNK);
3481 }
3482 for (i = 0; i < NUM_SMALL_CLASSES(kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses
+ gNumSubPageClasses)
; i++) {
3483 MOZ_RELEASE_ASSERT(mBins[i].mNonFullRuns.isEmpty(), "Bin is not empty")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mBins[i].mNonFullRuns.isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mBins[i].mNonFullRuns.isEmpty
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mBins[i].mNonFullRuns.isEmpty()" " (" "Bin is not empty" ")"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3483); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mBins[i].mNonFullRuns.isEmpty()" ") ("
"Bin is not empty" ")"); do { MOZ_CrashSequence(__null, 3483
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
3484 }
3485#ifdef MOZ_DEBUG1
3486 {
3487 MutexAutoLock lock(huge_mtx);
3488 // This is an expensive check, so we only do it on debug builds.
3489 for (auto node : huge.iter()) {
3490 MOZ_RELEASE_ASSERT(node->mArenaId != mId, "Arena has huge allocations")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mArenaId != mId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->mArenaId != mId))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("node->mArenaId != mId"
" (" "Arena has huge allocations" ")", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3490); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "node->mArenaId != mId"
") (" "Arena has huge allocations" ")"); do { MOZ_CrashSequence
(__null, 3490); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3491 }
3492 }
3493#endif
3494 mId = 0;
3495}
3496
3497arena_t* ArenaCollection::CreateArena(bool aIsPrivate,
3498 arena_params_t* aParams) {
3499 arena_t* ret = new (fallible) arena_t(aParams, aIsPrivate);
3500 if (!ret) {
3501 // Only reached if there is an OOM error.
3502
3503 // OOM here is quite inconvenient to propagate, since dealing with it
3504 // would require a check for failure in the fast path. Instead, punt
3505 // by using the first arena.
3506 // In practice, this is an extremely unlikely failure.
3507 _malloc_message(_getprogname(), ": (malloc) Error initializing arena\n");
3508
3509 return mDefaultArena;
3510 }
3511
3512 MutexAutoLock lock(mLock);
3513
3514 // For public arenas, it's fine to just use incrementing arena id
3515 if (!aIsPrivate) {
3516 ret->mId = mLastPublicArenaId++;
3517 mArenas.Insert(ret);
3518 return ret;
3519 }
3520
3521#ifdef NON_RANDOM_ARENA_IDS1
3522 // For private arenas, slightly obfuscate the id by XORing a key generated
3523 // once, and rotate the bits by an amount also generated once.
3524 if (mArenaIdKey == 0) {
3525 mozilla::Maybe<uint64_t> maybeRandom = mozilla::RandomUint64();
3526 MOZ_RELEASE_ASSERT(maybeRandom.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(maybeRandom.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(maybeRandom.isSome()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("maybeRandom.isSome()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3526); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "maybeRandom.isSome()" ")"); do { MOZ_CrashSequence
(__null, 3526); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3527 mArenaIdKey = maybeRandom.value();
3528 maybeRandom = mozilla::RandomUint64();
3529 MOZ_RELEASE_ASSERT(maybeRandom.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(maybeRandom.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(maybeRandom.isSome()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("maybeRandom.isSome()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3529); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "maybeRandom.isSome()" ")"); do { MOZ_CrashSequence
(__null, 3529); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3530 mArenaIdRotation = maybeRandom.value() & (sizeof(void*) * 8 - 1);
3531 }
3532 arena_id_t id = reinterpret_cast<arena_id_t>(ret) ^ mArenaIdKey;
3533 ret->mId =
3534 (id >> mArenaIdRotation) | (id << (sizeof(void*) * 8 - mArenaIdRotation));
3535 mPrivateArenas.Insert(ret);
3536 return ret;
3537#else
3538 // For private arenas, generate a cryptographically-secure random id for the
3539 // new arena. If an attacker manages to get control of the process, this
3540 // should make it more difficult for them to "guess" the ID of a memory
3541 // arena, stopping them from getting data they may want
3542 Tree& tree = (ret->IsMainThreadOnly()) ? mMainThreadArenas : mPrivateArenas;
3543 arena_id_t arena_id;
3544 do {
3545 arena_id = MakeRandArenaId(ret->IsMainThreadOnly());
3546 // Keep looping until we ensure that the random number we just generated
3547 // isn't already in use by another active arena
3548 } while (GetByIdInternal(tree, arena_id));
3549
3550 ret->mId = arena_id;
3551 tree.Insert(ret);
3552 return ret;
3553#endif
3554}
3555
3556#ifndef NON_RANDOM_ARENA_IDS1
3557arena_id_t ArenaCollection::MakeRandArenaId(bool aIsMainThreadOnly) const {
3558 uint64_t rand;
3559 do {
3560 mozilla::Maybe<uint64_t> maybeRandomId = mozilla::RandomUint64();
3561 MOZ_RELEASE_ASSERT(maybeRandomId.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(maybeRandomId.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(maybeRandomId.isSome()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("maybeRandomId.isSome()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3561); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "maybeRandomId.isSome()" ")"); do {
MOZ_CrashSequence(__null, 3561); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3562
3563 rand = maybeRandomId.value();
3564
3565 // Set or clear the least significant bit depending on if this is a
3566 // main-thread-only arena. We use this in GetById.
3567 if (aIsMainThreadOnly) {
3568 rand = rand | MAIN_THREAD_ARENA_BIT;
3569 } else {
3570 rand = rand & ~MAIN_THREAD_ARENA_BIT;
3571 }
3572
3573 // Avoid 0 as an arena Id. We use 0 for disposed arenas.
3574 } while (rand == 0);
3575
3576 return arena_id_t(rand);
3577}
3578#endif
3579
3580// End arena.
3581// ***************************************************************************
3582// Begin general internal functions.
3583
3584// Initialize huge allocation data.
3585static void huge_init() MOZ_REQUIRES(gInitLock)__attribute__((exclusive_locks_required(gInitLock))) {
3586 huge_mtx.Init();
3587 MOZ_PUSH_IGNORE_THREAD_SAFETYGCC diagnostic push GCC diagnostic ignored "-Wthread-safety"
3588 huge.Init();
3589 huge_allocated = 0;
3590 huge_mapped = 0;
3591 huge_operations = 0;
3592 MOZ_POP_THREAD_SAFETYGCC diagnostic pop
3593}
3594
3595void* arena_t::MallocHuge(size_t aSize, bool aZero) {
3596 return PallocHuge(aSize, kChunkSize, aZero);
3597}
3598
3599void* arena_t::PallocHuge(size_t aSize, size_t aAlignment, bool aZero) {
3600 void* ret;
3601 size_t csize;
3602 size_t psize;
3603 extent_node_t* node;
3604
3605 // We're going to configure guard pages in the region between the
3606 // page-aligned size and the chunk-aligned size, so if those are the same
3607 // then we need to force that region into existence.
3608 csize = CHUNK_CEILING(aSize + gPageSize)(((aSize + gPageSize) + kChunkSizeMask) & ~kChunkSizeMask
)
;
3609 if (csize < aSize) {
3610 // size is large enough to cause size_t wrap-around.
3611 return nullptr;
3612 }
3613
3614 // Allocate an extent node with which to track the chunk.
3615 node = ExtentAlloc::alloc();
3616 if (!node) {
3617 return nullptr;
3618 }
3619
3620 // Allocate one or more contiguous chunks for this request.
3621 ret = chunk_alloc(csize, aAlignment, false);
3622 if (!ret) {
3623 ExtentAlloc::dealloc(node);
3624 return nullptr;
3625 }
3626 psize = PAGE_CEILING(aSize)(((aSize) + gPageSizeMask) & ~gPageSizeMask);
3627#ifdef MOZ_DEBUG1
3628 if (aZero) {
3629 chunk_assert_zero(ret, psize);
3630 }
3631#endif
3632
3633 // Insert node into huge.
3634 node->mAddr = ret;
3635 node->mSize = psize;
3636 node->mArena = this;
3637 node->mArenaId = mId;
3638
3639 {
3640 MutexAutoLock lock(huge_mtx);
3641 huge.Insert(node);
3642
3643 // Although we allocated space for csize bytes, we indicate that we've
3644 // allocated only psize bytes.
3645 //
3646 // If DECOMMIT is defined, this is a reasonable thing to do, since
3647 // we'll explicitly decommit the bytes in excess of psize.
3648 //
3649 // If DECOMMIT is not defined, then we're relying on the OS to be lazy
3650 // about how it allocates physical pages to mappings. If we never
3651 // touch the pages in excess of psize, the OS won't allocate a physical
3652 // page, and we won't use more than psize bytes of physical memory.
3653 //
3654 // A correct program will only touch memory in excess of how much it
3655 // requested if it first calls malloc_usable_size and finds out how
3656 // much space it has to play with. But because we set node->mSize =
3657 // psize above, malloc_usable_size will return psize, not csize, and
3658 // the program will (hopefully) never touch bytes in excess of psize.
3659 // Thus those bytes won't take up space in physical memory, and we can
3660 // reasonably claim we never "allocated" them in the first place.
3661 huge_allocated += psize;
3662 huge_mapped += csize;
3663 huge_operations++;
3664 }
3665
3666 pages_decommit((void*)((uintptr_t)ret + psize), csize - psize);
3667
3668 if (!aZero) {
3669 ApplyZeroOrJunk(ret, psize);
3670 }
3671
3672 return ret;
3673}
3674
3675void* arena_t::RallocHuge(void* aPtr, size_t aSize, size_t aOldSize) {
3676 void* ret;
3677 size_t copysize;
3678
3679 // Avoid moving the allocation if the size class would not change.
3680 if (aOldSize > gMaxLargeClass &&
3681 CHUNK_CEILING(aSize + gPageSize)(((aSize + gPageSize) + kChunkSizeMask) & ~kChunkSizeMask
)
== CHUNK_CEILING(aOldSize + gPageSize)(((aOldSize + gPageSize) + kChunkSizeMask) & ~kChunkSizeMask
)
) {
3682 size_t psize = PAGE_CEILING(aSize)(((aSize) + gPageSizeMask) & ~gPageSizeMask);
3683 if (aSize < aOldSize) {
3684 MaybePoison((void*)((uintptr_t)aPtr + aSize), aOldSize - aSize);
3685 }
3686 if (psize < aOldSize) {
3687 extent_node_t key;
3688
3689 pages_decommit((void*)((uintptr_t)aPtr + psize), aOldSize - psize);
3690
3691 // Update recorded size.
3692 MutexAutoLock lock(huge_mtx);
3693 key.mAddr = const_cast<void*>(aPtr);
3694 extent_node_t* node = huge.Search(&key);
3695 MOZ_ASSERT(node)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(node))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("node", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3695); AnnotateMozCrashReason("MOZ_ASSERT" "(" "node" ")");
do { MOZ_CrashSequence(__null, 3695); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3696 MOZ_ASSERT(node->mSize == aOldSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mSize == aOldSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->mSize == aOldSize))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("node->mSize == aOldSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3696); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "node->mSize == aOldSize" ")"); do { MOZ_CrashSequence
(__null, 3696); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3697 MOZ_RELEASE_ASSERT(node->mArena == this)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mArena == this)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->mArena == this))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("node->mArena == this"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3697); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "node->mArena == this" ")"); do {
MOZ_CrashSequence(__null, 3697); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3698 huge_allocated -= aOldSize - psize;
3699 huge_operations++;
3700 // No need to change huge_mapped, because we didn't (un)map anything.
3701 node->mSize = psize;
3702 } else if (psize > aOldSize) {
3703 if (!pages_commit((void*)((uintptr_t)aPtr + aOldSize),
3704 psize - aOldSize)) {
3705 return nullptr;
3706 }
3707
3708 // We need to update the recorded size if the size increased,
3709 // so malloc_usable_size doesn't return a value smaller than
3710 // what was requested via realloc().
3711 extent_node_t key;
3712 MutexAutoLock lock(huge_mtx);
3713 key.mAddr = const_cast<void*>(aPtr);
3714 extent_node_t* node = huge.Search(&key);
3715 MOZ_ASSERT(node)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(node))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("node", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3715); AnnotateMozCrashReason("MOZ_ASSERT" "(" "node" ")");
do { MOZ_CrashSequence(__null, 3715); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3716 MOZ_ASSERT(node->mSize == aOldSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mSize == aOldSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->mSize == aOldSize))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("node->mSize == aOldSize"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3716); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "node->mSize == aOldSize" ")"); do { MOZ_CrashSequence
(__null, 3716); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3717 MOZ_RELEASE_ASSERT(node->mArena == this)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mArena == this)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->mArena == this))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("node->mArena == this"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3717); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "node->mArena == this" ")"); do {
MOZ_CrashSequence(__null, 3717); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3718 huge_allocated += psize - aOldSize;
3719 huge_operations++;
3720 // No need to change huge_mapped, because we didn't
3721 // (un)map anything.
3722 node->mSize = psize;
3723 }
3724
3725 if (aSize > aOldSize) {
3726 ApplyZeroOrJunk((void*)((uintptr_t)aPtr + aOldSize), aSize - aOldSize);
3727 }
3728 return aPtr;
3729 }
3730
3731 // If we get here, then aSize and aOldSize are different enough that we
3732 // need to use a different size class. In that case, fall back to allocating
3733 // new space and copying. Allow non-private arenas to switch arenas.
3734 ret = (mIsPrivate ? this : choose_arena(aSize))->MallocHuge(aSize, false);
3735 if (!ret) {
3736 return nullptr;
3737 }
3738
3739 copysize = (aSize < aOldSize) ? aSize : aOldSize;
3740#ifdef VM_COPY_MIN
3741 if (copysize >= VM_COPY_MIN) {
3742 pages_copy(ret, aPtr, copysize);
3743 } else
3744#endif
3745 {
3746 memcpy(ret, aPtr, copysize);
3747 }
3748 idalloc(aPtr, this);
3749 return ret;
3750}
3751
3752static void huge_dalloc(void* aPtr, arena_t* aArena) {
3753 extent_node_t* node;
3754 size_t mapped = 0;
3755 {
3756 extent_node_t key;
3757 MutexAutoLock lock(huge_mtx);
3758
3759 // Extract from tree of huge allocations.
3760 key.mAddr = aPtr;
3761 node = huge.Search(&key);
3762 MOZ_RELEASE_ASSERT(node, "Double-free?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(node))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("node" " (" "Double-free?" ")",
"/root/firefox-clang/memory/build/mozjemalloc.cpp", 3762); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "node" ") (" "Double-free?" ")"); do
{ MOZ_CrashSequence(__null, 3762); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3763 MOZ_ASSERT(node->mAddr == aPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mAddr == aPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->mAddr == aPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("node->mAddr == aPtr"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3763); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "node->mAddr == aPtr" ")"); do { MOZ_CrashSequence
(__null, 3763); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3764 MOZ_RELEASE_ASSERT(!aArena || node->mArena == aArena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aArena || node->mArena == aArena)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aArena || node->mArena ==
aArena))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!aArena || node->mArena == aArena", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3764); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!aArena || node->mArena == aArena"
")"); do { MOZ_CrashSequence(__null, 3764); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3765 // See AllocInfo::Arena.
3766 MOZ_RELEASE_ASSERT(node->mArenaId == node->mArena->mId)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->mArenaId == node->mArena->mId)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(node->mArenaId == node->mArena->mId))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("node->mArenaId == node->mArena->mId"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3766); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "node->mArenaId == node->mArena->mId"
")"); do { MOZ_CrashSequence(__null, 3766); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3767 huge.Remove(node);
3768
3769 mapped = CHUNK_CEILING(node->mSize + gPageSize)(((node->mSize + gPageSize) + kChunkSizeMask) & ~kChunkSizeMask
)
;
3770 huge_allocated -= node->mSize;
3771 huge_mapped -= mapped;
3772 huge_operations++;
3773 }
3774
3775 // Unmap chunk.
3776 chunk_dealloc(node->mAddr, mapped, HUGE_CHUNK);
3777
3778 ExtentAlloc::dealloc(node);
3779}
3780
3781// Returns whether the allocator was successfully initialized.
3782static bool malloc_init_hard() {
3783 unsigned i;
3784 const char* opts;
3785
3786 AutoLock<StaticMutex> lock(gInitLock);
3787
3788 if (malloc_initialized) {
3789 // Another thread initialized the allocator before this one
3790 // acquired gInitLock.
3791 return true;
3792 }
3793
3794 if (!thread_arena.init()) {
3795 return true;
3796 }
3797
3798 // Get page size and number of CPUs
3799 const size_t page_size = GetKernelPageSize();
3800 // We assume that the page size is a power of 2.
3801 MOZ_ASSERT(IsPowerOfTwo(page_size))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsPowerOfTwo(page_size))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsPowerOfTwo(page_size)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsPowerOfTwo(page_size)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3801); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsPowerOfTwo(page_size)" ")"); do { MOZ_CrashSequence
(__null, 3801); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3802#ifdef MALLOC_STATIC_PAGESIZE
3803 if (gPageSize % page_size) {
3804 _malloc_message(
3805 _getprogname(),
3806 "Compile-time page size does not divide the runtime one.\n");
3807 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3807); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 3807); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
3808 }
3809#else
3810 gRealPageSize = gPageSize = page_size;
3811#endif
3812
3813 // Get runtime configuration.
3814 if ((opts = getenv("MALLOC_OPTIONS"))) {
3815 for (i = 0; opts[i] != '\0'; i++) {
3816 // All options are single letters, some take a *prefix* numeric argument.
3817
3818 // Parse the argument.
3819 unsigned prefix_arg = 0;
3820 while (opts[i] >= '0' && opts[i] <= '9') {
3821 prefix_arg *= 10;
3822 prefix_arg += opts[i] - '0';
3823 i++;
3824 }
3825
3826 switch (opts[i]) {
3827 case 'f':
3828 opt_dirty_max >>= prefix_arg ? prefix_arg : 1;
3829 break;
3830 case 'F':
3831 prefix_arg = prefix_arg ? prefix_arg : 1;
3832 if (opt_dirty_max == 0) {
3833 opt_dirty_max = 1;
3834 prefix_arg--;
3835 }
3836 opt_dirty_max <<= prefix_arg;
3837 if (opt_dirty_max == 0) {
3838 // If the shift above overflowed all the bits then clamp the result
3839 // instead. If we started with DIRTY_MAX_DEFAULT then this will
3840 // always be a power of two so choose the maximum power of two that
3841 // fits in a size_t.
3842 opt_dirty_max = size_t(1) << (sizeof(size_t) * CHAR_BIT8 - 1);
3843 }
3844 break;
3845#ifdef MALLOC_RUNTIME_CONFIG
3846 case 'j':
3847 opt_junk = false;
3848 break;
3849 case 'J':
3850 opt_junk = true;
3851 break;
3852 case 'q':
3853 // The argument selects how much poisoning to do.
3854 opt_poison = NONE;
3855 break;
3856 case 'Q':
3857 if (opts[i + 1] == 'Q') {
3858 // Maximum poisoning.
3859 i++;
3860 opt_poison = ALL;
3861 } else {
3862 opt_poison = SOME;
3863 opt_poison_size = kCacheLineSize * prefix_arg;
3864 }
3865 break;
3866 case 'z':
3867 opt_zero = false;
3868 break;
3869 case 'Z':
3870 opt_zero = true;
3871 break;
3872# ifndef MALLOC_STATIC_PAGESIZE
3873 case 'P':
3874 MOZ_ASSERT(gPageSize >= 4_KiB)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gPageSize >= 4_KiB)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gPageSize >= 4_KiB))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("gPageSize >= 4_KiB"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3874); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gPageSize >= 4_KiB" ")"); do { MOZ_CrashSequence
(__null, 3874); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3875 MOZ_ASSERT(gPageSize <= 64_KiB)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gPageSize <= 64_KiB)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gPageSize <= 64_KiB))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gPageSize <= 64_KiB"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3875); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gPageSize <= 64_KiB" ")"); do { MOZ_CrashSequence
(__null, 3875); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3876 prefix_arg = prefix_arg ? prefix_arg : 1;
3877 gPageSize <<= prefix_arg;
3878 // We know that if the shift causes gPageSize to be zero then it's
3879 // because it shifted all the bits off. We didn't start with zero.
3880 // Therefore if gPageSize is out of bounds we set it to 64KiB.
3881 if (gPageSize < 4_KiB || gPageSize > 64_KiB) {
3882 gPageSize = 64_KiB;
3883 }
3884 break;
3885# endif
3886#endif
3887 case 'r':
3888 opt_randomize_small = false;
3889 break;
3890 case 'R':
3891 opt_randomize_small = true;
3892 break;
3893 default: {
3894 char cbuf[2];
3895
3896 cbuf[0] = opts[i];
3897 cbuf[1] = '\0';
3898 _malloc_message(_getprogname(),
3899 ": (malloc) Unsupported character "
3900 "in malloc options: '",
3901 cbuf, "'\n");
3902 }
3903 }
3904 }
3905 }
3906
3907#ifndef MALLOC_STATIC_PAGESIZE
3908 DefineGlobals();
3909#endif
3910 gRecycledSize = 0;
3911
3912 chunks_init();
3913 huge_init();
3914 base_init();
3915
3916 // Initialize arenas collection here.
3917 if (!gArenas.Init()) {
3918 return false;
3919 }
3920
3921 // Assign the default arena to the initial thread.
3922 thread_arena.set(gArenas.GetDefault());
3923
3924 if (!gChunkRTree.Init()) {
3925 return false;
3926 }
3927
3928 malloc_initialized = true;
3929
3930 // Dummy call so that the function is not removed by dead-code elimination
3931 Debug::jemalloc_ptr_info(nullptr);
3932
3933#if !defined(XP_WIN) && !defined(XP_DARWIN)
3934 // Prevent potential deadlock on malloc locks after fork.
3935 pthread_atfork(_malloc_prefork, _malloc_postfork_parent,
3936 _malloc_postfork_child);
3937#endif
3938
3939 return true;
3940}
3941
3942// End general internal functions.
3943// ***************************************************************************
3944// Begin malloc(3)-compatible functions.
3945
3946// The BaseAllocator class is a helper class that implements the base allocator
3947// functions (malloc, calloc, realloc, free, memalign) for a given arena,
3948// or an appropriately chosen arena (per choose_arena()) when none is given.
3949struct BaseAllocator {
3950#define MALLOC_DECL(name, return_type, ...) \
3951 inline return_type name(__VA_ARGS__);
3952
3953#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE1
3954#include "malloc_decls.h"
3955
3956 explicit BaseAllocator(arena_t* aArena) : mArena(aArena) {}
3957
3958 private:
3959 arena_t* mArena;
3960};
3961
3962#define MALLOC_DECL(name, return_type, ...) \
3963 inline return_type MozJemalloc::name( \
3964 ARGS_HELPER(TYPED_ARGS, ##__VA_ARGS__)##__VA_ARGS__ arg1) { \
3965 BaseAllocator allocator(nullptr); \
3966 return allocator.name(ARGS_HELPER(ARGS, ##__VA_ARGS__)arg1); \
3967 }
3968#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE1
3969#include "malloc_decls.h"
3970
3971inline void* BaseAllocator::malloc(size_t aSize) {
3972 void* ret;
3973 arena_t* arena;
3974
3975 if (!malloc_init()) {
3976 ret = nullptr;
3977 goto RETURN;
3978 }
3979
3980 if (aSize == 0) {
3981 aSize = 1;
3982 }
3983 // If mArena is non-null, it must not be in the first page.
3984 MOZ_DIAGNOSTIC_ASSERT_IF(mArena, (size_t)mArena >= gPageSize)do { if (mArena) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype((size_t)mArena >= gPageSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((size_t)mArena >= gPageSize
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"(size_t)mArena >= gPageSize", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 3984); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "(size_t)mArena >= gPageSize"
")"); do { MOZ_CrashSequence(__null, 3984); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3985 arena = mArena ? mArena : choose_arena(aSize);
3986 ret = arena->Malloc(aSize, /* aZero = */ false);
3987
3988RETURN:
3989 if (!ret) {
3990 errno(*__errno_location ()) = ENOMEM12;
3991 }
3992
3993 return ret;
3994}
3995
3996inline void* BaseAllocator::memalign(size_t aAlignment, size_t aSize) {
3997 MOZ_ASSERT(((aAlignment - 1) & aAlignment) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(((aAlignment - 1) & aAlignment) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(((aAlignment - 1) & aAlignment) == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("((aAlignment - 1) & aAlignment) == 0"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 3997); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "((aAlignment - 1) & aAlignment) == 0" ")"
); do { MOZ_CrashSequence(__null, 3997); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3998
3999 if (!malloc_init()) {
4000 return nullptr;
4001 }
4002
4003 if (aSize == 0) {
4004 aSize = 1;
4005 }
4006
4007 aAlignment = aAlignment < sizeof(void*) ? sizeof(void*) : aAlignment;
4008 arena_t* arena = mArena ? mArena : choose_arena(aSize);
4009 return arena->Palloc(aAlignment, aSize);
4010}
4011
4012inline void* BaseAllocator::calloc(size_t aNum, size_t aSize) {
4013 void* ret;
4014
4015 if (malloc_init()) {
4016 CheckedInt<size_t> checkedSize = CheckedInt<size_t>(aNum) * aSize;
4017 if (checkedSize.isValid()) {
4018 size_t allocSize = checkedSize.value();
4019 if (allocSize == 0) {
4020 allocSize = 1;
4021 }
4022 arena_t* arena = mArena ? mArena : choose_arena(allocSize);
4023 ret = arena->Malloc(allocSize, /* aZero = */ true);
4024 } else {
4025 ret = nullptr;
4026 }
4027 } else {
4028 ret = nullptr;
4029 }
4030
4031 if (!ret) {
4032 errno(*__errno_location ()) = ENOMEM12;
4033 }
4034
4035 return ret;
4036}
4037
4038inline void* BaseAllocator::realloc(void* aPtr, size_t aSize) {
4039 void* ret;
4040
4041 if (aSize == 0) {
4042 aSize = 1;
4043 }
4044
4045 if (aPtr) {
4046 MOZ_RELEASE_ASSERT(malloc_initialized)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(malloc_initialized)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(malloc_initialized))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("malloc_initialized"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4046); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "malloc_initialized" ")"); do { MOZ_CrashSequence
(__null, 4046); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4047
4048 auto info = AllocInfo::Get(aPtr);
4049 auto arena = info.Arena();
4050 MOZ_RELEASE_ASSERT(!mArena || arena == mArena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mArena || arena == mArena)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mArena || arena == mArena))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!mArena || arena == mArena"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4050); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "!mArena || arena == mArena" ")"); do
{ MOZ_CrashSequence(__null, 4050); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4051 ret = arena->Ralloc(aPtr, aSize, info.Size());
4052 } else {
4053 if (!malloc_init()) {
4054 ret = nullptr;
4055 } else {
4056 arena_t* arena = mArena ? mArena : choose_arena(aSize);
4057 ret = arena->Malloc(aSize, /* aZero = */ false);
4058 }
4059 }
4060
4061 if (!ret) {
4062 errno(*__errno_location ()) = ENOMEM12;
4063 }
4064 return ret;
4065}
4066
4067inline void BaseAllocator::free(void* aPtr) {
4068 size_t offset;
4069
4070 // A version of idalloc that checks for nullptr pointer.
4071 offset = GetChunkOffsetForPtr(aPtr);
4072 if (offset != 0) {
4073 MOZ_RELEASE_ASSERT(malloc_initialized)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(malloc_initialized)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(malloc_initialized))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("malloc_initialized"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4073); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "malloc_initialized" ")"); do { MOZ_CrashSequence
(__null, 4073); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4074 arena_dalloc(aPtr, offset, mArena);
4075 } else if (aPtr) {
4076 MOZ_RELEASE_ASSERT(malloc_initialized)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(malloc_initialized)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(malloc_initialized))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("malloc_initialized"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4076); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "malloc_initialized" ")"); do { MOZ_CrashSequence
(__null, 4076); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4077 huge_dalloc(aPtr, mArena);
4078 }
4079}
4080
4081inline int MozJemalloc::posix_memalign(void** aMemPtr, size_t aAlignment,
4082 size_t aSize) {
4083 return AlignedAllocator<memalign>::posix_memalign(aMemPtr, aAlignment, aSize);
4084}
4085
4086inline void* MozJemalloc::aligned_alloc(size_t aAlignment, size_t aSize) {
4087 return AlignedAllocator<memalign>::aligned_alloc(aAlignment, aSize);
4088}
4089
4090inline void* MozJemalloc::valloc(size_t aSize) {
4091 return AlignedAllocator<memalign>::valloc(aSize);
4092}
4093
4094// End malloc(3)-compatible functions.
4095// ***************************************************************************
4096// Begin non-standard functions.
4097
4098// This was added by Mozilla for use by SQLite.
4099inline size_t MozJemalloc::malloc_good_size(size_t aSize) {
4100 if (aSize <= gMaxLargeClass) {
4101 // Small or large
4102 aSize = SizeClass(aSize).Size();
4103 } else {
4104 // Huge. We use PAGE_CEILING to get psize, instead of using
4105 // CHUNK_CEILING to get csize. This ensures that this
4106 // malloc_usable_size(malloc(n)) always matches
4107 // malloc_good_size(n).
4108 aSize = PAGE_CEILING(aSize)(((aSize) + gPageSizeMask) & ~gPageSizeMask);
4109 }
4110 return aSize;
4111}
4112
4113inline size_t MozJemalloc::malloc_usable_size(usable_ptr_t aPtr) {
4114 return AllocInfo::GetValidated(aPtr).Size();
4115}
4116
4117inline void MozJemalloc::jemalloc_stats_internal(
4118 jemalloc_stats_t* aStats, jemalloc_bin_stats_t* aBinStats) {
4119 size_t non_arena_mapped, chunk_header_size;
4120
4121 if (!aStats) {
4122 return;
4123 }
4124 if (!malloc_init()) {
4125 memset(aStats, 0, sizeof(*aStats));
4126 return;
4127 }
4128 if (aBinStats) {
4129 memset(aBinStats, 0, sizeof(jemalloc_bin_stats_t) * NUM_SMALL_CLASSES(kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses
+ gNumSubPageClasses)
);
4130 }
4131
4132 // Gather runtime settings.
4133 aStats->opt_junk = opt_junk;
4134 aStats->opt_randomize_small = opt_randomize_small;
4135 aStats->opt_zero = opt_zero;
4136 aStats->quantum = kQuantum;
4137 aStats->quantum_max = kMaxQuantumClass;
4138 aStats->quantum_wide = kQuantumWide;
4139 aStats->quantum_wide_max = kMaxQuantumWideClass;
4140 aStats->subpage_max = gMaxSubPageClass;
4141 aStats->large_max = gMaxLargeClass;
4142 aStats->chunksize = kChunkSize;
4143 aStats->page_size = gPageSize;
4144 aStats->dirty_max = opt_dirty_max;
4145
4146 // Gather current memory usage statistics.
4147 aStats->narenas = 0;
4148 aStats->mapped = 0;
4149 aStats->allocated = 0;
4150 aStats->waste = 0;
4151 aStats->pages_dirty = 0;
4152 aStats->pages_fresh = 0;
4153 aStats->pages_madvised = 0;
4154 aStats->bookkeeping = 0;
4155 aStats->bin_unused = 0;
4156
4157 non_arena_mapped = 0;
4158
4159 // Get huge mapped/allocated.
4160 {
4161 MutexAutoLock lock(huge_mtx);
4162 non_arena_mapped += huge_mapped;
4163 aStats->allocated += huge_allocated;
4164 aStats->num_operations += huge_operations;
4165 MOZ_ASSERT(huge_mapped >= huge_allocated)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(huge_mapped >= huge_allocated)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(huge_mapped >= huge_allocated
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"huge_mapped >= huge_allocated", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4165); AnnotateMozCrashReason("MOZ_ASSERT" "(" "huge_mapped >= huge_allocated"
")"); do { MOZ_CrashSequence(__null, 4165); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4166 }
4167
4168 // Get base mapped/allocated.
4169 {
4170 MutexAutoLock lock(base_mtx);
4171 non_arena_mapped += base_mapped;
4172 aStats->bookkeeping += base_committed;
4173 MOZ_ASSERT(base_mapped >= base_committed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base_mapped >= base_committed)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base_mapped >= base_committed
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"base_mapped >= base_committed", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base_mapped >= base_committed"
")"); do { MOZ_CrashSequence(__null, 4173); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4174 }
4175
4176 gArenas.mLock.Lock();
4177
4178 // Stats can only read complete information if its run on the main thread.
4179 MOZ_ASSERT(gArenas.IsOnMainThreadWeak())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gArenas.IsOnMainThreadWeak())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gArenas.IsOnMainThreadWeak()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"gArenas.IsOnMainThreadWeak()", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4179); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gArenas.IsOnMainThreadWeak()"
")"); do { MOZ_CrashSequence(__null, 4179); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4180
4181 // Iterate over arenas.
4182 for (auto arena : gArenas.iter()) {
4183 // Cannot safely read stats for this arena and therefore stats would be
4184 // incomplete.
4185 MOZ_ASSERT(arena->mLock.SafeOnThisThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena->mLock.SafeOnThisThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arena->mLock.SafeOnThisThread
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("arena->mLock.SafeOnThisThread()", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4185); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arena->mLock.SafeOnThisThread()"
")"); do { MOZ_CrashSequence(__null, 4185); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4186
4187 size_t arena_mapped, arena_allocated, arena_committed, arena_dirty,
4188 arena_fresh, arena_madvised, j, arena_unused, arena_headers;
4189
4190 arena_headers = 0;
4191 arena_unused = 0;
4192
4193 {
4194 MaybeMutexAutoLock lock(arena->mLock);
4195
4196 arena_mapped = arena->mStats.mapped;
4197
4198 // "committed" counts dirty and allocated memory.
4199 arena_committed = arena->mStats.committed << gPageSize2Pow;
4200
4201 arena_allocated =
4202 arena->mStats.allocated_small + arena->mStats.allocated_large;
4203
4204 arena_dirty = arena->mNumDirty << gPageSize2Pow;
4205 arena_fresh = arena->mNumFresh << gPageSize2Pow;
4206 arena_madvised = arena->mNumMAdvised << gPageSize2Pow;
4207
4208 aStats->num_operations += arena->mStats.operations;
4209
4210 for (j = 0; j < NUM_SMALL_CLASSES(kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses
+ gNumSubPageClasses)
; j++) {
4211 arena_bin_t* bin = &arena->mBins[j];
4212 size_t bin_unused = 0;
4213 size_t num_non_full_runs = 0;
4214
4215 for (arena_run_t& run : bin->mNonFullRuns) {
4216 MOZ_DIAGNOSTIC_ASSERT(run.mMagic == ARENA_RUN_MAGIC)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run.mMagic == 0x384adf93)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run.mMagic == 0x384adf93))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("run.mMagic == 0x384adf93"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4216); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "run.mMagic == 0x384adf93" ")");
do { MOZ_CrashSequence(__null, 4216); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4217 MOZ_RELEASE_ASSERT(run.mNumFree > 0 &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run.mNumFree > 0 && run.mNumFree < bin
->mRunNumRegions)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run.mNumFree > 0 &&
run.mNumFree < bin->mRunNumRegions))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("run.mNumFree > 0 && run.mNumFree < bin->mRunNumRegions"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4218); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "run.mNumFree > 0 && run.mNumFree < bin->mRunNumRegions"
")"); do { MOZ_CrashSequence(__null, 4218); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4218 run.mNumFree < bin->mRunNumRegions)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run.mNumFree > 0 && run.mNumFree < bin
->mRunNumRegions)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run.mNumFree > 0 &&
run.mNumFree < bin->mRunNumRegions))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("run.mNumFree > 0 && run.mNumFree < bin->mRunNumRegions"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4218); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "run.mNumFree > 0 && run.mNumFree < bin->mRunNumRegions"
")"); do { MOZ_CrashSequence(__null, 4218); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4219 MOZ_RELEASE_ASSERT(run.mBin == bin)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(run.mBin == bin)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(run.mBin == bin))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("run.mBin == bin"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4219); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "run.mBin == bin" ")"); do { MOZ_CrashSequence
(__null, 4219); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4220 MOZ_RELEASE_ASSERT(bin->mNonFullRuns.ElementIsLinkedWell(&run))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bin->mNonFullRuns.ElementIsLinkedWell(&run))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(bin->mNonFullRuns.ElementIsLinkedWell(&run)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("bin->mNonFullRuns.ElementIsLinkedWell(&run)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4220); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "bin->mNonFullRuns.ElementIsLinkedWell(&run)"
")"); do { MOZ_CrashSequence(__null, 4220); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4221 arena_chunk_t* chunk = GetChunkForPtr(&run);
4222 MOZ_RELEASE_ASSERT(chunk->arena == arena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chunk->arena == arena)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(chunk->arena == arena))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("chunk->arena == arena"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4222); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "chunk->arena == arena" ")"); do
{ MOZ_CrashSequence(__null, 4222); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4223 bin_unused += run.mNumFree * bin->mSizeClass;
4224 num_non_full_runs++;
4225 }
4226
4227 arena_unused += bin_unused;
4228 arena_headers += bin->mNumRuns * bin->mRunFirstRegionOffset;
4229 if (aBinStats) {
4230 aBinStats[j].size = bin->mSizeClass;
4231 aBinStats[j].num_non_full_runs += num_non_full_runs;
4232 aBinStats[j].num_runs += bin->mNumRuns;
4233 aBinStats[j].bytes_unused += bin_unused;
4234 size_t bytes_per_run = static_cast<size_t>(bin->mRunSizePages)
4235 << gPageSize2Pow;
4236 aBinStats[j].bytes_total +=
4237 bin->mNumRuns * (bytes_per_run - bin->mRunFirstRegionOffset);
4238 aBinStats[j].bytes_per_run = bytes_per_run;
4239 aBinStats[j].regions_per_run = bin->mRunNumRegions;
4240 }
4241 }
4242 }
4243
4244 MOZ_ASSERT(arena_mapped >= arena_committed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena_mapped >= arena_committed)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arena_mapped >= arena_committed
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"arena_mapped >= arena_committed", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arena_mapped >= arena_committed"
")"); do { MOZ_CrashSequence(__null, 4244); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4245 MOZ_ASSERT(arena_committed >= arena_allocated + arena_dirty)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena_committed >= arena_allocated + arena_dirty)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(arena_committed >= arena_allocated + arena_dirty)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arena_committed >= arena_allocated + arena_dirty"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4245); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "arena_committed >= arena_allocated + arena_dirty"
")"); do { MOZ_CrashSequence(__null, 4245); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4246
4247 aStats->mapped += arena_mapped;
4248 aStats->allocated += arena_allocated;
4249 aStats->pages_dirty += arena_dirty;
4250 aStats->pages_fresh += arena_fresh;
4251 aStats->pages_madvised += arena_madvised;
4252 // "waste" is committed memory that is neither dirty nor
4253 // allocated. If you change this definition please update
4254 // memory/replace/logalloc/replay/Replay.cpp's jemalloc_stats calculation of
4255 // committed.
4256 MOZ_ASSERT(arena_committed >=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena_committed >= (arena_allocated + arena_dirty
+ arena_unused + arena_headers))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arena_committed >= (arena_allocated
+ arena_dirty + arena_unused + arena_headers)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("arena_committed >= (arena_allocated + arena_dirty + arena_unused + arena_headers)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4257); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "arena_committed >= (arena_allocated + arena_dirty + arena_unused + arena_headers)"
")"); do { MOZ_CrashSequence(__null, 4257); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4257 (arena_allocated + arena_dirty + arena_unused + arena_headers))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena_committed >= (arena_allocated + arena_dirty
+ arena_unused + arena_headers))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arena_committed >= (arena_allocated
+ arena_dirty + arena_unused + arena_headers)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("arena_committed >= (arena_allocated + arena_dirty + arena_unused + arena_headers)"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4257); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "arena_committed >= (arena_allocated + arena_dirty + arena_unused + arena_headers)"
")"); do { MOZ_CrashSequence(__null, 4257); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4258 aStats->waste += arena_committed - arena_allocated - arena_dirty -
4259 arena_unused - arena_headers;
4260 aStats->bin_unused += arena_unused;
4261 aStats->bookkeeping += arena_headers;
4262 aStats->narenas++;
4263 }
4264 gArenas.mLock.Unlock();
4265
4266 // Account for arena chunk headers in bookkeeping rather than waste.
4267 chunk_header_size =
4268 ((aStats->mapped / aStats->chunksize) * (gChunkHeaderNumPages - 1))
4269 << gPageSize2Pow;
4270
4271 aStats->mapped += non_arena_mapped;
4272 aStats->bookkeeping += chunk_header_size;
4273 aStats->waste -= chunk_header_size;
4274
4275 MOZ_ASSERT(aStats->mapped >= aStats->allocated + aStats->waste +do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStats->mapped >= aStats->allocated + aStats
->waste + aStats->pages_dirty + aStats->bookkeeping)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aStats->mapped >= aStats->allocated + aStats
->waste + aStats->pages_dirty + aStats->bookkeeping)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aStats->mapped >= aStats->allocated + aStats->waste + aStats->pages_dirty + aStats->bookkeeping"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4276); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aStats->mapped >= aStats->allocated + aStats->waste + aStats->pages_dirty + aStats->bookkeeping"
")"); do { MOZ_CrashSequence(__null, 4276); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4276 aStats->pages_dirty + aStats->bookkeeping)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStats->mapped >= aStats->allocated + aStats
->waste + aStats->pages_dirty + aStats->bookkeeping)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aStats->mapped >= aStats->allocated + aStats
->waste + aStats->pages_dirty + aStats->bookkeeping)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aStats->mapped >= aStats->allocated + aStats->waste + aStats->pages_dirty + aStats->bookkeeping"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4276); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aStats->mapped >= aStats->allocated + aStats->waste + aStats->pages_dirty + aStats->bookkeeping"
")"); do { MOZ_CrashSequence(__null, 4276); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4277}
4278
4279inline void MozJemalloc::jemalloc_stats_lite(jemalloc_stats_lite_t* aStats) {
4280 if (!aStats) {
4281 return;
4282 }
4283 if (!malloc_init()) {
4284 memset(aStats, 0, sizeof(*aStats));
4285 return;
4286 }
4287
4288 aStats->allocated_bytes = 0;
4289 aStats->num_operations = 0;
4290
4291 // Get huge mapped/allocated.
4292 {
4293 MutexAutoLock lock(huge_mtx);
4294 aStats->allocated_bytes += huge_allocated;
4295 aStats->num_operations += huge_operations;
4296 MOZ_ASSERT(huge_mapped >= huge_allocated)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(huge_mapped >= huge_allocated)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(huge_mapped >= huge_allocated
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"huge_mapped >= huge_allocated", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "huge_mapped >= huge_allocated"
")"); do { MOZ_CrashSequence(__null, 4296); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4297 }
4298
4299 {
4300 MutexAutoLock lock(gArenas.mLock);
4301 for (auto arena : gArenas.iter()) {
4302 // We don't need to lock the arena to access these fields.
4303 aStats->allocated_bytes += arena->AllocatedBytes();
4304 aStats->num_operations += arena->Operations();
4305 }
4306 aStats->num_operations += gArenas.OperationsDisposedArenas();
4307 }
4308}
4309
4310inline size_t MozJemalloc::jemalloc_stats_num_bins() {
4311 return NUM_SMALL_CLASSES(kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses
+ gNumSubPageClasses)
;
4312}
4313
4314inline void MozJemalloc::jemalloc_set_main_thread() {
4315 MOZ_ASSERT(malloc_initialized)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(malloc_initialized)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(malloc_initialized))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("malloc_initialized"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4315); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "malloc_initialized" ")"); do { MOZ_CrashSequence
(__null, 4315); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4316 gArenas.SetMainThread();
4317}
4318
4319#ifdef MALLOC_DOUBLE_PURGE
4320
4321// Explicitly remove all of this chunk's MADV_FREE'd pages from memory.
4322static size_t hard_purge_chunk(arena_chunk_t* aChunk) {
4323 size_t total_npages = 0;
4324 // See similar logic in arena_t::Purge().
4325 for (size_t i = gChunkHeaderNumPages; i < gChunkNumPages; i++) {
4326 // Find all adjacent pages with CHUNK_MAP_MADVISED set.
4327 size_t npages;
4328 for (npages = 0; aChunk->map[i + npages].bits & CHUNK_MAP_MADVISED((size_t)0x40U) &&
4329 i + npages < gChunkNumPages;
4330 npages++) {
4331 // Turn off the page's CHUNK_MAP_MADVISED bit and turn on its
4332 // CHUNK_MAP_FRESH bit.
4333 MOZ_DIAGNOSTIC_ASSERT(!(aChunk->map[i + npages].bits &do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aChunk->map[i + npages].bits & (((size_t)0x80U
) | ((size_t)0x20U))))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aChunk->map[i + npages]
.bits & (((size_t)0x80U) | ((size_t)0x20U)))))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!(aChunk->map[i + npages].bits & (((size_t)0x80U) | ((size_t)0x20U)))"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4334); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "!(aChunk->map[i + npages].bits & (((size_t)0x80U) | ((size_t)0x20U)))"
")"); do { MOZ_CrashSequence(__null, 4334); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4334 (CHUNK_MAP_FRESH | CHUNK_MAP_DECOMMITTED)))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!(aChunk->map[i + npages].bits & (((size_t)0x80U
) | ((size_t)0x20U))))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!(aChunk->map[i + npages]
.bits & (((size_t)0x80U) | ((size_t)0x20U)))))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!(aChunk->map[i + npages].bits & (((size_t)0x80U) | ((size_t)0x20U)))"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4334); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "!(aChunk->map[i + npages].bits & (((size_t)0x80U) | ((size_t)0x20U)))"
")"); do { MOZ_CrashSequence(__null, 4334); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4335 aChunk->map[i + npages].bits ^= (CHUNK_MAP_MADVISED((size_t)0x40U) | CHUNK_MAP_FRESH((size_t)0x80U));
4336 }
4337
4338 // We could use mincore to find out which pages are actually
4339 // present, but it's not clear that's better.
4340 if (npages > 0) {
4341 pages_decommit(((char*)aChunk) + (i << gPageSize2Pow),
4342 npages << gPageSize2Pow);
4343 Unused << pages_commit(((char*)aChunk) + (i << gPageSize2Pow),
4344 npages << gPageSize2Pow);
4345 }
4346 total_npages += npages;
4347 i += npages;
4348 }
4349
4350 return total_npages;
4351}
4352
4353// Explicitly remove all of this arena's MADV_FREE'd pages from memory.
4354void arena_t::HardPurge() {
4355 MaybeMutexAutoLock lock(mLock);
4356
4357 while (!mChunksMAdvised.isEmpty()) {
4358 arena_chunk_t* chunk = mChunksMAdvised.popFront();
4359 size_t npages = hard_purge_chunk(chunk);
4360 mNumMAdvised -= npages;
4361 mNumFresh += npages;
4362 }
4363}
4364
4365inline void MozJemalloc::jemalloc_purge_freed_pages() {
4366 if (malloc_initialized) {
4367 MutexAutoLock lock(gArenas.mLock);
4368 MOZ_ASSERT(gArenas.IsOnMainThreadWeak())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gArenas.IsOnMainThreadWeak())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gArenas.IsOnMainThreadWeak()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"gArenas.IsOnMainThreadWeak()", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4368); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gArenas.IsOnMainThreadWeak()"
")"); do { MOZ_CrashSequence(__null, 4368); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4369 for (auto arena : gArenas.iter()) {
4370 arena->HardPurge();
4371 }
4372 }
4373}
4374
4375#else // !defined MALLOC_DOUBLE_PURGE
4376
4377inline void MozJemalloc::jemalloc_purge_freed_pages() {
4378 // Do nothing.
4379}
4380
4381#endif // defined MALLOC_DOUBLE_PURGE
4382
4383inline void MozJemalloc::jemalloc_free_dirty_pages(void) {
4384 if (malloc_initialized) {
4385 gArenas.MayPurgeAll(PurgeUnconditional, __func__);
4386 }
4387}
4388
4389inline void MozJemalloc::jemalloc_free_excess_dirty_pages(void) {
4390 if (malloc_initialized) {
4391 gArenas.MayPurgeAll(PurgeIfThreshold, __func__);
4392 }
4393}
4394
4395#ifndef NON_RANDOM_ARENA_IDS1
4396inline arena_t* ArenaCollection::GetByIdInternal(Tree& aTree,
4397 arena_id_t aArenaId) {
4398 // Use AlignedStorage2 to avoid running the arena_t constructor, while
4399 // we only need it as a placeholder for mId.
4400 mozilla::AlignedStorage2<arena_t> key;
4401 key.addr()->mId = aArenaId;
4402 return aTree.Search(key.addr());
4403}
4404#endif
4405
4406inline arena_t* ArenaCollection::GetById(arena_id_t aArenaId, bool aIsPrivate) {
4407 if (!malloc_initialized) {
4408 return nullptr;
4409 }
4410
4411#ifdef NON_RANDOM_ARENA_IDS1
4412 // This function is never called with aIsPrivate = false, let's make sure it
4413 // doesn't silently change while we're making that assumption below because
4414 // we can't resolve non-private arenas this way.
4415 MOZ_RELEASE_ASSERT(aIsPrivate)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aIsPrivate)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aIsPrivate))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aIsPrivate", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4415); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "aIsPrivate"
")"); do { MOZ_CrashSequence(__null, 4415); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4416 // This function is not expected to be called before at least one private
4417 // arena was created.
4418 // coverity[missing_lock]
4419 MOZ_RELEASE_ASSERT(mArenaIdKey)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mArenaIdKey)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mArenaIdKey))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("mArenaIdKey", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4419); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mArenaIdKey"
")"); do { MOZ_CrashSequence(__null, 4419); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4420 arena_id_t id = (aArenaId << mArenaIdRotation) |
4421 (aArenaId >> (sizeof(void*) * 8 - mArenaIdRotation));
4422 arena_t* result = reinterpret_cast<arena_t*>(id ^ mArenaIdKey);
4423#else
4424 Tree* tree = nullptr;
4425 if (aIsPrivate) {
4426 if (ArenaIdIsMainThreadOnly(aArenaId)) {
4427 // The main thread only arenas support lock free access, so it's desirable
4428 // to do GetById without taking mLock either.
4429 //
4430 // Races can occur between writers and writers, or between writers and
4431 // readers. The only writer is the main thread and it will never race
4432 // against itself so we can elude the lock when the main thread is
4433 // reading.
4434 MOZ_ASSERT(IsOnMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsOnMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsOnMainThread()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("IsOnMainThread()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4434); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsOnMainThread()" ")"); do { MOZ_CrashSequence
(__null, 4434); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4435 MOZ_PUSH_IGNORE_THREAD_SAFETYGCC diagnostic push GCC diagnostic ignored "-Wthread-safety"
4436 arena_t* result = GetByIdInternal(mMainThreadArenas, aArenaId);
4437 MOZ_POP_THREAD_SAFETYGCC diagnostic pop
4438 MOZ_RELEASE_ASSERT(result)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(result)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(result))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("result", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4438); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "result"
")"); do { MOZ_CrashSequence(__null, 4438); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4439 return result;
4440 }
4441 tree = &mPrivateArenas;
4442 } else {
4443 tree = &mArenas;
4444 }
4445
4446 MutexAutoLock lock(mLock);
4447 arena_t* result = GetByIdInternal(*tree, aArenaId);
4448#endif
4449 MOZ_RELEASE_ASSERT(result)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(result)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(result))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("result", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4449); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "result"
")"); do { MOZ_CrashSequence(__null, 4449); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4450 MOZ_RELEASE_ASSERT(result->mId == aArenaId)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(result->mId == aArenaId)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(result->mId == aArenaId))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("result->mId == aArenaId"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4450); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "result->mId == aArenaId" ")"); do
{ MOZ_CrashSequence(__null, 4450); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4451 return result;
4452}
4453
4454inline arena_id_t MozJemalloc::moz_create_arena_with_params(
4455 arena_params_t* aParams) {
4456 if (malloc_init()) {
4457 arena_t* arena = gArenas.CreateArena(/* IsPrivate = */ true, aParams);
4458 return arena->mId;
4459 }
4460 return 0;
4461}
4462
4463inline void MozJemalloc::moz_dispose_arena(arena_id_t aArenaId) {
4464 arena_t* arena = gArenas.GetById(aArenaId, /* IsPrivate = */ true);
4465 MOZ_RELEASE_ASSERT(arena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arena)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(arena))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("arena", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4465); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "arena"
")"); do { MOZ_CrashSequence(__null, 4465); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4466 gArenas.DisposeArena(arena);
4467}
4468
4469inline void MozJemalloc::moz_set_max_dirty_page_modifier(int32_t aModifier) {
4470 if (malloc_init()) {
4471 gArenas.SetDefaultMaxDirtyPageModifier(aModifier);
4472 }
4473}
4474
4475inline void MozJemalloc::jemalloc_reset_small_alloc_randomization(
4476 bool aRandomizeSmall) {
4477 // When this process got forked by ForkServer then it inherited the existing
4478 // state of mozjemalloc. Specifically, parsing of MALLOC_OPTIONS has already
4479 // been done but it may not reflect anymore the current set of options after
4480 // the fork().
4481 //
4482 // Similar behavior is also present on Android where it is also required to
4483 // perform this step.
4484 //
4485 // Content process will have randomization on small malloc disabled via the
4486 // MALLOC_OPTIONS environment variable set by parent process, missing this
4487 // will lead to serious performance regressions because CPU prefetch will
4488 // break, cf bug 1912262. However on forkserver-forked Content processes, the
4489 // environment is not yet reset when the postfork child handler is being
4490 // called.
4491 //
4492 // This API is here to allow those Content processes (spawned by ForkServer or
4493 // Android service) to notify jemalloc to turn off the randomization on small
4494 // allocations and perform the required reinitialization of already existing
4495 // arena's PRNG. It is important to make sure that the PRNG state is properly
4496 // re-initialized otherwise child processes would share all the same state.
4497
4498 {
4499 AutoLock<StaticMutex> lock(gInitLock);
4500 opt_randomize_small = aRandomizeSmall;
4501 }
4502
4503 MutexAutoLock lock(gArenas.mLock);
4504 for (auto* arena : gArenas.iter()) {
4505 // We can only initialize the PRNG for main-thread-only arenas from the main
4506 // thread.
4507 if (!arena->IsMainThreadOnly() || gArenas.IsOnMainThreadWeak()) {
4508 arena->ResetSmallAllocRandomization();
4509 }
4510 }
4511}
4512
4513inline bool MozJemalloc::moz_enable_deferred_purge(bool aEnabled) {
4514 return gArenas.SetDeferredPurge(aEnabled);
4515}
4516
4517inline may_purge_now_result_t MozJemalloc::moz_may_purge_now(
4518 bool aPeekOnly, uint32_t aReuseGraceMS,
4519 const Maybe<std::function<bool()>>& aKeepGoing) {
4520 return gArenas.MayPurgeSteps(aPeekOnly, aReuseGraceMS, aKeepGoing);
4521}
4522
4523inline void ArenaCollection::AddToOutstandingPurges(arena_t* aArena) {
4524 MOZ_ASSERT(aArena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aArena)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aArena))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aArena", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4524); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aArena" ")"
); do { MOZ_CrashSequence(__null, 4524); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4525
4526 // We cannot trust the caller to know whether the element was already added
4527 // from another thread given we have our own lock.
4528 MutexAutoLock lock(mPurgeListLock);
4529 if (!mOutstandingPurges.ElementProbablyInList(aArena)) {
4530 mOutstandingPurges.pushBack(aArena);
4531 }
4532}
4533
4534inline bool ArenaCollection::RemoveFromOutstandingPurges(arena_t* aArena) {
4535 MOZ_ASSERT(aArena)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aArena)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aArena))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aArena", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aArena" ")"
); do { MOZ_CrashSequence(__null, 4535); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4536
4537 // We cannot trust the caller to know whether the element was already removed
4538 // from another thread given we have our own lock.
4539 MutexAutoLock lock(mPurgeListLock);
4540 if (mOutstandingPurges.ElementProbablyInList(aArena)) {
4541 mOutstandingPurges.remove(aArena);
4542 return true;
4543 }
4544 return false;
4545}
4546
4547may_purge_now_result_t ArenaCollection::MayPurgeSteps(
4548 bool aPeekOnly, uint32_t aReuseGraceMS,
4549 const Maybe<std::function<bool()>>& aKeepGoing) {
4550 // This only works on the main thread because it may process main-thread-only
4551 // arenas.
4552 MOZ_ASSERT(IsOnMainThreadWeak())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsOnMainThreadWeak())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsOnMainThreadWeak()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsOnMainThreadWeak()"
, "/root/firefox-clang/memory/build/mozjemalloc.cpp", 4552); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsOnMainThreadWeak()" ")"); do { MOZ_CrashSequence
(__null, 4552); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4553
4554 uint64_t now = GetTimestampNS();
4555 uint64_t reuseGraceNS = (uint64_t)aReuseGraceMS * 1000 * 1000;
4556 arena_t* found = nullptr;
4557 {
4558 MutexAutoLock lock(mPurgeListLock);
4559 if (mOutstandingPurges.isEmpty()) {
4560 return may_purge_now_result_t::Done;
4561 }
4562 for (arena_t& arena : mOutstandingPurges) {
4563 if (now - arena.mLastSignificantReuseNS >= reuseGraceNS) {
4564 found = &arena;
4565 break;
4566 }
4567 }
4568
4569 if (!found) {
4570 return may_purge_now_result_t::WantsLater;
4571 }
4572 if (aPeekOnly) {
4573 return may_purge_now_result_t::NeedsMore;
4574 }
4575
4576 // We need to avoid the invalid state where mIsDeferredPurgePending is set
4577 // but the arena is not in the list or about to be added. So remove the
4578 // arena from the list before calling Purge().
4579 mOutstandingPurges.remove(found);
4580 }
4581
4582 ArenaPurgeResult pr =
4583 found->PurgeLoop(PurgeIfThreshold, __func__, aReuseGraceMS, aKeepGoing);
4584
4585 if (pr == ArenaPurgeResult::NotDone) {
4586 // If there's more work to do we re-insert the arena into the purge queue.
4587 // If the arena was busy we don't since the other thread that's purging it
4588 // will finish that work.
4589
4590 // Note that after the above Purge() and taking the lock below there's a
4591 // chance another thread may be purging the arena and clear
4592 // mIsDeferredPurgePending. Resulting in the state of being in the list
4593 // with that flag clear. That's okay since the next time a purge occurs
4594 // (and one will because it's in the list) it'll clear the flag and the
4595 // state will be consistent again.
4596 MutexAutoLock lock(mPurgeListLock);
4597 if (!mOutstandingPurges.ElementProbablyInList(found)) {
4598 // Given we want to continue to purge this arena, push it to the front
4599 // to increase the probability to find it fast.
4600 mOutstandingPurges.pushFront(found);
4601 }
4602 } else if (pr == ArenaPurgeResult::Dying) {
4603 delete found;
4604 }
4605
4606 // Even if there is no other arena that needs work, let the caller just call
4607 // us again and we will do the above checks then and return their result.
4608 // Note that in the current surrounding setting this may (rarely) cause a
4609 // new slice of our idle task runner if we are exceeding idle budget.
4610 return may_purge_now_result_t::NeedsMore;
4611}
4612
4613void ArenaCollection::MayPurgeAll(PurgeCondition aCond, const char* aCaller) {
4614 MutexAutoLock lock(mLock);
4615 for (auto* arena : iter()) {
4616 // Arenas that are not IsMainThreadOnly can be purged from any thread.
4617 // So we do what we can even if called from another thread.
4618 if (!arena->IsMainThreadOnly() || IsOnMainThreadWeak()) {
4619 RemoveFromOutstandingPurges(arena);
4620 ArenaPurgeResult pr = arena->PurgeLoop(aCond, aCaller);
4621
4622 // No arena can die here because we're holding the arena collection lock.
4623 // Arenas are removed from the collection before setting their mDying
4624 // flag.
4625 MOZ_RELEASE_ASSERT(pr != ArenaPurgeResult::Dying)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pr != ArenaPurgeResult::Dying)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pr != ArenaPurgeResult::Dying
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pr != ArenaPurgeResult::Dying", "/root/firefox-clang/memory/build/mozjemalloc.cpp"
, 4625); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "pr != ArenaPurgeResult::Dying"
")"); do { MOZ_CrashSequence(__null, 4625); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4626 }
4627 }
4628}
4629
4630#define MALLOC_DECL(name, return_type, ...) \
4631 inline return_type MozJemalloc::moz_arena_##name( \
4632 arena_id_t aArenaId, ARGS_HELPER(TYPED_ARGS, ##__VA_ARGS__)##__VA_ARGS__ arg1) { \
4633 BaseAllocator allocator( \
4634 gArenas.GetById(aArenaId, /* IsPrivate = */ true)); \
4635 return allocator.name(ARGS_HELPER(ARGS, ##__VA_ARGS__)arg1); \
4636 }
4637#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE1
4638#include "malloc_decls.h"
4639
4640// End non-standard functions.
4641// ***************************************************************************
4642#ifndef XP_WIN
4643// Begin library-private functions, used by threading libraries for protection
4644// of malloc during fork(). These functions are only called if the program is
4645// running in threaded mode, so there is no need to check whether the program
4646// is threaded here.
4647//
4648// Note that the only way to keep the main-thread-only arenas in a consistent
4649// state for the child is if fork is called from the main thread only. Or the
4650// child must not use them, eg it should call exec(). We attempt to prevent the
4651// child for accessing these arenas by refusing to re-initialise them.
4652//
4653// This is only accessed in the fork handlers while gArenas.mLock is held.
4654static pthread_t gForkingThread;
4655
4656# ifdef XP_DARWIN
4657// This is only accessed in the fork handlers while gArenas.mLock is held.
4658static pid_t gForkingProcess;
4659# endif
4660
4661FORK_HOOKstatic
4662void _malloc_prefork(void) MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) {
4663 // Acquire all mutexes in a safe order.
4664 gArenas.mLock.Lock();
4665 gForkingThread = pthread_self();
4666# ifdef XP_DARWIN
4667 gForkingProcess = getpid();
4668# endif
4669
4670 for (auto arena : gArenas.iter()) {
4671 if (arena->mLock.LockIsEnabled()) {
4672 arena->mLock.Lock();
4673 }
4674 }
4675
4676 gArenas.mPurgeListLock.Lock();
4677
4678 base_mtx.Lock();
4679
4680 huge_mtx.Lock();
4681}
4682
4683FORK_HOOKstatic
4684void _malloc_postfork_parent(void) MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) {
4685 // Release all mutexes, now that fork() has completed.
4686 huge_mtx.Unlock();
4687
4688 base_mtx.Unlock();
4689
4690 gArenas.mPurgeListLock.Unlock();
4691
4692 for (auto arena : gArenas.iter()) {
4693 if (arena->mLock.LockIsEnabled()) {
4694 arena->mLock.Unlock();
4695 }
4696 }
4697
4698 gArenas.mLock.Unlock();
4699}
4700
4701FORK_HOOKstatic
4702void _malloc_postfork_child(void) {
4703 // Do this before iterating over the arenas.
4704 gArenas.ResetMainThread();
4705
4706 // Reinitialize all mutexes, now that fork() has completed.
4707 huge_mtx.Init();
4708
4709 base_mtx.Init();
4710
4711 gArenas.mPurgeListLock.Init();
4712
4713 MOZ_PUSH_IGNORE_THREAD_SAFETYGCC diagnostic push GCC diagnostic ignored "-Wthread-safety"
4714 for (auto arena : gArenas.iter()) {
4715 arena->mLock.Reinit(gForkingThread);
4716 }
4717 MOZ_POP_THREAD_SAFETYGCC diagnostic pop
4718
4719 gArenas.mLock.Init();
4720}
4721
4722# ifdef XP_DARWIN
4723FORK_HOOKstatic
4724void _malloc_postfork(void) {
4725 // On MacOS we need to check if this is running in the parent or child
4726 // process.
4727 bool is_in_parent = getpid() == gForkingProcess;
4728 gForkingProcess = 0;
4729 if (is_in_parent) {
4730 _malloc_postfork_parent();
4731 } else {
4732 _malloc_postfork_child();
4733 }
4734}
4735# endif // XP_DARWIN
4736#endif // ! XP_WIN
4737
4738// End library-private functions.
4739// ***************************************************************************
4740#ifdef MOZ_REPLACE_MALLOC1
4741// Windows doesn't come with weak imports as they are possible with
4742// LD_PRELOAD or DYLD_INSERT_LIBRARIES on Linux/OSX. On this platform,
4743// the replacement functions are defined as variable pointers to the
4744// function resolved with GetProcAddress() instead of weak definitions
4745// of functions. On Android, the same needs to happen as well, because
4746// the Android linker doesn't handle weak linking with non LD_PRELOADed
4747// libraries, but LD_PRELOADing is not very convenient on Android, with
4748// the zygote.
4749# ifdef XP_DARWIN
4750# define MOZ_REPLACE_WEAK__attribute__((weak)) __attribute__((weak_import))
4751# elif defined(XP_WIN) || defined(ANDROID)
4752# define MOZ_DYNAMIC_REPLACE_INIT
4753# define replace_init replace_init_decl
4754# elif defined(__GNUC__4)
4755# define MOZ_REPLACE_WEAK__attribute__((weak)) __attribute__((weak))
4756# endif
4757
4758# include "replace_malloc.h"
4759
4760# define MALLOC_DECL(name, return_type, ...) CanonicalMalloc::name,
4761
4762// The default malloc table, i.e. plain allocations. It never changes. It's
4763// used by init(), and not used after that.
4764static const malloc_table_t gDefaultMallocTable = {
4765# include "malloc_decls.h"
4766};
4767
4768// The malloc table installed by init(). It never changes from that point
4769// onward. It will be the same as gDefaultMallocTable if no replace-malloc tool
4770// is enabled at startup.
4771static malloc_table_t gOriginalMallocTable = {
4772# include "malloc_decls.h"
4773};
4774
4775// The malloc table installed by jemalloc_replace_dynamic(). (Read the
4776// comments above that function for more details.)
4777static malloc_table_t gDynamicMallocTable = {
4778# include "malloc_decls.h"
4779};
4780
4781// This briefly points to gDefaultMallocTable at startup. After that, it points
4782// to either gOriginalMallocTable or gDynamicMallocTable. It's atomic to avoid
4783// races when switching between tables.
4784static Atomic<malloc_table_t const*, mozilla::MemoryOrdering::Relaxed>
4785 gMallocTablePtr;
4786
4787# ifdef MOZ_DYNAMIC_REPLACE_INIT
4788# undef replace_init
4789typedef decltype(replace_init_decl) replace_init_impl_t;
4790static replace_init_impl_t* replace_init = nullptr;
4791# endif
4792
4793# ifdef XP_WIN
4794typedef HMODULE replace_malloc_handle_t;
4795
4796static replace_malloc_handle_t replace_malloc_handle() {
4797 wchar_t replace_malloc_lib[1024];
4798 if (GetEnvironmentVariableW(L"MOZ_REPLACE_MALLOC_LIB", replace_malloc_lib,
4799 std::size(replace_malloc_lib)) > 0) {
4800 return LoadLibraryW(replace_malloc_lib);
4801 }
4802 return nullptr;
4803}
4804
4805# define REPLACE_MALLOC_GET_INIT_FUNC(handle) \
4806 (replace_init_impl_t*)GetProcAddress(handle, "replace_init")
4807
4808# elif defined(ANDROID)
4809# include <dlfcn.h>
4810
4811typedef void* replace_malloc_handle_t;
4812
4813static replace_malloc_handle_t replace_malloc_handle() {
4814 const char* replace_malloc_lib = getenv("MOZ_REPLACE_MALLOC_LIB");
4815 if (replace_malloc_lib && *replace_malloc_lib) {
4816 return dlopen(replace_malloc_lib, RTLD_LAZY0x00001);
4817 }
4818 return nullptr;
4819}
4820
4821# define REPLACE_MALLOC_GET_INIT_FUNC(handle) \
4822 (replace_init_impl_t*)dlsym(handle, "replace_init")
4823
4824# endif
4825
4826static void replace_malloc_init_funcs(malloc_table_t*);
4827
4828# ifdef MOZ_REPLACE_MALLOC_STATIC1
4829extern "C" void logalloc_init(malloc_table_t*, ReplaceMallocBridge**);
4830
4831extern "C" void dmd_init(malloc_table_t*, ReplaceMallocBridge**);
4832# endif
4833
4834void phc_init(malloc_table_t*, ReplaceMallocBridge**);
4835
4836bool Equals(const malloc_table_t& aTable1, const malloc_table_t& aTable2) {
4837 return memcmp(&aTable1, &aTable2, sizeof(malloc_table_t)) == 0;
4838}
4839
4840// Below is the malloc implementation overriding jemalloc and calling the
4841// replacement functions if they exist.
4842static ReplaceMallocBridge* gReplaceMallocBridge = nullptr;
4843static void init() {
4844 malloc_table_t tempTable = gDefaultMallocTable;
4845
4846# ifdef MOZ_DYNAMIC_REPLACE_INIT
4847 replace_malloc_handle_t handle = replace_malloc_handle();
4848 if (handle) {
4849 replace_init = REPLACE_MALLOC_GET_INIT_FUNC(handle);
4850 }
4851# endif
4852
4853 // Set this *before* calling replace_init, otherwise if replace_init calls
4854 // malloc() we'll get an infinite loop.
4855 gMallocTablePtr = &gDefaultMallocTable;
4856
4857 // Pass in the default allocator table so replace functions can copy and use
4858 // it for their allocations. The replace_init() function should modify the
4859 // table if it wants to be active, otherwise leave it unmodified.
4860 if (replace_init) {
4861 replace_init(&tempTable, &gReplaceMallocBridge);
4862 }
4863# ifdef MOZ_REPLACE_MALLOC_STATIC1
4864 if (Equals(tempTable, gDefaultMallocTable)) {
4865 logalloc_init(&tempTable, &gReplaceMallocBridge);
4866 }
4867# ifdef MOZ_DMD1
4868 if (Equals(tempTable, gDefaultMallocTable)) {
4869 dmd_init(&tempTable, &gReplaceMallocBridge);
4870 }
4871# endif
4872# endif
4873 if (!Equals(tempTable, gDefaultMallocTable)) {
4874 replace_malloc_init_funcs(&tempTable);
4875 }
4876 gOriginalMallocTable = tempTable;
4877 gMallocTablePtr = &gOriginalMallocTable;
4878}
4879
4880// WARNING WARNING WARNING: this function should be used with extreme care. It
4881// is not as general-purpose as it looks. It is currently used by
4882// tools/profiler/core/memory_hooks.cpp for counting allocations and probably
4883// should not be used for any other purpose.
4884//
4885// This function allows the original malloc table to be temporarily replaced by
4886// a different malloc table. Or, if the argument is nullptr, it switches back to
4887// the original malloc table.
4888//
4889// Limitations:
4890//
4891// - It is not threadsafe. If multiple threads pass it the same
4892// `replace_init_func` at the same time, there will be data races writing to
4893// the malloc_table_t within that function.
4894//
4895// - Only one replacement can be installed. No nesting is allowed.
4896//
4897// - The new malloc table must be able to free allocations made by the original
4898// malloc table, and upon removal the original malloc table must be able to
4899// free allocations made by the new malloc table. This means the new malloc
4900// table can only do simple things like recording extra information, while
4901// delegating actual allocation/free operations to the original malloc table.
4902//
4903MOZ_JEMALLOC_APIextern "C" __attribute__((visibility("default"))) void jemalloc_replace_dynamic(
4904 jemalloc_init_func replace_init_func) {
4905 if (replace_init_func) {
4906 malloc_table_t tempTable = gOriginalMallocTable;
4907 (*replace_init_func)(&tempTable, &gReplaceMallocBridge);
4908 if (!Equals(tempTable, gOriginalMallocTable)) {
4909 replace_malloc_init_funcs(&tempTable);
4910
4911 // Temporarily switch back to the original malloc table. In the
4912 // (supported) non-nested case, this is a no-op. But just in case this is
4913 // a (unsupported) nested call, it makes the overwriting of
4914 // gDynamicMallocTable less racy, because ongoing calls to malloc() and
4915 // friends won't go through gDynamicMallocTable.
4916 gMallocTablePtr = &gOriginalMallocTable;
4917
4918 gDynamicMallocTable = tempTable;
4919 gMallocTablePtr = &gDynamicMallocTable;
4920 // We assume that dynamic replaces don't occur close enough for a
4921 // thread to still have old copies of the table pointer when the 2nd
4922 // replace occurs.
4923 }
4924 } else {
4925 // Switch back to the original malloc table.
4926 gMallocTablePtr = &gOriginalMallocTable;
4927 }
4928}
4929
4930# define MALLOC_DECL(name, return_type, ...) \
4931 inline return_type ReplaceMalloc::name( \
4932 ARGS_HELPER(TYPED_ARGS, ##__VA_ARGS__)##__VA_ARGS__ arg1) { \
4933 if (MOZ_UNLIKELY(!gMallocTablePtr)(__builtin_expect(!!(!gMallocTablePtr), 0))) { \
4934 init(); \
4935 } \
4936 return (*gMallocTablePtr).name(ARGS_HELPER(ARGS, ##__VA_ARGS__)arg1); \
4937 }
4938# include "malloc_decls.h"
4939
4940MOZ_JEMALLOC_APIextern "C" __attribute__((visibility("default"))) struct ReplaceMallocBridge* get_bridge(void) {
4941 if (MOZ_UNLIKELY(!gMallocTablePtr)(__builtin_expect(!!(!gMallocTablePtr), 0))) {
4942 init();
4943 }
4944 return gReplaceMallocBridge;
4945}
4946
4947// posix_memalign, aligned_alloc, memalign and valloc all implement some kind
4948// of aligned memory allocation. For convenience, a replace-malloc library can
4949// skip defining replace_posix_memalign, replace_aligned_alloc and
4950// replace_valloc, and default implementations will be automatically derived
4951// from replace_memalign.
4952static void replace_malloc_init_funcs(malloc_table_t* table) {
4953 if (table->posix_memalign == CanonicalMalloc::posix_memalign &&
4954 table->memalign != CanonicalMalloc::memalign) {
4955 table->posix_memalign =
4956 AlignedAllocator<ReplaceMalloc::memalign>::posix_memalign;
4957 }
4958 if (table->aligned_alloc == CanonicalMalloc::aligned_alloc &&
4959 table->memalign != CanonicalMalloc::memalign) {
4960 table->aligned_alloc =
4961 AlignedAllocator<ReplaceMalloc::memalign>::aligned_alloc;
4962 }
4963 if (table->valloc == CanonicalMalloc::valloc &&
4964 table->memalign != CanonicalMalloc::memalign) {
4965 table->valloc = AlignedAllocator<ReplaceMalloc::memalign>::valloc;
4966 }
4967 if (table->moz_create_arena_with_params ==
4968 CanonicalMalloc::moz_create_arena_with_params &&
4969 table->malloc != CanonicalMalloc::malloc) {
4970# define MALLOC_DECL(name, ...) \
4971 table->name = DummyArenaAllocator<ReplaceMalloc>::name;
4972# define MALLOC_FUNCS MALLOC_FUNCS_ARENA_BASE8
4973# include "malloc_decls.h"
4974 }
4975 if (table->moz_arena_malloc == CanonicalMalloc::moz_arena_malloc &&
4976 table->malloc != CanonicalMalloc::malloc) {
4977# define MALLOC_DECL(name, ...) \
4978 table->name = DummyArenaAllocator<ReplaceMalloc>::name;
4979# define MALLOC_FUNCS MALLOC_FUNCS_ARENA_ALLOC16
4980# include "malloc_decls.h"
4981 }
4982}
4983
4984#endif // MOZ_REPLACE_MALLOC
4985// ***************************************************************************
4986// Definition of all the _impl functions
4987// GENERIC_MALLOC_DECL2_MINGW is only used for the MinGW build, and aliases
4988// the malloc funcs (e.g. malloc) to the je_ versions. It does not generate
4989// aliases for the other functions (jemalloc and arena functions).
4990//
4991// We do need aliases for the other mozglue.def-redirected functions though,
4992// these are done at the bottom of mozmemory_wrap.cpp
4993#define GENERIC_MALLOC_DECL2_MINGW(name, name_impl, return_type, ...)return_type name(... arg1) __attribute__((alias("name_impl"))
);
\
4994 return_type name(ARGS_HELPER(TYPED_ARGS, ##__VA_ARGS__)##__VA_ARGS__ arg1) \
4995 __attribute__((alias(MOZ_STRINGIFY(name_impl)"name_impl")));
4996
4997#define GENERIC_MALLOC_DECL2(attributes, name, name_impl, return_type, ...)return_type name_impl(... arg1) attributes { return DefaultMalloc
::name(arg1); }
\
4998 return_type name_impl(ARGS_HELPER(TYPED_ARGS, ##__VA_ARGS__)##__VA_ARGS__ arg1) attributes { \
4999 return DefaultMalloc::name(ARGS_HELPER(ARGS, ##__VA_ARGS__)arg1); \
5000 }
5001
5002#ifndef __MINGW32__
5003# define GENERIC_MALLOC_DECL(attributes, name, return_type, ...)return_type name(... arg1) attributes { return DefaultMalloc::
name(arg1); }
\
5004 GENERIC_MALLOC_DECL2(attributes, name, name##_impl, return_type, \return_type name##_impl(##__VA_ARGS__ arg1) attributes { return
DefaultMalloc::name(arg1); }
5005 ##__VA_ARGS__)return_type name##_impl(##__VA_ARGS__ arg1) attributes { return
DefaultMalloc::name(arg1); }
5006#else
5007# define GENERIC_MALLOC_DECL(attributes, name, return_type, ...)return_type name(... arg1) attributes { return DefaultMalloc::
name(arg1); }
\
5008 GENERIC_MALLOC_DECL2(attributes, name, name##_impl, return_type, \return_type name##_impl(##__VA_ARGS__ arg1) attributes { return
DefaultMalloc::name(arg1); }
5009 ##__VA_ARGS__)return_type name##_impl(##__VA_ARGS__ arg1) attributes { return
DefaultMalloc::name(arg1); }
\
5010 GENERIC_MALLOC_DECL2_MINGW(name, name##_impl, return_type, ##__VA_ARGS__)return_type name(##__VA_ARGS__ arg1) __attribute__((alias("name##_impl"
)));
5011#endif
5012
5013#define NOTHROW_MALLOC_DECL(...) \
5014 MOZ_MEMORY_APIextern "C" __attribute__((visibility("default"))) MACRO_CALL(GENERIC_MALLOC_DECL, (noexcept(true), __VA_ARGS__))GENERIC_MALLOC_DECL
5015#define MALLOC_DECL(...) \
5016 MOZ_MEMORY_APIextern "C" __attribute__((visibility("default"))) MACRO_CALL(GENERIC_MALLOC_DECL, (, __VA_ARGS__))GENERIC_MALLOC_DECL
5017#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC(1 | 2)
5018#include "malloc_decls.h"
5019
5020#undef GENERIC_MALLOC_DECL
5021#define GENERIC_MALLOC_DECL(attributes, name, return_type, ...)return_type name(... arg1) attributes { return DefaultMalloc::
name(arg1); }
\
5022 GENERIC_MALLOC_DECL2(attributes, name, name, return_type, ##__VA_ARGS__)return_type name(##__VA_ARGS__ arg1) attributes { return DefaultMalloc
::name(arg1); }
5023
5024#define MALLOC_DECL(...) \
5025 MOZ_JEMALLOC_APIextern "C" __attribute__((visibility("default"))) MACRO_CALL(GENERIC_MALLOC_DECL, (, __VA_ARGS__))GENERIC_MALLOC_DECL
5026#define MALLOC_FUNCS (MALLOC_FUNCS_JEMALLOC4 | MALLOC_FUNCS_ARENA(8 | 16))
5027#include "malloc_decls.h"
5028// ***************************************************************************
5029
5030#ifdef HAVE_DLFCN_H1
5031# include <dlfcn.h>
5032#endif
5033
5034#if defined(__GLIBC__2) && !defined(__UCLIBC__)
5035// glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible
5036// to inconsistently reference libc's malloc(3)-compatible functions
5037// (bug 493541).
5038//
5039// These definitions interpose hooks in glibc. The functions are actually
5040// passed an extra argument for the caller return address, which will be
5041// ignored.
5042
5043extern "C" {
5044MOZ_EXPORT__attribute__((visibility("default"))) void (*__free_hook)(void*) = free_implfree;
5045MOZ_EXPORT__attribute__((visibility("default"))) void* (*__malloc_hook)(size_t) = malloc_implmalloc;
5046MOZ_EXPORT__attribute__((visibility("default"))) void* (*__realloc_hook)(void*, size_t) = realloc_implrealloc;
5047MOZ_EXPORT__attribute__((visibility("default"))) void* (*__memalign_hook)(size_t, size_t) = memalign_implmemalign;
5048}
5049
5050#elif defined(RTLD_DEEPBIND0x00008)
5051// XXX On systems that support RTLD_GROUP or DF_1_GROUP, do their
5052// implementations permit similar inconsistencies? Should STV_SINGLETON
5053// visibility be used for interposition where available?
5054# error \
5055 "Interposing malloc is unsafe on this system without libc malloc hooks."
5056#endif
5057
5058#ifdef XP_WIN
5059MOZ_EXPORT__attribute__((visibility("default"))) void* _recalloc(void* aPtr, size_t aCount, size_t aSize) {
5060 size_t oldsize = aPtr ? AllocInfo::Get(aPtr).Size() : 0;
5061 CheckedInt<size_t> checkedSize = CheckedInt<size_t>(aCount) * aSize;
5062
5063 if (!checkedSize.isValid()) {
5064 return nullptr;
5065 }
5066
5067 size_t newsize = checkedSize.value();
5068
5069 // In order for all trailing bytes to be zeroed, the caller needs to
5070 // use calloc(), followed by recalloc(). However, the current calloc()
5071 // implementation only zeros the bytes requested, so if recalloc() is
5072 // to work 100% correctly, calloc() will need to change to zero
5073 // trailing bytes.
5074 aPtr = DefaultMalloc::realloc(aPtr, newsize);
5075 if (aPtr && oldsize < newsize) {
5076 memset((void*)((uintptr_t)aPtr + oldsize), 0, newsize - oldsize);
5077 }
5078
5079 return aPtr;
5080}
5081
5082// This impl of _expand doesn't ever actually expand or shrink blocks: it
5083// simply replies that you may continue using a shrunk block.
5084MOZ_EXPORT__attribute__((visibility("default"))) void* _expand(void* aPtr, size_t newsize) {
5085 if (AllocInfo::Get(aPtr).Size() >= newsize) {
5086 return aPtr;
5087 }
5088
5089 return nullptr;
5090}
5091
5092MOZ_EXPORT__attribute__((visibility("default"))) size_t _msize(void* aPtr) {
5093 return DefaultMalloc::malloc_usable_size(aPtr);
5094}
5095#endif
5096
5097#ifdef MOZ_PHC1
5098// Compile PHC and mozjemalloc together so that PHC can inline mozjemalloc.
5099# include "PHC.cpp"
5100#endif