Skip to content

Commit 0c40743

Browse files
Generate module rundown events (#116338)
* Generate module rundown events This is sufficient to get module information in `dotnet-gcdump collect`. The tool itself will need tweaks because it only looks at IL paths right now, but this should be all that's necessary to build on top. * Put back GUID * Fix linux * More Linux fixing * linux * wow * Feedback * Maybe it works? * Apply suggestions from code review Co-authored-by: Jan Kotas <[email protected]> * Build break * Copilot commit Prompt: The use of mbstowcs_s is incorrect here. The input buffer is UTF-8, output is UTF-16. Replace the incorrect use of mbstowcs_s with MultiByteToWideChar. * Update issues.targets --------- Co-authored-by: Jan Kotas <[email protected]>
1 parent a7bd51e commit 0c40743

File tree

15 files changed

+448
-23
lines changed

15 files changed

+448
-23
lines changed

src/coreclr/nativeaot/Runtime/Pal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <pthread.h>
2424
#endif
2525

26+
#include <minipal/guid.h>
27+
2628
#include "CommonTypes.h"
2729
#include "CommonMacros.h"
2830
#include "PalLimitedContext.h"
@@ -159,6 +161,8 @@ bool PalInit();
159161
// Given the OS handle of a loaded module, compute the upper and lower virtual address bounds (inclusive).
160162
void PalGetModuleBounds(HANDLE hOsHandle, _Out_ uint8_t ** ppLowerBound, _Out_ uint8_t ** ppUpperBound);
161163

164+
void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath);
165+
162166
struct NATIVE_CONTEXT;
163167

164168
#if _WIN32

src/coreclr/nativeaot/Runtime/disabledeventtrace.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "common.h"
1010

1111
#include "eventtrace.h"
12+
#include "eventtracebase.h"
1213

1314
void EventTracing_Initialize() { }
1415

@@ -18,6 +19,7 @@ bool IsRuntimeProviderEnabled(uint8_t level, uint64_t keyword)
1819
}
1920

2021
void ETW::GCLog::FireGcStart(ETW_GC_INFO * pGcInfo) { }
22+
void ETW::LoaderLog::ModuleLoad(HANDLE pModule) { }
2123

2224
#ifdef FEATURE_ETW
2325
BOOL ETW::GCLog::ShouldTrackMovementForEtw() { return FALSE; }

src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "threadstore.h"
2626
#include "threadstore.inl"
2727
#include "eventtrace_context.h"
28+
#include "eventtracebase.h"
2829

2930
// Uses _rt_aot_lock_internal_t that has CrstStatic as a field
3031
// This is initialized at the beginning and EventPipe library requires the lock handle to be maintained by the runtime
@@ -75,7 +76,19 @@ bool
7576
ep_rt_aot_providers_validate_all_disabled (void)
7677
{
7778
return !MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled
78-
&& !MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled;
79+
&& !MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled
80+
&& !MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled;
81+
}
82+
83+
void
84+
ep_rt_aot_provider_config_init (
85+
EventPipeProviderConfiguration *provider_config)
86+
{
87+
if (!ep_rt_utf8_string_compare (ep_config_get_rundown_provider_name_utf8 (), ep_provider_config_get_provider_name (provider_config))) {
88+
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.Level = (uint8_t) ep_provider_config_get_logging_level (provider_config);
89+
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.EnabledKeywordsBitmask = ep_provider_config_get_keywords (provider_config);
90+
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.EventPipeProvider.IsEnabled = true;
91+
}
7992
}
8093

8194
void
@@ -194,6 +207,12 @@ ep_rt_aot_entrypoint_assembly_name_get_utf8 (void)
194207
return entrypoint_assembly_name;
195208
}
196209

