Skip to content

Commit 0281235

Browse files
LakshanFjkotaselinor-fung
authored
Initial gcdump support (#94673)
* Initial gcdump support minus GCBulkRootStaticVar * Update src/coreclr/nativeaot/Runtime/eventtrace_bulktype.cpp Co-authored-by: Jan Kotas <[email protected]> * Apply suggestions from code review Co-authored-by: Elinor Fung <[email protected]> * remove rgTypeParameters * Add comments to the parameterized count --------- Co-authored-by: Jan Kotas <[email protected]> Co-authored-by: Elinor Fung <[email protected]>
1 parent f067016 commit 0281235

File tree

7 files changed

+79
-99
lines changed

7 files changed

+79
-99
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ BGCOverflow_V1
1515
BGCPlanEnd
1616
BGCRevisit
1717
BGCSweepEnd
18+
BulkType
1819
Contention
1920
ContentionLockCreated
2021
ContentionStart_V2

src/coreclr/nativeaot/Runtime/eventtrace_bulktype.cpp

Lines changed: 37 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
BulkTypeValue::BulkTypeValue()
2424
: cTypeParameters(0)
25-
, rgTypeParameters()
2625
, ullSingleTypeParameter(0)
2726
{
2827
LIMITED_METHOD_CONTRACT;
@@ -47,7 +46,6 @@ void BulkTypeValue::Clear()
4746
ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
4847
cTypeParameters = 0;
4948
ullSingleTypeParameter = 0;
50-
rgTypeParameters.Release();
5149
}
5250

5351
//---------------------------------------------------------------------------------------
@@ -59,7 +57,6 @@ void BulkTypeValue::Clear()
5957
// Fire an ETW event for all the types we batched so far, and then reset our state
6058
// so we can start batching new types at the beginning of the array.
6159
//
62-
6360
void BulkTypeEventLogger::FireBulkTypeEvent()
6461
{
6562
LIMITED_METHOD_CONTRACT;
@@ -69,64 +66,51 @@ void BulkTypeEventLogger::FireBulkTypeEvent()
6966
// No types were batched up, so nothing to send
7067
return;
7168
}
72-
73-
// Normally, we'd use the MC-generated FireEtwBulkType for all this gunk, but
74-
// it's insufficient as the bulk type event is too complex (arrays of structs of
75-
// varying size). So we directly log the event via EventDataDescCreate and
76-
// EventWrite
77-
78-
// We use one descriptor for the count + one for the ClrInstanceID + 4
79-
// per batched type (to include fixed-size data + name + param count + param
80-
// array). But the system limit of 128 descriptors per event kicks in way
81-
// before the 64K event size limit, and we already limit our batch size
82-
// (m_nBulkTypeValueCount) to stay within the 128 descriptor limit.
83-
EVENT_DATA_DESCRIPTOR EventData[128];
8469
UINT16 nClrInstanceID = GetClrInstanceId();
8570

86-
UINT iDesc = 0;
87-
88-
_ASSERTE(iDesc < _countof(EventData));
89-
EventDataDescCreate(&EventData[iDesc++], &m_nBulkTypeValueCount, sizeof(m_nBulkTypeValueCount));
71+
if(m_pBulkTypeEventBuffer == NULL)
72+
{
73+
// The buffer could not be allocated when this object was created, so bail.
74+
return;
75+
}
9076

91-
_ASSERTE(iDesc < _countof(EventData));
92-
EventDataDescCreate(&EventData[iDesc++], &nClrInstanceID, sizeof(nClrInstanceID));
77+
UINT iSize = 0;
9378

9479
for (int iTypeData = 0; iTypeData < m_nBulkTypeValueCount; iTypeData++)
9580
{
81+
BulkTypeValue& target = m_rgBulkTypeValues[iTypeData];
82+
9683
// Do fixed-size data as one bulk copy
97-
_ASSERTE(iDesc < _countof(EventData));
98-
EventDataDescCreate(
99-
&EventData[iDesc++],
100-
&(m_rgBulkTypeValues[iTypeData].fixedSizedData),
101-
sizeof(m_rgBulkTypeValues[iTypeData].fixedSizedData));
84+
memcpy(
85+
m_pBulkTypeEventBuffer + iSize,
86+
&(target.fixedSizedData),
87+
sizeof(target.fixedSizedData));
88+
iSize += sizeof(target.fixedSizedData);
10289

10390
// Do var-sized data individually per field
104-
105-
// Type name (nonexistent and thus empty on nativeaot)
106-
_ASSERTE(iDesc < _countof(EventData));
107-
EventDataDescCreate(&EventData[iDesc++], L"", sizeof(WCHAR));
91+
// No name in event, so just the null terminator
92+
m_pBulkTypeEventBuffer[iSize++] = 0;
93+
m_pBulkTypeEventBuffer[iSize++] = 0;
10894

10995
// Type parameter count
110-
_ASSERTE(iDesc < _countof(EventData));
111-
EventDataDescCreate(
112-
&EventData[iDesc++],
113-
&(m_rgBulkTypeValues[iTypeData].cTypeParameters),
114-
sizeof(m_rgBulkTypeValues[iTypeData].cTypeParameters));
96+
ULONG cTypeParams = target.cTypeParameters;
97+
ULONG *ptrInt = (ULONG*)(m_pBulkTypeEventBuffer + iSize);
98+
*ptrInt = cTypeParams;
99+
iSize += sizeof(ULONG);
115100

116101
// Type parameter array
117-
if (m_rgBulkTypeValues[iTypeData].cTypeParameters > 0)
102+
if (cTypeParams == 1)
103+
{
104+
memcpy(m_pBulkTypeEventBuffer + iSize, &target.ullSingleTypeParameter, sizeof(ULONGLONG) * cTypeParams);
105+
iSize += sizeof(ULONGLONG) * cTypeParams;
106+
}
107+
else if (cTypeParams > 1)
118108
{
119-
_ASSERTE(iDesc < _countof(EventData));
120-
EventDataDescCreate(
121-
&EventData[iDesc++],
122-
((m_rgBulkTypeValues[iTypeData].cTypeParameters == 1) ?
123-
&(m_rgBulkTypeValues[iTypeData].ullSingleTypeParameter) :
124-
(ULONGLONG*)(m_rgBulkTypeValues[iTypeData].rgTypeParameters)),
125-
sizeof(ULONGLONG) * m_rgBulkTypeValues[iTypeData].cTypeParameters);
109+
ASSERT_UNCONDITIONALLY("unexpected value of cTypeParams greater than 1");
126110
}
127111
}
128112

129-
EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &BulkType, iDesc, EventData);
113+
FireEtwBulkType(m_nBulkTypeValueCount, GetClrInstanceId(), iSize, m_pBulkTypeEventBuffer);
130114

