[clang] Support MemProf on darwin (PR #69640)

Manman Ren via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 19 14:13:13 PDT 2023


https://github.com/manman-ren created https://github.com/llvm/llvm-project/pull/69640

None

>From 86dc0db55bf6aa629639b1beac5c2cf5f39177ec Mon Sep 17 00:00:00 2001
From: Manman Ren <mren at fb.com>
Date: Tue, 10 Oct 2023 20:54:07 -0700
Subject: [PATCH 1/2] memprof support on Darwin

---
 clang/lib/Driver/ToolChains/Darwin.cpp        |  3 +
 clang/test/Driver/darwin-memprof.c            | 13 +++++
 .../cmake/Modules/AllSupportedArchDefs.cmake  |  2 +-
 compiler-rt/cmake/config-ix.cmake             |  4 +-
 compiler-rt/lib/memprof/CMakeLists.txt        | 30 +++++++++-
 compiler-rt/lib/memprof/memprof_allocator.cpp |  4 ++
 compiler-rt/lib/memprof/memprof_allocator.h   | 25 +++++---
 .../lib/memprof/memprof_interceptors.cpp      |  5 +-
 .../lib/memprof/memprof_interceptors.h        |  6 ++
 compiler-rt/lib/memprof/memprof_linux.cpp     |  5 +-
 compiler-rt/lib/memprof/memprof_mac.cpp       | 58 +++++++++++++++++++
 .../lib/memprof/memprof_malloc_linux.cpp      |  5 +-
 .../lib/memprof/memprof_malloc_mac.cpp        | 37 ++++++++++++
 .../lib/memprof/memprof_new_delete.cpp        |  4 ++
 compiler-rt/lib/memprof/tests/CMakeLists.txt  |  2 +-
 compiler-rt/test/memprof/CMakeLists.txt       |  2 +-
 16 files changed, 184 insertions(+), 21 deletions(-)
 create mode 100644 clang/test/Driver/darwin-memprof.c
 create mode 100644 compiler-rt/lib/memprof/memprof_mac.cpp
 create mode 100644 compiler-rt/lib/memprof/memprof_malloc_mac.cpp

diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
index 15b9889157b903c..e3a7a29e196e435 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -1505,6 +1505,9 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
              "Static sanitizer runtimes not supported");
       AddLinkSanitizerLibArgs(Args, CmdArgs, "tsan");
     }
+    if (Sanitize.needsMemProfRt()) {
+      AddLinkSanitizerLibArgs(Args, CmdArgs, "memprof");
+    }
     if (Sanitize.needsFuzzer() && !Args.hasArg(options::OPT_dynamiclib)) {
       AddLinkSanitizerLibArgs(Args, CmdArgs, "fuzzer", /*shared=*/false);
 
diff --git a/clang/test/Driver/darwin-memprof.c b/clang/test/Driver/darwin-memprof.c
new file mode 100644
index 000000000000000..67746d88823b4a8
--- /dev/null
+++ b/clang/test/Driver/darwin-memprof.c
@@ -0,0 +1,13 @@
+// Test sanitizer link flags on Darwin.
+
+// RUN: %clang -### --target=x86_64-darwin \
+// RUN:   -stdlib=platform -fmemory-profile %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-MEMPROF %s
+
+// CHECK-MEMPROF: "{{.*}}ld{{(.exe)?}}"
+// CHECK-MEMPROF-NOT: "-lstdc++"
+// CHECK-MEMPROF-NOT: "-lc++"
+// CHECK-MEMPROF: libclang_rt.memprof_osx_dynamic.dylib"
+// CHECK-MEMPROF: "-rpath" "@executable_path"
+// CHECK-MEMPROF: "-rpath" "{{.*}}lib{{.*}}darwin"
+
diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
index 416777171d2ca75..e34f4d8804ddcb8 100644
--- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
+++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
@@ -61,7 +61,7 @@ endif()
 set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64} ${S390X}
     ${LOONGARCH64})
 set(ALL_HWASAN_SUPPORTED_ARCH ${X86_64} ${ARM64} ${RISCV64})
-set(ALL_MEMPROF_SUPPORTED_ARCH ${X86_64})
+set(ALL_MEMPROF_SUPPORTED_ARCH ${X86_64} ${ARM64})
 set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} ${PPC64}
     ${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
     ${RISCV32} ${RISCV64} ${LOONGARCH64})
diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
index a8e078f1ebc9888..ebf62b2fdb5e8ee 100644
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -446,6 +446,7 @@ if(APPLE)
   endif()
 
   set(SANITIZER_COMMON_SUPPORTED_OS osx)
+  set(MEMPROF_SUPPORTED_OS osx)
   set(PROFILE_SUPPORTED_OS osx)
   set(TSAN_SUPPORTED_OS osx)
   set(XRAY_SUPPORTED_OS osx)
@@ -566,6 +567,7 @@ if(APPLE)
         message(STATUS "${platform} supported arches: ${DARWIN_${platform}_ARCHS}")
         if(DARWIN_${platform}_ARCHS)
           list(APPEND SANITIZER_COMMON_SUPPORTED_OS ${platform})