210+
void
211+
ep_rt_aot_execute_rundown (dn_vector_ptr_t* execution_checkpoints)
212+
{
213+
ETW::EnumerationLog::EndRundown();
214+
}
215+
197216
const ep_char8_t *
198217
ep_rt_aot_diagnostics_command_line_get (void)
199218
{

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ void
346346
ep_rt_provider_config_init (EventPipeProviderConfiguration *provider_config)
347347
{
348348
STATIC_CONTRACT_NOTHROW;
349+
extern void ep_rt_aot_provider_config_init (EventPipeProviderConfiguration *provider_config);
350+
ep_rt_aot_provider_config_init(provider_config);
349351
}
350352

351353
// This function is auto-generated from /src/scripts/genEventPipe.py
@@ -707,7 +709,9 @@ ep_rt_execute_rundown (dn_vector_ptr_t *execution_checkpoints)
707709
{
708710
STATIC_CONTRACT_NOTHROW;
709711

710-
// NativeAOT does not currently support rundown
712+
extern void
713+
ep_rt_aot_execute_rundown (dn_vector_ptr_t *execution_checkpoints);
714+
ep_rt_aot_execute_rundown (execution_checkpoints);
711715
}
712716

713717
/*
@@ -1445,8 +1449,8 @@ void
14451449
ep_rt_thread_setup (void)
14461450
{
14471451
STATIC_CONTRACT_NOTHROW;
1448-
1449-
// Likely not needed and do nothing until testing shows to be required
1452+
extern ep_rt_thread_handle_t ep_rt_aot_setup_thread (void);
1453+
ep_rt_aot_setup_thread ();
14501454
}
14511455

14521456
static

src/coreclr/nativeaot/Runtime/eventpipe/gen-eventing-event-inc.lst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Contention
2121
ContentionLockCreated
2222
ContentionStart_V2
2323
ContentionStop_V1
24+
DCEndComplete_V1
25+
DCEndInit_V1
2426
DecreaseMemoryPressure
2527
DestroyGCHandle
2628
ExceptionCatchStart
@@ -81,6 +83,7 @@ GenAwareBegin
8183
GenAwareEnd
8284
IncreaseMemoryPressure
8385
LockCreated
86+
ModuleDCEnd_V2
8487
ModuleLoad_V2
8588
PinObjectAtGCTime
8689
PinPlugAtGCTime

src/coreclr/nativeaot/Runtime/eventtrace.cpp

Lines changed: 165 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "threadstore.h"
2222
#include "threadstore.inl"
2323

24+
#include <minipal/utf8.h>
25+
2426
EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context = { W("Microsoft-Windows-DotNETRuntime"), 0, false, 0 };
2527
DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context = {
2628
#ifdef FEATURE_ETW
@@ -29,6 +31,14 @@ DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context = {
2931
MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context
3032
};
3133

34+
EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context = { W("Microsoft-Windows-DotNETRuntimeRundown"), 0, false, 0 };
35+
DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context = {
36+
#ifdef FEATURE_ETW
37+
&MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
38+
#endif
39+
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context
40+
};
41+
3242
EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context = { W("Microsoft-Windows-DotNETRuntimePrivate"), 0, false, 0 };
3343
DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context = {
3444
#ifdef FEATURE_ETW
@@ -42,6 +52,11 @@ bool IsRuntimeProviderEnabled(uint8_t level, uint64_t keyword)
4252
return RUNTIME_PROVIDER_CATEGORY_ENABLED(level, keyword);
4353
}
4454

55+
bool IsRuntimeRundownProviderEnabled(uint8_t level, uint64_t keyword)
56+
{
57+
return RUNTIME_RUNDOWN_PROVIDER_CATEGORY_ENABLED(level, keyword);
58+
}
59+
4560
volatile LONGLONG ETW::GCLog::s_l64LastClientSequenceNumber = 0;
4661

4762
//---------------------------------------------------------------------------------------
@@ -81,13 +96,16 @@ void EventTracing_Initialize()
8196
#ifdef FEATURE_ETW
8297
MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.IsEnabled = FALSE;
8398
MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.IsEnabled = FALSE;
99+
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context.IsEnabled = FALSE;
84100

85101
// Register the ETW providers with the system.
86102
EventRegisterMicrosoft_Windows_DotNETRuntimePrivate();
87103
EventRegisterMicrosoft_Windows_DotNETRuntime();
104+
EventRegisterMicrosoft_Windows_DotNETRuntimeRundown();
88105

89106
MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimePrivateHandle;
90107
MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeHandle;
108+
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeRundownHandle;
91109
#endif // FEATURE_ETW
92110
}
93111

@@ -187,9 +205,13 @@ void EtwCallbackCommon(
187205
case DotNETRuntime:
188206
ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
189207
break;
208+
case DotNETRuntimeRundown:
209+
ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context;
210+
break;
190211
case DotNETRuntimePrivate:
191212
ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context;
192213
break;
214+
193215
default:
194216
_ASSERTE(!"EtwCallbackCommon was called with invalid context");
195217
return;
@@ -261,7 +283,7 @@ void EtwCallbackCommon(
261283

262284
void EtwCallback(
263285
const GUID * /*SourceId*/,
264-
uint32_t IsEnabled,
286+
uint32_t ControlCode,
265287
uint8_t Level,
266288
uint64_t MatchAnyKeyword,
267289
uint64_t MatchAllKeyword,
@@ -272,16 +294,27 @@ void EtwCallback(
272294
if (context == NULL)
273295
return;
274296

297+
// A manifest based provider can be enabled to multiple event tracing sessions
298+
// As long as there is atleast 1 enabled session, IsEnabled will be TRUE
299+
// Since classic providers can be enabled to only a single session,
300+
// IsEnabled will be TRUE when it is enabled and FALSE when disabled
301+
BOOL bEnabled =
302+
((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
303+
(ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE));
304+
275305
context->Level = Level;
276306
context->MatchAnyKeyword = MatchAnyKeyword;
277307
context->MatchAllKeyword = MatchAllKeyword;
278-
context->IsEnabled = IsEnabled;
308+
context->IsEnabled = bEnabled;
279309

280310
CallbackProviderIndex providerIndex = DotNETRuntime;
281311
DOTNET_TRACE_CONTEXT providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
282312
if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeHandle) {
283313
providerIndex = DotNETRuntime;
284314
providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
315+
} else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
316+
providerIndex = DotNETRuntimeRundown;
317+
providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context;
285318
} else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
286319
providerIndex = DotNETRuntimePrivate;
287320
providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context;
@@ -290,20 +323,128 @@ void EtwCallback(
290323
return;
291324
}
292325

293-
EtwCallbackCommon(providerIndex, IsEnabled, Level, MatchAnyKeyword, FilterData, EtwSessionChangeUnknown);
326+
EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData, EtwSessionChangeUnknown);
294327

