memprof_allocator.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. //===-- memprof_allocator.cpp --------------------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file is a part of MemProfiler, a memory profiler.
  10. //
  11. // Implementation of MemProf's memory allocator, which uses the allocator
  12. // from sanitizer_common.
  13. //
  14. //===----------------------------------------------------------------------===//
  15. #include "memprof_allocator.h"
  16. #include "memprof_mapping.h"
  17. #include "memprof_mibmap.h"
  18. #include "memprof_rawprofile.h"
  19. #include "memprof_stack.h"
  20. #include "memprof_thread.h"
  21. #include "profile/MemProfData.inc"
  22. #include "sanitizer_common/sanitizer_allocator_checks.h"
  23. #include "sanitizer_common/sanitizer_allocator_interface.h"
  24. #include "sanitizer_common/sanitizer_allocator_report.h"
  25. #include "sanitizer_common/sanitizer_errno.h"
  26. #include "sanitizer_common/sanitizer_file.h"
  27. #include "sanitizer_common/sanitizer_flags.h"
  28. #include "sanitizer_common/sanitizer_internal_defs.h"
  29. #include "sanitizer_common/sanitizer_procmaps.h"
  30. #include "sanitizer_common/sanitizer_stackdepot.h"
  31. #include <sched.h>
  32. #include <time.h>
  33. namespace __memprof {
  34. namespace {
  35. using ::llvm::memprof::MemInfoBlock;
  36. void Print(const MemInfoBlock &M, const u64 id, bool print_terse) {
  37. u64 p;
  38. if (print_terse) {
  39. p = M.TotalSize * 100 / M.AllocCount;
  40. Printf("MIB:%llu/%u/%llu.%02llu/%u/%u/", id, M.AllocCount, p / 100, p % 100,
  41. M.MinSize, M.MaxSize);
  42. p = M.TotalAccessCount * 100 / M.AllocCount;
  43. Printf("%llu.%02llu/%llu/%llu/", p / 100, p % 100, M.MinAccessCount,
  44. M.MaxAccessCount);
  45. p = M.TotalLifetime * 100 / M.AllocCount;
  46. Printf("%llu.%02llu/%u/%u/", p / 100, p % 100, M.MinLifetime,
  47. M.MaxLifetime);
  48. Printf("%u/%u/%u/%u\n", M.NumMigratedCpu, M.NumLifetimeOverlaps,
  49. M.NumSameAllocCpu, M.NumSameDeallocCpu);
  50. } else {
  51. p = M.TotalSize * 100 / M.AllocCount;
  52. Printf("Memory allocation stack id = %llu\n", id);
  53. Printf("\talloc_count %u, size (ave/min/max) %llu.%02llu / %u / %u\n",
  54. M.AllocCount, p / 100, p % 100, M.MinSize, M.MaxSize);
  55. p = M.TotalAccessCount * 100 / M.AllocCount;
  56. Printf("\taccess_count (ave/min/max): %llu.%02llu / %llu / %llu\n", p / 100,
  57. p % 100, M.MinAccessCount, M.MaxAccessCount);
  58. p = M.TotalLifetime * 100 / M.AllocCount;
  59. Printf("\tlifetime (ave/min/max): %llu.%02llu / %u / %u\n", p / 100,
  60. p % 100, M.MinLifetime, M.MaxLifetime);
  61. Printf("\tnum migrated: %u, num lifetime overlaps: %u, num same alloc "
  62. "cpu: %u, num same dealloc_cpu: %u\n",
  63. M.NumMigratedCpu, M.NumLifetimeOverlaps, M.NumSameAllocCpu,
  64. M.NumSameDeallocCpu);
  65. }
  66. }
  67. } // namespace
  68. static int GetCpuId(void) {
  69. // _memprof_preinit is called via the preinit_array, which subsequently calls
  70. // malloc. Since this is before _dl_init calls VDSO_SETUP, sched_getcpu
  71. // will seg fault as the address of __vdso_getcpu will be null.
  72. if (!memprof_init_done)
  73. return -1;
  74. return sched_getcpu();
  75. }
  76. // Compute the timestamp in ms.
  77. static int GetTimestamp(void) {
  78. // timespec_get will segfault if called from dl_init
  79. if (!memprof_timestamp_inited) {
  80. // By returning 0, this will be effectively treated as being
  81. // timestamped at memprof init time (when memprof_init_timestamp_s
  82. // is initialized).
  83. return 0;
  84. }
  85. timespec ts;
  86. clock_gettime(CLOCK_REALTIME, &ts);
  87. return (ts.tv_sec - memprof_init_timestamp_s) * 1000 + ts.tv_nsec / 1000000;
  88. }
  89. static MemprofAllocator &get_allocator();
  90. // The memory chunk allocated from the underlying allocator looks like this:
  91. // H H U U U U U U
  92. // H -- ChunkHeader (32 bytes)
  93. // U -- user memory.
  94. // If there is left padding before the ChunkHeader (due to use of memalign),
  95. // we store a magic value in the first uptr word of the memory block and
  96. // store the address of ChunkHeader in the next uptr.
  97. // M B L L L L L L L L L H H U U U U U U
  98. // | ^
  99. // ---------------------|
  100. // M -- magic value kAllocBegMagic
  101. // B -- address of ChunkHeader pointing to the first 'H'
  102. constexpr uptr kMaxAllowedMallocBits = 40;
  103. // Should be no more than 32-bytes
  104. struct ChunkHeader {
  105. // 1-st 4 bytes.
  106. u32 alloc_context_id;
  107. // 2-nd 4 bytes
  108. u32 cpu_id;
  109. // 3-rd 4 bytes
  110. u32 timestamp_ms;
  111. // 4-th 4 bytes
  112. // Note only 1 bit is needed for this flag if we need space in the future for
  113. // more fields.
  114. u32 from_memalign;
  115. // 5-th and 6-th 4 bytes
  116. // The max size of an allocation is 2^40 (kMaxAllowedMallocSize), so this
  117. // could be shrunk to kMaxAllowedMallocBits if we need space in the future for
  118. // more fields.
  119. atomic_uint64_t user_requested_size;
  120. // 23 bits available
  121. // 7-th and 8-th 4 bytes
  122. u64 data_type_id; // TODO: hash of type name
  123. };
  124. static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
  125. COMPILER_CHECK(kChunkHeaderSize == 32);
  126. struct MemprofChunk : ChunkHeader {
  127. uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
  128. uptr UsedSize() {
  129. return atomic_load(&user_requested_size, memory_order_relaxed);
  130. }
  131. void *AllocBeg() {
  132. if (from_memalign)
  133. return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this));
  134. return reinterpret_cast<void *>(this);
  135. }
  136. };
  137. class LargeChunkHeader {
  138. static constexpr uptr kAllocBegMagic =
  139. FIRST_32_SECOND_64(0xCC6E96B9, 0xCC6E96B9CC6E96B9ULL);
  140. atomic_uintptr_t magic;
  141. MemprofChunk *chunk_header;
  142. public:
  143. MemprofChunk *Get() const {
  144. return atomic_load(&magic, memory_order_acquire) == kAllocBegMagic
  145. ? chunk_header
  146. : nullptr;
  147. }
  148. void Set(MemprofChunk *p) {
  149. if (p) {
  150. chunk_header = p;
  151. atomic_store(&magic, kAllocBegMagic, memory_order_release);
  152. return;
  153. }
  154. uptr old = kAllocBegMagic;
  155. if (!atomic_compare_exchange_strong(&magic, &old, 0,
  156. memory_order_release)) {
  157. CHECK_EQ(old, kAllocBegMagic);
  158. }
  159. }
  160. };
  161. void FlushUnneededMemProfShadowMemory(uptr p, uptr size) {
  162. // Since memprof's mapping is compacting, the shadow chunk may be
  163. // not page-aligned, so we only flush the page-aligned portion.
  164. ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
  165. }
  166. void MemprofMapUnmapCallback::OnMap(uptr p, uptr size) const {
  167. // Statistics.
  168. MemprofStats &thread_stats = GetCurrentThreadStats();
  169. thread_stats.mmaps++;
  170. thread_stats.mmaped += size;
  171. }
  172. void MemprofMapUnmapCallback::OnUnmap(uptr p, uptr size) const {
  173. // We are about to unmap a chunk of user memory.
  174. // Mark the corresponding shadow memory as not needed.
  175. FlushUnneededMemProfShadowMemory(p, size);
  176. // Statistics.
  177. MemprofStats &thread_stats = GetCurrentThreadStats();
  178. thread_stats.munmaps++;
  179. thread_stats.munmaped += size;
  180. }
  181. AllocatorCache *GetAllocatorCache(MemprofThreadLocalMallocStorage *ms) {
  182. CHECK(ms);
  183. return &ms->allocator_cache;
  184. }
  185. // Accumulates the access count from the shadow for the given pointer and size.
  186. u64 GetShadowCount(uptr p, u32 size) {
  187. u64 *shadow = (u64 *)MEM_TO_SHADOW(p);
  188. u64 *shadow_end = (u64 *)MEM_TO_SHADOW(p + size);
  189. u64 count = 0;
  190. for (; shadow <= shadow_end; shadow++)
  191. count += *shadow;
  192. return count;
  193. }
  194. // Clears the shadow counters (when memory is allocated).
  195. void ClearShadow(uptr addr, uptr size) {
  196. CHECK(AddrIsAlignedByGranularity(addr));
  197. CHECK(AddrIsInMem(addr));
  198. CHECK(AddrIsAlignedByGranularity(addr + size));
  199. CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
  200. CHECK(REAL(memset));
  201. uptr shadow_beg = MEM_TO_SHADOW(addr);
  202. uptr shadow_end = MEM_TO_SHADOW(addr + size - SHADOW_GRANULARITY) + 1;
  203. if (shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
  204. REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
  205. } else {
  206. uptr page_size = GetPageSizeCached();
  207. uptr page_beg = RoundUpTo(shadow_beg, page_size);
  208. uptr page_end = RoundDownTo(shadow_end, page_size);
  209. if (page_beg >= page_end) {
  210. REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
  211. } else {
  212. if (page_beg != shadow_beg) {
  213. REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg);
  214. }
  215. if (page_end != shadow_end) {
  216. REAL(memset)((void *)page_end, 0, shadow_end - page_end);
  217. }
  218. ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
  219. }
  220. }
  221. }
  222. struct Allocator {
  223. static const uptr kMaxAllowedMallocSize = 1ULL << kMaxAllowedMallocBits;
  224. MemprofAllocator allocator;
  225. StaticSpinMutex fallback_mutex;
  226. AllocatorCache fallback_allocator_cache;
  227. uptr max_user_defined_malloc_size;
  228. // Holds the mapping of stack ids to MemInfoBlocks.
  229. MIBMapTy MIBMap;
  230. atomic_uint8_t destructing;
  231. atomic_uint8_t constructed;
  232. bool print_text;
  233. // ------------------- Initialization ------------------------
  234. explicit Allocator(LinkerInitialized) : print_text(flags()->print_text) {
  235. atomic_store_relaxed(&destructing, 0);
  236. atomic_store_relaxed(&constructed, 1);
  237. }
  238. ~Allocator() {
  239. atomic_store_relaxed(&destructing, 1);
  240. FinishAndWrite();
  241. }
  242. static void PrintCallback(const uptr Key, LockedMemInfoBlock *const &Value,
  243. void *Arg) {
  244. SpinMutexLock l(&Value->mutex);
  245. Print(Value->mib, Key, bool(Arg));
  246. }
  247. void FinishAndWrite() {
  248. if (print_text && common_flags()->print_module_map)
  249. DumpProcessMap();
  250. allocator.ForceLock();
  251. InsertLiveBlocks();
  252. if (print_text) {
  253. if (!flags()->print_terse)
  254. Printf("Recorded MIBs (incl. live on exit):\n");
  255. MIBMap.ForEach(PrintCallback,
  256. reinterpret_cast<void *>(flags()->print_terse));
  257. StackDepotPrintAll();
  258. } else {
  259. // Serialize the contents to a raw profile. Format documented in
  260. // memprof_rawprofile.h.
  261. char *Buffer = nullptr;
  262. MemoryMappingLayout Layout(/*cache_enabled=*/true);
  263. u64 BytesSerialized = SerializeToRawProfile(MIBMap, Layout, Buffer);
  264. CHECK(Buffer && BytesSerialized && "could not serialize to buffer");
  265. report_file.Write(Buffer, BytesSerialized);
  266. }
  267. allocator.ForceUnlock();
  268. }
  269. // Inserts any blocks which have been allocated but not yet deallocated.
  270. void InsertLiveBlocks() {
  271. allocator.ForEachChunk(
  272. [](uptr chunk, void *alloc) {
  273. u64 user_requested_size;
  274. Allocator *A = (Allocator *)alloc;
  275. MemprofChunk *m =
  276. A->GetMemprofChunk((void *)chunk, user_requested_size);
  277. if (!m)
  278. return;
  279. uptr user_beg = ((uptr)m) + kChunkHeaderSize;
  280. u64 c = GetShadowCount(user_beg, user_requested_size);
  281. long curtime = GetTimestamp();
  282. MemInfoBlock newMIB(user_requested_size, c, m->timestamp_ms, curtime,
  283. m->cpu_id, GetCpuId());
  284. InsertOrMerge(m->alloc_context_id, newMIB, A->MIBMap);
  285. },
  286. this);
  287. }
  288. void InitLinkerInitialized() {
  289. SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
  290. allocator.InitLinkerInitialized(
  291. common_flags()->allocator_release_to_os_interval_ms);
  292. max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
  293. ? common_flags()->max_allocation_size_mb
  294. << 20
  295. : kMaxAllowedMallocSize;
  296. }
  297. // -------------------- Allocation/Deallocation routines ---------------
  298. void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
  299. AllocType alloc_type) {
  300. if (UNLIKELY(!memprof_inited))
  301. MemprofInitFromRtl();
  302. if (UNLIKELY(IsRssLimitExceeded())) {
  303. if (AllocatorMayReturnNull())
  304. return nullptr;
  305. ReportRssLimitExceeded(stack);
  306. }
  307. CHECK(stack);
  308. const uptr min_alignment = MEMPROF_ALIGNMENT;
  309. if (alignment < min_alignment)
  310. alignment = min_alignment;
  311. if (size == 0) {
  312. // We'd be happy to avoid allocating memory for zero-size requests, but
  313. // some programs/tests depend on this behavior and assume that malloc
  314. // would not return NULL even for zero-size allocations. Moreover, it
  315. // looks like operator new should never return NULL, and results of
  316. // consecutive "new" calls must be different even if the allocated size
  317. // is zero.
  318. size = 1;
  319. }
  320. CHECK(IsPowerOfTwo(alignment));
  321. uptr rounded_size = RoundUpTo(size, alignment);
  322. uptr needed_size = rounded_size + kChunkHeaderSize;
  323. if (alignment > min_alignment)
  324. needed_size += alignment;
  325. CHECK(IsAligned(needed_size, min_alignment));
  326. if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize ||
  327. size > max_user_defined_malloc_size) {
  328. if (AllocatorMayReturnNull()) {
  329. Report("WARNING: MemProfiler failed to allocate 0x%zx bytes\n", size);
  330. return nullptr;
  331. }
  332. uptr malloc_limit =
  333. Min(kMaxAllowedMallocSize, max_user_defined_malloc_size);
  334. ReportAllocationSizeTooBig(size, malloc_limit, stack);
  335. }
  336. MemprofThread *t = GetCurrentThread();
  337. void *allocated;
  338. if (t) {
  339. AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
  340. allocated = allocator.Allocate(cache, needed_size, 8);
  341. } else {
  342. SpinMutexLock l(&fallback_mutex);
  343. AllocatorCache *cache = &fallback_allocator_cache;
  344. allocated = allocator.Allocate(cache, needed_size, 8);
  345. }
  346. if (UNLIKELY(!allocated)) {
  347. SetAllocatorOutOfMemory();
  348. if (AllocatorMayReturnNull())
  349. return nullptr;
  350. ReportOutOfMemory(size, stack);
  351. }
  352. uptr alloc_beg = reinterpret_cast<uptr>(allocated);
  353. uptr alloc_end = alloc_beg + needed_size;
  354. uptr beg_plus_header = alloc_beg + kChunkHeaderSize;
  355. uptr user_beg = beg_plus_header;
  356. if (!IsAligned(user_beg, alignment))
  357. user_beg = RoundUpTo(user_beg, alignment);
  358. uptr user_end = user_beg + size;
  359. CHECK_LE(user_end, alloc_end);
  360. uptr chunk_beg = user_beg - kChunkHeaderSize;
  361. MemprofChunk *m = reinterpret_cast<MemprofChunk *>(chunk_beg);
  362. m->from_memalign = alloc_beg != chunk_beg;
  363. CHECK(size);
  364. m->cpu_id = GetCpuId();
  365. m->timestamp_ms = GetTimestamp();
  366. m->alloc_context_id = StackDepotPut(*stack);
  367. uptr size_rounded_down_to_granularity =
  368. RoundDownTo(size, SHADOW_GRANULARITY);
  369. if (size_rounded_down_to_granularity)
  370. ClearShadow(user_beg, size_rounded_down_to_granularity);
  371. MemprofStats &thread_stats = GetCurrentThreadStats();
  372. thread_stats.mallocs++;
  373. thread_stats.malloced += size;
  374. thread_stats.malloced_overhead += needed_size - size;
  375. if (needed_size > SizeClassMap::kMaxSize)
  376. thread_stats.malloc_large++;
  377. else
  378. thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
  379. void *res = reinterpret_cast<void *>(user_beg);
  380. atomic_store(&m->user_requested_size, size, memory_order_release);
  381. if (alloc_beg != chunk_beg) {
  382. CHECK_LE(alloc_beg + sizeof(LargeChunkHeader), chunk_beg);
  383. reinterpret_cast<LargeChunkHeader *>(alloc_beg)->Set(m);
  384. }
  385. RunMallocHooks(res, size);
  386. return res;
  387. }
  388. void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment,
  389. BufferedStackTrace *stack, AllocType alloc_type) {
  390. uptr p = reinterpret_cast<uptr>(ptr);
  391. if (p == 0)
  392. return;
  393. RunFreeHooks(ptr);
  394. uptr chunk_beg = p - kChunkHeaderSize;
  395. MemprofChunk *m = reinterpret_cast<MemprofChunk *>(chunk_beg);
  396. u64 user_requested_size =
  397. atomic_exchange(&m->user_requested_size, 0, memory_order_acquire);
  398. if (memprof_inited && memprof_init_done &&
  399. atomic_load_relaxed(&constructed) &&
  400. !atomic_load_relaxed(&destructing)) {
  401. u64 c = GetShadowCount(p, user_requested_size);
  402. long curtime = GetTimestamp();
  403. MemInfoBlock newMIB(user_requested_size, c, m->timestamp_ms, curtime,
  404. m->cpu_id, GetCpuId());
  405. InsertOrMerge(m->alloc_context_id, newMIB, MIBMap);
  406. }
  407. MemprofStats &thread_stats = GetCurrentThreadStats();
  408. thread_stats.frees++;
  409. thread_stats.freed += user_requested_size;
  410. void *alloc_beg = m->AllocBeg();
  411. if (alloc_beg != m) {
  412. // Clear the magic value, as allocator internals may overwrite the
  413. // contents of deallocated chunk, confusing GetMemprofChunk lookup.
  414. reinterpret_cast<LargeChunkHeader *>(alloc_beg)->Set(nullptr);
  415. }
  416. MemprofThread *t = GetCurrentThread();
  417. if (t) {
  418. AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
  419. allocator.Deallocate(cache, alloc_beg);
  420. } else {
  421. SpinMutexLock l(&fallback_mutex);
  422. AllocatorCache *cache = &fallback_allocator_cache;
  423. allocator.Deallocate(cache, alloc_beg);
  424. }
  425. }
  426. void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
  427. CHECK(old_ptr && new_size);
  428. uptr p = reinterpret_cast<uptr>(old_ptr);
  429. uptr chunk_beg = p - kChunkHeaderSize;
  430. MemprofChunk *m = reinterpret_cast<MemprofChunk *>(chunk_beg);
  431. MemprofStats &thread_stats = GetCurrentThreadStats();
  432. thread_stats.reallocs++;
  433. thread_stats.realloced += new_size;
  434. void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC);
  435. if (new_ptr) {
  436. CHECK_NE(REAL(memcpy), nullptr);
  437. uptr memcpy_size = Min(new_size, m->UsedSize());
  438. REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
  439. Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC);
  440. }
  441. return new_ptr;
  442. }
  443. void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
  444. if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
  445. if (AllocatorMayReturnNull())
  446. return nullptr;
  447. ReportCallocOverflow(nmemb, size, stack);
  448. }
  449. void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC);
  450. // If the memory comes from the secondary allocator no need to clear it
  451. // as it comes directly from mmap.
  452. if (ptr && allocator.FromPrimary(ptr))
  453. REAL(memset)(ptr, 0, nmemb * size);
  454. return ptr;
  455. }
  456. void CommitBack(MemprofThreadLocalMallocStorage *ms,
  457. BufferedStackTrace *stack) {
  458. AllocatorCache *ac = GetAllocatorCache(ms);
  459. allocator.SwallowCache(ac);
  460. }
  461. // -------------------------- Chunk lookup ----------------------
  462. // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
  463. MemprofChunk *GetMemprofChunk(void *alloc_beg, u64 &user_requested_size) {
  464. if (!alloc_beg)
  465. return nullptr;
  466. MemprofChunk *p = reinterpret_cast<LargeChunkHeader *>(alloc_beg)->Get();
  467. if (!p) {
  468. if (!allocator.FromPrimary(alloc_beg))
  469. return nullptr;
  470. p = reinterpret_cast<MemprofChunk *>(alloc_beg);
  471. }
  472. // The size is reset to 0 on deallocation (and a min of 1 on
  473. // allocation).
  474. user_requested_size =
  475. atomic_load(&p->user_requested_size, memory_order_acquire);
  476. if (user_requested_size)
  477. return p;
  478. return nullptr;
  479. }
  480. MemprofChunk *GetMemprofChunkByAddr(uptr p, u64 &user_requested_size) {
  481. void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
  482. return GetMemprofChunk(alloc_beg, user_requested_size);
  483. }
  484. uptr AllocationSize(uptr p) {
  485. u64 user_requested_size;
  486. MemprofChunk *m = GetMemprofChunkByAddr(p, user_requested_size);
  487. if (!m)
  488. return 0;
  489. if (m->Beg() != p)
  490. return 0;
  491. return user_requested_size;
  492. }
  493. void Purge(BufferedStackTrace *stack) { allocator.ForceReleaseToOS(); }
  494. void PrintStats() { allocator.PrintStats(); }
  495. void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
  496. allocator.ForceLock();
  497. fallback_mutex.Lock();
  498. }
  499. void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
  500. fallback_mutex.Unlock();
  501. allocator.ForceUnlock();
  502. }
  503. };
  504. static Allocator instance(LINKER_INITIALIZED);
  505. static MemprofAllocator &get_allocator() { return instance.allocator; }
  506. void InitializeAllocator() { instance.InitLinkerInitialized(); }
  507. void MemprofThreadLocalMallocStorage::CommitBack() {
  508. GET_STACK_TRACE_MALLOC;
  509. instance.CommitBack(this, &stack);
  510. }
  511. void PrintInternalAllocatorStats() { instance.PrintStats(); }
  512. void memprof_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
  513. instance.Deallocate(ptr, 0, 0, stack, alloc_type);
  514. }
  515. void memprof_delete(void *ptr, uptr size, uptr alignment,
  516. BufferedStackTrace *stack, AllocType alloc_type) {
  517. instance.Deallocate(ptr, size, alignment, stack, alloc_type);
  518. }
  519. void *memprof_malloc(uptr size, BufferedStackTrace *stack) {
  520. return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC));
  521. }
  522. void *memprof_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
  523. return SetErrnoOnNull(instance.Calloc(nmemb, size, stack));
  524. }
  525. void *memprof_reallocarray(void *p, uptr nmemb, uptr size,
  526. BufferedStackTrace *stack) {
  527. if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
  528. errno = errno_ENOMEM;
  529. if (AllocatorMayReturnNull())
  530. return nullptr;
  531. ReportReallocArrayOverflow(nmemb, size, stack);
  532. }
  533. return memprof_realloc(p, nmemb * size, stack);
  534. }
  535. void *memprof_realloc(void *p, uptr size, BufferedStackTrace *stack) {
  536. if (!p)
  537. return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC));
  538. if (size == 0) {
  539. if (flags()->allocator_frees_and_returns_null_on_realloc_zero) {
  540. instance.Deallocate(p, 0, 0, stack, FROM_MALLOC);
  541. return nullptr;
  542. }
  543. // Allocate a size of 1 if we shouldn't free() on Realloc to 0
  544. size = 1;
  545. }
  546. return SetErrnoOnNull(instance.Reallocate(p, size, stack));
  547. }
  548. void *memprof_valloc(uptr size, BufferedStackTrace *stack) {
  549. return SetErrnoOnNull(
  550. instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC));
  551. }
  552. void *memprof_pvalloc(uptr size, BufferedStackTrace *stack) {
  553. uptr PageSize = GetPageSizeCached();
  554. if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
  555. errno = errno_ENOMEM;
  556. if (AllocatorMayReturnNull())
  557. return nullptr;
  558. ReportPvallocOverflow(size, stack);
  559. }
  560. // pvalloc(0) should allocate one page.
  561. size = size ? RoundUpTo(size, PageSize) : PageSize;
  562. return SetErrnoOnNull(instance.Allocate(size, PageSize, stack, FROM_MALLOC));
  563. }
  564. void *memprof_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
  565. AllocType alloc_type) {
  566. if (UNLIKELY(!IsPowerOfTwo(alignment))) {
  567. errno = errno_EINVAL;
  568. if (AllocatorMayReturnNull())
  569. return nullptr;
  570. ReportInvalidAllocationAlignment(alignment, stack);
  571. }
  572. return SetErrnoOnNull(instance.Allocate(size, alignment, stack, alloc_type));
  573. }
  574. void *memprof_aligned_alloc(uptr alignment, uptr size,
  575. BufferedStackTrace *stack) {
  576. if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
  577. errno = errno_EINVAL;
  578. if (AllocatorMayReturnNull())
  579. return nullptr;
  580. ReportInvalidAlignedAllocAlignment(size, alignment, stack);
  581. }
  582. return SetErrnoOnNull(instance.Allocate(size, alignment, stack, FROM_MALLOC));
  583. }
  584. int memprof_posix_memalign(void **memptr, uptr alignment, uptr size,
  585. BufferedStackTrace *stack) {
  586. if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
  587. if (AllocatorMayReturnNull())
  588. return errno_EINVAL;
  589. ReportInvalidPosixMemalignAlignment(alignment, stack);
  590. }
  591. void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC);
  592. if (UNLIKELY(!ptr))
  593. // OOM error is already taken care of by Allocate.
  594. return errno_ENOMEM;
  595. CHECK(IsAligned((uptr)ptr, alignment));
  596. *memptr = ptr;
  597. return 0;
  598. }
  599. uptr memprof_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
  600. if (!ptr)
  601. return 0;
  602. uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
  603. return usable_size;
  604. }
  605. } // namespace __memprof
  606. // ---------------------- Interface ---------------- {{{1
  607. using namespace __memprof;
  608. uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
  609. int __sanitizer_get_ownership(const void *p) {
  610. return memprof_malloc_usable_size(p, 0, 0) != 0;
  611. }
  612. uptr __sanitizer_get_allocated_size(const void *p) {
  613. return memprof_malloc_usable_size(p, 0, 0);
  614. }
  615. int __memprof_profile_dump() {
  616. instance.FinishAndWrite();
  617. // In the future we may want to return non-zero if there are any errors
  618. // detected during the dumping process.
  619. return 0;
  620. }