+          list(APPEND MEMPROF_SUPPORTED_OS ${platform})
           list(APPEND PROFILE_SUPPORTED_OS ${platform})
 
           list_intersect(DARWIN_${platform}_TSAN_ARCHS DARWIN_${platform}_ARCHS ALL_TSAN_SUPPORTED_ARCH)
@@ -782,7 +784,7 @@ else()
 endif()
 
 if (COMPILER_RT_HAS_SANITIZER_COMMON AND MEMPROF_SUPPORTED_ARCH AND
-    OS_NAME MATCHES "Linux")
+    OS_NAME MATCHES "Darwin|Linux")
   set(COMPILER_RT_HAS_MEMPROF TRUE)
 else()
   set(COMPILER_RT_HAS_MEMPROF FALSE)
diff --git a/compiler-rt/lib/memprof/CMakeLists.txt b/compiler-rt/lib/memprof/CMakeLists.txt
index 3f55c2f5e075eed..b8bbcc55da11866 100644
--- a/compiler-rt/lib/memprof/CMakeLists.txt
+++ b/compiler-rt/lib/memprof/CMakeLists.txt
@@ -7,7 +7,9 @@ set(MEMPROF_SOURCES
   memprof_interceptors.cpp
   memprof_interceptors_memintrinsics.cpp
   memprof_linux.cpp
+  memprof_mac.cpp
   memprof_malloc_linux.cpp
+  memprof_malloc_mac.cpp
   memprof_mibmap.cpp
   memprof_posix.cpp
   memprof_rawprofile.cpp
@@ -79,7 +81,7 @@ append_list_if(COMPILER_RT_HAS_LIBLOG log MEMPROF_DYNAMIC_LIBS)
 # Compile MemProf sources into an object library.
 
 add_compiler_rt_object_libraries(RTMemprof_dynamic
-  OS ${SANITIZER_COMMON_SUPPORTED_OS}
+  OS ${MEMPROF_SUPPORTED_OS}
   ARCHS ${MEMPROF_SUPPORTED_ARCH}
   SOURCES ${MEMPROF_SOURCES} ${MEMPROF_CXX_SOURCES}
   ADDITIONAL_HEADERS ${MEMPROF_HEADERS}
@@ -87,6 +89,7 @@ add_compiler_rt_object_libraries(RTMemprof_dynamic
   DEFS ${MEMPROF_DYNAMIC_DEFINITIONS}
   DEPS ${MEMPROF_DEPS})
 
+if(NOT APPLE)
 add_compiler_rt_object_libraries(RTMemprof
   ARCHS ${MEMPROF_SUPPORTED_ARCH}
   SOURCES ${MEMPROF_SOURCES}
@@ -116,10 +119,31 @@ add_compiler_rt_object_libraries(RTMemprof_dynamic_version_script_dummy
   CFLAGS ${MEMPROF_DYNAMIC_CFLAGS}
   DEFS ${MEMPROF_DYNAMIC_DEFINITIONS}
   DEPS ${MEMPROF_DEPS})
+endif()
 
 # Build MemProf runtimes shipped with Clang.
 add_compiler_rt_component(memprof)
-
+if(APPLE)
+  add_weak_symbols("asan" WEAK_SYMBOL_LINK_FLAGS)
+  add_weak_symbols("lsan" WEAK_SYMBOL_LINK_FLAGS)
+  add_weak_symbols("ubsan" WEAK_SYMBOL_LINK_FLAGS)
+  add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
+  add_weak_symbols("xray" WEAK_SYMBOL_LINK_FLAGS)
+  add_compiler_rt_runtime(clang_rt.memprof
+    SHARED
+    OS ${MEMPROF_SUPPORTED_OS}
+    ARCHS ${MEMPROF_SUPPORTED_ARCH}
+    OBJECT_LIBS RTMemprof_dynamic
+                RTInterception
+                RTSanitizerCommon
+                RTSanitizerCommonLibc
+                RTSanitizerCommonCoverage
+                RTSanitizerCommonSymbolizer
+    CFLAGS ${MEMPROF_DYNAMIC_CFLAGS}
+    LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS} # copied from asan, non-APPLE uses MEMPROF_DYNAMIC_LINK_FLAGS
+    DEFS ${MEMPROF_DYNAMIC_DEFINITIONS}
+    PARENT_TARGET memprof)
+else()
 # Build separate libraries for each target.
 
 set(MEMPROF_COMMON_RUNTIME_OBJECT_LIBS
@@ -204,7 +228,7 @@ foreach(arch ${MEMPROF_SUPPORTED_ARCH})
     add_dependencies(memprof clang_rt.memprof-${arch}-symbols)
   endif()
 endforeach()
-
+endif()
 
 if(COMPILER_RT_INCLUDE_TESTS)
   add_subdirectory(tests)
diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp
index efdfa5ad04a6917..aeaa458ef0d9b4f 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.cpp
+++ b/compiler-rt/lib/memprof/memprof_allocator.cpp
@@ -78,7 +78,11 @@ static int GetCpuId(void) {
   // will seg fault as the address of __vdso_getcpu will be null.
   if (!memprof_inited)
     return -1;
+#if SANITIZER_APPLE
+  return 0;
+#else
   return sched_getcpu();
+#endif
 }
 
 // Compute the timestamp in ms.
diff --git a/compiler-rt/lib/memprof/memprof_allocator.h b/compiler-rt/lib/memprof/memprof_allocator.h
index 14c61c7325e3f03..3f379825059936b 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.h
+++ b/compiler-rt/lib/memprof/memprof_allocator.h
@@ -20,13 +20,6 @@
 #include "sanitizer_common/sanitizer_allocator.h"
 #include "sanitizer_common/sanitizer_list.h"
 
-#if !defined(__x86_64__)
-#error Unsupported platform
-#endif
-#if !SANITIZER_CAN_USE_ALLOCATOR64
-#error Only 64-bit allocator supported
-#endif
-
 namespace __memprof {
 
 enum AllocType {
@@ -46,6 +39,7 @@ struct MemprofMapUnmapCallback {
   void OnUnmap(uptr p, uptr size) const;
 };
 
+#if SANITIZER_CAN_USE_ALLOCATOR64
 constexpr uptr kAllocatorSpace = 0x600000000000ULL;
 constexpr uptr kAllocatorSize = 0x40000000000ULL; // 4T.
 typedef DefaultSizeClassMap SizeClassMap;
@@ -63,6 +57,23 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
 template <typename AddressSpaceView>
 using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
 using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
+#endif
+#if !SANITIZER_CAN_USE_ALLOCATOR64
+typedef CompactSizeClassMap SizeClassMap;
+template <typename AddressSpaceViewTy> struct AP32 {
+  static const uptr kSpaceBeg = 0;
+  static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
+  static const uptr kMetadataSize = 0;
+  typedef __memprof::SizeClassMap SizeClassMap;
+  static const uptr kRegionSizeLog = 20;
+  using AddressSpaceView = AddressSpaceViewTy;
+  typedef MemprofMapUnmapCallback MapUnmapCallback;
+  static const uptr kFlags = 0;
+};
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
+#endif
 
 static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
 
diff --git a/compiler-rt/lib/memprof/memprof_interceptors.cpp b/compiler-rt/lib/memprof/memprof_interceptors.cpp
index 8925ec5bbaa3728..35d7621e61dc3f8 100644
--- a/compiler-rt/lib/memprof/memprof_interceptors.cpp
+++ b/compiler-rt/lib/memprof/memprof_interceptors.cpp
@@ -131,6 +131,7 @@ static thread_return_t THREAD_CALLING_CONV memprof_thread_start(void *arg) {
   return t->ThreadStart(GetTid(), &param->is_registered);
 }
 
+#if !defined(SANITIZER_APPLE)
 INTERCEPTOR(int, pthread_create, void *thread, void *attr,
             void *(*start_routine)(void *), void *arg) {
   EnsureMainThreadIDIsCorrect();
@@ -305,6 +306,7 @@ INTERCEPTOR(long long, atoll, const char *nptr) {
   MEMPROF_READ_STRING(nptr, (real_endptr - nptr) + 1);
   return result;
 }
+#endif
 
 // ---------------------- InitializeMemprofInterceptors ---------------- {{{1
 namespace __memprof {
@@ -314,6 +316,7 @@ void InitializeMemprofInterceptors() {
   was_called_once = true;
   InitializeCommonInterceptors();
 
+#if !defined(SANITIZER_APPLE)
   // Intercept str* functions.
   MEMPROF_INTERCEPT_FUNC(strcat);
   MEMPROF_INTERCEPT_FUNC(strcpy);
@@ -322,7 +325,6 @@ void InitializeMemprofInterceptors() {
   MEMPROF_INTERCEPT_FUNC(strdup);
   MEMPROF_INTERCEPT_FUNC(__strdup);
   MEMPROF_INTERCEPT_FUNC(index);
-
   MEMPROF_INTERCEPT_FUNC(atoi);
   MEMPROF_INTERCEPT_FUNC(atol);
   MEMPROF_INTERCEPT_FUNC(strtol);
@@ -332,6 +334,7 @@ void InitializeMemprofInterceptors() {
   // Intercept threading-related functions
   MEMPROF_INTERCEPT_FUNC(pthread_create);
   MEMPROF_INTERCEPT_FUNC(pthread_join);
+#endif
 
   InitializePlatformInterceptors();
 
diff --git a/compiler-rt/lib/memprof/memprof_interceptors.h b/compiler-rt/lib/memprof/memprof_interceptors.h
index 20edef42a5150b5..1021c15aff1183c 100644
--- a/compiler-rt/lib/memprof/memprof_interceptors.h
+++ b/compiler-rt/lib/memprof/memprof_interceptors.h
@@ -40,6 +40,7 @@ DECLARE_REAL(char *, strncpy, char *to, const char *from, uptr size)
 DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
 DECLARE_REAL(char *, strstr, const char *s1, const char *s2)
 
+#if !SANITIZER_APPLE
 #define MEMPROF_INTERCEPT_FUNC(name)                                           \
   do {                                                                         \
     if (!INTERCEPT_FUNCTION(name))                                             \
@@ -56,6 +57,11 @@ DECLARE_REAL(char *, strstr, const char *s1, const char *s2)
       VReport(1, "MemProfiler: failed to intercept '%s@@%s' or '%s'\n", #name, \
               ver, #name);                                                     \
   } while (0)
+#endif
+#if SANITIZER_APPLE
+// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
+#define MEMPROF_INTERCEPT_FUNC(name)
+#endif // SANITIZER_APPLE
 
 #define MEMPROF_INTERCEPTOR_ENTER(ctx, func)                                   \
   ctx = 0;                                                                     \
diff --git a/compiler-rt/lib/memprof/memprof_linux.cpp b/compiler-rt/lib/memprof/memprof_linux.cpp
index fcd927023f5c3de..09122864a1a16b1 100644
--- a/compiler-rt/lib/memprof/memprof_linux.cpp
+++ b/compiler-rt/lib/memprof/memprof_linux.cpp
@@ -12,9 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "sanitizer_common/sanitizer_platform.h"
-#if !SANITIZER_LINUX
-#error Unsupported OS
-#endif
+#if SANITIZER_LINUX
 
 #include "memprof_interceptors.h"
 #include "memprof_internal.h"
@@ -72,3 +70,4 @@ uptr FindDynamicShadowStart() {
 void *MemprofDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, sym); }
 
 } // namespace __memprof
+#endif
diff --git a/compiler-rt/lib/memprof/memprof_mac.cpp b/compiler-rt/lib/memprof/memprof_mac.cpp
new file mode 100644
index 000000000000000..71439501bb11f50
--- /dev/null
+++ b/compiler-rt/lib/memprof/memprof_mac.cpp
@@ -0,0 +1,58 @@
+//===-- memprof_mac.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemProfiler, a memory profiler.
+//
+// Mac-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_APPLE
+
+#include "memprof_interceptors.h"
+#include "memprof_internal.h"
+#include "memprof_thread.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_freebsd.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+#include <unwind.h>
+
+namespace __memprof {
+
+void InitializePlatformInterceptors() {}
+void InitializePlatformExceptionHandlers() {}
+
+// No-op. Mac does not support static linkage anyway.
+void *MemprofDoesNotSupportStaticLinkage() {
+  return 0;
+}
+
+uptr FindDynamicShadowStart() {
+  uptr shadow_size_bytes = MemToShadowSize(kHighMemEnd);
+  return MapDynamicShadow(shadow_size_bytes, SHADOW_SCALE,
+                          /*min_shadow_base_alignment*/ 0, kHighMemEnd);
+}
+
+void *MemprofDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, sym); }
+
+} // namespace __memprof
+#endif
diff --git a/compiler-rt/lib/memprof/memprof_malloc_linux.cpp b/compiler-rt/lib/memprof/memprof_malloc_linux.cpp
index ef753fcaa4addd8..fc6ee797ddd34fb 100644
--- a/compiler-rt/lib/memprof/memprof_malloc_linux.cpp
+++ b/compiler-rt/lib/memprof/memprof_malloc_linux.cpp
@@ -14,9 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "sanitizer_common/sanitizer_platform.h"
-#if !SANITIZER_LINUX
-#error Unsupported OS
-#endif
+#if SANITIZER_LINUX
 
 #include "memprof_allocator.h"
 #include "memprof_interceptors.h"
@@ -149,3 +147,4 @@ INTERCEPTOR(void, malloc_stats, void) { __memprof_print_accumulated_stats(); }
 namespace __memprof {
 void ReplaceSystemMalloc() {}
 } // namespace __memprof
+#endif
diff --git a/compiler-rt/lib/memprof/memprof_malloc_mac.cpp b/compiler-rt/lib/memprof/memprof_malloc_mac.cpp
new file mode 100644
index 000000000000000..eb3427ba8dbc295
--- /dev/null
+++ b/compiler-rt/lib/memprof/memprof_malloc_mac.cpp
@@ -0,0 +1,37 @@
+//===-- memprof_malloc_mac.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemProfiler, a memory profiler.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_APPLE
+
+#include "memprof_allocator.h"
+#include "memprof_interceptors.h"
+#include "memprof_internal.h"
+#include "memprof_stack.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_dlsym.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
+// ---------------------- Replacement functions ---------------- {{{1
+using namespace __memprof;
+
+struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
+  static bool UseImpl() { return memprof_init_is_running; }
+};
+
+namespace __memprof {
+void ReplaceSystemMalloc() {}
+} // namespace __memprof
+
+#endif
diff --git a/compiler-rt/lib/memprof/memprof_new_delete.cpp b/compiler-rt/lib/memprof/memprof_new_delete.cpp
index cae5de301367abe..312b8e01e8849c3 100644
--- a/compiler-rt/lib/memprof/memprof_new_delete.cpp
+++ b/compiler-rt/lib/memprof/memprof_new_delete.cpp
@@ -43,6 +43,7 @@ enum class align_val_t : size_t {};
     ReportOutOfMemory(size, &stack);                                           \
   return res;
 
+#if !SANITIZER_APPLE
 CXX_OPERATOR_ATTRIBUTE
 void *operator new(size_t size) {
   OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/);
@@ -77,6 +78,7 @@ void *operator new[](size_t size, std::align_val_t align,
                      std::nothrow_t const &) {
   OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/);
 }
+#endif
 
 #define OPERATOR_DELETE_BODY(type)                                             \
   GET_STACK_TRACE_FREE;                                                        \
@@ -94,6 +96,7 @@ void *operator new[](size_t size, std::align_val_t align,
   GET_STACK_TRACE_FREE;                                                        \
   memprof_delete(ptr, size, static_cast<uptr>(align), &stack, type);
 
+#if !SANITIZER_APPLE
 CXX_OPERATOR_ATTRIBUTE
 void operator delete(void *ptr)NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW); }
 CXX_OPERATOR_ATTRIBUTE
@@ -143,3 +146,4 @@ void operator delete[](void *ptr, size_t size,
                        std::align_val_t align) NOEXCEPT {
   OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR);
 }
+#endif
diff --git a/compiler-rt/lib/memprof/tests/CMakeLists.txt b/compiler-rt/lib/memprof/tests/CMakeLists.txt
index f812bd1f86ff8fa..5763c0b7107292e 100644
--- a/compiler-rt/lib/memprof/tests/CMakeLists.txt
+++ b/compiler-rt/lib/memprof/tests/CMakeLists.txt
@@ -46,7 +46,7 @@ set(MEMPROF_UNITTEST_LINK_LIBRARIES
   ${SANITIZER_TEST_CXX_LIBRARIES})
 list(APPEND MEMPROF_UNITTEST_LINK_LIBRARIES "dl")
 
-if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST MEMPROF_SUPPORTED_ARCH)
+if(NOT APPLE AND COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST MEMPROF_SUPPORTED_ARCH)
   # MemProf unit tests are only run on the host machine.
   set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
 
diff --git a/compiler-rt/test/memprof/CMakeLists.txt b/compiler-rt/test/memprof/CMakeLists.txt
index 8a29919b177024d..5f68b383de2990b 100644
--- a/compiler-rt/test/memprof/CMakeLists.txt
+++ b/compiler-rt/test/memprof/CMakeLists.txt
@@ -4,7 +4,7 @@ set(MEMPROF_TESTSUITES)
 set(MEMPROF_DYNAMIC_TESTSUITES)
 
 macro(get_bits_for_arch arch bits)
-  if (${arch} MATCHES "x86_64")
+  if (${arch} MATCHES "x86_64|arm64")
     set(${bits} 64)
   else()
     message(FATAL_ERROR "Unexpected target architecture: ${arch}")

>From 8ce32592e780e693a9378f30237a25629a822897 Mon Sep 17 00:00:00 2001
From: Manman Ren <mren at fb.com>
Date: Wed, 18 Oct 2023 10:36:24 -0700
Subject: [PATCH 2/2] update memprof on darwin

---
 compiler-rt/cmake/config-ix.cmake             |  2 +
 compiler-rt/lib/memprof/memprof_allocator.cpp | 58 ++++++++++++-
 compiler-rt/lib/memprof/memprof_allocator.h   |  4 +
 compiler-rt/lib/memprof/memprof_flags.inc     |  2 +
 .../lib/memprof/memprof_interceptors.cpp      |  2 +
 .../lib/memprof/memprof_malloc_mac.cpp        | 41 ++++++++-
 .../Darwin/asan-symbolize-templated-cxx.cpp   |  2 +-
 compiler-rt/test/memprof/CMakeLists.txt       | 34 ++++----
 .../memprof/TestCases/Darwin/memprof_dump.cpp | 83 +++++++++++++++++++
 compiler-rt/test/memprof/lit.cfg.py           |  2 +-
 10 files changed, 209 insertions(+), 21 deletions(-)
 create mode 100644 compiler-rt/test/memprof/TestCases/Darwin/memprof_dump.cpp

diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
index ebf62b2fdb5e8ee..58ca5469f34021e 100644
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -749,8 +749,10 @@ endif()
 
 if (OS_NAME MATCHES "Linux|FreeBSD|Windows|NetBSD|SunOS")
   set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME TRUE)
+  set(COMPILER_RT_MEMPROF_HAS_STATIC_RUNTIME TRUE)
 else()
   set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME FALSE)