131115
// Reset state
132116
m_nBulkTypeValueCount = 0;
@@ -251,7 +235,6 @@ SHash<LoggedTypesTraits>* s_loggedTypesHash = NULL;
251235
// Index into internal array where the info got batched. Or -1 if there was a
252236
// failure.
253237
//
254-
255238
int BulkTypeEventLogger::LogSingleType(MethodTable * pEEType)
256239
{
257240
#ifdef MULTIPLE_HEAPS
@@ -295,8 +278,13 @@ int BulkTypeEventLogger::LogSingleType(MethodTable * pEEType)
295278
// Determine this MethodTable's module.
296279
RuntimeInstance * pRuntimeInstance = GetRuntimeInstance();
297280

298-
ULONGLONG osModuleHandle = (ULONGLONG) pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle();
299-
281+
// EEType for GC statics are not fully populated and they do not have a valid TypeManager. We will identify them by checking for `ElementType_Unknown`.
282+
// We will not be able to get the osModuleHandle for these
283+
ULONGLONG osModuleHandle = 0;
284+
if (pEEType->GetElementType() != ElementType_Unknown)
285+
{
286+
osModuleHandle = (ULONGLONG) pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle();
287+
}
300288
pVal->fixedSizedData.ModuleID = osModuleHandle;
301289

302290
if (pEEType->IsParameterizedType())
@@ -371,15 +359,6 @@ int BulkTypeEventLogger::LogSingleType(MethodTable * pEEType)
371359

372360
void BulkTypeEventLogger::LogTypeAndParameters(uint64_t thAsAddr)
373361
{
374-
// BulkTypeEventLogger currently fires ETW events only
375-
if (!ETW_TRACING_CATEGORY_ENABLED(
376-
MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
377-
TRACE_LEVEL_INFORMATION,
378-
CLR_TYPE_KEYWORD))
379-
{
380-
return;
381-
}
382-
383362
MethodTable * pEEType = (MethodTable *) thAsAddr;
384363

385364
// Batch up this type. This grabs useful info about the type, including any
@@ -398,25 +377,15 @@ void BulkTypeEventLogger::LogTypeAndParameters(uint64_t thAsAddr)
398377
// We're about to recursively call ourselves for the type parameters, so make a
399378
// local copy of their type handles first (else, as we log them we could flush
400379
// and clear out m_rgBulkTypeValues, thus trashing pVal)
401-
NewArrayHolder<ULONGLONG> rgTypeParameters;
402380
DWORD cTypeParams = pVal->cTypeParameters;
403381
if (cTypeParams == 1)
404382
{
405383
LogTypeAndParameters(pVal->ullSingleTypeParameter);
406384
}
407385
else if (cTypeParams > 1)
408386
{
409-
rgTypeParameters = new (nothrow) ULONGLONG[cTypeParams];
410-
for (DWORD i=0; i < cTypeParams; i++)
411-
{
412-
rgTypeParameters[i] = pVal->rgTypeParameters[i];
413-
}
414-
415-
// Recursively log any referenced parameter types
416-
for (DWORD i=0; i < cTypeParams; i++)
417-
{
418-
LogTypeAndParameters(rgTypeParameters[i]);
419-
}
387+
388+
ASSERT_UNCONDITIONALLY("unexpected value of cTypeParams greater than 1");
420389
}
421390
}
422391

src/coreclr/nativeaot/Runtime/eventtrace_gcheap.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,35 +35,29 @@
3535

3636
BOOL ETW::GCLog::ShouldWalkHeapObjectsForEtw()
3737
{
38-
// @TODO: until the below issue is fixed correctly
39-
// https://github.com/dotnet/runtime/issues/88491
40-
return FALSE;
38+
return RUNTIME_PROVIDER_CATEGORY_ENABLED(
39+
TRACE_LEVEL_INFORMATION,
40+
CLR_GCHEAPDUMP_KEYWORD);
4141
}
4242

4343
BOOL ETW::GCLog::ShouldWalkHeapRootsForEtw()
4444
{
45-
// @TODO: until the below issue is fixed correctly
46-
// https://github.com/dotnet/runtime/issues/88491
47-
return FALSE;
45+
return RUNTIME_PROVIDER_CATEGORY_ENABLED(
46+
TRACE_LEVEL_INFORMATION,
47+
CLR_GCHEAPDUMP_KEYWORD);
4848
}
4949

5050
BOOL ETW::GCLog::ShouldTrackMovementForEtw()
5151
{
52-
LIMITED_METHOD_CONTRACT;
5352
return RUNTIME_PROVIDER_CATEGORY_ENABLED(
5453
TRACE_LEVEL_INFORMATION,
5554
CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD);
5655
}
5756

5857
BOOL ETW::GCLog::ShouldWalkStaticsAndCOMForEtw()
5958
{
60-
// @TODO:
61-
return FALSE;
62-
}
63-
64-
void ETW::GCLog::WalkStaticsAndCOMForETW()
65-
{
66-
// @TODO:
59+
// @TODO
60+
return false;
6761
}
6862

6963
// Batches the list of moved/surviving references for the GCBulkMovedObjectRanges /
@@ -482,6 +476,14 @@ HRESULT ETW::GCLog::ForceGCForDiagnostics()
482476
return hr;
483477
}
484478

