// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // Trace.cpp // // Implementation of ConcRT tracing API. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- #include "concrtinternal.h" #pragma warning (push) #pragma warning (disable : 4702) // unreachable code namespace Concurrency { /// /// The ETW provider GUID for the Concurrency Runtime. /// /**/ _CONCRTIMP const GUID ConcRT_ProviderGuid = { 0xF7B697A3, 0x4DB5, 0x4d3b, { 0xBE, 0x71, 0xC4, 0xD2, 0x84, 0xE6, 0x59, 0x2F } }; // // GUIDS for events // /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are not more specifically described by another category. /// /// /// This category of events is not currently fired by the Concurrency Runtime. /// /**/ _CONCRTIMP const GUID ConcRTEventGuid = { 0x72B14A7D, 0x704C, 0x423e, { 0x92, 0xF8, 0x7E, 0x6D, 0x64, 0xBC, 0xB9, 0x2A } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to scheduler activity. /// /// /// /**/ _CONCRTIMP const GUID SchedulerEventGuid = { 0xE2091F8A, 0x1E0A, 0x4731, { 0x84, 0xA2, 0x0D, 0xD5, 0x7C, 0x8A, 0x52, 0x61 } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to schedule groups. /// /// /// This category of events is not currently fired by the Concurrency Runtime. /// /// /**/ _CONCRTIMP const GUID ScheduleGroupEventGuid = { 0xE8A3BF1F, 0xA86B, 0x4390, { 0x9C, 0x60, 0x53, 0x90, 0xB9, 0x69, 0xD2, 0x2C } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to contexts. /// /// /**/ _CONCRTIMP const GUID ContextEventGuid = { 0x5727A00F, 0x50BE, 0x4519, { 0x82, 0x56, 0xF7, 0x69, 0x98, 0x71, 0xFE, 0xCB } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to chores or tasks. /// /// /// This category of events is not currently fired by the Concurrency Runtime. /// /// /// /**/ _CONCRTIMP const GUID ChoreEventGuid = { 0x7E854EC7, 0xCDC4, 0x405a, { 0xB5, 0xB2, 0xAA, 0xF7, 0xC9, 0xE7, 0xD4, 0x0C } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to virtual processors. /// /**/ _CONCRTIMP const GUID VirtualProcessorEventGuid = { 0x2f27805f, 0x1676, 0x4ecc, { 0x96, 0xfa, 0x7e, 0xb0, 0x9d, 0x44, 0x30, 0x2f } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to locks. /// /// /// This category of events is not currently fired by the Concurrency Runtime. /// /// /// /**/ _CONCRTIMP const GUID LockEventGuid = { 0x79A60DC6, 0x5FC8, 0x4952, { 0xA4, 0x1C, 0x11, 0x63, 0xAE, 0xEC, 0x5E, 0xB8 } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to the resource manager. /// /// /// This category of events is not currently fired by the Concurrency Runtime. /// /// /**/ _CONCRTIMP const GUID ResourceManagerEventGuid = { 0x2718D25B, 0x5BF5, 0x4479, { 0x8E, 0x88, 0xBA, 0xBC, 0x64, 0xBD, 0xBF, 0xCA } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to usage of the parallel_invoke /// function. /// /// /**/ _CONCRTIMP const GUID PPLParallelInvokeEventGuid = { 0xd1b5b133, 0xec3d, 0x49f4, { 0x98, 0xa3, 0x46, 0x4d, 0x1a, 0x9e, 0x46, 0x82 } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to usage of the parallel_for /// function. /// /// /**/ _CONCRTIMP const GUID PPLParallelForEventGuid = { 0x31c8da6b, 0x6165, 0x4042, { 0x8b, 0x92, 0x94, 0x9e, 0x31, 0x5f, 0x4d, 0x84 } }; /// /// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to usage of the parallel_for_each /// function. /// /// /**/ _CONCRTIMP const GUID PPLParallelForeachEventGuid = { 0x5cb7d785, 0x9d66, 0x465d, { 0xba, 0xe1, 0x46, 0x11, 0x6, 0x1b, 0x54, 0x34 } }; /// /// A category GUID ({B9B5B78C-0713-4898-A21A-C67949DCED07}) describing ETW events fired by the Agents library in the Concurrency Runtime. /// /**/ _CONCRTIMP const GUID AgentEventGuid = {0xb9b5b78c, 0x713, 0x4898, { 0xa2, 0x1a, 0xc6, 0x79, 0x49, 0xdc, 0xed, 0x7 } }; namespace details { TRACEHANDLE g_ConcRTPRoviderHandle; TRACEHANDLE g_ConcRTSessionHandle; _CONCRT_TRACE_INFO g_TraceInfo = {0}; Etw* g_pEtw = NULL; Etw::Etw() noexcept { #ifdef _ONECORE #define TRACE_DLL L"api-ms-win-eventing-classicprovider-l1-1-0.dll" #else #define TRACE_DLL L"advapi32.dll" #endif HMODULE hTraceapi = LoadLibraryExW(TRACE_DLL, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); #ifndef _ONECORE if (!hTraceapi && GetLastError() == ERROR_INVALID_PARAMETER) { // LOAD_LIBRARY_SEARCH_SYSTEM32 is not supported on this platform, but TRACE_DLL is a KnownDLL so it is safe to load // it without supplying the full path. On Windows 8 and higher, LOAD_LIBRARY_SEARCH_SYSTEM32 should be used as a best practice. hTraceapi = LoadLibraryW(TRACE_DLL); } #endif if (hTraceapi != NULL) { m_pfnRegisterTraceGuidsW = (FnRegisterTraceGuidsW*) Security::EncodePointer(GetProcAddress(hTraceapi, "RegisterTraceGuidsW")); m_pfnUnregisterTraceGuids = (FnUnregisterTraceGuids*) Security::EncodePointer(GetProcAddress(hTraceapi, "UnregisterTraceGuids")); m_pfnTraceEvent = (FnTraceEvent*) Security::EncodePointer(GetProcAddress(hTraceapi, "TraceEvent")); m_pfnGetTraceLoggerHandle = (FnGetTraceLoggerHandle*) Security::EncodePointer(GetProcAddress(hTraceapi, "GetTraceLoggerHandle")); m_pfnGetTraceEnableLevel = (FnGetTraceEnableLevel*) Security::EncodePointer(GetProcAddress(hTraceapi, "GetTraceEnableLevel")); m_pfnGetTraceEnableFlags = (FnGetTraceEnableFlags*) Security::EncodePointer(GetProcAddress(hTraceapi, "GetTraceEnableFlags")); } } ULONG Etw::RegisterGuids(WMIDPREQUEST controlCallBack, LPCGUID providerGuid, ULONG guidCount, PTRACE_GUID_REGISTRATION eventGuidRegistration, PTRACEHANDLE providerHandle) { if (m_pfnRegisterTraceGuidsW != Security::EncodePointer(NULL)) { FnRegisterTraceGuidsW* pfnRegisterTraceGuidsW = (FnRegisterTraceGuidsW*) Security::DecodePointer(m_pfnRegisterTraceGuidsW); return pfnRegisterTraceGuidsW(controlCallBack, NULL, providerGuid, guidCount, eventGuidRegistration, NULL, NULL, providerHandle); } return ERROR_PROC_NOT_FOUND; } ULONG Etw::UnregisterGuids(TRACEHANDLE handle) { if (m_pfnUnregisterTraceGuids != Security::EncodePointer(NULL)) { FnUnregisterTraceGuids* pfnUnregisterTraceGuids = (FnUnregisterTraceGuids*) Security::DecodePointer(m_pfnUnregisterTraceGuids); return pfnUnregisterTraceGuids(handle); } return ERROR_PROC_NOT_FOUND; } ULONG Etw::Trace(TRACEHANDLE handle, PEVENT_TRACE_HEADER eventHeader) { if (m_pfnTraceEvent != Security::EncodePointer(NULL)) { FnTraceEvent* pfnTraceEvent = (FnTraceEvent*) Security::DecodePointer(m_pfnTraceEvent); return pfnTraceEvent(handle, eventHeader); } return ERROR_PROC_NOT_FOUND; } TRACEHANDLE Etw::GetLoggerHandle(PVOID buffer) { if (m_pfnGetTraceLoggerHandle != Security::EncodePointer(NULL)) { FnGetTraceLoggerHandle* pfnGetTraceLoggerHandle = (FnGetTraceLoggerHandle*) Security::DecodePointer(m_pfnGetTraceLoggerHandle); return pfnGetTraceLoggerHandle(buffer); } SetLastError(ERROR_PROC_NOT_FOUND); return (TRACEHANDLE)INVALID_HANDLE_VALUE; } UCHAR Etw::GetEnableLevel(TRACEHANDLE handle) { if (m_pfnGetTraceEnableLevel != Security::EncodePointer(NULL)) { FnGetTraceEnableLevel* pfnGetTraceEnableLevel = (FnGetTraceEnableLevel*) Security::DecodePointer(m_pfnGetTraceEnableLevel); return pfnGetTraceEnableLevel(handle); } SetLastError(ERROR_PROC_NOT_FOUND); return 0; } ULONG Etw::GetEnableFlags(TRACEHANDLE handle) { if (m_pfnGetTraceEnableFlags != Security::EncodePointer(NULL)) { FnGetTraceEnableFlags* pfnGetTraceEnableFlags = (FnGetTraceEnableFlags*) Security::DecodePointer(m_pfnGetTraceEnableFlags); return pfnGetTraceEnableFlags(handle); } SetLastError(ERROR_PROC_NOT_FOUND); return 0; } /// WMI control call back ULONG WINAPI ControlCallback(WMIDPREQUESTCODE requestCode, void*, ULONG*, void* buffer) { DWORD rc; switch (requestCode) { case WMI_ENABLE_EVENTS: // Enable the provider { g_ConcRTSessionHandle = g_pEtw->GetLoggerHandle(buffer); if ((HANDLE)g_ConcRTSessionHandle == INVALID_HANDLE_VALUE) return GetLastError(); SetLastError(ERROR_SUCCESS); ULONG level = g_pEtw->GetEnableLevel(g_ConcRTSessionHandle); if (level == 0) { rc = GetLastError(); if (rc == ERROR_SUCCESS) { // Enable level of 0 means TRACE_LEVEL_INFORMATION level = TRACE_LEVEL_INFORMATION; } else { return rc; } } ULONG flags = g_pEtw->GetEnableFlags(g_ConcRTSessionHandle); if (flags == 0) { rc = GetLastError(); if (rc == ERROR_SUCCESS) { flags = static_cast(AllEventsFlag); } else { return rc; } } // Tracing is now enabled. g_TraceInfo._EnableTrace((unsigned char)level, (unsigned long)flags); } break; case WMI_DISABLE_EVENTS: // Disable the provider g_TraceInfo._DisableTrace(); g_ConcRTSessionHandle = 0; break; case WMI_EXECUTE_METHOD: case WMI_REGINFO: case WMI_DISABLE_COLLECTION: case WMI_ENABLE_COLLECTION: case WMI_SET_SINGLE_ITEM: case WMI_SET_SINGLE_INSTANCE: case WMI_GET_SINGLE_INSTANCE: case WMI_GET_ALL_DATA: default: return ERROR_INVALID_PARAMETER; } return ERROR_SUCCESS; } void PPL_Trace_Event(const GUID& guid, ConcRT_EventType eventType, UCHAR level) { if (g_pEtw != NULL) { CONCRT_TRACE_EVENT_HEADER_COMMON concrtHeader = {0}; concrtHeader.header.Size = sizeof concrtHeader; concrtHeader.header.Flags = WNODE_FLAG_TRACED_GUID; concrtHeader.header.Guid = guid; concrtHeader.header.Class.Type = (UCHAR) eventType; concrtHeader.header.Class.Level = level; g_pEtw->Trace(g_ConcRTSessionHandle, &concrtHeader.header); } } void _RegisterConcRTEventTracing() { #if defined(_ONECORE) g_pEtw = NULL; #else // Initialize ETW dynamically, and only once, to avoid a static dependency on Advapi32.dll. _StaticLock::_Scoped_lock lockHolder(Etw::s_lock); if (g_pEtw == NULL) { g_pEtw = _concrt_new Etw(); static TRACE_GUID_REGISTRATION eventGuidRegistration[] = { { &Concurrency::ConcRTEventGuid, NULL }, { &Concurrency::SchedulerEventGuid, NULL }, { &Concurrency::ScheduleGroupEventGuid, NULL }, { &Concurrency::ContextEventGuid, NULL }, { &Concurrency::ChoreEventGuid, NULL }, { &Concurrency::LockEventGuid, NULL }, { &Concurrency::ResourceManagerEventGuid, NULL } }; ULONG eventGuidCount = sizeof eventGuidRegistration / sizeof eventGuidRegistration[0]; g_pEtw->RegisterGuids(Concurrency::details::ControlCallback, &ConcRT_ProviderGuid, eventGuidCount, eventGuidRegistration, &g_ConcRTPRoviderHandle); } #endif // defined(_ONECORE) } void _UnregisterConcRTEventTracing() { if (g_pEtw != NULL) { ENSURE_NOT_APP(); g_TraceInfo._DisableTrace(); g_pEtw->UnregisterGuids(g_ConcRTPRoviderHandle); delete g_pEtw; g_pEtw = NULL; } } } // namespace details /// /// Enable tracing /// /// /// If tracing was correctly initiated, S_OK is returned, otherwise E_NOT_STARTED is returned /// _CONCRTIMP HRESULT EnableTracing() { // Deprecated return S_OK; } /// /// Disables tracing /// /// /// If tracing was correctly disabled, S_OK is returned. If tracing was not previously initiated, /// E_NOT_STARTED is returned /// _CONCRTIMP HRESULT DisableTracing() { // Deprecated return S_OK; } _CONCRTIMP const _CONCRT_TRACE_INFO* _GetConcRTTraceInfo() { if (g_pEtw == NULL) { ::Concurrency::details::_RegisterConcRTEventTracing(); } return &g_TraceInfo; } // Trace an event signaling the begin of a PPL function _CONCRTIMP void _Trace_ppl_function(const GUID& guid, UCHAR level, ConcRT_EventType type) { const _CONCRT_TRACE_INFO * traceInfo = _GetConcRTTraceInfo(); if (traceInfo->_IsEnabled(level, PPLEventFlag)) Concurrency::details::PPL_Trace_Event(guid, type, level); } // Trace an event from the Agents library _CONCRTIMP void _Trace_agents(::Concurrency::Agents_EventType eventType, __int64 agentId, ...) { va_list args; va_start(args, agentId); UCHAR level = TRACE_LEVEL_INFORMATION; const _CONCRT_TRACE_INFO * traceInfo = _GetConcRTTraceInfo(); if (traceInfo->_IsEnabled(level, AgentEventFlag)) { AGENTS_TRACE_EVENT_DATA agentsData = {0}; // Header agentsData.header.Size = sizeof(agentsData); agentsData.header.Flags = WNODE_FLAG_TRACED_GUID; agentsData.header.Guid = AgentEventGuid; agentsData.header.Class.Type = (UCHAR) eventType; agentsData.header.Class.Level = level; // Payload agentsData.payload.AgentId1 = agentId; switch (eventType) { case AGENTS_EVENT_CREATE: // LWT id agentsData.payload.AgentId2 = va_arg(args, __int64); break; case AGENTS_EVENT_DESTROY: // No other payload break; case AGENTS_EVENT_LINK: case AGENTS_EVENT_UNLINK: // Target's id agentsData.payload.AgentId2 = va_arg(args, __int64); break; case AGENTS_EVENT_START: // No other payload break; case AGENTS_EVENT_END: // Number of messages processed agentsData.payload.Count = va_arg(args, long); break; case AGENTS_EVENT_SCHEDULE: // No other payload break; case AGENTS_EVENT_NAME: { wchar_t * name = va_arg(args, wchar_t*); if (name != NULL) { wcsncpy_s(agentsData.payload.Name, _countof(agentsData.payload.Name), name, _TRUNCATE); } } break; default: break; }; va_end(args); g_pEtw->Trace(g_ConcRTSessionHandle, &agentsData.header); } } } // namespace Concurrency #pragma warning (pop)