memprof_allocator.cpp 24 KB

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