diff --git a/src/native/eventpipe/configure.cmake b/src/native/eventpipe/configure.cmake index 8bb378efd3f20d..294a6767e3d783 100644 --- a/src/native/eventpipe/configure.cmake +++ b/src/native/eventpipe/configure.cmake @@ -28,6 +28,11 @@ check_include_file( HAVE_SYS_IOCTL_H ) +check_include_file( + sys/syscall.h + HAVE_SYS_SYSCALL_H +) + check_include_file( unistd.h HAVE_UNISTD_H @@ -43,6 +48,17 @@ check_include_file( HAVE_ERRNO_H ) +check_include_file( + sys/mman.h + HAVE_SYS_MMAN_H +) + +check_symbol_exists( + __NR_memfd_create + sys/syscall.h + HAVE_MEMFD_CREATE +) + if (NOT DEFINED EP_GENERATED_HEADER_PATH) message(FATAL_ERROR "Required configuration EP_GENERATED_HEADER_PATH not set.") endif (NOT DEFINED EP_GENERATED_HEADER_PATH) diff --git a/src/native/eventpipe/ds-ipc.c b/src/native/eventpipe/ds-ipc.c index cb2b051cb9ed20..05cddd7618c7cc 100644 --- a/src/native/eventpipe/ds-ipc.c +++ b/src/native/eventpipe/ds-ipc.c @@ -10,6 +10,15 @@ #include "ep.h" #include "ds-rt.h" +#if HAVE_SYS_MMAN_H && HAVE_MEMFD_CREATE && HAVE_UNISTD_H +#include +#include +#include +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif +#endif + /* * Globals and volatile access functions. */ @@ -342,6 +351,19 @@ ds_ipc_stream_factory_configure (ds_ipc_error_callback_func callback) DS_LOG_DEBUG_0 ("ds_ipc_stream_factory_configure - Ignoring default LISTEN port"); #endif +#if HAVE_SYS_MMAN_H && HAVE_MEMFD_CREATE && HAVE_UNISTD_H + // Create a mapping to signal that the diagnostic server IPC is available. + // External tools can use this to detect when the runtime is ready to accept diagnostic commands. + // The mapping's lifetime is tied to the process, so tools that start after the .NET process can still detect it. + if (ds_ipc_stream_factory_any_listen_ports ()) { + int fd = (int)syscall (__NR_memfd_create, "dotnet_ipc_created", (unsigned int)MFD_CLOEXEC); + if (fd >= 0) { + mmap (NULL, 1, PROT_EXEC | PROT_READ, MAP_PRIVATE, fd, 0); + close (fd); + } + } +#endif + return result; } @@ -479,6 +501,17 @@ ds_ipc_stream_factory_resume_current_port (void) _ds_current_port->has_resumed_runtime = true; } +bool +ds_ipc_stream_factory_any_listen_ports (void) +{ + bool any_listen_ports = false; + DN_VECTOR_PTR_FOREACH_BEGIN (DiagnosticsPort *, port, _ds_port_array) { + any_listen_ports |= (port->type == DS_PORT_TYPE_LISTEN); + } DN_VECTOR_PTR_FOREACH_END; + + return any_listen_ports; +} + bool ds_ipc_stream_factory_any_suspended_ports (void) { diff --git a/src/native/eventpipe/ds-ipc.h b/src/native/eventpipe/ds-ipc.h index 7da0c5d75f300e..a13e82c8403bbc 100644 --- a/src/native/eventpipe/ds-ipc.h +++ b/src/native/eventpipe/ds-ipc.h @@ -32,6 +32,9 @@ ds_ipc_stream_factory_get_next_available_stream (ds_ipc_error_callback_func call void ds_ipc_stream_factory_resume_current_port (void); +bool +ds_ipc_stream_factory_any_listen_ports (void); + bool ds_ipc_stream_factory_any_suspended_ports (void); diff --git a/src/native/eventpipe/ep-shared-config.h.in b/src/native/eventpipe/ep-shared-config.h.in index 0eeb73e1dcb4f2..025bbab83a557e 100644 --- a/src/native/eventpipe/ep-shared-config.h.in +++ b/src/native/eventpipe/ep-shared-config.h.in @@ -2,6 +2,8 @@ #define EP_SHARED_CONFIG_H_INCLUDED #cmakedefine01 HAVE_ERRNO_H +#cmakedefine01 HAVE_SYS_MMAN_H +#cmakedefine01 HAVE_MEMFD_CREATE #cmakedefine01 HAVE_LINUX_USER_EVENTS_H #cmakedefine01 HAVE_SYS_IOCTL_H #cmakedefine01 HAVE_SYS_SOCKET_H