Bug Summary

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