diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index 734349ba2f1d34..f8593ec4c87e1d 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -82,13 +82,13 @@ - + + + diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 619f7ae0426f52..fac6af9914e22a 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -39,10 +39,6 @@ if(CLR_CMAKE_TARGET_WASI) add_compile_definitions(_WASI_EMULATED_PROCESS_CLOCKS) add_compile_definitions(_WASI_EMULATED_MMAN) add_compile_definitions(_WASI_EMULATED_GETPID) - # EventPipe / diag-server PAL require sockets we have not validated - # for the WASI bring-up; revisit once corerun.wasm runs. - set(FEATURE_EVENT_TRACE 0) - set(FEATURE_PERFTRACING 0) include_directories(BEFORE SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/pal/src/include/pal/wasi) # Cross-compile try_run() answers — wasm32 binaries cannot execute on # the build host, so pre-seed the cache. diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 4ce076aabd2fc0..d7e767113eb060 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -28,18 +28,6 @@ false - - - false - - true true diff --git a/src/coreclr/debug/debug-pal/CMakeLists.txt b/src/coreclr/debug/debug-pal/CMakeLists.txt index 59b18e2c91ddb8..d7a233790e7316 100644 --- a/src/coreclr/debug/debug-pal/CMakeLists.txt +++ b/src/coreclr/debug/debug-pal/CMakeLists.txt @@ -27,12 +27,7 @@ if(CLR_CMAKE_HOST_WIN32) endif(CLR_CMAKE_HOST_WIN32) if(CLR_CMAKE_HOST_UNIX) - if (NOT CLR_CMAKE_TARGET_WASI) - # ds-ipc-pal-socket.c needs UNIX/TCP sockets; re-enable once - # WASI PERFTRACING is wired. Tracked by https://github.com/dotnet/runtime/issues/130383 - - set(DEBUG_PAL_REFEREENCE_DIAGNOSTICSERVER ON) - endif() + set(DEBUG_PAL_REFEREENCE_DIAGNOSTICSERVER ON) add_definitions(-DPAL_IMPLEMENTATION) add_definitions(-D_POSIX_C_SOURCE=200809L) diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h index d54e561cbf9871..2372cff7abc808 100644 --- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h @@ -314,12 +314,24 @@ ep_rt_init_finish (void) STATIC_CONTRACT_NOTHROW; } +#if defined(TARGET_WASI) && defined(PERFTRACING_DISABLE_THREADS) +// WASI has no host event loop, so jobs are kept on a native list that the managed +// WASI event loop drains (see eventpipeinternal.cpp). +bool ep_rt_coreclr_wasi_queue_job (size_t (*cb)(void *data), void *data); +void ep_rt_coreclr_wasi_run_jobs (void); +#endif + static inline void ep_rt_shutdown (void) { STATIC_CONTRACT_NOTHROW; +#if defined(TARGET_WASI) && defined(PERFTRACING_DISABLE_THREADS) + // The event loop no longer runs, so run the queued jobs once to let the now-disabled streaming + // sessions release their references; freeing a session ends its trace file. + ep_rt_coreclr_wasi_run_jobs (); +#endif } static @@ -1111,6 +1123,8 @@ ep_rt_queue_job ( if (!cb (params)) SystemJS_DiagnosticServerQueueJob (cb, params); return true; +#elif defined(TARGET_WASI) + return ep_rt_coreclr_wasi_queue_job ((size_t (*)(void *))job_func, params); #else EP_UNREACHABLE ("Not implemented on this platform"); return false; diff --git a/src/coreclr/vm/eventpipeinternal.cpp b/src/coreclr/vm/eventpipeinternal.cpp index 5a2dc9933c800c..72eafc9f1e9cab 100644 --- a/src/coreclr/vm/eventpipeinternal.cpp +++ b/src/coreclr/vm/eventpipeinternal.cpp @@ -296,4 +296,86 @@ extern "C" BOOL QCALLTYPE EventPipeInternal_WaitForSessionSignal(UINT64 sessionI return result; } +#if defined(TARGET_WASI) && defined(PERFTRACING_DISABLE_THREADS) +// WASI has no host event loop to schedule EventPipe jobs (session streaming, diagnostic server) on, +// so they are kept on this list and run by the managed WasiEventLoop (WasiEventPipeJobs.cs). +// A job returns non-zero when it is done; otherwise it stays queued and runs again on the next pump. +// Single-threaded by construction, so no locking. +struct WasiEventPipeJob +{ + size_t (*Callback)(void *data); + void *Data; + WasiEventPipeJob *Next; +}; + +static WasiEventPipeJob *s_wasiJobsHead = nullptr; +static WasiEventPipeJob *s_wasiJobsTail = nullptr; + +static void WasiAppendJob(WasiEventPipeJob *job) +{ + job->Next = nullptr; + if (s_wasiJobsTail != nullptr) + s_wasiJobsTail->Next = job; + else + s_wasiJobsHead = job; + s_wasiJobsTail = job; +} + +bool ep_rt_coreclr_wasi_queue_job(size_t (*cb)(void *data), void *data) +{ + _ASSERTE(cb != nullptr); + WasiEventPipeJob *job = new (nothrow) WasiEventPipeJob(); + if (job == nullptr) + return false; + + job->Callback = cb; + job->Data = data; + WasiAppendJob(job); + return true; +} + +extern "C" CLR_BOOL QCALLTYPE EventPipeInternal_WasiHasPendingJobs(QCallExceptionStatus* qcallError) +{ + QCALL_CONTRACT; + + CLR_BOOL pending = FALSE; + + BEGIN_QCALL; + + pending = s_wasiJobsHead != nullptr ? TRUE : FALSE; + + END_QCALL; + return pending; +} + +void ep_rt_coreclr_wasi_run_jobs() +{ + // Detach the current list so jobs queued while running are picked up on the next pump. + WasiEventPipeJob *job = s_wasiJobsHead; + s_wasiJobsHead = nullptr; + s_wasiJobsTail = nullptr; + + while (job != nullptr) + { + WasiEventPipeJob *next = job->Next; + if (job->Callback(job->Data) != 0) + delete job; + else + WasiAppendJob(job); + job = next; + } +} + +extern "C" void QCALLTYPE EventPipeInternal_WasiRunJobs(QCallExceptionStatus* qcallError) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + ep_rt_coreclr_wasi_run_jobs(); + + END_QCALL; +} +#endif // TARGET_WASI && PERFTRACING_DISABLE_THREADS + #endif // FEATURE_PERFTRACING diff --git a/src/coreclr/vm/eventpipeinternal.h b/src/coreclr/vm/eventpipeinternal.h index 09cb9acedaca28..cbfd0f040a43dc 100644 --- a/src/coreclr/vm/eventpipeinternal.h +++ b/src/coreclr/vm/eventpipeinternal.h @@ -104,6 +104,12 @@ extern "C" BOOL QCALLTYPE EventPipeInternal_WaitForSessionSignal( INT32 timeoutMs, QCallExceptionStatus* qcallError); +#if defined(TARGET_WASI) && defined(PERFTRACING_DISABLE_THREADS) +extern "C" CLR_BOOL QCALLTYPE EventPipeInternal_WasiHasPendingJobs(QCallExceptionStatus* qcallError); + +extern "C" void QCALLTYPE EventPipeInternal_WasiRunJobs(QCallExceptionStatus* qcallError); +#endif // TARGET_WASI && PERFTRACING_DISABLE_THREADS + #endif // FEATURE_PERFTRACING #endif // __EVENTPIPEINTERNAL_H__ diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 6582a1ccf5e9f4..41e423998dfa4b 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -489,6 +489,10 @@ static const Entry s_QCall[] = DllImportEntry(EventPipeInternal_GetNextEvent) DllImportEntry(EventPipeInternal_SignalSession) DllImportEntry(EventPipeInternal_WaitForSessionSignal) +#if defined(TARGET_WASI) && defined(PERFTRACING_DISABLE_THREADS) + DllImportEntry(EventPipeInternal_WasiHasPendingJobs) + DllImportEntry(EventPipeInternal_WasiRunJobs) +#endif // TARGET_WASI && PERFTRACING_DISABLE_THREADS #endif // FEATURE_PERFTRACING #if defined(TARGET_UNIX) DllImportEntry(FreeEnvironmentStringsW) diff --git a/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp index bab5632c834b02..1cab6a0762ff7d 100644 --- a/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp @@ -316,6 +316,12 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I64(2)); } + static void CallFunc_I32_I32_I64_I32_I32_I32_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + int32_t (*fptr)(int32_t, int32_t, int64_t, int32_t, int32_t, int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int64_t, int32_t, int32_t, int32_t, int32_t, int32_t))pcode; + *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I64(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7)); + } + static void CallFunc_I32_I32_I64_I64_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t, int64_t, int64_t, int32_t) = (int32_t (*)(int32_t, int32_t, int64_t, int64_t, int32_t))pcode; @@ -372,6 +378,18 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I64(0)); } + static void CallFunc_I64_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + int32_t (*fptr)(int64_t, int32_t) = (int32_t (*)(int64_t, int32_t))pcode; + *((int32_t*)pRet) = (*fptr)(ARG_I64(0), ARG_I32(1)); + } + + static void CallFunc_I64_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + int32_t (*fptr)(int64_t, int32_t, int32_t) = (int32_t (*)(int64_t, int32_t, int32_t))pcode; + *((int32_t*)pRet) = (*fptr)(ARG_I64(0), ARG_I32(1), ARG_I32(2)); + } + static void CallFunc_I64_I32_I64_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int64_t, int32_t, int64_t, int32_t, int32_t) = (int32_t (*)(int64_t, int32_t, int64_t, int32_t, int32_t))pcode; @@ -397,12 +415,24 @@ namespace *((int64_t*)pRet) = (*fptr)(ARG_I32(0)); } + static void CallFunc_I32_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + int64_t (*fptr)(int32_t, int32_t) = (int64_t (*)(int32_t, int32_t))pcode; + *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1)); + } + static void CallFunc_I32_I32_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t, int32_t, int32_t) = (int64_t (*)(int32_t, int32_t, int32_t))pcode; *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2)); } + static void CallFunc_I32_I32_I32_I32_I32_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + int64_t (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) = (int64_t (*)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))pcode; + *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5)); + } + static void CallFunc_I32_I32_I32_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t, int32_t, int32_t, int64_t) = (int64_t (*)(int32_t, int32_t, int32_t, int64_t))pcode; @@ -523,6 +553,24 @@ namespace (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); } + static void CallFunc_F64_F64_F64_F64_F64_F64_F64_F64_F64_I32_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + void (*fptr)(double, double, double, double, double, double, double, double, double, int32_t, int32_t, int32_t) = (void (*)(double, double, double, double, double, double, double, double, double, int32_t, int32_t, int32_t))pcode; + (*fptr)(ARG_F64(0), ARG_F64(1), ARG_F64(2), ARG_F64(3), ARG_F64(4), ARG_F64(5), ARG_F64(6), ARG_F64(7), ARG_F64(8), ARG_I32(9), ARG_I32(10), ARG_I32(11)); + } + + static void CallFunc_F64_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + void (*fptr)(double, int32_t, int32_t) = (void (*)(double, int32_t, int32_t))pcode; + (*fptr)(ARG_F64(0), ARG_I32(1), ARG_I32(2)); + } + + static void CallFunc_F64_I32_I32_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + void (*fptr)(double, int32_t, int32_t, int32_t, int32_t) = (void (*)(double, int32_t, int32_t, int32_t, int32_t))pcode; + (*fptr)(ARG_F64(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4)); + } + NOINLINE static void CallFunc_F64_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -567,6 +615,12 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_IND(3), ARG_I32(4), ARG_I32(5), ARG_I32(6)); } + static void CallFunc_I32_I32_F64_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + void (*fptr)(int32_t, int32_t, double, int32_t) = (void (*)(int32_t, int32_t, double, int32_t))pcode; + (*fptr)(ARG_I32(0), ARG_I32(1), ARG_F64(2), ARG_I32(3)); + } + static void CallFunc_I32_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t))pcode; @@ -609,6 +663,12 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6)); } + static void CallFunc_I32_I32_I32_I32_I64_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + { + void (*fptr)(int32_t, int32_t, int32_t, int32_t, int64_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t, int64_t, int32_t))pcode; + (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I64(4), ARG_I32(5)); + } + NOINLINE static void CallFunc_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -698,6 +758,7 @@ const StringToPortableSigThunk g_portableCallHelperThunks[] = { { "Miiiil", (void*)&CallFunc_I32_I32_I32_I64_RetI32 }, { "Miiiip", (void*)&CallFunc_I32_I32_I32_RetI32_PE }, { "Miiil", (void*)&CallFunc_I32_I32_I64_RetI32 }, + { "Miiiliiiii", (void*)&CallFunc_I32_I32_I64_I32_I32_I32_I32_I32_RetI32 }, { "Miiilli", (void*)&CallFunc_I32_I32_I64_I64_I32_RetI32 }, { "Miiip", (void*)&CallFunc_I32_I32_RetI32_PE }, { "Miil", (void*)&CallFunc_I32_I64_RetI32 }, @@ -707,11 +768,15 @@ const StringToPortableSigThunk g_portableCallHelperThunks[] = { { "Miilli", (void*)&CallFunc_I32_I64_I64_I32_RetI32 }, { "Miip", (void*)&CallFunc_I32_RetI32_PE }, { "Mil", (void*)&CallFunc_I64_RetI32 }, + { "Mili", (void*)&CallFunc_I64_I32_RetI32 }, + { "Milii", (void*)&CallFunc_I64_I32_I32_RetI32 }, { "Mililii", (void*)&CallFunc_I64_I32_I64_I32_I32_RetI32 }, { "Mip", (void*)&CallFunc_Void_RetI32_PE }, { "Ml", (void*)&CallFunc_Void_RetI64 }, { "Mli", (void*)&CallFunc_I32_RetI64 }, + { "Mlii", (void*)&CallFunc_I32_I32_RetI64 }, { "Mliii", (void*)&CallFunc_I32_I32_I32_RetI64 }, + { "Mliiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI64 }, { "Mliiil", (void*)&CallFunc_I32_I32_I32_I64_RetI64 }, { "Mlili", (void*)&CallFunc_I32_I64_I32_RetI64 }, { "Mlillp", (void*)&CallFunc_I32_I64_I64_RetI64_PE }, @@ -731,6 +796,9 @@ const StringToPortableSigThunk g_portableCallHelperThunks[] = { { "MvS8iiiiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_I32_I32_RetVoid }, { "MvS8iiiiiiiiiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetVoid }, { "MvTp", (void*)&CallFunc_This_RetVoid_PE }, + { "Mvdddddddddiii", (void*)&CallFunc_F64_F64_F64_F64_F64_F64_F64_F64_F64_I32_I32_I32_RetVoid }, + { "Mvdii", (void*)&CallFunc_F64_I32_I32_RetVoid }, + { "Mvdiiii", (void*)&CallFunc_F64_I32_I32_I32_I32_RetVoid }, { "Mvdiip", (void*)&CallFunc_F64_I32_I32_RetVoid_PE }, { "Mvfiip", (void*)&CallFunc_F32_I32_I32_RetVoid_PE }, { "Mvi", (void*)&CallFunc_I32_RetVoid }, @@ -738,6 +806,7 @@ const StringToPortableSigThunk g_portableCallHelperThunks[] = { { "Mvii", (void*)&CallFunc_I32_I32_RetVoid }, { "MviiS8S8ii", (void*)&CallFunc_I32_I32_S8_S8_I32_I32_RetVoid }, { "MviiS8S8iii", (void*)&CallFunc_I32_I32_S8_S8_I32_I32_I32_RetVoid }, + { "Mviidi", (void*)&CallFunc_I32_I32_F64_I32_RetVoid }, { "Mviii", (void*)&CallFunc_I32_I32_I32_RetVoid }, { "MviiiS8S8i", (void*)&CallFunc_I32_I32_I32_S8_S8_I32_RetVoid }, { "MviiiS8S8ii", (void*)&CallFunc_I32_I32_I32_S8_S8_I32_I32_RetVoid }, @@ -745,6 +814,7 @@ const StringToPortableSigThunk g_portableCallHelperThunks[] = { { "Mviiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_RetVoid }, { "Mviiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetVoid }, { "Mviiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetVoid }, + { "Mviiiili", (void*)&CallFunc_I32_I32_I32_I32_I64_I32_RetVoid }, { "Mviiip", (void*)&CallFunc_I32_I32_I32_RetVoid_PE }, { "Mviip", (void*)&CallFunc_I32_I32_RetVoid_PE }, { "Mvili", (void*)&CallFunc_I32_I64_I32_RetVoid }, diff --git a/src/coreclr/vm/wasm/wasi/callhelpers-reverse.cpp b/src/coreclr/vm/wasm/wasi/callhelpers-reverse.cpp index f60c87519e1a7a..9cd802b741c60d 100644 --- a/src/coreclr/vm/wasm/wasi/callhelpers-reverse.cpp +++ b/src/coreclr/vm/wasm/wasi/callhelpers-reverse.cpp @@ -92,6 +92,19 @@ static void Call_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeH ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallToString_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallToString_I32_I32_I32_RetVoid); } +static MethodDesc* MD_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid(void * arg0, int32_t arg1, uint32_t arg2, int64_t arg3, int64_t arg4, void * arg5, void * arg6) +{ + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Diagnostics.Tracing.EventPipeEventProvider, System.Private.CoreLib", "Callback", &MD_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid); + } + + int64_t args[7] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6 }; + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid); +} + static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ClearManaged_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ClearManaged_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, void * arg3) { @@ -1023,6 +1036,7 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 3962535319, "CallEntryPoint#5:System.Private.CoreLib:System:Environment", { &MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid } }, { 1821934012, "CallStartupHook#2:System.Private.CoreLib:System:StartupHookProvider", { &MD_System_Private_CoreLib_System_StartupHookProvider_CallStartupHook_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StartupHookProvider_CallStartupHook_I32_I32_RetVoid } }, { 2915047114, "CallToString#3:System.Private.CoreLib:System.Runtime.CompilerServices:RuntimeHelpers", { &MD_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallToString_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallToString_I32_I32_I32_RetVoid } }, + { 4077371982, "Callback#7:System.Private.CoreLib:System.Diagnostics.Tracing:EventPipeEventProvider", { &MD_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider_Callback_I32_I32_I32_I64_I64_I32_I32_RetVoid } }, { 3358042195, "ClearManaged#4:System.Private.CoreLib:System.StubHelpers:MngdRefCustomMarshaler", { &MD_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ClearManaged_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ClearManaged_I32_I32_I32_I32_RetVoid } }, { 2311968855, "ClearNative#4:System.Private.CoreLib:System.StubHelpers:MngdRefCustomMarshaler", { &MD_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ClearNative_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ClearNative_I32_I32_I32_I32_RetVoid } }, { 3378852959, "ConfigCallback#5:System.Private.CoreLib:System:GC", { &MD_System_Private_CoreLib_System_GC_ConfigCallback_I32_I32_I32_I32_I64_RetVoid, (void*)&Call_System_Private_CoreLib_System_GC_ConfigCallback_I32_I32_I32_I32_I64_RetVoid } }, diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs index b5d71dacd83346..aecf69e0ca1846 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs @@ -30,7 +30,7 @@ public static TheoryData GetListeners() { TheoryData data = new TheoryData(); - if (PlatformDetection.IsNetCore && PlatformDetection.IsNotAndroid && PlatformDetection.IsNotBrowser && + if (PlatformDetection.IsNetCore && PlatformDetection.IsNotAndroid && PlatformDetection.IsNotBrowser && PlatformDetection.IsNotWasi && (PlatformDetection.IsNotMonoRuntime || PlatformDetection.IsMacCatalyst)) { data.Add(new EventPipeListener()); diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs index ae0d1602e8407c..079d623bb16b1c 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs @@ -30,7 +30,7 @@ public static TheoryData GetListenerConfigurations() public static TheoryData GetListeners() { TheoryData data = new TheoryData(); - if (PlatformDetection.IsNetCore && PlatformDetection.IsNotAndroid && PlatformDetection.IsNotBrowser && + if (PlatformDetection.IsNetCore && PlatformDetection.IsNotAndroid && PlatformDetection.IsNotBrowser && PlatformDetection.IsNotWasi && (PlatformDetection.IsNotMonoRuntime || PlatformDetection.IsMacCatalyst)) { data.Add(new EventPipeListener()); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index abe0dcc14a7c0f..a8abf0ac747810 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2993,6 +2993,9 @@ + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index 0dba1ec0041502..074425c7d660a7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -56,6 +56,10 @@ internal static T PollWasiEventLoopUntilResolved(Task mainTask) s_mainTask = mainTask; while (!mainTask.IsCompleted) { +#if FEATURE_PERFTRACING && CORECLR && !FEATURE_MULTITHREADING + // Before Dispatch, which can block in wasi:io/poll, so the pump's timer is registered first. + WasiEventPipeJobs.EnsurePumpIfPending(); +#endif ThreadPoolWorkQueue.Dispatch(); WasiFinalizerScheduler.DrainIfPending(); } @@ -78,6 +82,10 @@ internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) s_mainTask = mainTask; while (!mainTask.IsCompleted) { +#if FEATURE_PERFTRACING && CORECLR && !FEATURE_MULTITHREADING + // Before Dispatch, which can block in wasi:io/poll, so the pump's timer is registered first. + WasiEventPipeJobs.EnsurePumpIfPending(); +#endif ThreadPoolWorkQueue.Dispatch(); WasiFinalizerScheduler.DrainIfPending(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventPipeJobs.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventPipeJobs.cs new file mode 100644 index 00000000000000..01aee8d405d8f1 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventPipeJobs.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Threading +{ + // Runs the native EventPipe jobs (session streaming, diagnostic server) that browser schedules with + // setTimeout. WASI has no host event loop, so the native side keeps a job list and WasiEventLoop + // starts this pump when it sees pending jobs. Jobs only run while the event loop is being pumped. + internal static partial class WasiEventPipeJobs + { + // Matches the browser re-schedule interval for unfinished jobs. + private const int PumpIntervalMs = 100; + + private static bool s_pumpRunning; + + [ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EventPipeInternal_WasiHasPendingJobs")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool HasPendingJobs(); + + [ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EventPipeInternal_WasiRunJobs")] + private static partial void RunJobs(); + + internal static void EnsurePumpIfPending() + { + if (s_pumpRunning || !HasPendingJobs()) + { + return; + } + + s_pumpRunning = true; + _ = PumpAsync(); + } + + private static async Task PumpAsync() + { + try + { + while (true) + { + RunJobs(); + if (!HasPendingJobs()) + { + break; + } + + await Task.Delay(PumpIntervalMs).ConfigureAwait(false); + } + } + finally + { + s_pumpRunning = false; + } + } + } +} diff --git a/src/native/eventpipe/ep-session.c b/src/native/eventpipe/ep-session.c index 09c6a12b41f104..17d63b49f1a853 100644 --- a/src/native/eventpipe/ep-session.c +++ b/src/native/eventpipe/ep-session.c @@ -161,8 +161,8 @@ static size_t streaming_loop_tick(EventPipeSession *const session) { ok = ep_session_write_all_buffers_to_file (session, &events_written); EP_GCX_PREEMP_EXIT if (!ok) { + // Keep the job so the next tick sees streaming disabled and releases its reference. ep_disable ((EventPipeSessionID)session); - return 1; // done } return 0; // continue } @@ -190,7 +190,11 @@ session_create_streaming_thread (EventPipeSession *session) #else ep_session_inc_ref (session); ep_rt_volatile_store_uint32_t (&session->started, 1); - ep_rt_queue_job ((void *)streaming_loop_tick, (void *)session); + if (!ep_rt_queue_job ((void *)streaming_loop_tick, (void *)session)) { + // Release the job's reference directly; ep_session_dec_ref asserts streaming is + // disabled, and the session's own reference keeps it alive. + ep_rt_atomic_dec_uint32_t (&session->ref_count); + } #endif }