xref: /llvm-project/compiler-rt/lib/memprof/memprof_allocator.cpp (revision 968e3b682362e46042a718036ea7a641909b6375)
13d4bba30STeresa Johnson //===-- memprof_allocator.cpp --------------------------------------------===//
23d4bba30STeresa Johnson //
33d4bba30STeresa Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43d4bba30STeresa Johnson // See https://llvm.org/LICENSE.txt for license information.
53d4bba30STeresa Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63d4bba30STeresa Johnson //
73d4bba30STeresa Johnson //===----------------------------------------------------------------------===//
83d4bba30STeresa Johnson //
93d4bba30STeresa Johnson // This file is a part of MemProfiler, a memory profiler.
103d4bba30STeresa Johnson //
113d4bba30STeresa Johnson // Implementation of MemProf's memory allocator, which uses the allocator
123d4bba30STeresa Johnson // from sanitizer_common.
133d4bba30STeresa Johnson //
143d4bba30STeresa Johnson //===----------------------------------------------------------------------===//
153d4bba30STeresa Johnson 
163d4bba30STeresa Johnson #include "memprof_allocator.h"
173d4bba30STeresa Johnson #include "memprof_mapping.h"
181243cef2SSnehasish Kumar #include "memprof_mibmap.h"
19545866cbSSnehasish Kumar #include "memprof_rawprofile.h"
203d4bba30STeresa Johnson #include "memprof_stack.h"
213d4bba30STeresa Johnson #include "memprof_thread.h"
228306968bSSnehasish Kumar #include "profile/MemProfData.inc"
233d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_allocator_checks.h"
243d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_allocator_interface.h"
253d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_allocator_report.h"
2605181357SVitaly Buka #include "sanitizer_common/sanitizer_array_ref.h"
27a1bbf5acSSnehasish Kumar #include "sanitizer_common/sanitizer_common.h"
283d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_errno.h"
293d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_file.h"
303d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_flags.h"
313d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_internal_defs.h"
323d4bba30STeresa Johnson #include "sanitizer_common/sanitizer_stackdepot.h"
333d4bba30STeresa Johnson 
343d4bba30STeresa Johnson #include <sched.h>
35ed4fbe6dSPetr Hosek #include <time.h>
363d4bba30STeresa Johnson 
3730b93db5SMatthew Weingarten #define MAX_HISTOGRAM_PRINT_SIZE 32U
3830b93db5SMatthew Weingarten 
3930b93db5SMatthew Weingarten extern bool __memprof_histogram;
4030b93db5SMatthew Weingarten 
413d4bba30STeresa Johnson namespace __memprof {
428306968bSSnehasish Kumar namespace {
438306968bSSnehasish Kumar using ::llvm::memprof::MemInfoBlock;
448306968bSSnehasish Kumar 
458306968bSSnehasish Kumar void Print(const MemInfoBlock &M, const u64 id, bool print_terse) {
468306968bSSnehasish Kumar   u64 p;
478306968bSSnehasish Kumar 
488306968bSSnehasish Kumar   if (print_terse) {
49f89319b8SSnehasish Kumar     p = M.TotalSize * 100 / M.AllocCount;
50f89319b8SSnehasish Kumar     Printf("MIB:%llu/%u/%llu.%02llu/%u/%u/", id, M.AllocCount, p / 100, p % 100,
51f89319b8SSnehasish Kumar            M.MinSize, M.MaxSize);
52f89319b8SSnehasish Kumar     p = M.TotalAccessCount * 100 / M.AllocCount;
53f89319b8SSnehasish Kumar     Printf("%llu.%02llu/%llu/%llu/", p / 100, p % 100, M.MinAccessCount,
54f89319b8SSnehasish Kumar            M.MaxAccessCount);
55f89319b8SSnehasish Kumar     p = M.TotalLifetime * 100 / M.AllocCount;
56f89319b8SSnehasish Kumar     Printf("%llu.%02llu/%u/%u/", p / 100, p % 100, M.MinLifetime,
57f89319b8SSnehasish Kumar            M.MaxLifetime);
58f89319b8SSnehasish Kumar     Printf("%u/%u/%u/%u\n", M.NumMigratedCpu, M.NumLifetimeOverlaps,
59f89319b8SSnehasish Kumar            M.NumSameAllocCpu, M.NumSameDeallocCpu);
608306968bSSnehasish Kumar   } else {
61f89319b8SSnehasish Kumar     p = M.TotalSize * 100 / M.AllocCount;
628306968bSSnehasish Kumar     Printf("Memory allocation stack id = %llu\n", id);
638306968bSSnehasish Kumar     Printf("\talloc_count %u, size (ave/min/max) %llu.%02llu / %u / %u\n",
64f89319b8SSnehasish Kumar            M.AllocCount, p / 100, p % 100, M.MinSize, M.MaxSize);
65f89319b8SSnehasish Kumar     p = M.TotalAccessCount * 100 / M.AllocCount;
668306968bSSnehasish Kumar     Printf("\taccess_count (ave/min/max): %llu.%02llu / %llu / %llu\n", p / 100,
67f89319b8SSnehasish Kumar            p % 100, M.MinAccessCount, M.MaxAccessCount);
68f89319b8SSnehasish Kumar     p = M.TotalLifetime * 100 / M.AllocCount;
698306968bSSnehasish Kumar     Printf("\tlifetime (ave/min/max): %llu.%02llu / %u / %u\n", p / 100,
70f89319b8SSnehasish Kumar            p % 100, M.MinLifetime, M.MaxLifetime);
718306968bSSnehasish Kumar     Printf("\tnum migrated: %u, num lifetime overlaps: %u, num same alloc "
728306968bSSnehasish Kumar            "cpu: %u, num same dealloc_cpu: %u\n",
73f89319b8SSnehasish Kumar            M.NumMigratedCpu, M.NumLifetimeOverlaps, M.NumSameAllocCpu,
74f89319b8SSnehasish Kumar            M.NumSameDeallocCpu);
7530b93db5SMatthew Weingarten     Printf("AccessCountHistogram[%u]: ", M.AccessHistogramSize);
7630b93db5SMatthew Weingarten     uint32_t PrintSize = M.AccessHistogramSize > MAX_HISTOGRAM_PRINT_SIZE
7730b93db5SMatthew Weingarten                              ? MAX_HISTOGRAM_PRINT_SIZE
7830b93db5SMatthew Weingarten                              : M.AccessHistogramSize;
7930b93db5SMatthew Weingarten     for (size_t i = 0; i < PrintSize; ++i) {
8030b93db5SMatthew Weingarten       Printf("%llu ", ((uint64_t *)M.AccessHistogram)[i]);
8130b93db5SMatthew Weingarten     }
8230b93db5SMatthew Weingarten     Printf("\n");
838306968bSSnehasish Kumar   }
848306968bSSnehasish Kumar }
858306968bSSnehasish Kumar } // namespace
863d4bba30STeresa Johnson 
873d4bba30STeresa Johnson static int GetCpuId(void) {
883d4bba30STeresa Johnson   // _memprof_preinit is called via the preinit_array, which subsequently calls
893d4bba30STeresa Johnson   // malloc. Since this is before _dl_init calls VDSO_SETUP, sched_getcpu
903d4bba30STeresa Johnson   // will seg fault as the address of __vdso_getcpu will be null.
9179ebb638SSnehasish Kumar   if (!memprof_inited)
923d4bba30STeresa Johnson     return -1;
933d4bba30STeresa Johnson   return sched_getcpu();
943d4bba30STeresa Johnson }
953d4bba30STeresa Johnson 
963d4bba30STeresa Johnson // Compute the timestamp in ms.
973d4bba30STeresa Johnson static int GetTimestamp(void) {
983d4bba30STeresa Johnson   // timespec_get will segfault if called from dl_init
993d4bba30STeresa Johnson   if (!memprof_timestamp_inited) {
1003d4bba30STeresa Johnson     // By returning 0, this will be effectively treated as being
1013d4bba30STeresa Johnson     // timestamped at memprof init time (when memprof_init_timestamp_s
1023d4bba30STeresa Johnson     // is initialized).
1033d4bba30STeresa Johnson     return 0;
1043d4bba30STeresa Johnson   }
1053d4bba30STeresa Johnson   timespec ts;
106d7e71b5dSJeroen Dobbelaere   clock_gettime(CLOCK_REALTIME, &ts);
1073d4bba30STeresa Johnson   return (ts.tv_sec - memprof_init_timestamp_s) * 1000 + ts.tv_nsec / 1000000;
1083d4bba30STeresa Johnson }
1093d4bba30STeresa Johnson 
1103d4bba30STeresa Johnson static MemprofAllocator &get_allocator();
1113d4bba30STeresa Johnson 
1123d4bba30STeresa Johnson // The memory chunk allocated from the underlying allocator looks like this:
1133d4bba30STeresa Johnson // H H U U U U U U
1143d4bba30STeresa Johnson //   H -- ChunkHeader (32 bytes)
1153d4bba30STeresa Johnson //   U -- user memory.
1163d4bba30STeresa Johnson 
1173d4bba30STeresa Johnson // If there is left padding before the ChunkHeader (due to use of memalign),
1183d4bba30STeresa Johnson // we store a magic value in the first uptr word of the memory block and
1193d4bba30STeresa Johnson // store the address of ChunkHeader in the next uptr.
1203d4bba30STeresa Johnson // M B L L L L L L L L L  H H U U U U U U
1213d4bba30STeresa Johnson //   |                    ^
1223d4bba30STeresa Johnson //   ---------------------|
1233d4bba30STeresa Johnson //   M -- magic value kAllocBegMagic
1243d4bba30STeresa Johnson //   B -- address of ChunkHeader pointing to the first 'H'
1253d4bba30STeresa Johnson 
1263d4bba30STeresa Johnson constexpr uptr kMaxAllowedMallocBits = 40;
1273d4bba30STeresa Johnson 
1283d4bba30STeresa Johnson // Should be no more than 32-bytes
1293d4bba30STeresa Johnson struct ChunkHeader {
1303d4bba30STeresa Johnson   // 1-st 4 bytes.
1313d4bba30STeresa Johnson   u32 alloc_context_id;
1323d4bba30STeresa Johnson   // 2-nd 4 bytes
1333d4bba30STeresa Johnson   u32 cpu_id;
1343d4bba30STeresa Johnson   // 3-rd 4 bytes
1353d4bba30STeresa Johnson   u32 timestamp_ms;
1363d4bba30STeresa Johnson   // 4-th 4 bytes
1373d4bba30STeresa Johnson   // Note only 1 bit is needed for this flag if we need space in the future for
1383d4bba30STeresa Johnson   // more fields.
1393d4bba30STeresa Johnson   u32 from_memalign;
1403d4bba30STeresa Johnson   // 5-th and 6-th 4 bytes
1413d4bba30STeresa Johnson   // The max size of an allocation is 2^40 (kMaxAllowedMallocSize), so this
1423d4bba30STeresa Johnson   // could be shrunk to kMaxAllowedMallocBits if we need space in the future for
1433d4bba30STeresa Johnson   // more fields.
1443d4bba30STeresa Johnson   atomic_uint64_t user_requested_size;
1453d4bba30STeresa Johnson   // 23 bits available
1463d4bba30STeresa Johnson   // 7-th and 8-th 4 bytes
1473d4bba30STeresa Johnson   u64 data_type_id; // TODO: hash of type name
1483d4bba30STeresa Johnson };
1493d4bba30STeresa Johnson 
1503d4bba30STeresa Johnson static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
1513d4bba30STeresa Johnson COMPILER_CHECK(kChunkHeaderSize == 32);
1523d4bba30STeresa Johnson 
1533d4bba30STeresa Johnson struct MemprofChunk : ChunkHeader {
1543d4bba30STeresa Johnson   uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
1553d4bba30STeresa Johnson   uptr UsedSize() {
1563d4bba30STeresa Johnson     return atomic_load(&user_requested_size, memory_order_relaxed);
1573d4bba30STeresa Johnson   }
1583d4bba30STeresa Johnson   void *AllocBeg() {
1593d4bba30STeresa Johnson     if (from_memalign)
1603d4bba30STeresa Johnson       return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this));
1613d4bba30STeresa Johnson     return reinterpret_cast<void *>(this);
1623d4bba30STeresa Johnson   }
1633d4bba30STeresa Johnson };
1643d4bba30STeresa Johnson 
1653d4bba30STeresa Johnson class LargeChunkHeader {
1663d4bba30STeresa Johnson   static constexpr uptr kAllocBegMagic =
1673d4bba30STeresa Johnson       FIRST_32_SECOND_64(0xCC6E96B9, 0xCC6E96B9CC6E96B9ULL);
1683d4bba30STeresa Johnson   atomic_uintptr_t magic;
1693d4bba30STeresa Johnson   MemprofChunk *chunk_header;
1703d4bba30STeresa Johnson 
1713d4bba30STeresa Johnson public:
1723d4bba30STeresa Johnson   MemprofChunk *Get() const {
1733d4bba30STeresa Johnson     return atomic_load(&magic, memory_order_acquire) == kAllocBegMagic
1743d4bba30STeresa Johnson                ? chunk_header
1753d4bba30STeresa Johnson                : nullptr;
1763d4bba30STeresa Johnson   }
1773d4bba30STeresa Johnson 
1783d4bba30STeresa Johnson   void Set(MemprofChunk *p) {
1793d4bba30STeresa Johnson     if (p) {
1803d4bba30STeresa Johnson       chunk_header = p;
1813d4bba30STeresa Johnson       atomic_store(&magic, kAllocBegMagic, memory_order_release);
1823d4bba30STeresa Johnson       return;
1833d4bba30STeresa Johnson     }
1843d4bba30STeresa Johnson 
1853d4bba30STeresa Johnson     uptr old = kAllocBegMagic;
1863d4bba30STeresa Johnson     if (!atomic_compare_exchange_strong(&magic, &old, 0,
1873d4bba30STeresa Johnson                                         memory_order_release)) {
1883d4bba30STeresa Johnson       CHECK_EQ(old, kAllocBegMagic);
1893d4bba30STeresa Johnson     }
1903d4bba30STeresa Johnson   }
1913d4bba30STeresa Johnson };
1923d4bba30STeresa Johnson 
1933d4bba30STeresa Johnson void FlushUnneededMemProfShadowMemory(uptr p, uptr size) {
1943d4bba30STeresa Johnson   // Since memprof's mapping is compacting, the shadow chunk may be
1953d4bba30STeresa Johnson   // not page-aligned, so we only flush the page-aligned portion.
1963d4bba30STeresa Johnson   ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
1973d4bba30STeresa Johnson }
1983d4bba30STeresa Johnson 
1993d4bba30STeresa Johnson void MemprofMapUnmapCallback::OnMap(uptr p, uptr size) const {
2003d4bba30STeresa Johnson   // Statistics.
2013d4bba30STeresa Johnson   MemprofStats &thread_stats = GetCurrentThreadStats();
2023d4bba30STeresa Johnson   thread_stats.mmaps++;
2033d4bba30STeresa Johnson   thread_stats.mmaped += size;
2043d4bba30STeresa Johnson }
20596928abbSVitaly Buka 
2063d4bba30STeresa Johnson void MemprofMapUnmapCallback::OnUnmap(uptr p, uptr size) const {
2073d4bba30STeresa Johnson   // We are about to unmap a chunk of user memory.
2083d4bba30STeresa Johnson   // Mark the corresponding shadow memory as not needed.
2093d4bba30STeresa Johnson   FlushUnneededMemProfShadowMemory(p, size);
2103d4bba30STeresa Johnson   // Statistics.
2113d4bba30STeresa Johnson   MemprofStats &thread_stats = GetCurrentThreadStats();
2123d4bba30STeresa Johnson   thread_stats.munmaps++;
2133d4bba30STeresa Johnson   thread_stats.munmaped += size;
2143d4bba30STeresa Johnson }
2153d4bba30STeresa Johnson 
2163d4bba30STeresa Johnson AllocatorCache *GetAllocatorCache(MemprofThreadLocalMallocStorage *ms) {
2173d4bba30STeresa Johnson   CHECK(ms);
2183d4bba30STeresa Johnson   return &ms->allocator_cache;
2193d4bba30STeresa Johnson }
2203d4bba30STeresa Johnson 
2213d4bba30STeresa Johnson // Accumulates the access count from the shadow for the given pointer and size.
2223d4bba30STeresa Johnson u64 GetShadowCount(uptr p, u32 size) {
2233d4bba30STeresa Johnson   u64 *shadow = (u64 *)MEM_TO_SHADOW(p);
2243d4bba30STeresa Johnson   u64 *shadow_end = (u64 *)MEM_TO_SHADOW(p + size);
2253d4bba30STeresa Johnson   u64 count = 0;
2263d4bba30STeresa Johnson   for (; shadow <= shadow_end; shadow++)
2273d4bba30STeresa Johnson     count += *shadow;
2283d4bba30STeresa Johnson   return count;
2293d4bba30STeresa Johnson }
2303d4bba30STeresa Johnson 
23130b93db5SMatthew Weingarten // Accumulates the access count from the shadow for the given pointer and size.
23230b93db5SMatthew Weingarten // See memprof_mapping.h for an overview on histogram counters.
23330b93db5SMatthew Weingarten u64 GetShadowCountHistogram(uptr p, u32 size) {
23430b93db5SMatthew Weingarten   u8 *shadow = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p);
23530b93db5SMatthew Weingarten   u8 *shadow_end = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p + size);
23630b93db5SMatthew Weingarten   u64 count = 0;
23730b93db5SMatthew Weingarten   for (; shadow <= shadow_end; shadow++)
23830b93db5SMatthew Weingarten     count += *shadow;
23930b93db5SMatthew Weingarten   return count;
24030b93db5SMatthew Weingarten }
24130b93db5SMatthew Weingarten 
2423d4bba30STeresa Johnson // Clears the shadow counters (when memory is allocated).
2433d4bba30STeresa Johnson void ClearShadow(uptr addr, uptr size) {
2443d4bba30STeresa Johnson   CHECK(AddrIsAlignedByGranularity(addr));
2453d4bba30STeresa Johnson   CHECK(AddrIsInMem(addr));
2463d4bba30STeresa Johnson   CHECK(AddrIsAlignedByGranularity(addr + size));
2473d4bba30STeresa Johnson   CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
2483d4bba30STeresa Johnson   CHECK(REAL(memset));
24930b93db5SMatthew Weingarten   uptr shadow_beg;
25030b93db5SMatthew Weingarten   uptr shadow_end;
25130b93db5SMatthew Weingarten   if (__memprof_histogram) {
25230b93db5SMatthew Weingarten     shadow_beg = HISTOGRAM_MEM_TO_SHADOW(addr);
25330b93db5SMatthew Weingarten     shadow_end = HISTOGRAM_MEM_TO_SHADOW(addr + size);
25430b93db5SMatthew Weingarten   } else {
25530b93db5SMatthew Weingarten     shadow_beg = MEM_TO_SHADOW(addr);
25630b93db5SMatthew Weingarten     shadow_end = MEM_TO_SHADOW(addr + size - SHADOW_GRANULARITY) + 1;
25730b93db5SMatthew Weingarten   }
25830b93db5SMatthew Weingarten 
2593d4bba30STeresa Johnson   if (shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
2603d4bba30STeresa Johnson     REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
2613d4bba30STeresa Johnson   } else {
2623d4bba30STeresa Johnson     uptr page_size = GetPageSizeCached();
2633d4bba30STeresa Johnson     uptr page_beg = RoundUpTo(shadow_beg, page_size);
2643d4bba30STeresa Johnson     uptr page_end = RoundDownTo(shadow_end, page_size);
2653d4bba30STeresa Johnson 
2663d4bba30STeresa Johnson     if (page_beg >= page_end) {
2673d4bba30STeresa Johnson       REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
2683d4bba30STeresa Johnson     } else {
2693d4bba30STeresa Johnson       if (page_beg != shadow_beg) {
2703d4bba30STeresa Johnson         REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg);
2713d4bba30STeresa Johnson       }
2723d4bba30STeresa Johnson       if (page_end != shadow_end) {
2733d4bba30STeresa Johnson         REAL(memset)((void *)page_end, 0, shadow_end - page_end);
2743d4bba30STeresa Johnson       }
2753d4bba30STeresa Johnson       ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
2763d4bba30STeresa Johnson     }
2773d4bba30STeresa Johnson   }
2783d4bba30STeresa Johnson }
2793d4bba30STeresa Johnson 
2803d4bba30STeresa Johnson struct Allocator {
2813d4bba30STeresa Johnson   static const uptr kMaxAllowedMallocSize = 1ULL << kMaxAllowedMallocBits;
2823d4bba30STeresa Johnson 
2833d4bba30STeresa Johnson   MemprofAllocator allocator;
2843d4bba30STeresa Johnson   StaticSpinMutex fallback_mutex;
2853d4bba30STeresa Johnson   AllocatorCache fallback_allocator_cache;
2863d4bba30STeresa Johnson 
2873d4bba30STeresa Johnson   uptr max_user_defined_malloc_size;
2883d4bba30STeresa Johnson 
2891243cef2SSnehasish Kumar   // Holds the mapping of stack ids to MemInfoBlocks.
2901243cef2SSnehasish Kumar   MIBMapTy MIBMap;
2911243cef2SSnehasish Kumar 
292545866cbSSnehasish Kumar   atomic_uint8_t destructing;
293545866cbSSnehasish Kumar   atomic_uint8_t constructed;
294545866cbSSnehasish Kumar   bool print_text;
2953d4bba30STeresa Johnson 
2963d4bba30STeresa Johnson   // ------------------- Initialization ------------------------
297545866cbSSnehasish Kumar   explicit Allocator(LinkerInitialized) : print_text(flags()->print_text) {
298545866cbSSnehasish Kumar     atomic_store_relaxed(&destructing, 0);
299545866cbSSnehasish Kumar     atomic_store_relaxed(&constructed, 1);
300545866cbSSnehasish Kumar   }
301545866cbSSnehasish Kumar 
302545866cbSSnehasish Kumar   ~Allocator() {
303545866cbSSnehasish Kumar     atomic_store_relaxed(&destructing, 1);
304*968e3b68SEllis Hoag     if (flags()->dump_at_exit)
305545866cbSSnehasish Kumar       FinishAndWrite();
306545866cbSSnehasish Kumar   }
3073d4bba30STeresa Johnson 
3081243cef2SSnehasish Kumar   static void PrintCallback(const uptr Key, LockedMemInfoBlock *const &Value,
3091243cef2SSnehasish Kumar                             void *Arg) {
310cf5c5372SSnehasish Kumar     SpinMutexLock l(&Value->mutex);
3118306968bSSnehasish Kumar     Print(Value->mib, Key, bool(Arg));
3121243cef2SSnehasish Kumar   }
3131243cef2SSnehasish Kumar 
31430b93db5SMatthew Weingarten   // See memprof_mapping.h for an overview on histogram counters.
31530b93db5SMatthew Weingarten   static MemInfoBlock CreateNewMIB(uptr p, MemprofChunk *m, u64 user_size) {
31630b93db5SMatthew Weingarten     if (__memprof_histogram) {
31730b93db5SMatthew Weingarten       return CreateNewMIBWithHistogram(p, m, user_size);
31830b93db5SMatthew Weingarten     } else {
31930b93db5SMatthew Weingarten       return CreateNewMIBWithoutHistogram(p, m, user_size);
32030b93db5SMatthew Weingarten     }
32130b93db5SMatthew Weingarten   }
32230b93db5SMatthew Weingarten 
32330b93db5SMatthew Weingarten   static MemInfoBlock CreateNewMIBWithHistogram(uptr p, MemprofChunk *m,
32430b93db5SMatthew Weingarten                                                 u64 user_size) {
32530b93db5SMatthew Weingarten 
32630b93db5SMatthew Weingarten     u64 c = GetShadowCountHistogram(p, user_size);
32730b93db5SMatthew Weingarten     long curtime = GetTimestamp();
32830b93db5SMatthew Weingarten     uint32_t HistogramSize =
32930b93db5SMatthew Weingarten         RoundUpTo(user_size, HISTOGRAM_GRANULARITY) / HISTOGRAM_GRANULARITY;
33030b93db5SMatthew Weingarten     uintptr_t Histogram =
33130b93db5SMatthew Weingarten         (uintptr_t)InternalAlloc(HistogramSize * sizeof(uint64_t));
33230b93db5SMatthew Weingarten     memset((void *)Histogram, 0, HistogramSize * sizeof(uint64_t));
33330b93db5SMatthew Weingarten     for (size_t i = 0; i < HistogramSize; ++i) {
33430b93db5SMatthew Weingarten       u8 Counter =
33530b93db5SMatthew Weingarten           *((u8 *)HISTOGRAM_MEM_TO_SHADOW(p + HISTOGRAM_GRANULARITY * i));
33630b93db5SMatthew Weingarten       ((uint64_t *)Histogram)[i] = (uint64_t)Counter;
33730b93db5SMatthew Weingarten     }
33830b93db5SMatthew Weingarten     MemInfoBlock newMIB(user_size, c, m->timestamp_ms, curtime, m->cpu_id,
33930b93db5SMatthew Weingarten                         GetCpuId(), Histogram, HistogramSize);
34030b93db5SMatthew Weingarten     return newMIB;
34130b93db5SMatthew Weingarten   }
34230b93db5SMatthew Weingarten 
34330b93db5SMatthew Weingarten   static MemInfoBlock CreateNewMIBWithoutHistogram(uptr p, MemprofChunk *m,
34430b93db5SMatthew Weingarten                                                    u64 user_size) {
34530b93db5SMatthew Weingarten     u64 c = GetShadowCount(p, user_size);
34630b93db5SMatthew Weingarten     long curtime = GetTimestamp();
34730b93db5SMatthew Weingarten     MemInfoBlock newMIB(user_size, c, m->timestamp_ms, curtime, m->cpu_id,
34830b93db5SMatthew Weingarten                         GetCpuId(), 0, 0);
34930b93db5SMatthew Weingarten     return newMIB;
35030b93db5SMatthew Weingarten   }
35130b93db5SMatthew Weingarten 
352545866cbSSnehasish Kumar   void FinishAndWrite() {
353545866cbSSnehasish Kumar     if (print_text && common_flags()->print_module_map)
3541243cef2SSnehasish Kumar       DumpProcessMap();
355545866cbSSnehasish Kumar 
3563d4bba30STeresa Johnson     allocator.ForceLock();
357545866cbSSnehasish Kumar 
358545866cbSSnehasish Kumar     InsertLiveBlocks();
359545866cbSSnehasish Kumar     if (print_text) {
360a4b92d61SSnehasish Kumar       if (!flags()->print_terse)
361a4b92d61SSnehasish Kumar         Printf("Recorded MIBs (incl. live on exit):\n");
362545866cbSSnehasish Kumar       MIBMap.ForEach(PrintCallback,
363545866cbSSnehasish Kumar                      reinterpret_cast<void *>(flags()->print_terse));
364545866cbSSnehasish Kumar       StackDepotPrintAll();
365545866cbSSnehasish Kumar     } else {
366545866cbSSnehasish Kumar       // Serialize the contents to a raw profile. Format documented in
367545866cbSSnehasish Kumar       // memprof_rawprofile.h.
368545866cbSSnehasish Kumar       char *Buffer = nullptr;
369545866cbSSnehasish Kumar 
370a1bbf5acSSnehasish Kumar       __sanitizer::ListOfModules List;
371a1bbf5acSSnehasish Kumar       List.init();
372a1bbf5acSSnehasish Kumar       ArrayRef<LoadedModule> Modules(List.begin(), List.end());
373a1bbf5acSSnehasish Kumar       u64 BytesSerialized = SerializeToRawProfile(MIBMap, Modules, Buffer);
374545866cbSSnehasish Kumar       CHECK(Buffer && BytesSerialized && "could not serialize to buffer");
375545866cbSSnehasish Kumar       report_file.Write(Buffer, BytesSerialized);
376545866cbSSnehasish Kumar     }
377545866cbSSnehasish Kumar 
378545866cbSSnehasish Kumar     allocator.ForceUnlock();
379545866cbSSnehasish Kumar   }
380545866cbSSnehasish Kumar 
381545866cbSSnehasish Kumar   // Inserts any blocks which have been allocated but not yet deallocated.
382545866cbSSnehasish Kumar   void InsertLiveBlocks() {
3833d4bba30STeresa Johnson     allocator.ForEachChunk(
3843d4bba30STeresa Johnson         [](uptr chunk, void *alloc) {
3853d4bba30STeresa Johnson           u64 user_requested_size;
3861243cef2SSnehasish Kumar           Allocator *A = (Allocator *)alloc;
3873d4bba30STeresa Johnson           MemprofChunk *m =
3881243cef2SSnehasish Kumar               A->GetMemprofChunk((void *)chunk, user_requested_size);
3893d4bba30STeresa Johnson           if (!m)
3903d4bba30STeresa Johnson             return;
3913d4bba30STeresa Johnson           uptr user_beg = ((uptr)m) + kChunkHeaderSize;
39230b93db5SMatthew Weingarten           MemInfoBlock newMIB = CreateNewMIB(user_beg, m, user_requested_size);
3931243cef2SSnehasish Kumar           InsertOrMerge(m->alloc_context_id, newMIB, A->MIBMap);
3943d4bba30STeresa Johnson         },
3953d4bba30STeresa Johnson         this);
3963d4bba30STeresa Johnson   }
3973d4bba30STeresa Johnson 
3983d4bba30STeresa Johnson   void InitLinkerInitialized() {
3993d4bba30STeresa Johnson     SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
4003d4bba30STeresa Johnson     allocator.InitLinkerInitialized(
4013d4bba30STeresa Johnson         common_flags()->allocator_release_to_os_interval_ms);
4023d4bba30STeresa Johnson     max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
4033d4bba30STeresa Johnson                                        ? common_flags()->max_allocation_size_mb
4043d4bba30STeresa Johnson                                              << 20
4053d4bba30STeresa Johnson                                        : kMaxAllowedMallocSize;
4063d4bba30STeresa Johnson   }
4073d4bba30STeresa Johnson 
4083d4bba30STeresa Johnson   // -------------------- Allocation/Deallocation routines ---------------
4093d4bba30STeresa Johnson   void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
4103d4bba30STeresa Johnson                  AllocType alloc_type) {
4113d4bba30STeresa Johnson     if (UNLIKELY(!memprof_inited))
4123d4bba30STeresa Johnson       MemprofInitFromRtl();
413d48d8670SVitaly Buka     if (UNLIKELY(IsRssLimitExceeded())) {
4143d4bba30STeresa Johnson       if (AllocatorMayReturnNull())
4153d4bba30STeresa Johnson         return nullptr;
4163d4bba30STeresa Johnson       ReportRssLimitExceeded(stack);
4173d4bba30STeresa Johnson     }
4183d4bba30STeresa Johnson     CHECK(stack);
4193d4bba30STeresa Johnson     const uptr min_alignment = MEMPROF_ALIGNMENT;
4203d4bba30STeresa Johnson     if (alignment < min_alignment)
4213d4bba30STeresa Johnson       alignment = min_alignment;
4223d4bba30STeresa Johnson     if (size == 0) {
4233d4bba30STeresa Johnson       // We'd be happy to avoid allocating memory for zero-size requests, but
4243d4bba30STeresa Johnson       // some programs/tests depend on this behavior and assume that malloc
4253d4bba30STeresa Johnson       // would not return NULL even for zero-size allocations. Moreover, it
4263d4bba30STeresa Johnson       // looks like operator new should never return NULL, and results of
4273d4bba30STeresa Johnson       // consecutive "new" calls must be different even if the allocated size
4283d4bba30STeresa Johnson       // is zero.
4293d4bba30STeresa Johnson       size = 1;
4303d4bba30STeresa Johnson     }
4313d4bba30STeresa Johnson     CHECK(IsPowerOfTwo(alignment));
4323d4bba30STeresa Johnson     uptr rounded_size = RoundUpTo(size, alignment);
4333d4bba30STeresa Johnson     uptr needed_size = rounded_size + kChunkHeaderSize;
4343d4bba30STeresa Johnson     if (alignment > min_alignment)
4353d4bba30STeresa Johnson       needed_size += alignment;
4363d4bba30STeresa Johnson     CHECK(IsAligned(needed_size, min_alignment));
4373d4bba30STeresa Johnson     if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize ||
4383d4bba30STeresa Johnson         size > max_user_defined_malloc_size) {
4393d4bba30STeresa Johnson       if (AllocatorMayReturnNull()) {
44024252474STeresa Johnson         Report("WARNING: MemProfiler failed to allocate 0x%zx bytes\n", size);
4413d4bba30STeresa Johnson         return nullptr;
4423d4bba30STeresa Johnson       }
4433d4bba30STeresa Johnson       uptr malloc_limit =
4443d4bba30STeresa Johnson           Min(kMaxAllowedMallocSize, max_user_defined_malloc_size);
4453d4bba30STeresa Johnson       ReportAllocationSizeTooBig(size, malloc_limit, stack);
4463d4bba30STeresa Johnson     }
4473d4bba30STeresa Johnson 
4483d4bba30STeresa Johnson     MemprofThread *t = GetCurrentThread();
4493d4bba30STeresa Johnson     void *allocated;
4503d4bba30STeresa Johnson     if (t) {
4513d4bba30STeresa Johnson       AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
4523d4bba30STeresa Johnson       allocated = allocator.Allocate(cache, needed_size, 8);
4533d4bba30STeresa Johnson     } else {
4543d4bba30STeresa Johnson       SpinMutexLock l(&fallback_mutex);
4553d4bba30STeresa Johnson       AllocatorCache *cache = &fallback_allocator_cache;
4563d4bba30STeresa Johnson       allocated = allocator.Allocate(cache, needed_size, 8);
4573d4bba30STeresa Johnson     }
4583d4bba30STeresa Johnson     if (UNLIKELY(!allocated)) {
4593d4bba30STeresa Johnson       SetAllocatorOutOfMemory();
4603d4bba30STeresa Johnson       if (AllocatorMayReturnNull())
4613d4bba30STeresa Johnson         return nullptr;
4623d4bba30STeresa Johnson       ReportOutOfMemory(size, stack);
4633d4bba30STeresa Johnson     }
4643d4bba30STeresa Johnson 
4653d4bba30STeresa Johnson     uptr alloc_beg = reinterpret_cast<uptr>(allocated);
4663d4bba30STeresa Johnson     uptr alloc_end = alloc_beg + needed_size;
4673d4bba30STeresa Johnson     uptr beg_plus_header = alloc_beg + kChunkHeaderSize;
4683d4bba30STeresa Johnson     uptr user_beg = beg_plus_header;
4693d4bba30STeresa Johnson     if (!IsAligned(user_beg, alignment))
4703d4bba30STeresa Johnson       user_beg = RoundUpTo(user_beg, alignment);
4713d4bba30STeresa Johnson     uptr user_end = user_beg + size;
4723d4bba30STeresa Johnson     CHECK_LE(user_end, alloc_end);
4733d4bba30STeresa Johnson     uptr chunk_beg = user_beg - kChunkHeaderSize;
4743d4bba30STeresa Johnson     MemprofChunk *m = reinterpret_cast<MemprofChunk *>(chunk_beg);
4753d4bba30STeresa Johnson     m->from_memalign = alloc_beg != chunk_beg;
4763d4bba30STeresa Johnson     CHECK(size);
4773d4bba30STeresa Johnson 
4783d4bba30STeresa Johnson     m->cpu_id = GetCpuId();
4793d4bba30STeresa Johnson     m->timestamp_ms = GetTimestamp();
4803d4bba30STeresa Johnson     m->alloc_context_id = StackDepotPut(*stack);
4813d4bba30STeresa Johnson 
4823d4bba30STeresa Johnson     uptr size_rounded_down_to_granularity =
4833d4bba30STeresa Johnson         RoundDownTo(size, SHADOW_GRANULARITY);
4843d4bba30STeresa Johnson     if (size_rounded_down_to_granularity)
4853d4bba30STeresa Johnson       ClearShadow(user_beg, size_rounded_down_to_granularity);
4863d4bba30STeresa Johnson 
4873d4bba30STeresa Johnson     MemprofStats &thread_stats = GetCurrentThreadStats();
4883d4bba30STeresa Johnson     thread_stats.mallocs++;
4893d4bba30STeresa Johnson     thread_stats.malloced += size;
4903d4bba30STeresa Johnson     thread_stats.malloced_overhead += needed_size - size;
4913d4bba30STeresa Johnson     if (needed_size > SizeClassMap::kMaxSize)
4923d4bba30STeresa Johnson       thread_stats.malloc_large++;
4933d4bba30STeresa Johnson     else
4943d4bba30STeresa Johnson       thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
4953d4bba30STeresa Johnson 
4963d4bba30STeresa Johnson     void *res = reinterpret_cast<void *>(user_beg);
4973d4bba30STeresa Johnson     atomic_store(&m->user_requested_size, size, memory_order_release);
4983d4bba30STeresa Johnson     if (alloc_beg != chunk_beg) {
4993d4bba30STeresa Johnson       CHECK_LE(alloc_beg + sizeof(LargeChunkHeader), chunk_beg);
5003d4bba30STeresa Johnson       reinterpret_cast<LargeChunkHeader *>(alloc_beg)->Set(m);
5013d4bba30STeresa Johnson     }
502b84673b3SVitaly Buka     RunMallocHooks(res, size);
5033d4bba30STeresa Johnson     return res;
5043d4bba30STeresa Johnson   }
5053d4bba30STeresa Johnson 
5063d4bba30STeresa Johnson   void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment,
5073d4bba30STeresa Johnson                   BufferedStackTrace *stack, AllocType alloc_type) {
5083d4bba30STeresa Johnson     uptr p = reinterpret_cast<uptr>(ptr);
5093d4bba30STeresa Johnson     if (p == 0)
5103d4bba30STeresa Johnson       return;
5113d4bba30STeresa Johnson 
512b84673b3SVitaly Buka     RunFreeHooks(ptr);
5133d4bba30STeresa Johnson 
5143d4bba30STeresa Johnson     uptr chunk_beg = p - kChunkHeaderSize;
5153d4bba30STeresa Johnson     MemprofChunk *m = reinterpret_cast<MemprofChunk *>(chunk_beg);
5163d4bba30STeresa Johnson 
5173d4bba30STeresa Johnson     u64 user_requested_size =
5183d4bba30STeresa Johnson         atomic_exchange(&m->user_requested_size, 0, memory_order_acquire);
51979ebb638SSnehasish Kumar     if (memprof_inited && atomic_load_relaxed(&constructed) &&
520545866cbSSnehasish Kumar         !atomic_load_relaxed(&destructing)) {
52130b93db5SMatthew Weingarten       MemInfoBlock newMIB = this->CreateNewMIB(p, m, user_requested_size);
5221243cef2SSnehasish Kumar       InsertOrMerge(m->alloc_context_id, newMIB, MIBMap);
5233d4bba30STeresa Johnson     }
5243d4bba30STeresa Johnson 
5253d4bba30STeresa Johnson     MemprofStats &thread_stats = GetCurrentThreadStats();
5263d4bba30STeresa Johnson     thread_stats.frees++;
5273d4bba30STeresa Johnson     thread_stats.freed += user_requested_size;
5283d4bba30STeresa Johnson 
5293d4bba30STeresa Johnson     void *alloc_beg = m->AllocBeg();
5303d4bba30STeresa Johnson     if (alloc_beg != m) {
5313d4bba30STeresa Johnson       // Clear the magic value, as allocator internals may overwrite the
5323d4bba30STeresa Johnson       // contents of deallocated chunk, confusing GetMemprofChunk lookup.
5333d4bba30STeresa Johnson       reinterpret_cast<LargeChunkHeader *>(alloc_beg)->Set(nullptr);
5343d4bba30STeresa Johnson     }
5353d4bba30STeresa Johnson 
5363d4bba30STeresa Johnson     MemprofThread *t = GetCurrentThread();
5373d4bba30STeresa Johnson     if (t) {
5383d4bba30STeresa Johnson       AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
5393d4bba30STeresa Johnson       allocator.Deallocate(cache, alloc_beg);
5403d4bba30STeresa Johnson     } else {
5413d4bba30STeresa Johnson       SpinMutexLock l(&fallback_mutex);
5423d4bba30STeresa Johnson       AllocatorCache *cache = &fallback_allocator_cache;
5433d4bba30STeresa Johnson       allocator.Deallocate(cache, alloc_beg);
5443d4bba30STeresa Johnson     }
5453d4bba30STeresa Johnson   }
5463d4bba30STeresa Johnson 
5473d4bba30STeresa Johnson   void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
5483d4bba30STeresa Johnson     CHECK(old_ptr && new_size);
5493d4bba30STeresa Johnson     uptr p = reinterpret_cast<uptr>(old_ptr);
5503d4bba30STeresa Johnson     uptr chunk_beg = p - kChunkHeaderSize;
5513d4bba30STeresa Johnson     MemprofChunk *m = reinterpret_cast<MemprofChunk *>(chunk_beg);
5523d4bba30STeresa Johnson 
5533d4bba30STeresa Johnson     MemprofStats &thread_stats = GetCurrentThreadStats();
5543d4bba30STeresa Johnson     thread_stats.reallocs++;
5553d4bba30STeresa Johnson     thread_stats.realloced += new_size;
5563d4bba30STeresa Johnson 
5573d4bba30STeresa Johnson     void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC);
5583d4bba30STeresa Johnson     if (new_ptr) {
5593d4bba30STeresa Johnson       CHECK_NE(REAL(memcpy), nullptr);
5603d4bba30STeresa Johnson       uptr memcpy_size = Min(new_size, m->UsedSize());
5613d4bba30STeresa Johnson       REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
5623d4bba30STeresa Johnson       Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC);
5633d4bba30STeresa Johnson     }
5643d4bba30STeresa Johnson     return new_ptr;
5653d4bba30STeresa Johnson   }
5663d4bba30STeresa Johnson 
5673d4bba30STeresa Johnson   void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
5683d4bba30STeresa Johnson     if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
5693d4bba30STeresa Johnson       if (AllocatorMayReturnNull())
5703d4bba30STeresa Johnson         return nullptr;
5713d4bba30STeresa Johnson       ReportCallocOverflow(nmemb, size, stack);
5723d4bba30STeresa Johnson     }
5733d4bba30STeresa Johnson     void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC);
5743d4bba30STeresa Johnson     // If the memory comes from the secondary allocator no need to clear it
5753d4bba30STeresa Johnson     // as it comes directly from mmap.
5763d4bba30STeresa Johnson     if (ptr && allocator.FromPrimary(ptr))
5773d4bba30STeresa Johnson       REAL(memset)(ptr, 0, nmemb * size);
5783d4bba30STeresa Johnson     return ptr;
5793d4bba30STeresa Johnson   }
5803d4bba30STeresa Johnson 
581bd132411SEnna1   void CommitBack(MemprofThreadLocalMallocStorage *ms) {
5823d4bba30STeresa Johnson     AllocatorCache *ac = GetAllocatorCache(ms);
5833d4bba30STeresa Johnson     allocator.SwallowCache(ac);
5843d4bba30STeresa Johnson   }
5853d4bba30STeresa Johnson 
5863d4bba30STeresa Johnson   // -------------------------- Chunk lookup ----------------------
5873d4bba30STeresa Johnson 
5883d4bba30STeresa Johnson   // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
5893d4bba30STeresa Johnson   MemprofChunk *GetMemprofChunk(void *alloc_beg, u64 &user_requested_size) {
5903d4bba30STeresa Johnson     if (!alloc_beg)
5913d4bba30STeresa Johnson       return nullptr;
5923d4bba30STeresa Johnson     MemprofChunk *p = reinterpret_cast<LargeChunkHeader *>(alloc_beg)->Get();
5933d4bba30STeresa Johnson     if (!p) {
5943d4bba30STeresa Johnson       if (!allocator.FromPrimary(alloc_beg))
5953d4bba30STeresa Johnson         return nullptr;
5963d4bba30STeresa Johnson       p = reinterpret_cast<MemprofChunk *>(alloc_beg);
5973d4bba30STeresa Johnson     }
5983d4bba30STeresa Johnson     // The size is reset to 0 on deallocation (and a min of 1 on
5993d4bba30STeresa Johnson     // allocation).
6003d4bba30STeresa Johnson     user_requested_size =
6013d4bba30STeresa Johnson         atomic_load(&p->user_requested_size, memory_order_acquire);
6023d4bba30STeresa Johnson     if (user_requested_size)
6033d4bba30STeresa Johnson       return p;
6043d4bba30STeresa Johnson     return nullptr;
6053d4bba30STeresa Johnson   }
6063d4bba30STeresa Johnson 
6073d4bba30STeresa Johnson   MemprofChunk *GetMemprofChunkByAddr(uptr p, u64 &user_requested_size) {
6083d4bba30STeresa Johnson     void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
6093d4bba30STeresa Johnson     return GetMemprofChunk(alloc_beg, user_requested_size);
6103d4bba30STeresa Johnson   }
6113d4bba30STeresa Johnson 
6123d4bba30STeresa Johnson   uptr AllocationSize(uptr p) {
6133d4bba30STeresa Johnson     u64 user_requested_size;
6143d4bba30STeresa Johnson     MemprofChunk *m = GetMemprofChunkByAddr(p, user_requested_size);
6153d4bba30STeresa Johnson     if (!m)
6163d4bba30STeresa Johnson       return 0;
6173d4bba30STeresa Johnson     if (m->Beg() != p)
6183d4bba30STeresa Johnson       return 0;
6193d4bba30STeresa Johnson     return user_requested_size;
6203d4bba30STeresa Johnson   }
6213d4bba30STeresa Johnson 
6227639265aSJin Xin Ng   uptr AllocationSizeFast(uptr p) {
6237639265aSJin Xin Ng     return reinterpret_cast<MemprofChunk *>(p - kChunkHeaderSize)->UsedSize();
6247639265aSJin Xin Ng   }
6257639265aSJin Xin Ng 
626bd132411SEnna1   void Purge() { allocator.ForceReleaseToOS(); }
6273d4bba30STeresa Johnson 
6283d4bba30STeresa Johnson   void PrintStats() { allocator.PrintStats(); }
6293d4bba30STeresa Johnson 
630765921deSDmitry Vyukov   void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
6313d4bba30STeresa Johnson     allocator.ForceLock();
6323d4bba30STeresa Johnson     fallback_mutex.Lock();
6333d4bba30STeresa Johnson   }
6343d4bba30STeresa Johnson 
635765921deSDmitry Vyukov   void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
6363d4bba30STeresa Johnson     fallback_mutex.Unlock();
6373d4bba30STeresa Johnson     allocator.ForceUnlock();
6383d4bba30STeresa Johnson   }
6393d4bba30STeresa Johnson };
6403d4bba30STeresa Johnson 
6413d4bba30STeresa Johnson static Allocator instance(LINKER_INITIALIZED);
6423d4bba30STeresa Johnson 
6433d4bba30STeresa Johnson static MemprofAllocator &get_allocator() { return instance.allocator; }
6443d4bba30STeresa Johnson 
6453d4bba30STeresa Johnson void InitializeAllocator() { instance.InitLinkerInitialized(); }
6463d4bba30STeresa Johnson 
6473d4bba30STeresa Johnson void MemprofThreadLocalMallocStorage::CommitBack() {
648bd132411SEnna1   instance.CommitBack(this);
6493d4bba30STeresa Johnson }
6503d4bba30STeresa Johnson 
6513d4bba30STeresa Johnson void PrintInternalAllocatorStats() { instance.PrintStats(); }
6523d4bba30STeresa Johnson 
6533d4bba30STeresa Johnson void memprof_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
6543d4bba30STeresa Johnson   instance.Deallocate(ptr, 0, 0, stack, alloc_type);
6553d4bba30STeresa Johnson }
6563d4bba30STeresa Johnson 
6573d4bba30STeresa Johnson void memprof_delete(void *ptr, uptr size, uptr alignment,
6583d4bba30STeresa Johnson                     BufferedStackTrace *stack, AllocType alloc_type) {
6593d4bba30STeresa Johnson   instance.Deallocate(ptr, size, alignment, stack, alloc_type);
6603d4bba30STeresa Johnson }
6613d4bba30STeresa Johnson 
6623d4bba30STeresa Johnson void *memprof_malloc(uptr size, BufferedStackTrace *stack) {
6633d4bba30STeresa Johnson   return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC));
6643d4bba30STeresa Johnson }
6653d4bba30STeresa Johnson 
6663d4bba30STeresa Johnson void *memprof_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
6673d4bba30STeresa Johnson   return SetErrnoOnNull(instance.Calloc(nmemb, size, stack));
6683d4bba30STeresa Johnson }
6693d4bba30STeresa Johnson 
6703d4bba30STeresa Johnson void *memprof_reallocarray(void *p, uptr nmemb, uptr size,
6713d4bba30STeresa Johnson                            BufferedStackTrace *stack) {
6723d4bba30STeresa Johnson   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
6733d4bba30STeresa Johnson     errno = errno_ENOMEM;
6743d4bba30STeresa Johnson     if (AllocatorMayReturnNull())
6753d4bba30STeresa Johnson       return nullptr;
6763d4bba30STeresa Johnson     ReportReallocArrayOverflow(nmemb, size, stack);
6773d4bba30STeresa Johnson   }
6783d4bba30STeresa Johnson   return memprof_realloc(p, nmemb * size, stack);
6793d4bba30STeresa Johnson }
6803d4bba30STeresa Johnson 
6813d4bba30STeresa Johnson void *memprof_realloc(void *p, uptr size, BufferedStackTrace *stack) {
6823d4bba30STeresa Johnson   if (!p)
6833d4bba30STeresa Johnson     return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC));
6843d4bba30STeresa Johnson   if (size == 0) {
6853d4bba30STeresa Johnson     if (flags()->allocator_frees_and_returns_null_on_realloc_zero) {
6863d4bba30STeresa Johnson       instance.Deallocate(p, 0, 0, stack, FROM_MALLOC);
6873d4bba30STeresa Johnson       return nullptr;
6883d4bba30STeresa Johnson     }
6893d4bba30STeresa Johnson     // Allocate a size of 1 if we shouldn't free() on Realloc to 0
6903d4bba30STeresa Johnson     size = 1;
6913d4bba30STeresa Johnson   }
6923d4bba30STeresa Johnson   return SetErrnoOnNull(instance.Reallocate(p, size, stack));
6933d4bba30STeresa Johnson }
6943d4bba30STeresa Johnson 
6953d4bba30STeresa Johnson void *memprof_valloc(uptr size, BufferedStackTrace *stack) {
6963d4bba30STeresa Johnson   return SetErrnoOnNull(
6973d4bba30STeresa Johnson       instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC));
6983d4bba30STeresa Johnson }
6993d4bba30STeresa Johnson 
7003d4bba30STeresa Johnson void *memprof_pvalloc(uptr size, BufferedStackTrace *stack) {
7013d4bba30STeresa Johnson   uptr PageSize = GetPageSizeCached();
7023d4bba30STeresa Johnson   if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
7033d4bba30STeresa Johnson     errno = errno_ENOMEM;
7043d4bba30STeresa Johnson     if (AllocatorMayReturnNull())
7053d4bba30STeresa Johnson       return nullptr;
7063d4bba30STeresa Johnson     ReportPvallocOverflow(size, stack);
7073d4bba30STeresa Johnson   }
7083d4bba30STeresa Johnson   // pvalloc(0) should allocate one page.
7093d4bba30STeresa Johnson   size = size ? RoundUpTo(size, PageSize) : PageSize;
7103d4bba30STeresa Johnson   return SetErrnoOnNull(instance.Allocate(size, PageSize, stack, FROM_MALLOC));
7113d4bba30STeresa Johnson }
7123d4bba30STeresa Johnson 
7133d4bba30STeresa Johnson void *memprof_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
7143d4bba30STeresa Johnson                        AllocType alloc_type) {
7153d4bba30STeresa Johnson   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
7163d4bba30STeresa Johnson     errno = errno_EINVAL;
7173d4bba30STeresa Johnson     if (AllocatorMayReturnNull())
7183d4bba30STeresa Johnson       return nullptr;
7193d4bba30STeresa Johnson     ReportInvalidAllocationAlignment(alignment, stack);
7203d4bba30STeresa Johnson   }
7213d4bba30STeresa Johnson   return SetErrnoOnNull(instance.Allocate(size, alignment, stack, alloc_type));
7223d4bba30STeresa Johnson }
7233d4bba30STeresa Johnson 
7243d4bba30STeresa Johnson void *memprof_aligned_alloc(uptr alignment, uptr size,
7253d4bba30STeresa Johnson                             BufferedStackTrace *stack) {
7263d4bba30STeresa Johnson   if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
7273d4bba30STeresa Johnson     errno = errno_EINVAL;
7283d4bba30STeresa Johnson     if (AllocatorMayReturnNull())
7293d4bba30STeresa Johnson       return nullptr;
7303d4bba30STeresa Johnson     ReportInvalidAlignedAllocAlignment(size, alignment, stack);
7313d4bba30STeresa Johnson   }
7323d4bba30STeresa Johnson   return SetErrnoOnNull(instance.Allocate(size, alignment, stack, FROM_MALLOC));
7333d4bba30STeresa Johnson }
7343d4bba30STeresa Johnson 
7353d4bba30STeresa Johnson int memprof_posix_memalign(void **memptr, uptr alignment, uptr size,
7363d4bba30STeresa Johnson                            BufferedStackTrace *stack) {
7373d4bba30STeresa Johnson   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
7383d4bba30STeresa Johnson     if (AllocatorMayReturnNull())
7393d4bba30STeresa Johnson       return errno_EINVAL;
7403d4bba30STeresa Johnson     ReportInvalidPosixMemalignAlignment(alignment, stack);
7413d4bba30STeresa Johnson   }
7423d4bba30STeresa Johnson   void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC);
7433d4bba30STeresa Johnson   if (UNLIKELY(!ptr))
7443d4bba30STeresa Johnson     // OOM error is already taken care of by Allocate.
7453d4bba30STeresa Johnson     return errno_ENOMEM;
7463d4bba30STeresa Johnson   CHECK(IsAligned((uptr)ptr, alignment));
7473d4bba30STeresa Johnson   *memptr = ptr;
7483d4bba30STeresa Johnson   return 0;
7493d4bba30STeresa Johnson }
7503d4bba30STeresa Johnson 
7518c63dc6fSFangrui Song static const void *memprof_malloc_begin(const void *p) {
752415b1cfdSThurston Dang   u64 user_requested_size;
753415b1cfdSThurston Dang   MemprofChunk *m =
754415b1cfdSThurston Dang       instance.GetMemprofChunkByAddr((uptr)p, user_requested_size);
755415b1cfdSThurston Dang   if (!m)
756415b1cfdSThurston Dang     return nullptr;
757415b1cfdSThurston Dang   if (user_requested_size == 0)
758415b1cfdSThurston Dang     return nullptr;
759415b1cfdSThurston Dang 
760d644ab02SThurston Dang   return (const void *)m->Beg();
761415b1cfdSThurston Dang }
762415b1cfdSThurston Dang 
763bd132411SEnna1 uptr memprof_malloc_usable_size(const void *ptr) {
7643d4bba30STeresa Johnson   if (!ptr)
7653d4bba30STeresa Johnson     return 0;
7663d4bba30STeresa Johnson   uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
7673d4bba30STeresa Johnson   return usable_size;
7683d4bba30STeresa Johnson }
7693d4bba30STeresa Johnson 
7703d4bba30STeresa Johnson } // namespace __memprof
7713d4bba30STeresa Johnson 
7723d4bba30STeresa Johnson // ---------------------- Interface ---------------- {{{1
7733d4bba30STeresa Johnson using namespace __memprof;
7743d4bba30STeresa Johnson 
7753d4bba30STeresa Johnson uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
7763d4bba30STeresa Johnson 
7773d4bba30STeresa Johnson int __sanitizer_get_ownership(const void *p) {
778bd132411SEnna1   return memprof_malloc_usable_size(p) != 0;
7793d4bba30STeresa Johnson }
7803d4bba30STeresa Johnson 
781d644ab02SThurston Dang const void *__sanitizer_get_allocated_begin(const void *p) {
782415b1cfdSThurston Dang   return memprof_malloc_begin(p);
783415b1cfdSThurston Dang }
784415b1cfdSThurston Dang 
7853d4bba30STeresa Johnson uptr __sanitizer_get_allocated_size(const void *p) {
786bd132411SEnna1   return memprof_malloc_usable_size(p);
7873d4bba30STeresa Johnson }
788a75b2e87STeresa Johnson 
7897639265aSJin Xin Ng uptr __sanitizer_get_allocated_size_fast(const void *p) {
7907639265aSJin Xin Ng   DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
7917639265aSJin Xin Ng   uptr ret = instance.AllocationSizeFast(reinterpret_cast<uptr>(p));
7927639265aSJin Xin Ng   DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
7937639265aSJin Xin Ng   return ret;
7947639265aSJin Xin Ng }
7957639265aSJin Xin Ng 
796bd132411SEnna1 void __sanitizer_purge_allocator() { instance.Purge(); }
797bd132411SEnna1 
798a75b2e87STeresa Johnson int __memprof_profile_dump() {
799545866cbSSnehasish Kumar   instance.FinishAndWrite();
800a75b2e87STeresa Johnson   // In the future we may want to return non-zero if there are any errors
801a75b2e87STeresa Johnson   // detected during the dumping process.
802a75b2e87STeresa Johnson   return 0;
803a75b2e87STeresa Johnson }
804ae86239eSTeresa Johnson 
805ae86239eSTeresa Johnson void __memprof_profile_reset() {
806ae86239eSTeresa Johnson   if (report_file.fd != kInvalidFd && report_file.fd != kStdoutFd &&
807ae86239eSTeresa Johnson       report_file.fd != kStderrFd) {
808ae86239eSTeresa Johnson     CloseFile(report_file.fd);
809ae86239eSTeresa Johnson     // Setting the file descriptor to kInvalidFd ensures that we will reopen the
810ae86239eSTeresa Johnson     // file when invoking Write again.
811ae86239eSTeresa Johnson     report_file.fd = kInvalidFd;
812ae86239eSTeresa Johnson   }
813ae86239eSTeresa Johnson }
814