479+
//---------------------------------------------------------------------------------------
480+
// WalkStaticsAndCOMForETW walks both CCW/RCW objects and static variables.
481+
//---------------------------------------------------------------------------------------
482+
483+
void ETW::GCLog::WalkStaticsAndCOMForETW()
484+
{
485+
}
486+
485487
// Holds state that batches of roots, nodes, edges, and types as the GC walks the heap
486488
// at the end of a collection.
487489
class EtwGcHeapDumpContext

src/coreclr/nativeaot/Runtime/eventtracepriv.h

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,9 @@ class BulkTypeValue
114114
// Below are the remainder of each struct in the bulk type event (i.e., the
115115
// variable-sized data). The var-sized fields are copied into the event individually
116116
// (not directly), so they don't need to have the same layout as in the ETW manifest
117-
118-
// This is really a denorm of the size already stored in rgTypeParameters, but we
119-
// need a persistent place to stash this away so EventDataDescCreate & EventWrite
120-
// have a reliable place to copy it from. This is filled in at the last minute,
121-
// when sending the event.
122117
ULONG cTypeParameters;
123118

124-
// If > 1 type parameter, this is an array of their MethodTable*'s
125-
NewArrayHolder<ULONGLONG> rgTypeParameters;
126-
127-
// If exactly one type parameter, this is its MethodTable*. (If != 1 type parameter,
128-
// this is 0.)
119+
// We expect only one type parameter. See the explanation at BulkTypeEventLogger::LogSingleType on generic parameters for additional details.
129120
ULONGLONG ullSingleTypeParameter;
130121
};
131122

