Skip to content

Commit

Permalink
[EventTrace] AddEventPipe filter data parsing (#109624)
Browse files Browse the repository at this point in the history
* [EventTrace] Add EventPipe FilterData parsing

* Add NativeAOT counterpart

* Address feedback

Only keep implemented filter types in enum
Add EventPipe parsing to nativeaot counterpart
Add bounds check on value portion of buffer

* Guard against non null-terminated buffers

* Fix macro guard

* Fix break condition
  • Loading branch information
mdh1418 authored Nov 12, 2024
1 parent 63d93b7 commit bb5baa8
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 15 deletions.
69 changes: 62 additions & 7 deletions src/coreclr/nativeaot/Runtime/eventtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,67 @@ enum CallbackProviderIndex
DotNETRuntimePrivate = 3
};

#ifdef FEATURE_ETW
// EventFilterType identifies the filter type used by the PEVENT_FILTER_DESCRIPTOR
enum EventFilterType
{
// data should be pairs of UTF8 null terminated strings all concatenated together.
// The first element of the pair is the key and the 2nd is the value. We expect one of the
// keys to be the string "GCSeqNumber" and the value to be a number encoded as text.
// This is the standard way EventPipe encodes filter values
StringKeyValueEncoding = 0,
// data should be an 8 byte binary LONGLONG value
// this is the historic encoding defined by .NET Framework for use with ETW
LongBinaryClientSequenceNumber = 1
};

void ParseFilterDataClientSequenceNumber(
EVENT_FILTER_DESCRIPTOR * FilterData,
LONGLONG * pClientSequenceNumber)
{
if (FilterData == NULL)
return;

if (FilterData->Type == LongBinaryClientSequenceNumber && FilterData->Size == sizeof(LONGLONG))
{
*pClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr);
}
else if (FilterData->Type == StringKeyValueEncoding)
{
const char* buffer = reinterpret_cast<const char*>(FilterData->Ptr);
const char* buffer_end = buffer + FilterData->Size;

while (buffer < buffer_end)
{
const char* key = buffer;
size_t key_len = strnlen(key, buffer_end - buffer);
buffer += key_len + 1;

if (buffer >= buffer_end)
break;

const char* value = buffer;
size_t value_len = strnlen(value, buffer_end - buffer);
buffer += value_len + 1;

if (buffer > buffer_end)
break;

if (strcmp(key, "GCSeqNumber") != 0)
continue;

char* endPtr = nullptr;
long parsedValue = strtol(value, &endPtr, 10);
if (endPtr != value && *endPtr == '\0')
{
*pClientSequenceNumber = static_cast<LONGLONG>(parsedValue);
break;
}
}
}
}
#endif // FEATURE_ETW

void EtwCallbackCommon(
CallbackProviderIndex ProviderIndex,
ULONG ControlCode,
Expand Down Expand Up @@ -164,13 +225,7 @@ void EtwCallbackCommon(
// Profilers may (optionally) specify extra data in the filter parameter
// to log with the GCStart event.
LONGLONG l64ClientSequenceNumber = 0;
EVENT_FILTER_DESCRIPTOR* filterDesc = (EVENT_FILTER_DESCRIPTOR*)pFilterData;
if ((filterDesc != NULL) &&
(filterDesc->Type == 1) &&
(filterDesc->Size == sizeof(l64ClientSequenceNumber)))
{
l64ClientSequenceNumber = *(LONGLONG *) (filterDesc->Ptr);
}
ParseFilterDataClientSequenceNumber((EVENT_FILTER_DESCRIPTOR*)pFilterData, &l64ClientSequenceNumber);
ETW::GCLog::ForceGC(l64ClientSequenceNumber);
}
#endif
Expand Down
71 changes: 64 additions & 7 deletions src/coreclr/vm/eventtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2328,6 +2328,69 @@ enum CallbackProviderIndex
DotNETRuntimePrivate = 3
};

#if !defined(HOST_UNIX)
// EventFilterType identifies the filter type used by the PEVENT_FILTER_DESCRIPTOR
enum EventFilterType
{
// data should be pairs of UTF8 null terminated strings all concatenated together.
// The first element of the pair is the key and the 2nd is the value. We expect one of the
// keys to be the string "GCSeqNumber" and the value to be a number encoded as text.
// This is the standard way EventPipe encodes filter values
StringKeyValueEncoding = 0,
// data should be an 8 byte binary LONGLONG value
// this is the historic encoding defined by .NET Framework for use with ETW
LongBinaryClientSequenceNumber = 1
};

VOID ParseFilterDataClientSequenceNumber(
PEVENT_FILTER_DESCRIPTOR FilterData,
LONGLONG * pClientSequenceNumber)
{
LIMITED_METHOD_CONTRACT;

if (FilterData == NULL)
return;

if (FilterData->Type == LongBinaryClientSequenceNumber && FilterData->Size == sizeof(LONGLONG))
{
*pClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr);
}
else if (FilterData->Type == StringKeyValueEncoding)
{
const char* buffer = reinterpret_cast<const char*>(FilterData->Ptr);
const char* buffer_end = buffer + FilterData->Size;

while (buffer < buffer_end)
{
const char* key = buffer;
size_t key_len = strnlen(key, buffer_end - buffer);
buffer += key_len + 1;

if (buffer >= buffer_end)
break;

const char* value = buffer;
size_t value_len = strnlen(value, buffer_end - buffer);
buffer += value_len + 1;

if (buffer > buffer_end)
break;

if (strcmp(key, "GCSeqNumber") != 0)
continue;

char* endPtr = nullptr;
long parsedValue = strtol(value, &endPtr, 10);
if (endPtr != value && *endPtr == '\0')
{
*pClientSequenceNumber = static_cast<LONGLONG>(parsedValue);
break;
}
}
}
}
#endif // !defined(HOST_UNIX)

// Common handler for all ETW or EventPipe event notifications. Based on the provider that
// was enabled/disabled, this implementation forwards the event state change onto GCHeapUtilities
// which will inform the GC to update its local state about what events are enabled.
Expand Down Expand Up @@ -2412,13 +2475,7 @@ VOID EtwCallbackCommon(
// to log with the GCStart event.
LONGLONG l64ClientSequenceNumber = 0;
#if !defined(HOST_UNIX)
PEVENT_FILTER_DESCRIPTOR FilterData = (PEVENT_FILTER_DESCRIPTOR)pFilterData;
if ((FilterData != NULL) &&
(FilterData->Type == 1) &&
(FilterData->Size == sizeof(l64ClientSequenceNumber)))
{
l64ClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr);
}
ParseFilterDataClientSequenceNumber((PEVENT_FILTER_DESCRIPTOR)pFilterData, &l64ClientSequenceNumber);
#endif // !defined(HOST_UNIX)
ETW::GCLog::ForceGC(l64ClientSequenceNumber);
}
Expand Down
2 changes: 1 addition & 1 deletion src/native/eventpipe/ep-provider.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ provider_invoke_callback (EventPipeProviderCallbackData *provider_callback_data)
if (j < filter_data_len)
buffer_size = j + 1;

ep_event_filter_desc_init (&event_filter_desc, (uint64_t)buffer, buffer_size, 0);
ep_event_filter_desc_init (&event_filter_desc, (uint64_t)buffer, buffer_size, /* EventFilterType.StringKeyValueEncoding */ 0);
is_event_filter_desc_init = true;
}

Expand Down

0 comments on commit bb5baa8

Please sign in to comment.