+  set(COMPILER_RT_MEMPROF_HAS_STATIC_RUNTIME TRUE)
 endif()
 
 # TODO: Add builtins support.
diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp
index aeaa458ef0d9b4f..1e4891ca2e519fc 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.cpp
+++ b/compiler-rt/lib/memprof/memprof_allocator.cpp
@@ -265,9 +265,12 @@ struct Allocator {
   atomic_uint8_t destructing;
   atomic_uint8_t constructed;
   bool print_text;
+  bool print_binary_refs;
 
   // ------------------- Initialization ------------------------
-  explicit Allocator(LinkerInitialized) : print_text(flags()->print_text) {
+  explicit Allocator(LinkerInitialized)
+      : print_text(flags()->print_text),
+        print_binary_refs(flags()->print_binary_refs) {
     atomic_store_relaxed(&destructing, 0);
     atomic_store_relaxed(&constructed, 1);
   }
@@ -283,6 +286,7 @@ struct Allocator {
     Print(Value->mib, Key, bool(Arg));
   }
 
+  using SegmentEntry = ::llvm::memprof::SegmentEntry;
   void FinishAndWrite() {
     if (print_text && common_flags()->print_module_map)
       DumpProcessMap();
@@ -290,6 +294,46 @@ struct Allocator {
     allocator.ForceLock();
 
     InsertLiveBlocks();
+#if SANITIZER_APPLE
+    if (print_binary_refs) {
+      __sanitizer::ListOfModules List;
+      List.init();
+      ArrayRef<LoadedModule> Modules(List.begin(), List.end());
+      for (const auto &Module : Modules) {
+        for (const auto &Segment : Module.ranges()) {
+          if (true) { // Segment.executable) {
+            SegmentEntry Entry(Segment.beg, Segment.end, Module.base_address());
+            // CHECK(Module.uuid_size() <= MEMPROF_BUILDID_MAX_SIZE);
+            // Entry.BuildIdSize = Module.uuid_size();
+            memcpy(Entry.BuildId, Module.uuid(), Module.uuid_size());
+            // Print out the segment information.
+            Printf("  -\n");
+            Printf("[MemProf] BuildId: ");
+            for (size_t I = 0; I < Module.uuid_size() /*Entry.BuildIdSize*/;
+                 I++) {
+              Printf("%02x", Entry.BuildId[I]);
+            }
+            Printf("\n");
+            Printf("[MemProf] BuildIdName: %s\n", Module.full_name());
+            // If it is the main binary, check shadow.
+            Printf("[MemProf] Start: 0x%zx\n", Entry.Start);
+            if (AddrIsInLowMem(Entry.Start)) {
+              for (auto t = Entry.Start & SHADOW_MASK; t < Entry.End;
+                   t += MEM_GRANULARITY) {
+                // should not be 64, as it will include the next shadow memory
+                u64 c = GetShadowCount(t, 60);
+                if (c > 0)
+                  Printf("[MemProf] Shadow: %p %d\n", (void *)t, c);
+              }
+            }
+            Printf("[MemProf] End: 0x%zx\n", Entry.End);
+            Printf("[MemProf] Offset: 0x%zx\n", Entry.Offset);
+          } else {
+          }
+        }
+      }
+    }
+#endif
     if (print_text) {
       if (!flags()->print_terse)
         Printf("Recorded MIBs (incl. live on exit):\n");
@@ -710,6 +754,18 @@ uptr memprof_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
   return usable_size;
 }
 
+uptr memprof_mz_size(const void *ptr) {
+  return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+}
+
+void memprof_mz_force_lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+  instance.ForceLock();
+}
+
+void memprof_mz_force_unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+  instance.ForceUnlock();
+}
+
 } // namespace __memprof
 
 // ---------------------- Interface ---------------- {{{1
diff --git a/compiler-rt/lib/memprof/memprof_allocator.h b/compiler-rt/lib/memprof/memprof_allocator.h
index 3f379825059936b..e55b0e041ffb310 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.h
+++ b/compiler-rt/lib/memprof/memprof_allocator.h
@@ -112,6 +112,10 @@ int memprof_posix_memalign(void **memptr, uptr alignment, uptr size,
                            BufferedStackTrace *stack);
 uptr memprof_malloc_usable_size(const void *ptr, uptr pc, uptr bp);
 
+uptr memprof_mz_size(const void *ptr);
+void memprof_mz_force_lock();
+void memprof_mz_force_unlock();
+
 void PrintInternalAllocatorStats();
 
 } // namespace __memprof
