Initial work to enable EventPipe in Linux (#86226)
authorLakshan Fernando <lakshanf@hotmail.com>
Thu, 25 May 2023 11:57:41 +0000 (04:57 -0700)
committerGitHub <noreply@github.com>
Thu, 25 May 2023 11:57:41 +0000 (04:57 -0700)
* 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ý <MichalStrehovsky@users.noreply.github.com>
* FB

* license header

* Update src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h

Co-authored-by: Elinor Fung <elfung@microsoft.com>
* FB

---------

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
Co-authored-by: Elinor Fung <elfung@microsoft.com>
13 files changed:
src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets
src/coreclr/nativeaot/Directory.Build.props
src/coreclr/nativeaot/Runtime/PalRedhawk.h
src/coreclr/nativeaot/Runtime/eventpipe/CMakeLists.txt
src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp [new file with mode: 0644]
src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h
src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp
src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h
src/coreclr/nativeaot/Runtime/startup.cpp
src/coreclr/nativeaot/Runtime/stressLog.cpp
src/coreclr/nativeaot/Runtime/thread.cpp
src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp

index 7cbcf4b..2e12462 100644 (file)
@@ -48,7 +48,7 @@ The .NET Foundation licenses this file to you under the MIT license.
       <IlcRPath Condition="'$(IlcRPath)' == '' and '$(_IsApplePlatform)' == 'true'">@executable_path</IlcRPath>
 
       <EventPipeName>libeventpipe-disabled</EventPipeName>
-      <EventPipeName Condition="'$(EnableNativeEventPipe)' == 'true'">libeventpipe-enabled</EventPipeName>
+      <EventPipeName Condition="'$(EnableNativeEventPipe)' == 'true' or ('$(EventSourceSupport)' == 'true' and '$(_IsApplePlatform)' != 'true')">libeventpipe-enabled</EventPipeName>
     </PropertyGroup>
 
     <ItemGroup>
index 7fdf743..76c5cb9 100644 (file)
@@ -66,7 +66,7 @@
   </PropertyGroup>
   <PropertyGroup>
     <FeaturePerfTracing>false</FeaturePerfTracing>
-    <FeaturePerfTracing Condition="'$(TargetsWindows)' == 'true'">true</FeaturePerfTracing>
+    <FeaturePerfTracing Condition="'$(TargetsWindows)' == 'true' or ('$(TargetsUnix)' == 'true' and '$(TargetsOSX)' != 'true' and '$(TargetsiOS)' != 'true' and '$(TargetsMacCatalyst)' != 'true' and '$(TargetstvOS)' != 'true')">true</FeaturePerfTracing>
   </PropertyGroup>
   <PropertyGroup>
     <DefineConstants Condition="'$(FeaturePerfTracing)' == 'true'">FEATURE_PERFTRACING;$(DefineConstants)</DefineConstants>
index 81d0826..0b2d717 100644 (file)
@@ -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();
index 287c50d..d0b918e 100644 (file)
@@ -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 (file)
index 0000000..9bb8d08
--- /dev/null
@@ -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 <eventpipe/ep-rt-config.h>
+
+#ifdef ENABLE_PERFTRACING
+#include <eventpipe/ep-types.h>
+#include <eventpipe/ep.h>
+#include <eventpipe/ep-stack-contents.h>
+#include <eventpipe/ep-rt.h>
+
+#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/<pid>/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 */
index df255f7..aaea673 100644 (file)
@@ -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);
 }
 
 /*
index fea284d..72c040c 100644 (file)
 #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<ep_rt_thread_id_t>(0);
+    return static_cast<ep_rt_thread_id_t>(PalGetCurrentOSThreadId());
 #else
     return static_cast<ep_rt_thread_id_t>(::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<LPCWSTR>(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<LPCWSTR>(str));
+    #endif
 }
 
 uint32_t
@@ -509,6 +520,17 @@ ep_rt_aot_volatile_store_ptr_without_barrier (
     VolatileStoreWithoutBarrier<void *> ((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<EventPipeThreadHolder*>(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)
index 786a679..ffa5abb 100644 (file)
@@ -6,6 +6,10 @@
 #define __EVENTPIPE_RT_AOT_H__
 
 #include <ctype.h>  // For isspace
+#ifdef TARGET_UNIX
+#include <sys/time.h>
+#include <pthread.h>
+#endif
 
 #include <eventpipe/ep-rt-config.h>
 #ifdef ENABLE_PERFTRACING
 #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<ep_char16_t *>(value.GetValue ()), -1);
+//    return ep_rt_utf16_to_utf8_string (reinterpret_cast<ep_char16_t *>(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<ep_char8_t *>(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<EventPipeThreadHolder*>(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<EventPipeThreadHolder*>(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);
 }
index ed44c9e..a178a11 100644 (file)
@@ -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
 
index 3c8dd0f..ed2c02a 100644 (file)
@@ -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 ();
index 4d8c2c2..9eb428c 100644 (file)
@@ -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
index 407adf5..e950a1e 100644 (file)
@@ -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);
index 568d9c1..c6a0082 100644 (file)
@@ -227,7 +227,7 @@ extern "C" uint64_t PalQueryPerformanceFrequency()
     return GCToOSInterface::QueryPerformanceFrequency();
 }
 
-extern "C" uint64_t PalGetCurrentThreadIdForLogging()
+extern "C" uint64_t PalGetCurrentOSThreadId()
 {
     return GetCurrentThreadId();
 }