<MicrosoftNETCoreRuntimeCoreCLRPackageVersion>3.0.0-preview6.19280.1</MicrosoftNETCoreRuntimeCoreCLRPackageVersion>
<XunitPackageVersion>2.4.1-pre.build.4059</XunitPackageVersion>
<XunitPerformanceApiPackageVersion>1.0.0-beta-build0015</XunitPerformanceApiPackageVersion>
- <MicrosoftDiagnosticsTracingTraceEventPackageVersion>2.0.40</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
+ <MicrosoftDiagnosticsTracingTraceEventPackageVersion>2.0.43</MicrosoftDiagnosticsTracingTraceEventPackageVersion>
<CommandLineParserVersion>2.2.0</CommandLineParserVersion>
<!-- Scenario tests install this version of Microsoft.NetCore.App, then patch coreclr binaries via xcopy. At the moment it is
internal string? FilterData => m_filterData;
}
+ internal enum EventPipeSerializationFormat
+ {
+ NetPerf,
+ NetTrace
+ }
+
internal sealed class EventPipeConfiguration
{
private string m_outputFile;
+ private EventPipeSerializationFormat m_format;
private uint m_circularBufferSizeInMB;
private List<EventPipeProviderConfiguration> m_providers;
private TimeSpan m_minTimeBetweenSamples = TimeSpan.FromMilliseconds(1);
internal EventPipeConfiguration(
string outputFile,
+ EventPipeSerializationFormat format,
uint circularBufferSizeInMB)
{
if(string.IsNullOrEmpty(outputFile))
throw new ArgumentOutOfRangeException(nameof(circularBufferSizeInMB));
}
m_outputFile = outputFile;
+ m_format = format;
m_circularBufferSizeInMB = circularBufferSizeInMB;
m_providers = new List<EventPipeProviderConfiguration>();
}
get { return m_outputFile; }
}
+ internal EventPipeSerializationFormat Format
+ {
+ get { return m_format; }
+ }
+
internal uint CircularBufferSizeInMB
{
get { return m_circularBufferSizeInMB; }
s_sessionID = EventPipeInternal.Enable(
configuration.OutputFile,
+ configuration.Format,
configuration.CircularBufferSizeInMB,
providers,
(uint)providers.Length);
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern UInt64 Enable(
string? outputFile,
+ EventPipeSerializationFormat format,
uint circularBufferSizeInMB,
EventPipeProviderConfiguration[] providers,
uint numProviders);
// Create a new configuration object.
EventPipeConfiguration config = new EventPipeConfiguration(
outputFilePath,
+ (Config_NetTraceFormat != 0) ? EventPipeSerializationFormat.NetTrace : EventPipeSerializationFormat.NetPerf,
Config_EventPipeCircularMB);
// Get the configuration.
private static string BuildTraceFileName()
{
return GetAppName() + "." + Interop.GetCurrentProcessId().ToString() +
- ((Config_NetTraceFormat > 0) ? NetTraceFileExtension : NetPerfFileExtension);
+ ((Config_NetTraceFormat != 0) ? NetTraceFileExtension : NetPerfFileExtension);
}
private static string GetAppName()
new EventPipeProviderConfiguration(NativeRuntimeEventSource.EventSourceName, (ulong) aggregatedKeywords, (uint) highestLevel, null)
};
- m_sessionID = EventPipeInternal.Enable(null, 1024, providerConfiguration, 1);
+ m_sessionID = EventPipeInternal.Enable(null, EventPipeSerializationFormat.NetTrace, 1024, providerConfiguration, 1);
Debug.Assert(m_sessionID != 0);
// Get the session information that is required to properly dispatch events.
// EventPipe
//
RETAIL_CONFIG_DWORD_INFO(INTERNAL_EnableEventPipe, W("EnableEventPipe"), 0, "Enable/disable event pipe. Non-zero values enable tracing.")
-RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeNetTraceFormat, W("EventPipeNetTraceFormat"), 0, "Enable/disable using the newer nettrace file format.")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeNetTraceFormat, W("EventPipeNetTraceFormat"), 1, "Enable/disable using the newer nettrace file format.")
RETAIL_CONFIG_STRING_INFO(INTERNAL_EventPipeOutputPath, W("EventPipeOutputPath"), "The full path excluding file name for the trace file that will be written when COMPlus_EnableEventPipe=1")
RETAIL_CONFIG_STRING_INFO(INTERNAL_EventPipeConfig, W("EventPipeConfig"), "Configuration for EventPipe.")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeRundown, W("EventPipeRundown"), 1, "Enable/disable eventpipe rundown.")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeCircularMB, W("EventPipeCircularMB"), 1024, "The EventPipe circular buffer size in megabytes.")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeProcNumbers, W("EventPipeProcNumbers"), 0, "Enable/disable capturing processor numbers in EventPipe event headers")
#ifdef FEATURE_GDBJIT
///
EventPipeConfiguration EventPipe::s_config;
EventPipeEventSource *EventPipe::s_pEventSource = nullptr;
VolatilePtr<EventPipeSession> EventPipe::s_pSessions[MaxNumberOfSessions];
+#ifndef FEATURE_PAL
+unsigned int * EventPipe::s_pProcGroupOffsets = nullptr;
+#endif
#ifdef FEATURE_PAL
// This function is auto-generated from /src/scripts/genEventPipe.py
const unsigned long DefaultProfilerSamplingRateInNanoseconds = 1000000; // 1 msec.
SampleProfiler::SetSamplingRate(DefaultProfilerSamplingRateInNanoseconds);
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeProcNumbers) != 0)
+ {
+#ifndef FEATURE_PAL
+ // setup the windows processor group offset table
+ WORD numGroups = ::GetActiveProcessorGroupCount();
+ s_pProcGroupOffsets = new (nothrow) unsigned int[numGroups];
+ if (s_pProcGroupOffsets)
+ {
+ unsigned int countProcs = 0;
+ for (WORD i = 0; i < numGroups; i++)
+ {
+ s_pProcGroupOffsets[i] = countProcs;
+ countProcs += GetActiveProcessorCount(i);
+ }
+ }
+#endif
+ }
+
+
{
CrstHolder _crst(GetLock());
s_tracingInitialized = tracingInitialized;
// as opposed a a buffer copy here
EventPipeEventInstance instance(
event,
+ GetCurrentProcessorNumber(),
pEventPipeThread->GetOSThreadId(),
pData,
payload.GetSize(),
InvokeCallback(eventPipeProviderCallbackData);
}
+ // Returns the a number 0...N representing the processor number this thread is currently
+ // running on. If for any reason we can't tell then return 0xFFFFFFFF.
+ static unsigned int GetCurrentProcessorNumber()
+ {
+#ifndef FEATURE_PAL
+ if (s_pProcGroupOffsets)
+ {
+ PROCESSOR_NUMBER procNum;
+ GetCurrentProcessorNumberEx(&procNum);
+ return s_pProcGroupOffsets[procNum.Group] + procNum.Number;
+ }
+#endif
+ return 0xFFFFFFFF;
+ }
+
private:
static void InvokeCallback(EventPipeProviderCallbackData eventPipeProviderCallbackData);
static EventPipeConfiguration s_config;
static VolatilePtr<EventPipeSession> s_pSessions[MaxNumberOfSessions];
static EventPipeEventSource *s_pEventSource;
+
+ // A mapping from windows processor group index to the total number of processors
+ // in all groups preceding it. For example if there are three groups with sizes:
+ // 1, 7, 6 the table would be 0, 1, 8
+#ifndef FEATURE_PAL
+ static unsigned int * s_pProcGroupOffsets;
+#endif
};
static_assert(EventPipe::MaxNumberOfSessions == 64, "Maximum number of EventPipe sessions is not 64.");
unsigned int dataLength = 0;
BYTE* alignedEnd = NULL;
+ unsigned int captureProcNumber = instance.GetProcNumber();
if (!m_fUseHeaderCompression)
{
memcpy(m_pWritePointer, &captureThreadId, sizeof(captureThreadId));
m_pWritePointer += sizeof(captureThreadId);
+ memcpy(m_pWritePointer, &captureProcNumber, sizeof(captureProcNumber));
+ m_pWritePointer += sizeof(captureProcNumber);
+
memcpy(m_pWritePointer, &stackId, sizeof(stackId));
m_pWritePointer += sizeof(stackId);
}
}
if (m_lastHeader.SequenceNumber + (instance.GetMetadataId() != 0 ? 1 : 0) != sequenceNumber ||
- m_lastHeader.CaptureThreadId != captureThreadId)
+ m_lastHeader.CaptureThreadId != captureThreadId ||
+ m_lastHeader.CaptureProcNumber != captureProcNumber)
{
WriteVarUInt32(pWritePointer, sequenceNumber - m_lastHeader.SequenceNumber - 1);
WriteVarUInt64(pWritePointer, captureThreadId);
+ WriteVarUInt32(pWritePointer, captureProcNumber);
flags |= (1 << 1);
}
m_lastHeader.SequenceNumber = sequenceNumber;
m_lastHeader.ThreadId = instance.GetThreadId64();
m_lastHeader.CaptureThreadId = captureThreadId;
+ m_lastHeader.CaptureProcNumber = captureProcNumber;
m_lastHeader.StackId = stackId;
m_lastHeader.TimeStamp.QuadPart = timeStamp->QuadPart;
memcpy(&m_lastHeader.ActivityId, instance.GetActivityId(), sizeof(GUID));
DWORD SequenceNumber;
ULONGLONG ThreadId;
ULONGLONG CaptureThreadId;
+ DWORD CaptureProcNumber;
DWORD StackId;
LARGE_INTEGER TimeStamp;
GUID ActivityId;
pStack = &s;
}
+ unsigned int procNumber = EventPipe::GetCurrentProcessorNumber();
EventPipeEventInstance *pInstance = new (m_pCurrent) EventPipeEventInstance(
event,
+ procNumber,
(pThread == NULL) ?
#ifdef FEATURE_PAL
::PAL_GetCurrentOSThreadId()
// Construct the event instance.
EventPipeEventInstance *pInstance = new EventPipeEventInstance(
*m_pMetadataEvent,
+ EventPipe::GetCurrentProcessorNumber(),
#ifdef FEATURE_PAL
PAL_GetCurrentOSThreadId(),
#else
EventPipeEventInstance::EventPipeEventInstance(
EventPipeEvent &event,
+ unsigned int procNumber,
ULONGLONG threadId,
BYTE *pData,
unsigned int length,
m_debugEventEnd = 0xCAFEBABE;
#endif // _DEBUG
m_pEvent = &event;
+ m_procNumber = procNumber;
m_threadId = threadId;
if (pActivityId != NULL)
{
sizeof(unsigned int) + // Sequence number (implied by the buffer containing the event instance)
sizeof(m_threadId) + // Thread ID
sizeof(ULONGLONG) + // Capture Thread ID (implied by the buffer containing the event instance)
+ sizeof(m_procNumber) + // ProcNumber
sizeof(unsigned int) + // Stack intern table id
sizeof(m_timeStamp) + // TimeStamp
sizeof(m_activityId) + // Activity ID
public:
- EventPipeEventInstance(EventPipeEvent &event, ULONGLONG threadID, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+ EventPipeEventInstance(EventPipeEvent &event,
+ unsigned int procNumber,
+ ULONGLONG threadID,
+ BYTE *pData,
+ unsigned int length,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId);
void EnsureStack(const EventPipeSession &session);
m_metadataId = metadataId;
}
+ unsigned int GetProcNumber() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_procNumber;
+ }
+
DWORD GetThreadId32() const
{
LIMITED_METHOD_CONTRACT;
EventPipeEvent *m_pEvent;
unsigned int m_metadataId;
+ unsigned int m_procNumber;
ULONGLONG m_threadId;
LARGE_INTEGER m_timeStamp;
GUID m_activityId;
UINT64 QCALLTYPE EventPipeInternal::Enable(
__in_z LPCWSTR outputFile,
+ EventPipeSerializationFormat format,
UINT32 circularBufferSizeInMB,
EventPipeProviderConfiguration *pProviders,
UINT32 numProviders)
// Invalid input!
if (circularBufferSizeInMB == 0 ||
+ format >= EventPipeSerializationFormat::Count ||
numProviders == 0 ||
pProviders == nullptr)
{
BEGIN_QCALL;
{
- // This was a quick and dirty mechanism for testing but it may not be the final
- // configuration scheme we want. This path handles both the AI profiler scenario
- // doing private reflection and the EnableEventPipe env var. If we want to flip
- // the default for one but not the other we'll have to hoist the configuration
- // check into managed code.
- EventPipeSerializationFormat format = EventPipeSerializationFormat::NetPerfV3;
- if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeNetTraceFormat) > 0)
- {
- format = EventPipeSerializationFormat::NetTraceV4;
- }
-
sessionID = EventPipe::Enable(
outputFile,
circularBufferSizeInMB,
//!
static UINT64 QCALLTYPE Enable(
__in_z LPCWSTR outputFile,
+ EventPipeSerializationFormat format,
UINT32 circularBufferSizeInMB,
EventPipeProviderConfiguration *pProviders,
UINT32 numProviders);
return CanParse && (circularBufferSizeInMB > 0);
}
+static bool TryParseSerializationFormat(uint8_t*& bufferCursor, uint32_t& bufferLen, EventPipeSerializationFormat& serializationFormat)
+{
+ const bool CanParse = TryParse(bufferCursor, bufferLen, (uint32_t&)serializationFormat);
+ return CanParse && (0 <= (int)serializationFormat) && ((int)serializationFormat < (int)EventPipeSerializationFormat::Count);
+}
+
const EventPipeCollectTracingCommandPayload* EventPipeCollectTracingCommandPayload::TryParse(BYTE* lpBuffer, uint16_t& BufferSize)
{
CONTRACTL
uint8_t* pBufferCursor = payload->incomingBuffer;
uint32_t bufferLen = BufferSize;
if (!TryParseCircularBufferSize(pBufferCursor, bufferLen, payload->circularBufferSizeInMB) ||
+ !TryParseSerializationFormat(pBufferCursor, bufferLen, payload->serializationFormat) ||
!TryParseString(pBufferCursor, bufferLen, payload->outputPath) ||
!EventPipeProtocolHelper::TryParseProviderConfiguration(pBufferCursor, bufferLen, payload->providerConfigs))
{
return;
}
- // IPC should produce nettrace by default or be selectable via protocol
- // but this is a simple starting point for testing
- EventPipeSerializationFormat format = EventPipeSerializationFormat::NetPerfV3;
- if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeNetTraceFormat) > 0)
- {
- format = EventPipeSerializationFormat::NetTraceV4;
- }
-
auto sessionId = EventPipe::Enable(
nullptr, // strOutputPath (ignored in this scenario)
payload->circularBufferSizeInMB, // circularBufferSizeInMB
payload->providerConfigs.Ptr(), // pConfigs
static_cast<uint32_t>(payload->providerConfigs.Size()), // numConfigs
EventPipeSessionType::IpcStream, // EventPipeSessionType
- format, // EventPipeSerializationFormat
+ payload->serializationFormat, // EventPipeSerializationFormat
pStream); // IpcStream
if (sessionId == 0)
// The protocol buffer is defined as:
// X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z
- // message = uint circularBufferMB, string outputPath, array<provider_config> providers
+ // message = uint circularBufferMB, uint format, string outputPath, array<provider_config> providers
// uint = 4 little endian bytes
// wchar = 2 little endian bytes, UTF16 encoding
// array<T> = uint length, length # of Ts
// string = (array<char> where the last char must = 0) or (length = 0)
// provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data
uint32_t circularBufferSizeInMB;
+ EventPipeSerializationFormat serializationFormat;
LPCWSTR outputPath;
CQuickArray<EventPipeProviderConfiguration> providerConfigs;
static const EventPipeCollectTracingCommandPayload* TryParse(BYTE* lpBuffer, uint16_t& BufferSize);
namespace Tracing.Tests.Common
{
+ public enum EventPipeSerializationFormat
+ {
+ NetPerf,
+ NetTrace
+ }
+
public sealed class TraceConfiguration
{
private ConstructorInfo m_configurationCtor;
new object[]
{
outputFile,
+ EventPipeSerializationFormat.NetTrace,
circularBufferMB
});
}
return false;
}
- m_configurationCtor = configurationType.GetConstructor(
+ Type formatType = SPC.GetType("System.Diagnostics.Tracing.EventPipeSerializationFormat");
+ if (formatType == null)
+ {
+ Console.WriteLine("formatType == null");
+ return false;
+ }
+
+ m_configurationCtor = configurationType.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
- new Type[] { typeof(string), typeof(uint) },
+ new Type[] { typeof(string), formatType, typeof(uint) },
null);
if(m_configurationCtor == null)
{
namespace EventPipe.Issue22247
{
+ public enum EventPipeSerializationFormat
+ {
+ NetPerf,
+ NetTrace
+ }
+
public sealed class TraceConfiguration
{
private ConstructorInfo m_configurationCtor;
new object[]
{
outputFile,
+ EventPipeSerializationFormat.NetTrace,
circularBufferMB
});
}
Console.WriteLine("configurationType == null");
return false;
}
+ Type formatType = SPC.GetType("System.Diagnostics.Tracing.EventPipeSerializationFormat");
+ if (formatType == null)
+ {
+ Console.WriteLine("formatType == null");
+ return false;
+ }
m_configurationCtor = configurationType.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
- new Type[] { typeof(string), typeof(uint) },
+ new Type[] { typeof(string), formatType, typeof(uint) },
null);
if (m_configurationCtor == null)
{