diff --git a/compiler-rt/lib/memprof/memprof_flags.inc b/compiler-rt/lib/memprof/memprof_flags.inc
index ee0760ddc302a61..d0268a705a55d36 100644
--- a/compiler-rt/lib/memprof/memprof_flags.inc
+++ b/compiler-rt/lib/memprof/memprof_flags.inc
@@ -39,3 +39,5 @@ MEMPROF_FLAG(bool, print_text, false,
   "If set, prints the heap profile in text format. Else use the raw binary serialization format.")
 MEMPROF_FLAG(bool, print_terse, false,
              "If set, prints memory profile in a terse format. Only applicable if print_text = true.")
+MEMPROF_FLAG(bool, print_binary_refs, false,
+             "If set, prints the references to binaries.")
diff --git a/compiler-rt/lib/memprof/memprof_interceptors.cpp b/compiler-rt/lib/memprof/memprof_interceptors.cpp
index 35d7621e61dc3f8..231bf2ba2956907 100644
--- a/compiler-rt/lib/memprof/memprof_interceptors.cpp
+++ b/compiler-rt/lib/memprof/memprof_interceptors.cpp
@@ -18,7 +18,9 @@
 #include "memprof_stack.h"
 #include "memprof_stats.h"
 #include "sanitizer_common/sanitizer_libc.h"