295-
if (IsEnabled &&
296-
(context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) &&
297-
GCHeapUtilities::IsGCHeapInitialized())
328+
if (bEnabled)
298329
{
299-
FireEtwGCSettings_V1(GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(FALSE),
300-
GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(TRUE),
301-
GCHeapUtilities::IsServerHeap(), GetClrInstanceId());
302-
GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments();
330+
if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle &&
331+
GCHeapUtilities::IsGCHeapInitialized())
332+
{
333+
FireEtwGCSettings_V1(GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(FALSE),
334+
GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(TRUE),
335+
GCHeapUtilities::IsServerHeap(), GetClrInstanceId());
336+
GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments();
337+
}
338+
339+
if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeRundownHandle)
340+
{
341+
if (IsRuntimeRundownProviderEnabled(TRACE_LEVEL_INFORMATION, CLR_RUNDOWNEND_KEYWORD))
342+
ETW::EnumerationLog::EndRundown();
343+
}
303344
}
304345
}
346+
305347
#endif // FEATURE_ETW
306348

349+
void ETW::LoaderLog::ModuleLoad(HANDLE pModule)
350+
{
351+
if (IsRuntimeProviderEnabled(TRACE_LEVEL_INFORMATION, KEYWORDZERO))
352+
{
353+
SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad);
354+
}
355+
}
356+
357+
//---------------------------------------------------------------------------------------
358+
//
359+
// send a module load/unload or rundown event and domainmodule load and rundown event
360+
//
361+
// Arguments:
362+
// * pModule - Module loading or unloading
363+
// * dwEventOptions - Bitmask of which events to fire
364+
//
365+
void ETW::LoaderLog::SendModuleEvent(HANDLE pModule, uint32_t dwEventOptions)
366+
{
367+
const WCHAR * wszModuleFileName = NULL;
368+
const WCHAR * wszModuleILFileName = W("");
369+
370+
#if TARGET_WINDOWS
371+
PalGetModuleFileName(&wszModuleFileName, pModule);
372+
#else
373+
const char * wszModuleFileNameUtf8 = NULL;
374+
uint32_t moduleFileNameCharCount = PalGetModuleFileName(&wszModuleFileNameUtf8, pModule);
375+
CHAR16_T wszModuleFileNameUnicode[1024];
376+
minipal_convert_utf8_to_utf16(wszModuleFileNameUtf8, moduleFileNameCharCount, &wszModuleFileNameUnicode[0], ARRAY_SIZE(wszModuleFileNameUnicode), MINIPAL_MB_NO_REPLACE_INVALID_CHARS);
377+
wszModuleFileName = (const WCHAR *)&wszModuleFileNameUnicode[0];
378+
#endif
379+
380+
GUID nativeGuid;
381+
uint32_t dwAge;
382+
WCHAR wszPath[1024];
383+
PalGetPDBInfo(pModule, &nativeGuid, &dwAge, wszPath, ARRAY_SIZE(wszPath));
384+
385+
GUID zeroGuid = { 0 };
386+
387+
if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
388+
{
389+
FireEtwModuleLoad_V2(
390+
ULONGLONG(pModule),
391+
0, // AssemblyID
392+
ETW::LoaderLog::LoaderStructs::NativeModule, // Module Flags
393+
0, // Reserved1,
394+
wszModuleILFileName, // ModuleILPath,
395+
wszModuleFileName, // ModuleNativePath,
396+
GetClrInstanceId(),
397+
&zeroGuid, // ManagedPdbSignature,
398+
0, // ManagedPdbAge,
399+
NULL, // ManagedPdbBuildPath,
400+
&nativeGuid, // NativePdbSignature,
401+
dwAge, // NativePdbAge,
402+
wszPath // NativePdbBuildPath,
403+
);
404+
}
405+
else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
406+
{
407+
FireEtwModuleDCEnd_V2(
408+
ULONGLONG(pModule),
409+
0, // AssemblyID
410+
ETW::LoaderLog::LoaderStructs::NativeModule, // Module Flags
411+
0, // Reserved1,
412+
wszModuleILFileName, // ModuleILPath,
413+
wszModuleFileName, // ModuleNativePath,
414+
GetClrInstanceId(),
415+
&zeroGuid, // ManagedPdbSignature,
416+
0, // ManagedPdbAge,
417+
NULL, // ManagedPdbBuildPath,
418+
&nativeGuid, // NativePdbSignature,
419+
dwAge, // NativePdbAge,
420+
wszPath // NativePdbBuildPath,
421+
);
422+
}
423+
else
424+
{
425+
ASSERT(0);
426+
}
427+
}
428+
429+
/**************************************************************************************/
430+
/* Called when ETW is turned OFF on an existing process .Will be used by the controller for end rundown*/
431+
/**************************************************************************************/
432+
void ETW::EnumerationLog::EndRundown()
433+
{
434+
if (IsRuntimeRundownProviderEnabled(TRACE_LEVEL_INFORMATION, CLR_RUNDOWNLOADER_KEYWORD))
435+
{
436+
// begin marker event will go to the rundown provider
437+
FireEtwDCEndInit_V1(GetClrInstanceId());
438+
439+
HANDLE pModule = PalGetModuleHandleFromPointer((void*)&EndRundown);
440+
441+
LoaderLog::SendModuleEvent(pModule, EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd);
442+
443+
// end marker event will go to the rundown provider
444+
FireEtwDCEndComplete_V1(GetClrInstanceId());
445+
}
446+
}
447+
307448
void EventPipeEtwCallbackDotNETRuntime(
308449
_In_ GUID * SourceId,
309450
_In_ ULONG ControlCode,
@@ -318,6 +459,20 @@ void EventPipeEtwCallbackDotNETRuntime(
318459
EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, change);
319460
}
320461

462+
void EventPipeEtwCallbackDotNETRuntimeRundown(
463+
_In_ GUID * SourceId,
464+
_In_ ULONG ControlCode,
465+
_In_ unsigned char Level,
466+
_In_ ULONGLONG MatchAnyKeyword,
467+
_In_ ULONGLONG MatchAllKeyword,
468+
_In_opt_ EventFilterDescriptor* FilterData,
469+
_Inout_opt_ PVOID CallbackContext)
470+
{
471+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
472+
473+
EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData, change);
474+
}
475+
321476
void EventPipeEtwCallbackDotNETRuntimePrivate(
322477
_In_ GUID * SourceId,
323478
_In_ ULONG ControlCode,

0 commit comments

Comments
 (0)