diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 93cb436c42ec3d..e69da7ed4ac412 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -155,6 +155,9 @@ endif(CLR_CMAKE_TARGET_LINUX AND CLR_CMAKE_HOST_LINUX) if(CLR_CMAKE_TARGET_FREEBSD) add_compile_definitions(FEATURE_PERFMAP) endif(CLR_CMAKE_TARGET_FREEBSD) +if(CLR_CMAKE_TARGET_APPLE) + add_compile_definitions(FEATURE_PERFMAP) +endif(CLR_CMAKE_TARGET_APPLE) if(FEATURE_COMWRAPPERS) add_compile_definitions(FEATURE_COMWRAPPERS) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6427f717dee947..ddc7c79506ad4e 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -480,7 +480,7 @@ RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ProfAPI_ValidateNGENInstrumentation, W("ProfAPI_ValidateNGENInstrumentation"), 0, "This flag enables additional validations when using the IMetaDataEmit APIs for NGEN'ed images to ensure only supported edits are made.") #ifdef FEATURE_PERFMAP -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux and macOS to enable writing /tmp/perf-$pid.map. It is disabled by default") RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_PerfMapJitDumpPath, W("PerfMapJitDumpPath"), "Specifies a path to write the perf jitdump file. Defaults to /tmp", CLRConfig::LookupOptions::TrimWhiteSpaceFromStringValue) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapShowOptimizationTiers, W("PerfMapShowOptimizationTiers"), 1, "Shows optimization tiers in the perf map for methods, as part of the symbol name. Useful for seeing separate stack frames for different optimization tiers of each method.") diff --git a/src/coreclr/pal/src/misc/perfjitdump.cpp b/src/coreclr/pal/src/misc/perfjitdump.cpp index 50b0f2c6dadcf6..6223d533ac7f78 100644 --- a/src/coreclr/pal/src/misc/perfjitdump.cpp +++ b/src/coreclr/pal/src/misc/perfjitdump.cpp @@ -2,15 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // =========================================================================== -#if defined(__linux__) -#define JITDUMP_SUPPORTED -#endif - #include "pal/palinternal.h" #include "pal/dbgmsg.h" #include +#if defined(__linux__) || defined(__APPLE__) +#define JITDUMP_SUPPORTED +#endif + #ifdef JITDUMP_SUPPORTED #include @@ -61,24 +61,11 @@ namespace JIT_CODE_LOAD = 0, }; - uint64_t GetTimeStampNS() + static uint64_t GetTimeStampNS() { -#if HAVE_CLOCK_MONOTONIC - struct timespec ts; - int result = clock_gettime(CLOCK_MONOTONIC, &ts); - - if (result != 0) - { - ASSERT("clock_gettime(CLOCK_MONOTONIC) failed: %d\n", result); - return 0; - } - else - { - return ts.tv_sec * 1000000000ULL + ts.tv_nsec; - } -#else - #error "The PAL jitdump requires clock_gettime(CLOCK_MONOTONIC) to be supported." -#endif + LARGE_INTEGER result; + QueryPerformanceCounter(&result); + return result.QuadPart; } struct FileHeader @@ -115,7 +102,7 @@ namespace { JitCodeLoadRecord() : pid(getpid()), - tid(syscall(SYS_gettid)) + tid((uint32_t)PlatformGetCurrentThreadId()) { header.id = JIT_CODE_LOAD; header.timestamp = GetTimeStampNS(); @@ -170,6 +157,19 @@ struct PerfJitDumpState { int result = 0; + // On platforms where JITDUMP is used, the PAL QueryPerformanceFrequency + // returns tccSecondsToNanoSeconds, meaning QueryPerformanceCounter + // will return a direct nanosecond value. If this isn't true, + // then some other method will need to be used to implement GetTimeStampNS. + // Validate this is true once in Start here. + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + if (freq.QuadPart != tccSecondsToNanoSeconds) + { + _ASSERTE(!"QueryPerformanceFrequency does not return tccSecondsToNanoSeconds. Implement JITDUMP GetTimeStampNS directly for this platform.\n"); + FatalError(); + } + // Write file header FileHeader header; @@ -203,12 +203,18 @@ struct PerfJitDumpState if (result == -1) return FatalError(); +#if !defined(__APPLE__) // mmap jitdump file - // this is a marker for perf inject to find the jitdumpfile + // this is a marker for perf inject to find the jitdumpfile on linux. + // On OSX, samply and others hook open and mmap is not needed. It also fails on OSX, + // likely because of PROT_EXEC and hardened runtime mmapAddr = mmap(nullptr, sizeof(FileHeader), PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); if (mmapAddr == MAP_FAILED) return FatalError(); +#else + mmapAddr = NULL; +#endif enabled = true; @@ -308,16 +314,13 @@ struct PerfJitDumpState { enabled = false; - if (result != 0) - return FatalError(); - - if (!enabled) - goto exit; - - result = munmap(mmapAddr, sizeof(FileHeader)); + if (mmapAddr != NULL) + { + result = munmap(mmapAddr, sizeof(FileHeader)); - if (result == -1) - return FatalError(); + if (result == -1) + return FatalError(); + } mmapAddr = MAP_FAILED; @@ -333,7 +336,7 @@ struct PerfJitDumpState fd = -1; } -exit: + return 0; } };