+#if SANITIZER_POSIX
 #include "sanitizer_common/sanitizer_posix.h"
+#endif
 
 namespace __memprof {
 
diff --git a/compiler-rt/lib/memprof/memprof_malloc_mac.cpp b/compiler-rt/lib/memprof/memprof_malloc_mac.cpp
index eb3427ba8dbc295..8f3b3d8f601ea3e 100644
--- a/compiler-rt/lib/memprof/memprof_malloc_mac.cpp
+++ b/compiler-rt/lib/memprof/memprof_malloc_mac.cpp
@@ -30,8 +30,43 @@ struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
   static bool UseImpl() { return memprof_init_is_running; }
 };
 
-namespace __memprof {
-void ReplaceSystemMalloc() {}
-} // namespace __memprof
+#define COMMON_MALLOC_ZONE_NAME "memprof"
+#define COMMON_MALLOC_ENTER() ENSURE_MEMPROF_INITED()
+#define COMMON_MALLOC_SANITIZER_INITIALIZED memprof_inited
+#define COMMON_MALLOC_FORCE_LOCK() memprof_mz_force_lock()
+#define COMMON_MALLOC_FORCE_UNLOCK() memprof_mz_force_unlock()
+#define COMMON_MALLOC_MEMALIGN(alignment, size)                                \
+  GET_STACK_TRACE_MALLOC;                                                      \
+  void *p = memprof_memalign(alignment, size, &stack, FROM_MALLOC)
+#define COMMON_MALLOC_MALLOC(size)                                             \
+  GET_STACK_TRACE_MALLOC;                                                      \
+  void *p = memprof_malloc(size, &stack)
+#define COMMON_MALLOC_REALLOC(ptr, size)                                       \
+  GET_STACK_TRACE_MALLOC;                                                      \
+  void *p = memprof_realloc(ptr, size, &stack);
+#define COMMON_MALLOC_CALLOC(count, size)                                      \
+  GET_STACK_TRACE_MALLOC;                                                      \
+  void *p = memprof_calloc(count, size, &stack);
+#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size)                  \
+  GET_STACK_TRACE_MALLOC;                                                      \
+  int res = memprof_posix_memalign(memptr, alignment, size, &stack);
+#define COMMON_MALLOC_VALLOC(size)                                             \
+  GET_STACK_TRACE_MALLOC;                                                      \
+  void *p = memprof_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
+#define COMMON_MALLOC_FREE(ptr)                                                \
+  GET_STACK_TRACE_FREE;                                                        \
+  memprof_free(ptr, &stack, FROM_MALLOC);
+#define COMMON_MALLOC_SIZE(ptr) uptr size = memprof_mz_size(ptr);
+#define COMMON_MALLOC_FILL_STATS(zone, stats)
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name)
+#define COMMON_MALLOC_NAMESPACE __memprof
+#define COMMON_MALLOC_HAS_ZONE_ENUMERATOR 0
+#define COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT 1
 
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
+
+namespace {
+
+void mi_extra_init(sanitizer_malloc_introspection_t *mi) {}
+} // namespace
 #endif