@@ -138,6 +129,9 @@ class BulkTypeEventLogger
138129
{
139130
private:
140131

132+
// The maximum event size, and the size of the buffer that we allocate to hold the event contents.
133+
static const size_t kSizeOfEventBuffer = 65536;
134+
141135
// Estimate of how many bytes we can squeeze in the event data for the value struct
142136
// array. (Intentionally overestimate the size of the non-array parts to keep it safe.)
143137
static const int kMaxBytesTypeValues = (cbMaxEtwEvent - 0x30);
@@ -178,14 +172,25 @@ class BulkTypeEventLogger
178172
// List of types we've batched.
179173
BulkTypeValue m_rgBulkTypeValues[kMaxCountTypeValues];
180174

175+
BYTE *m_pBulkTypeEventBuffer;
176+
181177
int LogSingleType(MethodTable * pEEType);
182178

183179
public:
184180
BulkTypeEventLogger() :
185181
m_nBulkTypeValueCount(0),
186182
m_nBulkTypeValueByteCount(0)
183+
, m_pBulkTypeEventBuffer(NULL)
187184
{
188185
LIMITED_METHOD_CONTRACT;
186+
187+
m_pBulkTypeEventBuffer = new (nothrow) BYTE[kSizeOfEventBuffer];
188+
}
189+
190+
~BulkTypeEventLogger()
191+
{
192+
delete[] m_pBulkTypeEventBuffer;
193+
m_pBulkTypeEventBuffer = NULL;
189194
}
190195

191196
void LogTypeAndParameters(ULONGLONG thAsAddr);

src/coreclr/vm/eventtrace_bulktype.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "eventtracepriv.h"
2121

2222
//---------------------------------------------------------------------------------------
23-
// BulkStaticsLogger: Batches up and logs static variable roots
23+
// BulkComLogger: Batches up and logs RCW and CCW
2424
//---------------------------------------------------------------------------------------
2525

2626
BulkComLogger::BulkComLogger(BulkTypeEventLogger *typeLogger)

src/tests/tracing/eventpipe/gcdump/gcdump.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics.Tracing;
66
using System.IO;
77
using System.Linq;
8+
using System.Text;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using System.Collections.Generic;
@@ -43,7 +44,8 @@ public static int TestEntryPoint()
4344
new EventPipeProvider("Microsoft-Windows-DotNETRuntime", eventLevel: EventLevel.Verbose, keywords: (long)ClrTraceEventParser.Keywords.GCHeapSnapshot)
4445
};
4546

46-
return IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, providers, 1024, _DoesRundownContainMethodEvents);
47+
bool enableRundown = TestLibrary.Utilities.IsNativeAot? false: true;
48+
return IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, providers, 1024, _DoesRundownContainMethodEvents, enableRundownProvider: enableRundown);
4749
}
4850

4951
private static Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
@@ -101,18 +103,18 @@ public static int TestEntryPoint()
101103
// and high enough to catch issues. There should be between hundreds and thousands
102104
// for each, but the number is variable and the point of the test is to verify
103105
// that we get any events at all.
106+
104107
if (_seenGCStart
105108
&& _seenGCStop
106109
&& _bulkTypeCount > 50
107110
&& _bulkNodeCount > 50
108-
&& _bulkEdgeCount > 50
109-
&& _bulkRootEdgeCount > 50
110-
&& _bulkRootStaticVarCount > 50)
111+
&& _bulkEdgeCount > 50)
111112
{
112-
return 100;
113+
// Native AOT hasn't yet implemented statics. Hence _bulkRootStaticVarCount is zero and _bulkRootEdgeCount can be low
114+
if ((TestLibrary.Utilities.IsNativeAot && _bulkRootEdgeCount > 20) || (_bulkRootStaticVarCount > 50 && _bulkRootEdgeCount > 50))
115+
return 100;
113116
}
114117

115-
116118
Console.WriteLine($"Test failed due to missing GC heap events.");
117119
Console.WriteLine($"_seenGCStart = {_seenGCStart}");
118120
Console.WriteLine($"_seenGCStop = {_seenGCStop}");

src/tests/tracing/eventpipe/gcdump/gcdump.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313
<Compile Include="$(MSBuildProjectName).cs" />
1414
<ProjectReference Include="../common/eventpipe_common.csproj" />
1515
<ProjectReference Include="../common/Microsoft.Diagnostics.NETCore.Client/Microsoft.Diagnostics.NETCore.Client.csproj" />
16+
<ProjectReference Include="$(TestLibraryProjectPath)" />
1617
</ItemGroup>
1718
</Project>

0 commit comments

Comments
 (0)