Skip to content

Support MemProf on darwin #69640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Darwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
13 changes: 13 additions & 0 deletions clang/test/Driver/darwin-memprof.c
Original file line number Diff line number Diff line change
@@ -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"

2 changes: 1 addition & 1 deletion compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
6 changes: 5 additions & 1 deletion compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -747,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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be FALSE? Otherwise this new variable is always TRUE.

endif()

# TODO: Add builtins support.
Expand Down Expand Up @@ -782,7 +786,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)
Expand Down
30 changes: 27 additions & 3 deletions compiler-rt/lib/memprof/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -79,14 +81,15 @@ 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}
CFLAGS ${MEMPROF_DYNAMIC_CFLAGS}
DEFS ${MEMPROF_DYNAMIC_DEFINITIONS}
DEPS ${MEMPROF_DEPS})

if(NOT APPLE)
add_compiler_rt_object_libraries(RTMemprof
ARCHS ${MEMPROF_SUPPORTED_ARCH}
SOURCES ${MEMPROF_SOURCES}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
62 changes: 61 additions & 1 deletion compiler-rt/lib/memprof/memprof_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a way to do this on Apple then add a FIXME?

#else
return sched_getcpu();
#endif
}

// Compute the timestamp in ms.
Expand Down Expand Up @@ -261,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);
}
Expand All @@ -279,13 +286,54 @@ struct Allocator {
Print(Value->mib, Key, bool(Arg));
}

using SegmentEntry = ::llvm::memprof::SegmentEntry;
void FinishAndWrite() {
if (print_text && common_flags()->print_module_map)
DumpProcessMap();

allocator.ForceLock();

InsertLiveBlocks();
#if SANITIZER_APPLE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which part of this handling is Apple-specific?

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the if-condition be always true? Leftover debugging handling?

SegmentEntry Entry(Segment.beg, Segment.end, Module.base_address());
// CHECK(Module.uuid_size() <= MEMPROF_BUILDID_MAX_SIZE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check and the line below it be uncommented?

// 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you know if it is the main binary?

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");
Expand Down Expand Up @@ -706,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
Expand Down
29 changes: 22 additions & 7 deletions compiler-rt/lib/memprof/memprof_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine these 2 lines into an "#else" ?

#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;

Expand Down Expand Up @@ -101,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
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/memprof/memprof_flags.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
7 changes: 6 additions & 1 deletion compiler-rt/lib/memprof/memprof_interceptors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -131,6 +133,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();
Expand Down Expand Up @@ -305,6 +308,7 @@ INTERCEPTOR(long long, atoll, const char *nptr) {
MEMPROF_READ_STRING(nptr, (real_endptr - nptr) + 1);
return result;
}
#endif

// ---------------------- InitializeMemprofInterceptors ---------------- {{{1
namespace __memprof {
Expand All @@ -314,6 +318,7 @@ void InitializeMemprofInterceptors() {
was_called_once = true;
InitializeCommonInterceptors();

#if !defined(SANITIZER_APPLE)
// Intercept str* functions.
MEMPROF_INTERCEPT_FUNC(strcat);
MEMPROF_INTERCEPT_FUNC(strcpy);
Expand All @@ -322,7 +327,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);
Expand All @@ -332,6 +336,7 @@ void InitializeMemprofInterceptors() {
// Intercept threading-related functions
MEMPROF_INTERCEPT_FUNC(pthread_create);
MEMPROF_INTERCEPT_FUNC(pthread_join);
#endif

InitializePlatformInterceptors();

Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/memprof/memprof_interceptors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)) \
Expand All @@ -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; \
Expand Down
5 changes: 2 additions & 3 deletions compiler-rt/lib/memprof/memprof_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -72,3 +70,4 @@ uptr FindDynamicShadowStart() {
void *MemprofDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, sym); }

} // namespace __memprof
#endif
Loading