diff --git a/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp b/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp
index 3d726a32b7eaa50..858afebb09d3827 100644
--- a/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp
+++ b/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp
@@ -28,7 +28,7 @@ IntW *a;
 
 template <class T>
 void writeToA(T new_value) {
-  // CHECK: heap-use-after-free
+  // CHECK: heap-use-after-free bar
   // NOTE: atos seems to emit the `void` return type here for some reason.
   // CHECK: #{{[0-9]+}} 0x{{.+}} in {{(void +)?}}writeToA<IntWrapper<void{{ *}}(int)>{{ *}}>(IntWrapper<void{{ *}}(int)>) asan-symbolize-templated-cxx.cpp:[[@LINE+1]]
   *a = new_value;
diff --git a/compiler-rt/test/memprof/CMakeLists.txt b/compiler-rt/test/memprof/CMakeLists.txt
index 5f68b383de2990b..32b7bce97723674 100644
--- a/compiler-rt/test/memprof/CMakeLists.txt
+++ b/compiler-rt/test/memprof/CMakeLists.txt
@@ -36,14 +36,16 @@ foreach(arch ${MEMPROF_TEST_ARCH})
     )
   list(APPEND MEMPROF_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
 
-  string(TOLOWER "-${arch}-${OS_NAME}-dynamic" MEMPROF_TEST_CONFIG_SUFFIX)
-  set(MEMPROF_TEST_DYNAMIC True)
-  set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}DynamicConfig)
-  configure_lit_site_cfg(
-    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
-    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py)
-  list(APPEND MEMPROF_DYNAMIC_TESTSUITES
-    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+  if(COMPILER_RT_MEMPROF_HAS_STATIC_RUNTIME)
+    string(TOLOWER "-${arch}-${OS_NAME}-dynamic" MEMPROF_TEST_CONFIG_SUFFIX)
+    set(MEMPROF_TEST_DYNAMIC True)
+    set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}DynamicConfig)
+    configure_lit_site_cfg(
+      ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
+      ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py)
+    list(APPEND MEMPROF_DYNAMIC_TESTSUITES
+      ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+  endif()
 endforeach()
 
 add_lit_testsuite(check-memprof "Running the MemProfiler tests"
@@ -51,10 +53,12 @@ add_lit_testsuite(check-memprof "Running the MemProfiler tests"
   DEPENDS ${MEMPROF_TEST_DEPS})
 set_target_properties(check-memprof PROPERTIES FOLDER "Compiler-RT Misc")
 
-add_lit_testsuite(check-memprof-dynamic
-	"Running the MemProfiler tests with dynamic runtime"
-  ${MEMPROF_DYNAMIC_TESTSUITES}
-  ${exclude_from_check_all.g}
-  DEPENDS ${MEMPROF_DYNAMIC_TEST_DEPS})
-set_target_properties(check-memprof-dynamic
-  PROPERTIES FOLDER "Compiler-RT Misc")
+if(COMPILER_RT_MEMPROF_HAS_STATIC_RUNTIME)
+  add_lit_testsuite(check-memprof-dynamic
+    "Running the MemProfiler tests with dynamic runtime"
+    ${MEMPROF_DYNAMIC_TESTSUITES}
+    ${exclude_from_check_all.g}
+    DEPENDS ${MEMPROF_DYNAMIC_TEST_DEPS})
+  set_target_properties(check-memprof-dynamic
+    PROPERTIES FOLDER "Compiler-RT Misc")
+endif()
diff --git a/compiler-rt/test/memprof/TestCases/Darwin/memprof_dump.cpp b/compiler-rt/test/memprof/TestCases/Darwin/memprof_dump.cpp
new file mode 100644
index 000000000000000..aa132321c25c610
--- /dev/null
+++ b/compiler-rt/test/memprof/TestCases/Darwin/memprof_dump.cpp
@@ -0,0 +1,83 @@
+// UNSUPPORTED: ios
+
+// RUN: %clangxx_memprof -O0 %s -o %t
+// RUN: %env_memprof_opts=print_binary_refs=true:print_text=true:log_path=stdout:verbosity=2 %run %t &> %t.log
+// RUN: llvm-nm %t &> %t2.log
+// RUN: cat %t2.log %t.log | FileCheck %s
+
+#include <sanitizer/memprof_interface.h>
+struct __attribute__((visibility("default"))) A {
+  virtual void foo() {}
+};
+
+void test_1(A *p) {
+  // A has default visibility, so no need for type.checked.load.
+  p->foo();
+}
+
+struct __attribute__((visibility("hidden")))
+[[clang::lto_visibility_public]] B {
+  virtual void foo() {}
+};
+
+void test_2(B *p) {
+  // B has public LTO visibility, so no need for type.checked.load.
+  p->foo();
+}
+
+struct __attribute__((visibility("hidden"))) C {
+  virtual void foo() {}
+  virtual void bar() {}
+};
+
+void test_3(C *p) {
+  // C has hidden visibility, so we generate type.checked.load to allow VFE.
+  p->foo();
+}
+
+void test_4(C *p) {
+  // When using type.checked.load, we pass the vtable offset to the intrinsic,
+  // rather than adding it to the pointer with a GEP.
+  p->bar();
+}
+
+void test_5(C *p, void (C::*q)(void)) {
+  // We also use type.checked.load for the virtual side of member function
+  // pointer calls. We use a GEP to calculate the address to load from and pass
+  // 0 as the offset to the intrinsic, because we know that the load must be
+  // from exactly the point marked by one of the function-type metadatas (in
+  // this case "_ZTSM1CFvvE.virtual"). If we passed the offset from the member
+  // function pointer to the intrinsic, this information would be lost. No
+  // codegen changes on the non-virtual side.
+  (p->*q)();
+}
+int main() {
+  C *p = new C;
+  test_3(p);
+  test_4(p);
+  //__memprof_profile_dump(); // dump accesses to p, no dumping to access of the vtable
+  A *a = new A;
+  test_1(a);
+  B *b = new B;
+  test_2(b);
+  __sanitizer_set_report_path("stdout");
+  //__memprof_profile_dump();
+  //__sanitizer_set_report_path(nullptr);
+  return 0; // at exit, dump accesses to a, b
+}
+// CHECK: __ZTV1A
+// CHECK: __ZTV1B
+// 5 sections corresponding to xxx xxx __got __mod_init_func __const
+// vtables are in __const
+// CHECK: BuildIdName:{{.*}}memprof-vtable
+// CHECK: Offset:
+// CHECK: BuildIdName:{{.*}}memprof-vtable
+// CHECK: Offset:
+// CHECK: BuildIdName:{{.*}}memprof-vtable
+// CHECK: Offset:
+// CHECK: BuildIdName:{{.*}}memprof-vtable
+// CHECK: Offset:
+// CHECK: BuildIdName:{{.*}}memprof-vtable
+// CHECK-NEXT: Start:
+// symbol address for __ZTV1B + 0x10 Offset is the address in "Shadow:" (+0x10 to get the vfunc pointer)
+// CHECK-NEXT: Shadow: {{.*}}
diff --git a/compiler-rt/test/memprof/lit.cfg.py b/compiler-rt/test/memprof/lit.cfg.py
index 4e5d7ba405a2019..28446280cab6bc3 100644
--- a/compiler-rt/test/memprof/lit.cfg.py
+++ b/compiler-rt/test/memprof/lit.cfg.py
@@ -106,7 +106,7 @@ def build_invocation(compile_flags):
 config.substitutions.append(("%pie", "-pie"))
 
 # Only run the tests on supported OSs.
-if config.host_os not in ["Linux"]:
+if config.host_os not in ["Linux", "Darwin"]:
     config.unsupported = True
 
 if not config.parallelism_group:



More information about the cfe-commits mailing list