From ceb52b3dd1c7b9ea8d20ad46e7269391d8189614 Mon Sep 17 00:00:00 2001 From: Lakshan Fernando Date: Thu, 25 May 2023 04:57:41 -0700 Subject: [PATCH] Initial work to enable EventPipe in Linux (#86226) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * Initial work to enable EventPipe in Linux * GCC build break fix * Only enable Unix * FB * restricting iOS * Update src/coreclr/nativeaot/Directory.Build.props Co-authored-by: Michal Strehovský * FB * license header * Update src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h Co-authored-by: Elinor Fung * FB --------- Co-authored-by: Michal Strehovský Co-authored-by: Elinor Fung --- .../Microsoft.NETCore.Native.Unix.targets | 2 +- src/coreclr/nativeaot/Directory.Build.props | 2 +- src/coreclr/nativeaot/Runtime/PalRedhawk.h | 2 +- .../nativeaot/Runtime/eventpipe/CMakeLists.txt | 25 ++- .../nativeaot/Runtime/eventpipe/ds-rt-aot.cpp | 164 +++++++++++++++++ .../nativeaot/Runtime/eventpipe/ds-rt-aot.h | 9 +- .../nativeaot/Runtime/eventpipe/ep-rt-aot.cpp | 41 ++++- .../nativeaot/Runtime/eventpipe/ep-rt-aot.h | 198 +++++++++++++++++---- src/coreclr/nativeaot/Runtime/startup.cpp | 6 +- src/coreclr/nativeaot/Runtime/stressLog.cpp | 4 +- src/coreclr/nativeaot/Runtime/thread.cpp | 2 +- .../nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 2 +- .../nativeaot/Runtime/windows/PalRedhawkMinWin.cpp | 2 +- 13 files changed, 392 insertions(+), 67 deletions(-) create mode 100644 src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 7cbcf4b..2e12462 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -48,7 +48,7 @@ The .NET Foundation licenses this file to you under the MIT license. @executable_path libeventpipe-disabled - libeventpipe-enabled + libeventpipe-enabled diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props index 7fdf743..76c5cb9 100644 --- a/src/coreclr/nativeaot/Directory.Build.props +++ b/src/coreclr/nativeaot/Directory.Build.props @@ -66,7 +66,7 @@ false - true + true FEATURE_PERFTRACING;$(DefineConstants) diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index 81d0826..0b2d717 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -769,7 +769,7 @@ REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalCompatibleWaitAny(UInt32_BOOL alert REDHAWK_PALIMPORT void REDHAWK_PALAPI PalAttachThread(void* thread); REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalDetachThread(void* thread); -REDHAWK_PALIMPORT uint64_t PalGetCurrentThreadIdForLogging(); +REDHAWK_PALIMPORT uint64_t PalGetCurrentOSThreadId(); REDHAWK_PALIMPORT uint64_t PalQueryPerformanceCounter(); REDHAWK_PALIMPORT uint64_t PalQueryPerformanceFrequency(); diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt index 287c50d..d0b918e 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt @@ -18,13 +18,25 @@ set (SHARED_EVENTPIPE_SOURCE_PATH "${CLR_SRC_NATIVE_DIR}/eventpipe") include (${SHARED_EVENTPIPE_SOURCE_PATH}/eventpipe.cmake) include (${SHARED_CONTAINERS_SOURCE_PATH}/containers.cmake) -list(APPEND SHARED_DIAGNOSTIC_SERVER_SOURCES - ds-ipc-pal-namedpipe.c -) +if(CLR_CMAKE_HOST_WIN32) + list(APPEND SHARED_DIAGNOSTIC_SERVER_SOURCES + ds-ipc-pal-namedpipe.c + ) -list(APPEND SHARED_DIAGNOSTIC_SERVER_HEADERS - ds-ipc-pal-namedpipe.h -) + list(APPEND SHARED_DIAGNOSTIC_SERVER_HEADERS + ds-ipc-pal-namedpipe.h + ) +endif(CLR_CMAKE_HOST_WIN32) + +if(CLR_CMAKE_HOST_UNIX) + list(APPEND SHARED_DIAGNOSTIC_SERVER_SOURCES + ds-ipc-pal-socket.c + ) + + list(APPEND SHARED_DIAGNOSTIC_SERVER_HEADERS + ds-ipc-pal-socket.h + ) +endif(CLR_CMAKE_HOST_UNIX) list(APPEND EVENTPIPE_SOURCES ${SHARED_EVENTPIPE_SOURCES} @@ -63,6 +75,7 @@ endif() list(APPEND AOT_EVENTPIPE_SHIM_SOURCES ${AOT_EVENTPIPE_SHIM_DIR}/ep-rt-aot.cpp + ${AOT_EVENTPIPE_SHIM_DIR}/ds-rt-aot.cpp ) list(APPEND AOT_EVENTPIPE_SHIM_HEADERS diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp new file mode 100644 index 0000000..9bb8d08 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#ifdef ENABLE_PERFTRACING +#include +#include +#include +#include + +#include "ds-rt-aot.h" + +bool aot_ipc_get_process_id_disambiguation_key(uint32_t process_id, uint64_t *key); + +bool +aot_ipc_get_process_id_disambiguation_key( + uint32_t process_id, + uint64_t *key) +{ + if (!key) { + EP_ASSERT (!"key argument cannot be null!"); + return false; + } + + *key = 0; + +// Mono implementation, restricted just to Unix +#ifdef TARGET_UNIX + + // Here we read /proc//stat file to get the start time for the process. + // We return this value (which is expressed in jiffies since boot time). + + // Making something like: /proc/123/stat + char stat_file_name [64]; + snprintf (stat_file_name, sizeof (stat_file_name), "/proc/%d/stat", process_id); + + FILE *stat_file = fopen (stat_file_name, "r"); + if (!stat_file) { + EP_ASSERT (!"Failed to get start time of a process, fopen failed."); + return false; + } + + bool result = false; + unsigned long long start_time = 0; + char *scan_start_position; + int result_sscanf; + + char *line = NULL; + size_t line_len = 0; + if (getline (&line, &line_len, stat_file) == -1) + { + EP_ASSERT (!"Failed to get start time of a process, getline failed."); + ep_raise_error (); + } + + + // According to `man proc`, the second field in the stat file is the filename of the executable, + // in parentheses. Tokenizing the stat file using spaces as separators breaks when that name + // has spaces in it, so we start using sscanf_s after skipping everything up to and including the + // last closing paren and the space after it. + scan_start_position = strrchr (line, ')'); + if (!scan_start_position || scan_start_position [1] == '\0') { + EP_ASSERT (!"Failed to parse stat file contents with strrchr."); + ep_raise_error (); + } + + scan_start_position += 2; + + // All the format specifiers for the fields in the stat file are provided by 'man proc'. + result_sscanf = sscanf (scan_start_position, + "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %llu \n", + &start_time); + + if (result_sscanf != 1) { + EP_ASSERT (!"Failed to parse stat file contents with sscanf."); + ep_raise_error (); + } + + free (line); + fclose (stat_file); + result = true; + +ep_on_exit: + *key = (uint64_t)start_time; + return result; + +ep_on_error: + free (line); + fclose (stat_file); + result = false; + ep_exit_error_handler (); + +#else + // If we don't have /proc, we just return false. + DS_LOG_WARNING_0 ("ipc_get_process_id_disambiguation_key was called but is not implemented on this platform!"); + return false; +#endif +} + +bool +ds_rt_aot_transport_get_default_name ( + ep_char8_t *name, + int32_t name_len, + const ep_char8_t *prefix, + int32_t id, + const ep_char8_t *group_id, + const ep_char8_t *suffix) +{ + STATIC_CONTRACT_NOTHROW; + +#ifdef TARGET_UNIX + + EP_ASSERT (name != NULL); + + bool result = false; + int32_t format_result = 0; + uint64_t disambiguation_key = 0; + ep_char8_t *format_buffer = NULL; + + *name = '\0'; + + format_buffer = (ep_char8_t *)malloc (name_len + 1); + ep_raise_error_if_nok (format_buffer != NULL); + + *format_buffer = '\0'; + + // If ipc_get_process_id_disambiguation_key failed for some reason, it should set the value + // to 0. We expect that anyone else making the pipe name will also fail and thus will + // also try to use 0 as the value. + if (!aot_ipc_get_process_id_disambiguation_key (id, &disambiguation_key)) + EP_ASSERT (disambiguation_key == 0); + + // Get a temp file location + format_result = ep_rt_temp_path_get (format_buffer, name_len); + if (format_result == 0) { + DS_LOG_ERROR_0 ("ep_rt_temp_path_get failed"); + ep_raise_error (); + } + + EP_ASSERT (format_result <= name_len); + + format_result = snprintf(name, name_len, "%s%s-%d-%llu-%s", format_buffer, prefix, id, (unsigned long long)disambiguation_key, suffix); + if (format_result <= 0 || format_result > name_len) { + DS_LOG_ERROR_0 ("name buffer too small"); + ep_raise_error (); + } + + result = true; + +ep_on_exit: + free (format_buffer); + return result; + +ep_on_error: + EP_ASSERT (!result); + name [0] = '\0'; + ep_exit_error_handler (); + +#else + return true; +#endif +} +#endif /* ENABLE_PERFTRACING */ diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index df255f7..aaea673 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -191,11 +191,10 @@ ds_rt_transport_get_default_name ( const ep_char8_t *group_id, const ep_char8_t *suffix) { - STATIC_CONTRACT_NOTHROW; - - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: PAL_GetTransportName is defined in coreclr\pal\inc\pal.h - return true; + + extern bool ds_rt_aot_transport_get_default_name (ep_char8_t *name, int32_t name_len, const ep_char8_t *prefix, int32_t id, const ep_char8_t *group_id, const ep_char8_t *suffix); + + return ds_rt_aot_transport_get_default_name(name, name_len, prefix, id, group_id, suffix); } /* diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp index fea284d..72c040c 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp @@ -20,13 +20,19 @@ #include "holder.h" #include "SpinLock.h" +#ifdef TARGET_UNIX +// Per module (1 for NativeAOT), key that will be used to implement TLS in Unix +pthread_key_t eventpipe_tls_key; +__thread EventPipeThreadHolder* eventpipe_tls_instance; +#else +thread_local EventPipeAotThreadHolderTLS EventPipeAotThreadHolderTLS::g_threadHolderTLS; +#endif + // Uses _rt_aot_lock_internal_t that has CrstStatic as a field // This is initialized at the beginning and EventPipe library requires the lock handle to be maintained by the runtime ep_rt_lock_handle_t _ep_rt_aot_config_lock_handle; CrstStatic _ep_rt_aot_config_lock; -thread_local EventPipeAotThreadHolderTLS EventPipeAotThreadHolderTLS::g_threadHolderTLS; - ep_char8_t *volatile _ep_rt_aot_diagnostics_cmd_line; #ifndef TARGET_UNIX @@ -297,10 +303,7 @@ ep_rt_aot_current_thread_get_id (void) STATIC_CONTRACT_NOTHROW; #ifdef TARGET_UNIX - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: AOT doesn't have PAL_GetCurrentOSThreadId, as CoreCLR does. - // PalDebugBreak(); - return static_cast(0); + return static_cast(PalGetCurrentOSThreadId()); #else return static_cast(::GetCurrentThreadId ()); #endif @@ -378,7 +381,15 @@ ep_rt_aot_utf16_string_len (const ep_char16_t *str) STATIC_CONTRACT_NOTHROW; EP_ASSERT (str != NULL); - return wcslen (reinterpret_cast(str)); + #ifdef TARGET_UNIX + const uint16_t *a = (const uint16_t *)str; + size_t length = 0; + while (a [length]) + ++length; + return length; + #else + return wcslen (reinterpret_cast(str)); + #endif } uint32_t @@ -509,6 +520,17 @@ ep_rt_aot_volatile_store_ptr_without_barrier ( VolatileStoreWithoutBarrier ((void **)ptr, value); } +void unix_tls_callback_fn(void *value) +{ + if (value) { + // we need to do the unallocation here + EventPipeThreadHolder *thread_holder_old = static_cast(value); + // @TODO - inline + thread_holder_free_func (thread_holder_old); + value = NULL; + } +} + void ep_rt_aot_init (void) { extern ep_rt_lock_handle_t _ep_rt_aot_config_lock_handle; @@ -516,6 +538,11 @@ void ep_rt_aot_init (void) _ep_rt_aot_config_lock_handle.lock = &_ep_rt_aot_config_lock; _ep_rt_aot_config_lock_handle.lock->InitNoThrow (CrstType::CrstEventPipeConfig); + + // Initialize the pthread key used for TLS in Unix + #ifdef TARGET_UNIX + pthread_key_create(&eventpipe_tls_key, unix_tls_callback_fn); + #endif } bool ep_rt_aot_lock_acquire (ep_rt_lock_handle_t *lock) diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h index 786a679..ffa5abb 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h @@ -6,6 +6,10 @@ #define __EVENTPIPE_RT_AOT_H__ #include // For isspace +#ifdef TARGET_UNIX +#include +#include +#endif #include #ifdef ENABLE_PERFTRACING @@ -42,6 +46,11 @@ #undef EP_ALIGN_UP #define EP_ALIGN_UP(val,align) _rt_aot_align_up(val,align) +#ifdef TARGET_UNIX +extern pthread_key_t eventpipe_tls_key; +extern __thread EventPipeThreadHolder* eventpipe_tls_instance; +#endif + // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase // TODO: The NativeAOT ALIGN_UP is defined in a tangled manner that generates linker errors if // it is used here; instead, define a version tailored to the existing usage in the shared @@ -62,7 +71,7 @@ ep_rt_lock_handle_t * ep_rt_aot_config_lock_get (void) { extern ep_rt_lock_handle_t _ep_rt_aot_config_lock_handle; - return &_ep_rt_aot_config_lock_handle; + return &_ep_rt_aot_config_lock_handle; } static @@ -425,7 +434,7 @@ ep_rt_config_value_get_config (void) // (CLRConfig::INTERNAL_EventPipeConfig) // PalDebugBreak(); return nullptr; -// return ep_rt_utf16_to_utf8_string (reinterpret_cast(value.GetValue ()), -1); +// return ep_rt_utf16_to_utf8_string (reinterpret_cast(value.GetValue ()), -1); } static @@ -667,28 +676,28 @@ ep_rt_create_activity_id ( uint8_t data1[] = {0x67,0xac,0x33,0xf1,0x8d,0xed,0x41,0x01,0xb4,0x26,0xc9,0xb7,0x94,0x35,0xf7,0x8a}; memcpy (activity_id, data1, EP_ACTIVITY_ID_SIZE); - const uint16_t version_mask = 0xF000; - const uint16_t random_guid_version = 0x4000; - const uint8_t clock_seq_hi_and_reserved_mask = 0xC0; - const uint8_t clock_seq_hi_and_reserved_value = 0x80; + const uint16_t version_mask = 0xF000; + const uint16_t random_guid_version = 0x4000; + const uint8_t clock_seq_hi_and_reserved_mask = 0xC0; + const uint8_t clock_seq_hi_and_reserved_value = 0x80; - // Modify bits indicating the type of the GUID - uint8_t *activity_id_c = activity_id + sizeof (uint32_t) + sizeof (uint16_t); - uint8_t *activity_id_d = activity_id + sizeof (uint32_t) + sizeof (uint16_t) + sizeof (uint16_t); + // Modify bits indicating the type of the GUID + uint8_t *activity_id_c = activity_id + sizeof (uint32_t) + sizeof (uint16_t); + uint8_t *activity_id_d = activity_id + sizeof (uint32_t) + sizeof (uint16_t) + sizeof (uint16_t); - uint16_t c; - memcpy (&c, activity_id_c, sizeof (c)); + uint16_t c; + memcpy (&c, activity_id_c, sizeof (c)); - uint8_t d; - memcpy (&d, activity_id_d, sizeof (d)); + uint8_t d; + memcpy (&d, activity_id_d, sizeof (d)); - // time_hi_and_version - c = ((c & ~version_mask) | random_guid_version); - // clock_seq_hi_and_reserved - d = ((d & ~clock_seq_hi_and_reserved_mask) | clock_seq_hi_and_reserved_value); + // time_hi_and_version + c = ((c & ~version_mask) | random_guid_version); + // clock_seq_hi_and_reserved + d = ((d & ~clock_seq_hi_and_reserved_mask) | clock_seq_hi_and_reserved_value); - memcpy (activity_id_c, &c, sizeof (c)); - memcpy (activity_id_d, &d, sizeof (d)); + memcpy (activity_id_c, &c, sizeof (c)); + memcpy (activity_id_d, &d, sizeof (d)); } static @@ -898,19 +907,54 @@ ep_rt_system_time_get (EventPipeSystemTime *system_time) EP_ASSERT(system_time != NULL); ep_system_time_set ( - system_time, - value.wYear, - value.wMonth, - value.wDayOfWeek, - value.wDay, - value.wHour, - value.wMinute, - value.wSecond, - value.wMilliseconds); -#else - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Get System time - // PalDebugBreak(); + system_time, + value.wYear, + value.wMonth, + value.wDayOfWeek, + value.wDay, + value.wHour, + value.wMinute, + value.wSecond, + value.wMilliseconds); +#elif TARGET_UNIX + time_t tt; + struct tm *ut_ptr; + struct timeval time_val; + int timeofday_retval; + + EP_ASSERT (system_time != NULL); + + tt = time (NULL); + + timeofday_retval = gettimeofday (&time_val, NULL); + + ut_ptr = gmtime (&tt); + + uint16_t milliseconds = 0; + if (timeofday_retval != -1) { + int old_seconds; + int new_seconds; + + milliseconds = (uint16_t)(time_val.tv_usec / 1000); + + old_seconds = ut_ptr->tm_sec; + new_seconds = time_val.tv_sec % 60; + + /* just in case we reached the next second in the interval between time () and gettimeofday () */ + if (old_seconds != new_seconds) + milliseconds = 999; + } + + ep_system_time_set ( + system_time, + (uint16_t)(1900 + ut_ptr->tm_year), + (uint16_t)ut_ptr->tm_mon + 1, + (uint16_t)ut_ptr->tm_wday, + (uint16_t)ut_ptr->tm_mday, + (uint16_t)ut_ptr->tm_hour, + (uint16_t)ut_ptr->tm_min, + (uint16_t)ut_ptr->tm_sec, + milliseconds); #endif } @@ -1030,9 +1074,37 @@ ep_rt_temp_path_get ( uint32_t buffer_len) { STATIC_CONTRACT_NOTHROW; -// EP_UNREACHABLE ("Can not reach here"); +#ifdef TARGET_UNIX + + EP_ASSERT (buffer != NULL); + EP_ASSERT (buffer_len > 0); + + const ep_char8_t *path = getenv ("TMPDIR"); + if (path == NULL){ + path = getenv ("TMP"); + if (path == NULL){ + path = getenv ("TEMP"); + if (path == NULL) + path = "/tmp/"; + } + } + + int32_t result = snprintf (buffer, buffer_len, path[strlen(path) - 1] == '/' ? "%s" : "%s/", path); + if (result <= 0 || (uint32_t)result >= buffer_len) + ep_raise_error (); + + +ep_on_exit: + return result; + +ep_on_error: + result = 0; + ep_exit_error_handler (); + +#else return 0; +#endif } static @@ -1313,8 +1385,9 @@ ep_rt_utf8_to_utf16le_string ( if (!str) return NULL; - // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Implementation would just use strlen and malloc to make a new buffer, and would then copy the string chars one by one + // Shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase + // Implementation would just use strlen and malloc to make a new buffer, and would then copy the string chars one by one. + // Assumes that only ASCII is used for ep_char8_t size_t len_utf8 = strlen(str); if (len_utf8 == 0) return NULL; @@ -1325,6 +1398,7 @@ ep_rt_utf8_to_utf16le_string ( for (size_t i = 0; i < len_utf8; i++) { + EP_ASSERT(isascii(str[i])); str_utf16[i] = str[i]; } @@ -1383,12 +1457,10 @@ ep_rt_utf16_to_utf8_string ( return NULL; // shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase - // TODO: Temp implementation that is the reverse of ep_rt_utf8_to_utf16le_string + // Simple implementation to create a utf8 string from a utf16 one size_t len_utf16 = len; if(len_utf16 == (size_t)-1) - { len_utf16 = ep_rt_utf16_string_len (str); - } ep_char8_t *str_utf8 = reinterpret_cast(malloc ((len_utf16 + 1) * sizeof (ep_char8_t))); if (!str_utf8) @@ -1531,6 +1603,44 @@ ep_rt_thread_setup (void) // EP_ASSERT (thread_handle != NULL); } +#ifdef TARGET_UNIX +static +inline +EventPipeThreadHolder * +pthread_getThreadHolder (void) +{ + void *value = eventpipe_tls_instance; + if (value) { + EventPipeThreadHolder *thread_holder = static_cast(value); + return thread_holder; + } + return NULL; +} + +static +inline +EventPipeThreadHolder * +pthread_createThreadHolder (void) +{ + void *value = eventpipe_tls_instance; + if (value) { + // we need to do the unallocation here + EventPipeThreadHolder *thread_holder_old = static_cast(value); + thread_holder_free_func(thread_holder_old); + eventpipe_tls_instance = NULL; + + value = NULL; + } + EventPipeThreadHolder *instance = thread_holder_alloc_func(); + if (instance){ + // We need to know when the thread is no longer in use to clean up EventPipeThreadHolder instance and will use pthread destructor function to get notification when that happens. + pthread_setspecific(eventpipe_tls_key, instance); + eventpipe_tls_instance = instance; + } + return instance; +} +#endif + static inline EventPipeThread * @@ -1538,7 +1648,11 @@ ep_rt_thread_get (void) { STATIC_CONTRACT_NOTHROW; +#ifdef TARGET_UNIX + EventPipeThreadHolder *thread_holder = pthread_getThreadHolder (); +#else EventPipeThreadHolder *thread_holder = EventPipeAotThreadHolderTLS::getThreadHolder (); +#endif return thread_holder ? ep_thread_holder_get_thread (thread_holder) : NULL; } @@ -1549,9 +1663,15 @@ ep_rt_thread_get_or_create (void) { STATIC_CONTRACT_NOTHROW; - EventPipeThreadHolder *thread_holder = EventPipeAotThreadHolderTLS::getThreadHolder (); +#ifdef TARGET_UNIX + EventPipeThreadHolder *thread_holder = pthread_getThreadHolder (); + if (!thread_holder) + thread_holder = pthread_createThreadHolder (); +#else + EventPipeThreadHolder *thread_holder = EventPipeAotThreadHolderTLS::getThreadHolder (); if (!thread_holder) thread_holder = EventPipeAotThreadHolderTLS::createThreadHolder (); +#endif return ep_thread_holder_get_thread (thread_holder); } diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index ed44c9e..a178a11 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -474,22 +474,24 @@ static void UninitDLL() // the process is terminated via `exit()` or a signal. Thus there is no such distinction // between threads. Thread* g_threadPerformingShutdown = NULL; +#endif static void __cdecl OnProcessExit() { +#ifdef _WIN32 // The process is exiting and the current thread is performing the shutdown. // When this thread exits some threads may be already rudely terminated. // It would not be a good idea for this thread to wait on any locks // or run managed code at shutdown, so we will not try detaching it. Thread* currentThread = ThreadStore::RawGetCurrentThread(); g_threadPerformingShutdown = currentThread; +#endif #ifdef FEATURE_PERFTRACING EventPipeAdapter_Shutdown(); DiagnosticServerAdapter_Shutdown(); #endif } -#endif void RuntimeThreadShutdown(void* thread) { @@ -526,7 +528,7 @@ extern "C" bool RhInitialize() if (!PalInit()) return false; -#ifdef _WIN32 +#if defined(_WIN32) || defined(FEATURE_PERFTRACING) atexit(&OnProcessExit); #endif diff --git a/src/coreclr/nativeaot/Runtime/stressLog.cpp b/src/coreclr/nativeaot/Runtime/stressLog.cpp index 3c8dd0f..ed2c02a 100644 --- a/src/coreclr/nativeaot/Runtime/stressLog.cpp +++ b/src/coreclr/nativeaot/Runtime/stressLog.cpp @@ -334,7 +334,7 @@ void ThreadStressLog::LogMsg ( uint32_t facility, int cArgs, const char* format, msg->args[i] = data; } - ASSERT(IsValid() && threadId == PalGetCurrentThreadIdForLogging()); + ASSERT(IsValid() && threadId == PalGetCurrentOSThreadId()); } @@ -342,7 +342,7 @@ void ThreadStressLog::Activate (Thread * pThread) { _ASSERTE(pThread != NULL); //there is no need to zero buffers because we could handle garbage contents - threadId = PalGetCurrentThreadIdForLogging(); + threadId = PalGetCurrentOSThreadId(); isDead = FALSE; curWriteChunk = chunkListTail; curPtr = (StressMsg *)curWriteChunk->EndPtr (); diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 4d8c2c2..9eb428c 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -1307,7 +1307,7 @@ COOP_PINVOKE_HELPER(uint8_t*, RhCurrentNativeThreadId, ()) // This function is used to get the OS thread identifier for the current thread. COOP_PINVOKE_HELPER(uint64_t, RhCurrentOSThreadId, ()) { - return PalGetCurrentThreadIdForLogging(); + return PalGetCurrentOSThreadId(); } // Standard calling convention variant and actual implementation for RhpReversePInvokeAttachOrTrapThread diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index 407adf5..e950a1e 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -1256,7 +1256,7 @@ extern "C" uint64_t PalQueryPerformanceFrequency() return GCToOSInterface::QueryPerformanceFrequency(); } -extern "C" uint64_t PalGetCurrentThreadIdForLogging() +extern "C" uint64_t PalGetCurrentOSThreadId() { #if defined(__linux__) return (uint64_t)syscall(SYS_gettid); diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 568d9c1..c6a0082 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -227,7 +227,7 @@ extern "C" uint64_t PalQueryPerformanceFrequency() return GCToOSInterface::QueryPerformanceFrequency(); } -extern "C" uint64_t PalGetCurrentThreadIdForLogging() +extern "C" uint64_t PalGetCurrentOSThreadId() { return GetCurrentThreadId(); } -- 2.7.4