Bug Summary

File:root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp
Warning:line 285, column 5
The first element of the 2nd argument is undefined
Note:line 285, column 5
Other elements might also be undefined

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Replay.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/replace/logalloc/replay -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/replace/logalloc/replay -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D IMPL_MFBT -I /root/firefox-clang/memory/replace/logalloc/replay -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/replace/logalloc/replay -I /root/firefox-clang/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/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -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-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 -Wno-character-conversion -std=gnu++20 -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 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-10-17-231001-351276-1 -x c++ /root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#define MOZ_MEMORY_IMPL
6#include "mozmemory_wrap.h"
7
8#ifdef _WIN32
9# include <windows.h>
10# include <io.h>
11typedef intptr_t ssize_t;
12#else
13# include <sys/mman.h>
14# include <unistd.h>
15#endif
16#include <fcntl.h>
17#include <stdlib.h>
18#include <algorithm>
19#include <cmath>
20#include <cstdio>
21#include <cstring>
22
23#include "mozilla/Assertions.h"
24#include "mozilla/MathAlgorithms.h"
25#include "mozilla/Maybe.h"
26#include "FdPrintf.h"
27
28using namespace mozilla;
29
30static void die(const char* message) {
31 /* Here, it doesn't matter that fprintf may allocate memory. */
32 fprintf(stderrstderr, "%s\n", message);
33 exit(1);
34}
35
36#ifdef XP_LINUX1
37MOZ_RUNINIT static size_t sPageSize = []() { return sysconf(_SC_PAGESIZE_SC_PAGESIZE); }();
38#endif
39
40/* We don't want to be using malloc() to allocate our internal tracking
41 * data, because that would change the parameters of what is being measured,
42 * so we want to use data types that directly use mmap/VirtualAlloc. */
43template <typename T, size_t Len>
44class MappedArray {
45 public:
46 MappedArray() : mPtr(nullptr) {
47#ifdef XP_LINUX1
48 MOZ_RELEASE_ASSERT(!((sizeof(T) * Len) & (sPageSize - 1)),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!((sizeof(T) * Len) & (sPageSize - 1)))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!((sizeof(T) * Len) & (sPageSize - 1))))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!((sizeof(T) * Len) & (sPageSize - 1))"
" (" "MappedArray size must be a multiple of the page size" ")"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 49); do { } while (false); do { MOZ_CrashSequence(__null, 49
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
49 "MappedArray size must be a multiple of the page size")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!((sizeof(T) * Len) & (sPageSize - 1)))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!((sizeof(T) * Len) & (sPageSize - 1))))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!((sizeof(T) * Len) & (sPageSize - 1))"
" (" "MappedArray size must be a multiple of the page size" ")"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 49); do { } while (false); do { MOZ_CrashSequence(__null, 49
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
50#endif
51 }
52
53 ~MappedArray() {
54 if (mPtr) {
55#ifdef _WIN32
56 VirtualFree(mPtr, sizeof(T) * Len, MEM_RELEASE);
57#elif defined(XP_LINUX1)
58 munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(mPtr) -
59 sPageSize),
60 sizeof(T) * Len + sPageSize * 2);
61#else
62 munmap(mPtr, sizeof(T) * Len);
63#endif
64 }
65 }
66
67 T& operator[](size_t aIndex) const {
68 if (mPtr) {
69 return mPtr[aIndex];
70 }
71
72#ifdef _WIN32
73 mPtr = reinterpret_cast<T*>(VirtualAlloc(
74 nullptr, sizeof(T) * Len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
75 if (mPtr == nullptr) {
76 die("VirtualAlloc error");
77 }
78#else
79 size_t data_size = sizeof(T) * Len;
80 size_t size = data_size;
81# ifdef XP_LINUX1
82 // See below
83 size += sPageSize * 2;
84# endif
85 mPtr = reinterpret_cast<T*>(mmap(nullptr, size, PROT_READ0x1 | PROT_WRITE0x2,
86 MAP_ANON0x20 | MAP_PRIVATE0x02, -1, 0));
87 if (mPtr == MAP_FAILED((void *) -1)) {
88 die("Mmap error");
89 }
90# ifdef XP_LINUX1
91 // On Linux we request a page on either side of the allocation and
92 // mprotect them. This prevents mappings in /proc/self/smaps from being
93 // merged and allows us to parse this file to calculate the allocator's RSS.
94 MOZ_ASSERT(0 == mprotect(mPtr, sPageSize, 0))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 == mprotect(mPtr, sPageSize, 0))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(0 == mprotect(mPtr, sPageSize
, 0)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("0 == mprotect(mPtr, sPageSize, 0)", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 94); do { } while (false); do { MOZ_CrashSequence(__null, 94
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
95 MOZ_ASSERT(0 == mprotect(reinterpret_cast<void*>(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("0 == mprotect(reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0)"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 98); do { } while (false); do { MOZ_CrashSequence(__null, 98
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
96 reinterpret_cast<uintptr_t>(mPtr) + data_size +do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("0 == mprotect(reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0)"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 98); do { } while (false); do { MOZ_CrashSequence(__null, 98
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
97 sPageSize),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("0 == mprotect(reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0)"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 98); do { } while (false); do { MOZ_CrashSequence(__null, 98
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
98 sPageSize, 0))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(0 == mprotect(reinterpret_cast<void*>( reinterpret_cast
<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("0 == mprotect(reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(mPtr) + data_size + sPageSize), sPageSize, 0)"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 98); do { } while (false); do { MOZ_CrashSequence(__null, 98
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
99 mPtr = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(mPtr) + sPageSize);
100# endif
101#endif
102 return mPtr[aIndex];
103 }
104
105 bool ownsMapping(uintptr_t addr) const { return addr == (uintptr_t)mPtr; }
106
107 bool allocated() const { return !!mPtr; }
108
109 private:
110 mutable T* mPtr;
111};
112
113/* Type for records of allocations. */
114struct MemSlot {
115 void* mPtr;
116
117 // mRequest is only valid if mPtr is non-null. It doesn't need to be cleared
118 // when memory is freed or realloc()ed.
119 size_t mRequest;
120};
121
122/* An almost infinite list of slots.
123 * In essence, this is a linked list of arrays of groups of slots.
124 * Each group is 1MB. On 64-bits, one group allows to store 64k allocations.
125 * Each MemSlotList instance can store 1023 such groups, which means more
126 * than 67M allocations. In case more would be needed, we chain to another
127 * MemSlotList, and so on.
128 * Using 1023 groups makes the MemSlotList itself page sized on 32-bits
129 * and 2 pages-sized on 64-bits.
130 */
131class MemSlotList {
132 static constexpr size_t kGroups = 1024 - 1;
133 static constexpr size_t kGroupSize = (1024 * 1024) / sizeof(MemSlot);
134
135 MappedArray<MemSlot, kGroupSize> mSlots[kGroups];
136 MappedArray<MemSlotList, 1> mNext;
137
138 public:
139 MemSlot& operator[](size_t aIndex) const {
140 if (aIndex < kGroupSize * kGroups) {
141 return mSlots[aIndex / kGroupSize][aIndex % kGroupSize];
142 }
143 aIndex -= kGroupSize * kGroups;
144 return mNext[0][aIndex];
145 }
146
147 // Ask if any of the memory-mapped buffers use this range.
148 bool ownsMapping(uintptr_t aStart) const {
149 for (const auto& slot : mSlots) {
150 if (slot.allocated() && slot.ownsMapping(aStart)) {
151 return true;
152 }
153 }
154 return mNext.ownsMapping(aStart) ||
155 (mNext.allocated() && mNext[0].ownsMapping(aStart));
156 }
157};
158
159/* Helper class for memory buffers */
160class Buffer {
161 public:
162 Buffer() : mBuf(nullptr), mLength(0) {}
163
164 Buffer(const void* aBuf, size_t aLength)
165 : mBuf(reinterpret_cast<const char*>(aBuf)), mLength(aLength) {}
166
167 /* Constructor for string literals. */
168 template <size_t Size>
169 explicit Buffer(const char (&aStr)[Size]) : mBuf(aStr), mLength(Size - 1) {}
170
171 /* Returns a sub-buffer up-to but not including the given aNeedle character.
172 * The "parent" buffer itself is altered to begin after the aNeedle
173 * character.
174 * If the aNeedle character is not found, return the entire buffer, and empty
175 * the "parent" buffer. */
176 Buffer SplitChar(char aNeedle) {
177 char* buf = const_cast<char*>(mBuf);
178 char* c = reinterpret_cast<char*>(memchr(buf, aNeedle, mLength));
179 if (!c) {
180 return Split(mLength);
181 }
182
183 Buffer result = Split(c - buf);
184 // Remove the aNeedle character itself.
185 Split(1);
186 return result;
187 }
188
189 // Advance to the position after aNeedle. This is like SplitChar but does not
190 // return the skipped portion.
191 void Skip(char aNeedle, unsigned nTimes = 1) {
192 for (unsigned i = 0; i < nTimes; i++) {
193 SplitChar(aNeedle);
194 }
195 }
196
197 void SkipWhitespace() {
198 while (mLength > 0) {
199 if (!IsSpace(mBuf[0])) {
200 break;
201 }
202 mBuf++;
203 mLength--;
204 }
205 }
206
207 static bool IsSpace(char c) {
208 switch (c) {
209 case ' ':
210 case '\t':
211 case '\n':
212 case '\v':
213 case '\f':
214 case '\r':
215 return true;
216 }
217 return false;
218 }
219
220 /* Returns a sub-buffer of at most aLength characters. The "parent" buffer is
221 * amputated of those aLength characters. If the "parent" buffer is smaller
222 * than aLength, then its length is used instead. */
223 Buffer Split(size_t aLength) {
224 Buffer result(mBuf, std::min(aLength, mLength));
225 mLength -= result.mLength;
226 mBuf += result.mLength;
227 return result;
228 }
229
230 /* Move the buffer (including its content) to the memory address of the aOther
231 * buffer. */
232 void Slide(Buffer aOther) {
233 memmove(const_cast<char*>(aOther.mBuf), mBuf, mLength);
234 mBuf = aOther.mBuf;
235 }
236
237 /* Returns whether the two involved buffers have the same content. */
238 bool operator==(Buffer aOther) {
239 return mLength == aOther.mLength &&
240 (mBuf == aOther.mBuf || !strncmp(mBuf, aOther.mBuf, mLength));
241 }
242
243 bool operator!=(Buffer aOther) { return !(*this == aOther); }
244
245 /* Returns true if the buffer is not empty. */
246 explicit operator bool() { return mLength; }
247
248 char operator[](size_t n) const { return mBuf[n]; }
249
250 /* Returns the memory location of the buffer. */
251 const char* get() { return mBuf; }
252
253 /* Returns the memory location of the end of the buffer (technically, the
254 * first byte after the buffer). */
255 const char* GetEnd() { return mBuf + mLength; }
256
257 /* Extend the buffer over the content of the other buffer, assuming it is
258 * adjacent. */
259 void Extend(Buffer aOther) {
260 MOZ_ASSERT(aOther.mBuf == GetEnd())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther.mBuf == GetEnd())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aOther.mBuf == GetEnd()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aOther.mBuf == GetEnd()"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 260); do { } while (false); do { MOZ_CrashSequence(__null, 260
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
261 mLength += aOther.mLength;
262 }
263
264 size_t Length() const { return mLength; }
265
266 private:
267 const char* mBuf;
268 size_t mLength;
269};
270
271/* Helper class to read from a file descriptor line by line. */
272class FdReader {
273 public:
274 explicit FdReader(int aFd, bool aNeedClose = false)
275 : mFd(aFd),
276 mNeedClose(aNeedClose),
277 mData(&mRawBuf, 0),
278 mBuf(&mRawBuf, sizeof(mRawBuf)) {}
279
280 FdReader(FdReader&& aOther) noexcept
281 : mFd(aOther.mFd),
282 mNeedClose(aOther.mNeedClose),
283 mData(&mRawBuf, 0),
284 mBuf(&mRawBuf, sizeof(mRawBuf)) {
285 memcpy(mRawBuf, aOther.mRawBuf, sizeof(mRawBuf));
Other elements might also be undefined
8
The first element of the 2nd argument is undefined
286 aOther.forget();
287 }
288
289 FdReader& operator=(const FdReader&) = delete;
290 FdReader(const FdReader&) = delete;
291
292 FdReader& operator=(FdReader&& aOther) {
293 mFd = aOther.mFd;
294 mNeedClose = aOther.mNeedClose;
295 mData = aOther.mData;
296 mBuf = aOther.mBuf;
297
298 aOther.forget();
299
300 return *this;
301 }
302
303 ~FdReader() {
304 if (mNeedClose) {
305 close(mFd);
306 }
307 }
308
309 private:
310 void forget() {
311 mFd = -1;
312 mNeedClose = false;
313 mData = Buffer();
314 mBuf = Buffer();
315 }
316
317 public:
318 /* Read a line from the file descriptor and returns it as a Buffer instance */
319 Buffer ReadLine() {
320 while (true) {
321 Buffer result = mData.SplitChar('\n');
322
323 /* There are essentially three different cases here:
324 * - '\n' was found "early". In this case, the end of the result buffer
325 * is before the beginning of the mData buffer (since SplitChar
326 * amputated it).
327 * - '\n' was found as the last character of mData. In this case, mData
328 * is empty, but still points at the end of mBuf. result points to what
329 * used to be in mData, without the last character.
330 * - '\n' was not found. In this case too, mData is empty and points at
331 * the end of mBuf. But result points to the entire buffer that used to
332 * be pointed by mData.
333 * Only in the latter case do both result and mData's end match, and it's
334 * the only case where we need to refill the buffer.
335 */
336 if (result.GetEnd() != mData.GetEnd()) {
337 return result;
338 }
339
340 /* Since SplitChar emptied mData, make it point to what it had before. */
341 mData = result;
342
343 /* And move it to the beginning of the read buffer. */
344 mData.Slide(mBuf);
345
346 FillBuffer();
347
348 if (!mData) {
349 return Buffer();
350 }
351 }
352 }
353
354 private:
355 /* Fill the read buffer. */
356 void FillBuffer() {
357 size_t size = mBuf.GetEnd() - mData.GetEnd();
358 Buffer remainder(mData.GetEnd(), size);
359
360 ssize_t len = 1;
361 while (remainder && len > 0) {
362 len = ::read(mFd, const_cast<char*>(remainder.get()), size);
363 if (len < 0) {
364 die("Read error");
365 }
366 size -= len;
367 mData.Extend(remainder.Split(len));
368 }
369 }
370
371 /* File descriptor to read from. */
372 int mFd;
373 bool mNeedClose;
374
375 /* Part of data that was read from the file descriptor but not returned with
376 * ReadLine yet. */
377 Buffer mData;
378 /* Buffer representation of mRawBuf */
379 Buffer mBuf;
380 /* read() buffer */
381 char mRawBuf[4096];
382};
383
384MOZ_BEGIN_EXTERN_Cextern "C" {
385
386/* Function declarations for all the replace_malloc _impl functions.
387 * See memory/build/replace_malloc.c */
388#define MALLOC_DECL(name, return_type, ...) \
389 return_type name##_impl(__VA_ARGS__);
390#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC(1 | 2)
391#include "malloc_decls.h"
392
393#define MALLOC_DECL(name, return_type, ...) return_type name(__VA_ARGS__);
394#define MALLOC_FUNCS MALLOC_FUNCS_JEMALLOC4
395#include "malloc_decls.h"
396
397MOZ_END_EXTERN_C}
398
399template <unsigned Base = 10>
400size_t parseNumber(Buffer aBuf) {
401 if (!aBuf) {
402 die("Malformed input");
403 }
404
405 size_t result = 0;
406 for (const char *c = aBuf.get(), *end = aBuf.GetEnd(); c < end; c++) {
407 result *= Base;
408 if ((*c >= '0' && *c <= '9')) {
409 result += *c - '0';
410 } else if (Base == 16 && *c >= 'a' && *c <= 'f') {
411 result += *c - 'a' + 10;
412 } else if (Base == 16 && *c >= 'A' && *c <= 'F') {
413 result += *c - 'A' + 10;
414 } else {
415 die("Malformed input");
416 }
417 }
418 return result;
419}
420
421static size_t percent(size_t a, size_t b) {
422 if (!b) {
423 return 0;
424 }
425 return size_t(round(double(a) / double(b) * 100.0));
426}
427
428class Distribution {
429 public:
430 // Default constructor used for array initialisation.
431 Distribution()
432 : mMaxSize(0),
433 mNextSmallest(0),
434 mShift(0),
435 mArrayOffset(0),
436 mArraySlots(0),
437 mTotalRequests(0),
438 mRequests{0} {}
439
440 Distribution(size_t max_size, size_t next_smallest, size_t bucket_size)
441 : mMaxSize(max_size),
442 mNextSmallest(next_smallest),
443 mShift(CeilingLog2(bucket_size)),
444 mArrayOffset(1 + next_smallest),
445 mArraySlots((max_size - next_smallest) >> mShift),
446 mTotalRequests(0),
447 mRequests{
448 0,
449 } {
450 MOZ_ASSERT(mMaxSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMaxSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMaxSize))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mMaxSize", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 450); do { } while (false); do { MOZ_CrashSequence(__null, 450
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
451 MOZ_RELEASE_ASSERT(mArraySlots <= MAX_NUM_BUCKETS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mArraySlots <= MAX_NUM_BUCKETS)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mArraySlots <= MAX_NUM_BUCKETS
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mArraySlots <= MAX_NUM_BUCKETS", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 451); do { } while (false); do { MOZ_CrashSequence(__null, 451
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
452 }
453
454 Distribution& operator=(const Distribution& aOther) = default;
455
456 void addRequest(size_t request) {
457 MOZ_ASSERT(mMaxSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMaxSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMaxSize))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mMaxSize", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 457); do { } while (false); do { MOZ_CrashSequence(__null, 457
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
458
459 mRequests[(request - mArrayOffset) >> mShift]++;
460 mTotalRequests++;
461 }
462
463 void printDist(platform_handle_t std_err) {
464 MOZ_ASSERT(mMaxSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMaxSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMaxSize))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mMaxSize", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 464); do { } while (false); do { MOZ_CrashSequence(__null, 464
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
465
466 // The translation to turn a slot index into a memory request size.
467 const size_t array_offset_add = (1 << mShift) + mNextSmallest;
468
469 FdPrintf(std_err, "\n%zu-bin Distribution:\n", mMaxSize);
470 FdPrintf(std_err, " request : count percent\n");
471 size_t range_start = mNextSmallest + 1;
472 for (size_t j = 0; j < mArraySlots; j++) {
473 size_t range_end = (j << mShift) + array_offset_add;
474 FdPrintf(std_err, "%5zu - %5zu: %6zu %6zu%%\n", range_start, range_end,
475 mRequests[j], percent(mRequests[j], mTotalRequests));
476 range_start = range_end + 1;
477 }
478 }
479
480 size_t maxSize() const { return mMaxSize; }
481
482 private:
483 static constexpr size_t MAX_NUM_BUCKETS = 16;
484
485 // If size is zero this distribution is uninitialised.
486 size_t mMaxSize;
487 size_t mNextSmallest;
488
489 // Parameters to convert a size into a slot number.
490 unsigned mShift;
491 unsigned mArrayOffset;
492
493 // The number of slots.
494 unsigned mArraySlots;
495
496 size_t mTotalRequests;
497 size_t mRequests[MAX_NUM_BUCKETS];
498};
499
500#ifdef XP_LINUX1
501struct MemoryMap {
502 uintptr_t mStart;
503 uintptr_t mEnd;
504 bool mReadable;
505 bool mPrivate;
506 bool mAnon;
507 bool mIsStack;
508 bool mIsSpecial;
509 size_t mRSS;
510
511 bool IsCandidate() const {
512 // Candidates mappings are:
513 // * anonymous
514 // * they are private (not shared),
515 // * anonymous or "[heap]" (not another area such as stack),
516 //
517 // The only mappings we're falsely including are the .bss segments for
518 // shared libraries.
519 return mReadable && mPrivate && mAnon && !mIsStack && !mIsSpecial;
520 }
521};
522
523class SMapsReader : private FdReader {
524 private:
525 explicit SMapsReader(FdReader&& reader) : FdReader(std::move(reader)) {}
7
Calling move constructor for 'FdReader'
526
527 public:
528 static Maybe<SMapsReader> open() {
529 int fd = ::open(FILENAME, O_RDONLY00);
530 if (fd < 0) {
4
Assuming 'fd' is >= 0
5
Taking false branch
531 perror(FILENAME);
532 return mozilla::Nothing();
533 }
534
535 return Some(SMapsReader(FdReader(fd, true)));
6
Calling constructor for 'SMapsReader'
536 }
537
538 Maybe<MemoryMap> readMap(platform_handle_t aStdErr) {
539 // This is not very tolerant of format changes because things like
540 // parseNumber will crash if they get a bad value. TODO: make this
541 // soft-fail.
542
543 Buffer line = ReadLine();
544 if (!line) {
545 return Nothing();
546 }
547
548 // We're going to be at the start of an entry, start tokenising the first
549 // line.
550
551 // Range
552 Buffer range = line.SplitChar(' ');
553 uintptr_t range_start = parseNumber<16>(range.SplitChar('-'));
554 uintptr_t range_end = parseNumber<16>(range);
555
556 // Mode.
557 Buffer mode = line.SplitChar(' ');
558 if (mode.Length() != 4) {
559 FdPrintf(aStdErr, "Couldn't parse SMAPS file\n");
560 return Nothing();
561 }
562 bool readable = mode[0] == 'r';
563 bool private_ = mode[3] == 'p';
564
565 // Offset, device and inode.
566 line.SkipWhitespace();
567 bool zero_offset = !parseNumber<16>(line.SplitChar(' '));
568 line.SkipWhitespace();
569 bool no_device = line.SplitChar(' ') == Buffer("00:00");
570 line.SkipWhitespace();
571 bool zero_inode = !parseNumber(line.SplitChar(' '));
572 bool is_anon = zero_offset && no_device && zero_inode;
573
574 // Filename, or empty for anon mappings.
575 line.SkipWhitespace();
576 Buffer filename = line.SplitChar(' ');
577
578 bool is_stack;
579 bool is_special;
580 if (filename && filename[0] == '[') {
581 is_stack = filename == Buffer("[stack]");
582 is_special = filename == Buffer("[vdso]") ||
583 filename == Buffer("[vvar]") ||
584 filename == Buffer("[vsyscall]");
585 } else {
586 is_stack = false;
587 is_special = false;
588 }
589
590 size_t rss = 0;
591 while ((line = ReadLine())) {
592 Buffer field = line.SplitChar(':');
593 if (field == Buffer("VmFlags")) {
594 // This is the last field, at least in the current format. Break this
595 // loop to read the next mapping.
596 break;
597 }
598
599 if (field == Buffer("Rss")) {
600 line.SkipWhitespace();
601 Buffer value = line.SplitChar(' ');
602 rss = parseNumber(value) * 1024;
603 }
604 }
605
606 return Some(MemoryMap({range_start, range_end, readable, private_, is_anon,
607 is_stack, is_special, rss}));
608 }
609
610 static constexpr char FILENAME[] = "/proc/self/smaps";
611};
612#endif // XP_LINUX
613
614/* Class to handle dispatching the replay function calls to replace-malloc. */
615class Replay {
616 public:
617 Replay() {
618#ifdef _WIN32
619 // See comment in FdPrintf.h as to why native win32 handles are used.
620 mStdErr = GetStdHandle(STD_ERROR_HANDLE);
621#else
622 mStdErr = fileno(stderrstderr);
623#endif
624#ifdef XP_LINUX1
625 BuildInitialMapInfo();
1
Calling 'Replay::BuildInitialMapInfo'
626#endif
627 }
628
629 void enableSlopCalculation() { mCalculateSlop = true; }
630 void enableMemset() { mDoMemset = true; }
631
632 MemSlot& operator[](size_t index) const { return mSlots[index]; }
633
634 void malloc(Buffer& aArgs, Buffer& aResult) {
635 MemSlot& aSlot = SlotForResult(aResult);
636 mOps++;
637 size_t size = parseNumber(aArgs);
638 aSlot.mPtr = ::malloc_implmalloc(size);
639 if (aSlot.mPtr) {
640 aSlot.mRequest = size;
641 MaybeCommit(aSlot);
642 if (mCalculateSlop) {
643 mTotalRequestedSize += size;
644 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
645 }
646 }
647 }
648
649 void posix_memalign(Buffer& aArgs, Buffer& aResult) {
650 MemSlot& aSlot = SlotForResult(aResult);
651 mOps++;
652 size_t alignment = parseNumber(aArgs.SplitChar(','));
653 size_t size = parseNumber(aArgs);
654 void* ptr;
655 if (::posix_memalign_implposix_memalign(&ptr, alignment, size) == 0) {
656 aSlot.mPtr = ptr;
657 aSlot.mRequest = size;
658 MaybeCommit(aSlot);
659 if (mCalculateSlop) {
660 mTotalRequestedSize += size;
661 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
662 }
663 } else {
664 aSlot.mPtr = nullptr;
665 }
666 }
667
668 void aligned_alloc(Buffer& aArgs, Buffer& aResult) {
669 MemSlot& aSlot = SlotForResult(aResult);
670 mOps++;
671 size_t alignment = parseNumber(aArgs.SplitChar(','));
672 size_t size = parseNumber(aArgs);
673 aSlot.mPtr = ::aligned_alloc_implaligned_alloc(alignment, size);
674 if (aSlot.mPtr) {
675 aSlot.mRequest = size;
676 MaybeCommit(aSlot);
677 if (mCalculateSlop) {
678 mTotalRequestedSize += size;
679 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
680 }
681 }
682 }
683
684 void calloc(Buffer& aArgs, Buffer& aResult) {
685 MemSlot& aSlot = SlotForResult(aResult);
686 mOps++;
687 size_t num = parseNumber(aArgs.SplitChar(','));
688 size_t size = parseNumber(aArgs);
689 aSlot.mPtr = ::calloc_implcalloc(num, size);
690 if (aSlot.mPtr) {
691 aSlot.mRequest = num * size;
692 MaybeCommit(aSlot);
693 if (mCalculateSlop) {
694 mTotalRequestedSize += num * size;
695 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
696 }
697 }
698 }
699
700 void realloc(Buffer& aArgs, Buffer& aResult) {
701 MemSlot& aSlot = SlotForResult(aResult);
702 mOps++;
703 Buffer dummy = aArgs.SplitChar('#');
704 if (dummy) {
705 die("Malformed input");
706 }
707 size_t slot_id = parseNumber(aArgs.SplitChar(','));
708 size_t size = parseNumber(aArgs);
709 MemSlot& old_slot = (*this)[slot_id];
710 void* old_ptr = old_slot.mPtr;
711 old_slot.mPtr = nullptr;
712 aSlot.mPtr = ::realloc_implrealloc(old_ptr, size);
713 if (aSlot.mPtr) {
714 aSlot.mRequest = size;
715 MaybeCommit(aSlot);
716 if (mCalculateSlop) {
717 mTotalRequestedSize += size;
718 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
719 }
720 }
721 }
722
723 void free(Buffer& aArgs, Buffer& aResult) {
724 if (aResult) {
725 die("Malformed input");
726 }
727 mOps++;
728 Buffer dummy = aArgs.SplitChar('#');
729 if (dummy) {
730 die("Malformed input");
731 }
732 size_t slot_id = parseNumber(aArgs);
733 MemSlot& slot = (*this)[slot_id];
734 ::free_implfree(slot.mPtr);
735 slot.mPtr = nullptr;
736 }
737
738 void memalign(Buffer& aArgs, Buffer& aResult) {
739 MemSlot& aSlot = SlotForResult(aResult);
740 mOps++;
741 size_t alignment = parseNumber(aArgs.SplitChar(','));
742 size_t size = parseNumber(aArgs);
743 aSlot.mPtr = ::memalign_implmemalign(alignment, size);
744 if (aSlot.mPtr) {
745 aSlot.mRequest = size;
746 MaybeCommit(aSlot);
747 if (mCalculateSlop) {
748 mTotalRequestedSize += size;
749 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
750 }
751 }
752 }
753
754 void valloc(Buffer& aArgs, Buffer& aResult) {
755 MemSlot& aSlot = SlotForResult(aResult);
756 mOps++;
757 size_t size = parseNumber(aArgs);
758 aSlot.mPtr = ::valloc_implvalloc(size);
759 if (aSlot.mPtr) {
760 aSlot.mRequest = size;
761 MaybeCommit(aSlot);
762 if (mCalculateSlop) {
763 mTotalRequestedSize += size;
764 mTotalAllocatedSize += ::malloc_usable_size_implmalloc_usable_size(aSlot.mPtr);
765 }
766 }
767 }
768
769 void jemalloc_stats(Buffer& aArgs, Buffer& aResult) {
770 if (aArgs || aResult) {
771 die("Malformed input");
772 }
773 mOps++;
774 jemalloc_stats_t stats;
775 // Using a variable length array here is a GCC & Clang extension. But it
776 // allows us to place this on the stack and not alter jemalloc's profiling.
777 const size_t num_bins = ::jemalloc_stats_num_bins();
778 const size_t MAX_NUM_BINS = 100;
779 if (num_bins > MAX_NUM_BINS) {
780 die("Exceeded maximum number of jemalloc stats bins");
781 }
782 jemalloc_bin_stats_t bin_stats[MAX_NUM_BINS] = {{0}};
783 ::jemalloc_stats_internal(&stats, bin_stats);
784
785#ifdef XP_LINUX1
786 size_t rss = get_rss();
787#endif
788
789 size_t num_objects = 0;
790 size_t num_sloppy_objects = 0;
791 size_t total_allocated = 0;
792 size_t total_slop = 0;
793 size_t large_slop = 0;
794 size_t large_used = 0;
795 size_t huge_slop = 0;
796 size_t huge_used = 0;
797 size_t bin_slop[MAX_NUM_BINS] = {0};
798
799 for (size_t slot_id = 0; slot_id < mNumUsedSlots; slot_id++) {
800 MemSlot& slot = mSlots[slot_id];
801 if (slot.mPtr) {
802 size_t used = ::malloc_usable_size_implmalloc_usable_size(slot.mPtr);
803 size_t slop = used - slot.mRequest;
804 total_allocated += used;
805 total_slop += slop;
806 num_objects++;
807 if (slop) {
808 num_sloppy_objects++;
809 }
810
811 if (used <=
812 (stats.subpage_max ? stats.subpage_max : stats.quantum_wide_max)) {
813 // We know that this is an inefficient linear search, but there's a
814 // small number of bins and this is simple.
815 for (unsigned i = 0; i < num_bins; i++) {
816 auto& bin = bin_stats[i];
817 if (used == bin.size) {
818 bin_slop[i] += slop;
819 break;
820 }
821 }
822 } else if (used <= stats.large_max) {
823 large_slop += slop;
824 large_used += used;
825 } else {
826 huge_slop += slop;
827 huge_used += used;
828 }
829 }
830 }
831
832 // This formula corresponds to the calculation of wasted (from committed and
833 // the other parameters) within jemalloc_stats()
834 size_t committed = stats.allocated + stats.waste + stats.pages_dirty +
835 stats.bookkeeping + stats.bin_unused;
836
837 FdPrintf(mStdErr, "\n");
838 FdPrintf(mStdErr, "Objects: %9zu\n", num_objects);
839 FdPrintf(mStdErr, "Slots: %9zu\n", mNumUsedSlots);
840 FdPrintf(mStdErr, "Ops: %9zu\n", mOps);
841 FdPrintf(mStdErr, "mapped: %9zu\n", stats.mapped);
842 FdPrintf(mStdErr, "committed: %9zu\n", committed);
843#ifdef XP_LINUX1
844 if (rss) {
845 FdPrintf(mStdErr, "rss: %9zu\n", rss);
846 }
847#endif
848 FdPrintf(mStdErr, "allocated: %9zu\n", stats.allocated);
849 FdPrintf(mStdErr, "waste: %9zu\n", stats.waste);
850 FdPrintf(mStdErr, "dirty: %9zu\n", stats.pages_dirty);
851 FdPrintf(mStdErr, "fresh: %9zu\n", stats.pages_fresh);
852 FdPrintf(mStdErr, "madvised: %9zu\n", stats.pages_madvised);
853 FdPrintf(mStdErr, "bookkeep: %9zu\n", stats.bookkeeping);
854 FdPrintf(mStdErr, "bin-unused: %9zu\n", stats.bin_unused);
855 FdPrintf(mStdErr, "quantum-max: %9zu\n", stats.quantum_max);
856 FdPrintf(mStdErr, "quantum-wide-max: %9zu\n", stats.quantum_wide_max);
857 FdPrintf(mStdErr, "subpage-max: %9zu\n", stats.subpage_max);
858 FdPrintf(mStdErr, "large-max: %9zu\n", stats.large_max);
859 if (mCalculateSlop) {
860 size_t slop = mTotalAllocatedSize - mTotalRequestedSize;
861 FdPrintf(mStdErr,
862 "Total slop for all allocations: %zuKiB/%zuKiB (%zu%%)\n",
863 slop / 1024, mTotalAllocatedSize / 1024,
864 percent(slop, mTotalAllocatedSize));
865 }
866 FdPrintf(mStdErr, "Live sloppy objects: %zu/%zu (%zu%%)\n",
867 num_sloppy_objects, num_objects,
868 percent(num_sloppy_objects, num_objects));
869 FdPrintf(mStdErr, "Live sloppy bytes: %zuKiB/%zuKiB (%zu%%)\n",
870 total_slop / 1024, total_allocated / 1024,
871 percent(total_slop, total_allocated));
872
873 FdPrintf(mStdErr, "\n%8s %11s %10s %8s %9s %9s %8s\n", "bin-size",
874 "unused (c)", "total (c)", "used (c)", "non-full (r)", "total (r)",
875 "used (r)");
876 for (unsigned i = 0; i < num_bins; i++) {
877 auto& bin = bin_stats[i];
878 MOZ_ASSERT(bin.size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bin.size)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bin.size))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("bin.size", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 878); do { } while (false); do { MOZ_CrashSequence(__null, 878
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
879 FdPrintf(mStdErr, "%8zu %8zuKiB %7zuKiB %7zu%% %12zu %9zu %7zu%%\n",
880 bin.size, bin.bytes_unused / 1024, bin.bytes_total / 1024,
881 percent(bin.bytes_total - bin.bytes_unused, bin.bytes_total),
882 bin.num_non_full_runs, bin.num_runs,
883 percent(bin.num_runs - bin.num_non_full_runs, bin.num_runs));
884 }
885
886 FdPrintf(mStdErr, "\n%5s %8s %9s %7s\n", "bin", "slop", "used", "percent");
887 for (unsigned i = 0; i < num_bins; i++) {
888 auto& bin = bin_stats[i];
889 size_t used = bin.bytes_total - bin.bytes_unused;
890 FdPrintf(mStdErr, "%5zu %8zu %9zu %6zu%%\n", bin.size, bin_slop[i], used,
891 percent(bin_slop[i], used));
892 }
893 FdPrintf(mStdErr, "%5s %8zu %9zu %6zu%%\n", "large", large_slop, large_used,
894 percent(large_slop, large_used));
895 FdPrintf(mStdErr, "%5s %8zu %9zu %6zu%%\n", "huge", huge_slop, huge_used,
896 percent(huge_slop, huge_used));
897
898 print_distributions(stats, bin_stats);
899 }
900
901 private:
902 /*
903 * Create and print frequency distributions of memory requests.
904 */
905 void print_distributions(jemalloc_stats_t& stats,
906 jemalloc_bin_stats_t* bin_stats) {
907 const size_t num_bins = ::jemalloc_stats_num_bins();
908
909 // We compute distributions for all of the bins for small allocations
910 // (num_bins) plus two more distributions for larger allocations.
911 Distribution dists[num_bins + 2];
912
913 unsigned last_size = 0;
914 unsigned num_dists = 0;
915 for (unsigned i = 0; i < num_bins; i++) {
916 auto& bin = bin_stats[i];
917 auto& dist = dists[num_dists++];
918
919 MOZ_ASSERT(bin.size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bin.size)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bin.size))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("bin.size", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 919); do { } while (false); do { MOZ_CrashSequence(__null, 919
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
920 if (bin.size <= 16) {
921 // 1 byte buckets.
922 dist = Distribution(bin.size, last_size, 1);
923 } else if (bin.size <= stats.quantum_max) {
924 // 4 buckets, (4 bytes per bucket with a 16 byte quantum).
925 dist = Distribution(bin.size, last_size, stats.quantum / 4);
926 } else if (bin.size <= stats.quantum_wide_max) {
927 // 8 buckets, (32 bytes per bucket with a 256 byte quantum-wide).
928 dist = Distribution(bin.size, last_size, stats.quantum_wide / 8);
929 } else {
930 // 16 buckets.
931 dist = Distribution(bin.size, last_size, (bin.size - last_size) / 16);
932 }
933 last_size = bin.size;
934 }
935
936 // 16 buckets.
937 dists[num_dists] = Distribution(stats.page_size, last_size,
938 (stats.page_size - last_size) / 16);
939 num_dists++;
940
941 // Buckets are 1/4 of the page size (12 buckets).
942 dists[num_dists] =
943 Distribution(stats.page_size * 4, stats.page_size, stats.page_size / 4);
944 num_dists++;
945
946 MOZ_RELEASE_ASSERT(num_dists <= num_bins + 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(num_dists <= num_bins + 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(num_dists <= num_bins + 2
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"num_dists <= num_bins + 2", "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 946); do { } while (false); do { MOZ_CrashSequence(__null, 946
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
947
948 for (size_t slot_id = 0; slot_id < mNumUsedSlots; slot_id++) {
949 MemSlot& slot = mSlots[slot_id];
950 if (slot.mPtr) {
951 for (size_t i = 0; i < num_dists; i++) {
952 if (slot.mRequest <= dists[i].maxSize()) {
953 dists[i].addRequest(slot.mRequest);
954 break;
955 }
956 }
957 }
958 }
959
960 for (unsigned i = 0; i < num_dists; i++) {
961 dists[i].printDist(mStdErr);
962 }
963 }
964
965#ifdef XP_LINUX1
966 size_t get_rss() {
967 if (mGetRSSFailed) {
968 return 0;
969 }
970
971 // On Linux we can determine the RSS of the heap area by examining the
972 // smaps file.
973 mozilla::Maybe<SMapsReader> reader = SMapsReader::open();
974 if (!reader) {
975 mGetRSSFailed = true;
976 return 0;
977 }
978
979 size_t rss = 0;
980 while (Maybe<MemoryMap> map = reader->readMap(mStdErr)) {
981 if (map->IsCandidate() && !mSlots.ownsMapping(map->mStart) &&
982 !InitialMapsContains(map->mStart)) {
983 rss += map->mRSS;
984 }
985 }
986
987 return rss;
988 }
989
990 bool InitialMapsContains(uintptr_t aRangeStart) {
991 for (unsigned i = 0; i < mNumInitialMaps; i++) {
992 MOZ_ASSERT(i < MAX_INITIAL_MAPS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(i < MAX_INITIAL_MAPS)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(i < MAX_INITIAL_MAPS))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("i < MAX_INITIAL_MAPS"
, "/root/firefox-clang/memory/replace/logalloc/replay/Replay.cpp"
, 992); do { } while (false); do { MOZ_CrashSequence(__null, 992
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
993
994 if (mInitialMaps[i] == aRangeStart) {
995 return true;
996 }
997 }
998 return false;
999 }
1000
1001 public:
1002 void BuildInitialMapInfo() {
1003 if (mGetRSSFailed
1.1
Field 'mGetRSSFailed' is false
) {
2
Taking false branch
1004 return;
1005 }
1006
1007 Maybe<SMapsReader> reader = SMapsReader::open();
3
Calling 'SMapsReader::open'
1008 if (!reader) {
1009 mGetRSSFailed = true;
1010 return;
1011 }
1012
1013 while (Maybe<MemoryMap> map = reader->readMap(mStdErr)) {
1014 if (map->IsCandidate()) {
1015 if (mNumInitialMaps >= MAX_INITIAL_MAPS) {
1016 FdPrintf(mStdErr, "Too many initial mappings, can't compute RSS\n");
1017 mGetRSSFailed = false;
1018 return;
1019 }
1020
1021 mInitialMaps[mNumInitialMaps++] = map->mStart;
1022 }
1023 }
1024 }
1025#endif
1026
1027 private:
1028 MemSlot& SlotForResult(Buffer& aResult) {
1029 /* Parse result value and get the corresponding slot. */
1030 Buffer dummy = aResult.SplitChar('=');
1031 Buffer dummy2 = aResult.SplitChar('#');
1032 if (dummy || dummy2) {
1033 die("Malformed input");
1034 }
1035
1036 size_t slot_id = parseNumber(aResult);
1037 mNumUsedSlots = std::max(mNumUsedSlots, slot_id + 1);
1038
1039 return mSlots[slot_id];
1040 }
1041
1042 void MaybeCommit(MemSlot& aSlot) {
1043 if (mDoMemset) {
1044 // Write any byte, 0x55 isn't significant.
1045 memset(aSlot.mPtr, 0x55, aSlot.mRequest);
1046 }
1047 }
1048
1049 platform_handle_t mStdErr;
1050 size_t mOps = 0;
1051
1052 // The number of slots that have been used. It is used to iterate over slots
1053 // without accessing those we haven't initialised.
1054 size_t mNumUsedSlots = 0;
1055
1056 MemSlotList mSlots;
1057 size_t mTotalRequestedSize = 0;
1058 size_t mTotalAllocatedSize = 0;
1059 // Whether to calculate slop for all allocations over the runtime of a
1060 // process.
1061 bool mCalculateSlop = false;
1062 bool mDoMemset = false;
1063
1064#ifdef XP_LINUX1
1065 // If we have a failure reading smaps info then this is used to disable that
1066 // feature.
1067 bool mGetRSSFailed = false;
1068
1069 // The initial memory mappings are recorded here at start up. We exclude
1070 // memory in these mappings when computing RSS. We assume they do not grow
1071 // and that no regions are allocated near them, this is true because they'll
1072 // only record the .bss and .data segments from our binary and shared objects
1073 // or regions that logalloc-replay has created for MappedArrays.
1074 //
1075 // 64 should be enough for anybody.
1076 static constexpr unsigned MAX_INITIAL_MAPS = 64;
1077 uintptr_t mInitialMaps[MAX_INITIAL_MAPS];
1078 unsigned mNumInitialMaps = 0;
1079#endif // XP_LINUX
1080};
1081
1082MOZ_RUNINIT static Replay replay;
1083
1084int main(int argc, const char* argv[]) {
1085 size_t first_pid = 0;
1086 const char* filename = nullptr;
1087
1088 for (int i = 1; i < argc; i++) {
1089 const char* option = argv[i];
1090 if (strcmp(option, "-s") == 0) {
1091 // Do accounting to calculate allocation slop.
1092 replay.enableSlopCalculation();
1093 } else if (strcmp(option, "-c") == 0) {
1094 // Touch memory as we allocate it.
1095 replay.enableMemset();
1096 } else if (!filename) {
1097 // Assume the only other possiability is a single file name.
1098 filename = option;
1099 } else {
1100 fprintf(stderrstderr, "Unknown command line option: %s\n", option);
1101 return EXIT_FAILURE1;
1102 }
1103 }
1104
1105 FdReader reader(0);
1106 if (filename) {
1107#ifdef XP_WIN
1108 int fd = _open(filename, _O_RDONLY);
1109#else
1110 int fd = open(filename, O_RDONLY00);
1111#endif
1112 if (fd < 0) {
1113 perror(filename);
1114 exit(1);
1115 }
1116 reader = FdReader(fd, true);
1117 }
1118
1119 /* Read log from reader and dispatch function calls to the Replay instance.
1120 * The log format is essentially:
1121 * <pid> <tid> <function>([<args>])[=<result>]
1122 * <args> is a comma separated list of arguments.
1123 *
1124 * The logs are expected to be preprocessed so that allocations are
1125 * attributed a tracking slot. The input is trusted not to have crazy
1126 * values for these slot numbers.
1127 *
1128 * <result>, as well as some of the args to some of the function calls are
1129 * such slot numbers.
1130 */
1131 while (true) {
1132 Buffer line = reader.ReadLine();
1133
1134 if (!line) {
1135 break;
1136 }
1137
1138 size_t pid = parseNumber(line.SplitChar(' '));
1139 if (!first_pid) {
1140 first_pid = pid;
1141 }
1142
1143 /* The log may contain data for several processes, only entries for the
1144 * very first that appears are treated. */
1145 if (first_pid != pid) {
1146 continue;
1147 }
1148
1149 /* The log contains thread ids for manual analysis, but we just ignore them
1150 * for now. */
1151 parseNumber(line.SplitChar(' '));
1152
1153 Buffer func = line.SplitChar('(');
1154 Buffer args = line.SplitChar(')');
1155
1156 if (func == Buffer("jemalloc_stats")) {
1157 replay.jemalloc_stats(args, line);
1158 } else if (func == Buffer("free")) {
1159 replay.free(args, line);
1160 } else if (func == Buffer("malloc")) {
1161 replay.malloc(args, line);
1162 } else if (func == Buffer("posix_memalign")) {
1163 replay.posix_memalign(args, line);
1164 } else if (func == Buffer("aligned_alloc")) {
1165 replay.aligned_alloc(args, line);
1166 } else if (func == Buffer("calloc")) {
1167 replay.calloc(args, line);
1168 } else if (func == Buffer("realloc")) {
1169 replay.realloc(args, line);
1170 } else if (func == Buffer("memalign")) {
1171 replay.memalign(args, line);
1172 } else if (func == Buffer("valloc")) {
1173 replay.valloc(args, line);
1174 } else {
1175 die("Malformed input");
1176 }
1177 }
1178
1179 return 0;
1180}