ORT ETW dynamic logging that improves ORT diagnosability & performance (#18882)

### Description
This PR has several combined ORT ETW changes that improve ORT log
diagnosability & performance. 
- The existing log behavior in the ORT API and Severity behavior remain
the same as compiled by the dev using the ORT API
- The PR keeps the existing design which has 2 TraceLogging providers
defined (although both were not used before this PR)
- Keeps great inference (inf) and session load performance even with
dynamic logging enabled (see below)
- On Windows, when ONNXRuntimeTraceLoggingProvider is enabled, then ORT
will dynamically _add_ a new sink reflecting the severity level provided
by ETW dynamically. E.G Critical - Verbose per the need at runtime
- This allows previous printf style LOGS() statements both for CPU and
NPU cases to flow to ETW via a local trace (if enabled)
- This DOES NOT add any new Telemetry which can optionally be sent to
Microsoft.
- Telemetry are ETW events marked with the Measure keyword that _can_ be
sampled if a box opts-in
- Existing Microsoft.ML.ONNXRuntime events have appropriate keywords and
levels added if they were missing
- If Execution Providers (EPs) can provide more detailed insight into
their HW or component, then this PR allows for those to be dynamically
logged instead of just at compile time
- In this PR, the QNN EP for QC NPUs can have basic or detailed
profiling enabled to give some insight into how the NPU is performing
- When the Microsoft.ML.ONNXRuntime ETW provider is enabled with the
Profiling keyword and level 5 then QC QNN basic profiling info is output
to ETW
  
### Motivation and Context
- This make ORT logging and diagnosability more performant (on Windows)
and available in a wider variety of runtime environments.
- The performance difference for inf times was ~300x+ drastically
better/faster when these logs were output to ETW vs just stdout (Verbose
Severity)
- This style of ETW dynamic tracing is the widely used standard for
Windows components, and even by some 3rd party software since the ETW
API is open and part of the Windows API
- These ETW based logs can be seamlessly combined with other ETW logs
such as an AI component/feature using ORT, OS CPU profiling, scheduling,
and more
- Before the PR, ORT logging is largely printf style and output to a
sink (usually stdout) only if compiled with a certain log Severity. When
enabled the previous logging (to stdout) would vastly slow down
inference times. Once compiled for release the internal ORT logs were
not accessible by anyone except the AI model developer in their dev
inner loop. That means logs could not be enabled on a lab machine, or on
a production system where the runtime behavior or performance might be
different for various reasons on a wide variety of HW.
- This change was tested with performance in mind and tested with a
mobilenet small AI model with onnxruntime_perf_test
- CPU: There was no statistical difference for inf times with the
baseline (main) or this PR whether ETW was enabled or not (both ORT
providers all keywords level 5).
- NPU (QNN on SP9 or Dev Kit 2023 QC SQ3): There was no statistical
difference for inf times with this PR whether ETW (both ORT providers
all keywords) were enabled or not for Level 5 (Verbose). This is even
with QNN Basic profiling turned on and outputting NPU stats to ETW
- As expected and designed, there was perf slowdown when Max Level 255
is enabled which translates to QNN Detailed profiling. This mirrors the
expected slowdown in the NPU when individual model operations are
monitored & recorded as well. This perf is similar to the QNN SDK
Detailed profiling performance separate from this PR. This is designed
to be above level 5 (verbose) as that is commonly the max level used in
many trace profiles and won't affect inf performance.
- Other OSes such as Linux & Android are left untouched for now. 
- Out of scope for this PR but TraceLogging is available for Linux with
LTTng tracing. So in the future, this optional tracing could also be
made available on other OSes where a TraceLogging API is available
This commit is contained in:
ivberg 2024-01-11 12:43:27 -08:00 committed by GitHub
parent d0bac8216d
commit 4d1243b4b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 593 additions and 125 deletions

View file

@ -75,6 +75,21 @@ struct Category {
// TODO: What other high level categories are meaningful? Model? Optimizer? Execution?
};
/// <summary>
/// ORT TraceLogging keywords for categories of dynamic logging enablement
/// </summary>
enum class ORTTraceLoggingKeyword : uint64_t {
Session = 0x1, // ORT Session TraceLoggingWrite
Logs = 0x2, // LOGS() Macro ORT logs. Pair with an appropriate level depending on detail required
Reserved1 = 0x4, // Reserved if we want to add some specific sub-categories instead of just LOGS() or other uses
Reserved2 = 0x8,
Reserved3 = 0x10,
Reserved4 = 0x20,
Reserved5 = 0x40,
Reserved6 = 0x80,
Profiling = 0x100 // Enables profiling. At higher levels >5 can impact inference performance
};
class ISink;
class Logger;
class Capture;
@ -333,5 +348,17 @@ unsigned int GetThreadId();
*/
unsigned int GetProcessId();
/**
If the ONNXRuntimeTraceLoggingProvider ETW Provider is enabled, then adds to the existing logger.
*/
std::unique_ptr<ISink> EnhanceLoggerWithEtw(std::unique_ptr<ISink> existingLogger, logging::Severity originalSeverity,
logging::Severity etwSeverity);
/**
If the ONNXRuntimeTraceLoggingProvider ETW Provider is enabled, then can override the logging level.
But this overrided level only applies to the ETW sink. The original logger(s) retain their original logging level
*/
Severity OverrideLevelWithEtw(Severity originalSeverity);
} // namespace logging
} // namespace onnxruntime

View file

@ -12,6 +12,8 @@
#ifdef _WIN32
#include <Windows.h>
#include "core/platform/windows/logging/etw_sink.h"
#include "core/common/logging/sinks/composite_sink.h"
#else
#include <unistd.h>
#if defined(__MACH__) || defined(__wasm__) || defined(_AIX)
@ -243,5 +245,36 @@ unsigned int GetProcessId() {
#endif
}
std::unique_ptr<ISink> EnhanceLoggerWithEtw(std::unique_ptr<ISink> existingLogger, logging::Severity originalSeverity,
logging::Severity etwSeverity) {
#ifdef _WIN32
auto& manager = EtwRegistrationManager::Instance();
if (manager.IsEnabled()) {
auto compositeSink = std::make_unique<CompositeSink>();
compositeSink->AddSink(std::move(existingLogger), originalSeverity);
compositeSink->AddSink(std::make_unique<EtwSink>(), etwSeverity);
return compositeSink;
} else {
return existingLogger;
}
#else
// On non-Windows platforms, just return the existing logger
(void)originalSeverity;
(void)etwSeverity;
return existingLogger;
#endif // _WIN32
}
Severity OverrideLevelWithEtw(Severity originalSeverity) {
#ifdef _WIN32
auto& manager = logging::EtwRegistrationManager::Instance();
if (manager.IsEnabled() &&
(manager.Keyword() & static_cast<ULONGLONG>(onnxruntime::logging::ORTTraceLoggingKeyword::Logs)) != 0) {
return manager.MapLevelToSeverity();
}
#endif // _WIN32
return originalSeverity;
}
} // namespace logging
} // namespace onnxruntime

View file

@ -5,6 +5,8 @@
#include <string>
#include <vector>
#include <utility>
#include <memory>
#include "core/common/logging/isink.h"
#include "core/common/logging/logging.h"
@ -27,20 +29,31 @@ class CompositeSink : public ISink {
/// Adds a sink. Takes ownership of the sink (so pass unique_ptr by value).
/// </summary>
/// <param name="sink">The sink.</param>
/// <param name="severity">The min severity to send a message to that sink</param>
/// <returns>This instance to allow chaining.</returns>
CompositeSink& AddSink(std::unique_ptr<ISink> sink) {
sinks_.push_back(std::move(sink));
CompositeSink& AddSink(std::unique_ptr<ISink> sink, logging::Severity severity) {
sinks_with_severity_.emplace_back(std::move(sink), severity);
return *this;
}
/// <summary>
/// Gets a const reference to the collection of sinks and min severity for that sink
/// </summary>
/// <returns>A const reference to the vector pair of unique_ptr to ISink and severity.</returns>
const std::vector<std::pair<std::unique_ptr<ISink>, logging::Severity>>& GetSinks() const {
return sinks_with_severity_;
}
private:
void SendImpl(const Timestamp& timestamp, const std::string& logger_id, const Capture& message) override {
for (auto& sink : sinks_) {
sink->Send(timestamp, logger_id, message);
for (auto& sink_pair : sinks_with_severity_) {
if (message.Severity() >= sink_pair.second) {
sink_pair.first->Send(timestamp, logger_id, message);
}
}
}
std::vector<std::unique_ptr<ISink>> sinks_;
std::vector<std::pair<std::unique_ptr<ISink>, logging::Severity>> sinks_with_severity_;
};
} // namespace logging
} // namespace onnxruntime

View file

@ -13,6 +13,7 @@
#include "core/graph/graph_viewer.h"
#include "core/common/logging/logging.h"
#ifdef _WIN32
#include <winmeta.h>
#include "core/platform/tracing.h"
#endif
@ -47,6 +48,8 @@ class ExecutionProviders {
TraceLoggingWrite(
telemetry_provider_handle,
"ProviderOptions",
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Session)),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingString(provider_id.c_str(), "ProviderId"),
TraceLoggingString(config_pair.first.c_str(), "Key"),
TraceLoggingString(config_pair.second.c_str(), "Value"));

View file

@ -181,7 +181,7 @@ class SessionScope {
}
auto& logger = session_state_.Logger();
LOGS(logger, VERBOSE) << "Begin execution";
VLOGS(logger, 0) << "Begin execution";
const SequentialExecutionPlan& seq_exec_plan = *session_state_.GetExecutionPlan();
const auto& exec_plan_vec = seq_exec_plan.execution_plan;
VLOGS(logger, 1) << "Size of execution plan vector: " << exec_plan_vec.size();
@ -515,7 +515,7 @@ onnxruntime::Status ExecuteKernel(StreamExecutionContext& ctx,
return Status(status.Category(), status.Code(), msg_string);
}
ctx.RecycleNodeInputs(idx);
LOGS(logger, VERBOSE) << "stream " << stream_idx << " launch kernel with idx " << idx;
VLOGS(logger, 0) << "stream " << stream_idx << " launch kernel with idx " << idx;
return Status::OK();
}
@ -531,7 +531,7 @@ onnxruntime::Status ExecuteThePlan(const SessionState& session_state, gsl::span<
const bool only_execute_path_to_fetches,
bool single_thread_mode) {
auto* execution_plan = session_state.GetExecutionPlan();
LOGS(logger, VERBOSE) << "Number of streams: " << execution_plan->execution_plan.size();
VLOGS(logger, 0) << "Number of streams: " << execution_plan->execution_plan.size();
int32_t valid_streams = 0;
for (auto& stream : execution_plan->execution_plan) {
if (stream && stream->steps_.size() > 0)

View file

@ -168,7 +168,7 @@ void StreamExecutionContext::RecycleNodeInputs(onnxruntime::NodeIndex node_index
for (auto idx : execution_plan->node_release_list[node_index]) {
if (--release_plan_[idx] == 0) {
ORT_ENFORCE(frame_.ReleaseMLValue(static_cast<int>(execution_plan->release_actions[idx].value_index)).IsOK());
LOGS(*logger_, VERBOSE) << "ort value " << execution_plan->release_actions[idx].value_index << " released";
VLOGS(*logger_, 0) << "ort value " << execution_plan->release_actions[idx].value_index << " released";
}
}
}

View file

@ -12,6 +12,21 @@ void LogRuntimeError(uint32_t sessionId, const common::Status& status, const cha
env.GetTelemetryProvider().LogRuntimeError(sessionId, status, file, function, line);
}
bool Telemetry::IsEnabled() const {
return false;
}
// Get the current logging level
// The Level defined as uchar is coming from the ETW Enable callback in TraceLoggingRegisterEx.
unsigned char Telemetry::Level() const {
return 0;
}
// Get the current keyword
uint64_t Telemetry::Keyword() const {
return 0;
}
void Telemetry::EnableTelemetryEvents() const {
}

View file

@ -38,6 +38,14 @@ class Telemetry {
virtual void DisableTelemetryEvents() const;
virtual void SetLanguageProjection(uint32_t projection) const;
virtual bool IsEnabled() const;
// Get the current logging level
virtual unsigned char Level() const;
// Get the current keyword
virtual uint64_t Keyword() const;
virtual void LogProcessInfo() const;
virtual void LogSessionCreationStart() const;

View file

@ -58,42 +58,107 @@ TRACELOGGING_DEFINE_PROVIDER(etw_provider_handle, "ONNXRuntimeTraceLoggingProvid
#pragma warning(pop)
#endif
// Class to unregister ETW provider at shutdown.
// We expect one static instance to be created for the lifetime of the program.
class EtwRegistrationManager {
public:
static EtwRegistrationManager& Register() {
const HRESULT etw_status = ::TraceLoggingRegister(etw_provider_handle);
EtwRegistrationManager& EtwRegistrationManager::Instance() {
static EtwRegistrationManager instance;
instance.LazyInitialize();
return instance;
}
if (FAILED(etw_status)) {
ORT_THROW("ETW registration failed. Logging will be broken: " + std::to_string(etw_status));
bool EtwRegistrationManager::IsEnabled() const {
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
return is_enabled_;
}
UCHAR EtwRegistrationManager::Level() const {
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
return level_;
}
Severity EtwRegistrationManager::MapLevelToSeverity() {
switch (level_) {
case TRACE_LEVEL_NONE:
return Severity::kFATAL; // There is no none severity option
case TRACE_LEVEL_VERBOSE:
return Severity::kVERBOSE;
case TRACE_LEVEL_INFORMATION:
return Severity::kINFO;
case TRACE_LEVEL_WARNING:
return Severity::kWARNING;
case TRACE_LEVEL_ERROR:
return Severity::kERROR;
case TRACE_LEVEL_CRITICAL:
return Severity::kFATAL;
default:
return Severity::kVERBOSE;
}
}
ULONGLONG EtwRegistrationManager::Keyword() const {
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
return keyword_;
}
HRESULT EtwRegistrationManager::Status() const {
return etw_status_;
}
void EtwRegistrationManager::RegisterInternalCallback(const EtwInternalCallback& callback) {
std::lock_guard<OrtMutex> lock(callbacks_mutex_);
callbacks_.push_back(callback);
}
void NTAPI EtwRegistrationManager::ORT_TL_EtwEnableCallback(
_In_ LPCGUID SourceId,
_In_ ULONG IsEnabled,
_In_ UCHAR Level,
_In_ ULONGLONG MatchAnyKeyword,
_In_ ULONGLONG MatchAllKeyword,
_In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
_In_opt_ PVOID CallbackContext) {
auto& manager = EtwRegistrationManager::Instance();
{
std::lock_guard<OrtMutex> lock(manager.provider_change_mutex_);
manager.is_enabled_ = (IsEnabled != 0);
manager.level_ = Level;
manager.keyword_ = MatchAnyKeyword;
}
manager.InvokeCallbacks(SourceId, IsEnabled, Level, MatchAnyKeyword, MatchAllKeyword, FilterData, CallbackContext);
}
EtwRegistrationManager::~EtwRegistrationManager() {
::TraceLoggingUnregister(etw_provider_handle);
}
EtwRegistrationManager::EtwRegistrationManager() {
}
void EtwRegistrationManager::LazyInitialize() {
if (!initialized_) {
std::lock_guard<OrtMutex> lock(init_mutex_);
if (!initialized_) { // Double-check locking pattern
initialized_ = true;
etw_status_ = ::TraceLoggingRegisterEx(etw_provider_handle, ORT_TL_EtwEnableCallback, nullptr);
if (FAILED(etw_status_)) {
ORT_THROW("ETW registration failed. Logging will be broken: " + std::to_string(etw_status_));
}
}
// return an instance that is just used to unregister as the program exits
static EtwRegistrationManager instance(etw_status);
return instance;
}
}
const HRESULT Status() const noexcept {
return etw_status_;
void EtwRegistrationManager::InvokeCallbacks(LPCGUID SourceId, ULONG IsEnabled, UCHAR Level, ULONGLONG MatchAnyKeyword,
ULONGLONG MatchAllKeyword, PEVENT_FILTER_DESCRIPTOR FilterData,
PVOID CallbackContext) {
std::lock_guard<OrtMutex> lock(callbacks_mutex_);
for (const auto& callback : callbacks_) {
callback(SourceId, IsEnabled, Level, MatchAnyKeyword, MatchAllKeyword, FilterData, CallbackContext);
}
~EtwRegistrationManager() {
::TraceLoggingUnregister(etw_provider_handle);
}
private:
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(EtwRegistrationManager);
EtwRegistrationManager(const HRESULT status) noexcept : etw_status_{status} {}
const HRESULT etw_status_;
};
}
void EtwSink::SendImpl(const Timestamp& timestamp, const std::string& logger_id, const Capture& message) {
UNREFERENCED_PARAMETER(timestamp);
// register on first usage
static EtwRegistrationManager& etw_manager = EtwRegistrationManager::Register();
static EtwRegistrationManager& etw_manager = EtwRegistrationManager::Instance();
// do something (not that meaningful) with etw_manager so it doesn't get optimized out
// as we want an instance around to do the unregister
@ -101,9 +166,8 @@ void EtwSink::SendImpl(const Timestamp& timestamp, const std::string& logger_id,
return;
}
// Do we want to output Verbose level messages via ETW at any point it time?
// TODO: Validate if this filtering makes sense.
if (message.Severity() <= Severity::kVERBOSE || message.DataType() == DataType::USER) {
if (message.DataType() == DataType::USER) {
return;
}
@ -114,11 +178,13 @@ void EtwSink::SendImpl(const Timestamp& timestamp, const std::string& logger_id,
// TraceLoggingWrite requires (painfully) a compile time constant for the TraceLoggingLevel,
// forcing us to use an ugly macro for the call.
#define ETW_EVENT_NAME "ONNXRuntimeLogEvent"
#define TRACE_LOG_WRITE(level) \
TraceLoggingWrite(etw_provider_handle, ETW_EVENT_NAME, TraceLoggingLevel(level), \
TraceLoggingString(logger_id.c_str(), "logger"), \
TraceLoggingString(message.Category(), "category"), \
TraceLoggingString(message.Location().ToString().c_str(), "location"), \
#define TRACE_LOG_WRITE(level) \
TraceLoggingWrite(etw_provider_handle, ETW_EVENT_NAME, \
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Logs)), \
TraceLoggingLevel(level), \
TraceLoggingString(logger_id.c_str(), "logger"), \
TraceLoggingString(message.Category(), "category"), \
TraceLoggingString(message.Location().ToString().c_str(), "location"), \
TraceLoggingString(message.Message().c_str(), "message"))
const auto severity{message.Severity()};

View file

@ -3,7 +3,9 @@
#pragma once
#include <Windows.h>
#include <ntverp.h>
#include <evntrace.h>
// check for Windows 10 SDK or later
// https://stackoverflow.com/questions/2665755/how-can-i-determine-the-version-of-the-windows-sdk-installed-on-my-computer
@ -18,9 +20,11 @@
#include <atomic>
#include <iostream>
#include <string>
#include <vector>
#include "core/common/logging/capture.h"
#include "core/common/logging/isink.h"
#include "core/platform/ort_mutex.h"
namespace onnxruntime {
namespace logging {
@ -41,6 +45,62 @@ class EtwSink : public ISink {
// EtwTracingManager to ensure we cleanly unregister it
static std::atomic_flag have_instance_;
};
class EtwRegistrationManager {
public:
using EtwInternalCallback = std::function<void(LPCGUID SourceId, ULONG IsEnabled, UCHAR Level,
ULONGLONG MatchAnyKeyword, ULONGLONG MatchAllKeyword,
PEVENT_FILTER_DESCRIPTOR FilterData, PVOID CallbackContext)>;
// Singleton instance access
static EtwRegistrationManager& Instance();
// Check if ETW logging is enabled
bool IsEnabled() const;
// Get the current logging level
UCHAR Level() const;
Severity MapLevelToSeverity();
// Get the current keyword
uint64_t Keyword() const;
// Get the ETW registration status
HRESULT Status() const;
void RegisterInternalCallback(const EtwInternalCallback& callback);
private:
EtwRegistrationManager();
~EtwRegistrationManager();
void LazyInitialize();
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(EtwRegistrationManager);
void InvokeCallbacks(LPCGUID SourceId, ULONG IsEnabled, UCHAR Level, ULONGLONG MatchAnyKeyword,
ULONGLONG MatchAllKeyword, PEVENT_FILTER_DESCRIPTOR FilterData, PVOID CallbackContext);
static void NTAPI ORT_TL_EtwEnableCallback(
_In_ LPCGUID SourceId,
_In_ ULONG IsEnabled,
_In_ UCHAR Level,
_In_ ULONGLONG MatchAnyKeyword,
_In_ ULONGLONG MatchAllKeyword,
_In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
_In_opt_ PVOID CallbackContext);
std::vector<EtwInternalCallback> callbacks_;
OrtMutex callbacks_mutex_;
mutable OrtMutex provider_change_mutex_;
OrtMutex init_mutex_;
bool initialized_ = false;
bool is_enabled_;
UCHAR level_;
ULONGLONG keyword_;
HRESULT etw_status_;
};
} // namespace logging
} // namespace onnxruntime

View file

@ -2,6 +2,7 @@
// Licensed under the MIT License.
#include "core/platform/windows/telemetry.h"
#include "core/common/logging/logging.h"
#include "onnxruntime_config.h"
// ETW includes
@ -16,6 +17,7 @@
#include <TraceLoggingProvider.h>
#include <evntrace.h>
#include <winmeta.h>
// Seems this workaround can be dropped when we drop support for VS2017 toolchains
// https://developercommunity.visualstudio.com/content/problem/85934/traceloggingproviderh-is-incompatible-with-utf-8.html
@ -55,15 +57,18 @@ TRACELOGGING_DEFINE_PROVIDER(telemetry_provider_handle, "Microsoft.ML.ONNXRuntim
#endif
OrtMutex WindowsTelemetry::mutex_;
OrtMutex WindowsTelemetry::provider_change_mutex_;
uint32_t WindowsTelemetry::global_register_count_ = 0;
bool WindowsTelemetry::enabled_ = true;
uint32_t WindowsTelemetry::projection_ = 0;
UCHAR WindowsTelemetry::level_ = 0;
UINT64 WindowsTelemetry::keyword_ = 0;
WindowsTelemetry::WindowsTelemetry() {
std::lock_guard<OrtMutex> lock(mutex_);
if (global_register_count_ == 0) {
// TraceLoggingRegister is fancy in that you can only register once GLOBALLY for the whole process
HRESULT hr = TraceLoggingRegister(telemetry_provider_handle);
HRESULT hr = TraceLoggingRegisterEx(telemetry_provider_handle, ORT_TL_EtwEnableCallback, nullptr);
if (SUCCEEDED(hr)) {
global_register_count_ += 1;
}
@ -80,6 +85,44 @@ WindowsTelemetry::~WindowsTelemetry() {
}
}
bool WindowsTelemetry::IsEnabled() const {
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
return enabled_;
}
UCHAR WindowsTelemetry::Level() const {
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
return level_;
}
UINT64 WindowsTelemetry::Keyword() const {
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
return keyword_;
}
// HRESULT WindowsTelemetry::Status() {
// return etw_status_;
// }
void NTAPI WindowsTelemetry::ORT_TL_EtwEnableCallback(
_In_ LPCGUID SourceId,
_In_ ULONG IsEnabled,
_In_ UCHAR Level,
_In_ ULONGLONG MatchAnyKeyword,
_In_ ULONGLONG MatchAllKeyword,
_In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
_In_opt_ PVOID CallbackContext) {
(void)SourceId;
(void)MatchAllKeyword;
(void)FilterData;
(void)CallbackContext;
std::lock_guard<OrtMutex> lock(provider_change_mutex_);
enabled_ = (IsEnabled != 0);
level_ = Level;
keyword_ = MatchAnyKeyword;
}
void WindowsTelemetry::EnableTelemetryEvents() const {
enabled_ = true;
}
@ -110,6 +153,7 @@ void WindowsTelemetry::LogProcessInfo() const {
TraceLoggingBool(true, "UTCReplace_AppSessionGuid"),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
// Telemetry info
TraceLoggingUInt8(0, "schemaVersion"),
TraceLoggingString(ORT_VERSION, "runtimeVersion"),
@ -126,7 +170,8 @@ void WindowsTelemetry::LogSessionCreationStart() const {
"SessionCreationStart",
TraceLoggingBool(true, "UTCReplace_AppSessionGuid"),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES));
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TraceLoggingLevel(WINEVENT_LEVEL_INFO));
}
void WindowsTelemetry::LogEvaluationStop() const {
@ -199,6 +244,8 @@ void WindowsTelemetry::LogSessionCreation(uint32_t session_id, int64_t ir_versio
TraceLoggingBool(true, "UTCReplace_AppSessionGuid"),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Session)),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
// Telemetry info
TraceLoggingUInt8(0, "schemaVersion"),
TraceLoggingUInt32(session_id, "sessionId"),
@ -227,6 +274,7 @@ void WindowsTelemetry::LogRuntimeError(uint32_t session_id, const common::Status
TraceLoggingBool(true, "UTCReplace_AppSessionGuid"),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
// Telemetry info
TraceLoggingUInt8(0, "schemaVersion"),
TraceLoggingHResult(hr, "hResult"),
@ -243,6 +291,7 @@ void WindowsTelemetry::LogRuntimeError(uint32_t session_id, const common::Status
TraceLoggingBool(true, "UTCReplace_AppSessionGuid"),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
// Telemetry info
TraceLoggingUInt8(0, "schemaVersion"),
TraceLoggingUInt32(session_id, "sessionId"),

View file

@ -3,6 +3,8 @@
#pragma once
#include "core/platform/telemetry.h"
#include <Windows.h>
#include <TraceLoggingProvider.h>
#include "core/platform/ort_mutex.h"
#include "core/platform/windows/TraceLoggingConfig.h"
#include <atomic>
@ -22,6 +24,17 @@ class WindowsTelemetry : public Telemetry {
void DisableTelemetryEvents() const override;
void SetLanguageProjection(uint32_t projection) const override;
bool IsEnabled() const override;
// Get the current logging level
unsigned char Level() const override;
// Get the current keyword
UINT64 Keyword() const override;
// Get the ETW registration status
// static HRESULT Status();
void LogProcessInfo() const override;
void LogSessionCreationStart() const override;
@ -50,6 +63,19 @@ class WindowsTelemetry : public Telemetry {
static uint32_t global_register_count_;
static bool enabled_;
static uint32_t projection_;
static OrtMutex provider_change_mutex_;
static UCHAR level_;
static ULONGLONG keyword_;
static void NTAPI ORT_TL_EtwEnableCallback(
_In_ LPCGUID SourceId,
_In_ ULONG IsEnabled,
_In_ UCHAR Level,
_In_ ULONGLONG MatchAnyKeyword,
_In_ ULONGLONG MatchAllKeyword,
_In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
_In_opt_ PVOID CallbackContext);
};
} // namespace onnxruntime

View file

@ -18,6 +18,11 @@
#include "core/common/logging/capture.h"
#include "core/providers/qnn/builder/onnx_ctx_model_helper.h"
#ifdef _WIN32
#include <winmeta.h>
#include "core/platform/tracing.h"
#endif
// Flag to determine if Backend should do node validation for each opNode added
#define DO_GRAPH_NODE_VALIDATIONS 1
@ -843,28 +848,46 @@ Status QnnBackendManager::ExtractBackendProfilingInfo() {
LOGS(*logger_, VERBOSE) << "The QNN backend does not support extended event data.";
}
// Write to CSV in append mode
const char* profilingCsvFilename = "qnn-profiling-data.csv";
std::ifstream infile(profilingCsvFilename);
bool exists = infile.good();
infile.close();
bool tracelogging_provider_ep_enabled = false;
const Env& env = Env::Default();
auto& provider = env.GetTelemetryProvider();
if (provider.IsEnabled()) {
auto keyword = provider.Keyword();
if ((keyword & static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Profiling)) != 0) {
tracelogging_provider_ep_enabled = true;
}
}
std::ofstream outfile;
if (!tracelogging_provider_ep_enabled) {
// Write to CSV in append mode
const char* profilingCsvFilename = "qnn-profiling-data.csv";
std::ifstream infile(profilingCsvFilename);
bool exists = infile.good();
infile.close();
std::ofstream outfile(profilingCsvFilename, std::ios_base::app);
ORT_RETURN_IF(!outfile.is_open(), "Failed to open qnn-profiling-data.csv");
// If file didn't exist before, write the header
if (!exists) {
outfile << "Msg Timestamp,Message,Time,Unit of Measurement,Timing Source,Event Level,Event Identifier\n";
outfile.open(profilingCsvFilename, std::ios_base::app);
ORT_RETURN_IF(!outfile.is_open(), "Failed to open qnn-profiling-data.csv");
// If file didn't exist before, write the header
if (!exists) {
outfile << "Msg Timestamp,Message,Time,Unit of Measurement,Timing Source,Event Level,Event Identifier\n";
}
}
for (size_t event_idx = 0; event_idx < num_events; event_idx++) {
ORT_RETURN_IF_ERROR(
ExtractProfilingEvent(*(profile_events + event_idx), "ROOT", outfile, backendSupportsExtendedEventData));
ExtractProfilingEvent(*(profile_events + event_idx), "ROOT", outfile, backendSupportsExtendedEventData,
tracelogging_provider_ep_enabled));
ORT_RETURN_IF_ERROR(
ExtractProfilingSubEvents(*(profile_events + event_idx), outfile, backendSupportsExtendedEventData));
ExtractProfilingSubEvents(*(profile_events + event_idx), outfile, backendSupportsExtendedEventData,
tracelogging_provider_ep_enabled));
}
outfile.close();
LOGS(*logger_, INFO) << "Wrote QNN profiling events (" << num_events << ") to qnn-profiling-data.csv";
if (!tracelogging_provider_ep_enabled) {
outfile.close();
LOGS(*logger_, VERBOSE) << "Wrote QNN profiling events (" << num_events << ") to qnn-profiling-data.csv";
} else {
LOGS(*logger_, VERBOSE) << "Wrote QNN profiling events (" << num_events << ") to ETW";
}
}
return Status::OK();
@ -873,7 +896,8 @@ Status QnnBackendManager::ExtractBackendProfilingInfo() {
Status QnnBackendManager::ExtractProfilingSubEvents(
QnnProfile_EventId_t profile_event_id,
std::ofstream& outfile,
bool useExtendedEventData) {
bool useExtendedEventData,
bool tracelogging_provider_ep_enabled) {
const QnnProfile_EventId_t* profile_sub_events{nullptr};
uint32_t num_sub_events{0};
auto result = qnn_interface_.profileGetSubEvents(profile_event_id, &profile_sub_events, &num_sub_events);
@ -884,12 +908,14 @@ Status QnnBackendManager::ExtractProfilingSubEvents(
for (size_t sub_event_idx = 0; sub_event_idx < num_sub_events; sub_event_idx++) {
ORT_RETURN_IF_ERROR(
ExtractProfilingEvent(*(profile_sub_events + sub_event_idx), "SUB-EVENT", outfile, useExtendedEventData));
ExtractProfilingEvent(*(profile_sub_events + sub_event_idx), "SUB-EVENT", outfile, useExtendedEventData,
tracelogging_provider_ep_enabled));
ORT_RETURN_IF_ERROR(
ExtractProfilingSubEvents(*(profile_sub_events + sub_event_idx), outfile, useExtendedEventData));
ExtractProfilingSubEvents(*(profile_sub_events + sub_event_idx), outfile, useExtendedEventData,
tracelogging_provider_ep_enabled));
}
LOGS(*logger_, INFO) << "Wrote QNN profiling sub events (" << num_sub_events << ") to qnn-profiling-data.csv";
LOGS(*logger_, VERBOSE) << "Wrote QNN profiling sub events (" << num_sub_events << ")";
}
return Status::OK();
@ -899,18 +925,20 @@ Status QnnBackendManager::ExtractProfilingEvent(
QnnProfile_EventId_t profile_event_id,
const std::string& eventLevel,
std::ofstream& outfile,
bool useExtendedEventData) {
bool useExtendedEventData,
bool tracelogging_provider_ep_enabled) {
if (useExtendedEventData) {
return ExtractProfilingEventExtended(profile_event_id, eventLevel, outfile);
return ExtractProfilingEventExtended(profile_event_id, eventLevel, outfile, tracelogging_provider_ep_enabled);
} else {
return ExtractProfilingEventBasic(profile_event_id, eventLevel, outfile);
return ExtractProfilingEventBasic(profile_event_id, eventLevel, outfile, tracelogging_provider_ep_enabled);
}
}
Status QnnBackendManager::ExtractProfilingEventBasic(
QnnProfile_EventId_t profile_event_id,
const std::string& eventLevel,
std::ofstream& outfile) {
std::ofstream& outfile,
bool tracelogging_provider_ep_enabled) {
QnnProfile_EventData_t event_data;
auto result = qnn_interface_.profileGetEventData(profile_event_id, &event_data);
QnnProfile_Error_t errorCode = static_cast<QnnProfile_Error_t>(result & 0xFFFF);
@ -919,15 +947,32 @@ Status QnnBackendManager::ExtractProfilingEventBasic(
std::string message = GetEventTypeString(event_data.type);
std::string unit = GetUnitString(event_data.unit);
outfile << "UNKNOWN"
<< ","
<< message << ","
<< event_data.value << ","
<< unit << ","
<< "BACKEND"
<< ","
<< eventLevel << ","
<< (event_data.identifier ? event_data.identifier : "NULL") << "\n";
#ifndef _WIN32
tracelogging_provider_ep_enabled = false;
#endif
if (!tracelogging_provider_ep_enabled) {
outfile << "UNKNOWN"
<< ","
<< message << ","
<< event_data.value << ","
<< unit << ","
<< "BACKEND"
<< ","
<< eventLevel << ","
<< (event_data.identifier ? event_data.identifier : "NULL") << "\n";
} else {
#ifdef _WIN32
LogQnnProfileEventAsTraceLogging(
(uint64_t)0,
message,
std::to_string(event_data.value),
unit,
"BACKEND",
eventLevel,
(event_data.identifier ? event_data.identifier : "NULL"));
#endif
}
return Status::OK();
}
@ -935,7 +980,8 @@ Status QnnBackendManager::ExtractProfilingEventBasic(
Status QnnBackendManager::ExtractProfilingEventExtended(
QnnProfile_EventId_t profile_event_id,
const std::string& eventLevel,
std::ofstream& outfile) {
std::ofstream& outfile,
bool tracelogging_provider_ep_enabled) {
QnnProfile_ExtendedEventData_t event_data_extended;
auto resultGetExtendedEventData = qnn_interface_.profileGetExtendedEventData(profile_event_id, &event_data_extended);
QnnProfile_Error_t errorCode = static_cast<QnnProfile_Error_t>(resultGetExtendedEventData & 0xFFFF);
@ -944,20 +990,61 @@ Status QnnBackendManager::ExtractProfilingEventExtended(
std::string message = GetEventTypeString(event_data_extended.v1.type);
std::string unit = GetUnitString(event_data_extended.v1.unit);
if (event_data_extended.version == QNN_PROFILE_DATA_VERSION_1) {
outfile << event_data_extended.v1.timestamp << ","
<< message << ","
<< ExtractQnnScalarValue(event_data_extended.v1.value) << ","
<< unit << ","
<< "BACKEND"
<< ","
<< eventLevel << ","
<< (event_data_extended.v1.identifier ? event_data_extended.v1.identifier : "NULL") << "\n";
#ifndef _WIN32
tracelogging_provider_ep_enabled = false;
#endif
if (!tracelogging_provider_ep_enabled) {
if (event_data_extended.version == QNN_PROFILE_DATA_VERSION_1) {
outfile << event_data_extended.v1.timestamp << ","
<< message << ","
<< ExtractQnnScalarValue(event_data_extended.v1.value) << ","
<< unit << ","
<< "BACKEND"
<< ","
<< eventLevel << ","
<< (event_data_extended.v1.identifier ? event_data_extended.v1.identifier : "NULL") << "\n";
}
} else {
#ifdef _WIN32
LogQnnProfileEventAsTraceLogging(
event_data_extended.v1.timestamp,
message,
ExtractQnnScalarValue(event_data_extended.v1.value),
unit,
"BACKEND",
eventLevel,
(event_data_extended.v1.identifier ? event_data_extended.v1.identifier : "NULL"));
#endif
}
return Status::OK();
}
#ifdef _WIN32
void QnnBackendManager::LogQnnProfileEventAsTraceLogging(
uint64_t timestamp,
const std::string& message,
const std::string& qnnScalarValue,
const std::string& unit,
const std::string& timingSource,
const std::string& eventLevel,
const char* eventIdentifier) {
TraceLoggingWrite(
telemetry_provider_handle,
"QNNProfilingEvent",
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Profiling)),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingValue(timestamp, "Timestamp"),
TraceLoggingString(message.c_str(), "Message"),
TraceLoggingString(qnnScalarValue.c_str(), "Value"),
TraceLoggingString(unit.c_str(), "Unit of Measurement"),
TraceLoggingString(timingSource.c_str(), "Timing Source"),
TraceLoggingString(eventLevel.c_str(), "Event Level"),
TraceLoggingString(eventIdentifier, "Event Identifier"));
}
#endif
const std::string& QnnBackendManager::GetUnitString(QnnProfile_EventUnit_t unitType) {
const auto& unitStringMap = GetUnitStringMap();
auto it = unitStringMap.find(unitType);

View file

@ -6,10 +6,15 @@
#include <windows.h>
#include <psapi.h>
#include <libloaderapi.h>
#include <set>
#else
#include <dlfcn.h>
#endif
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "HTP/QnnHtpDevice.h"
#include "QnnLog.h"
#include "System/QnnSystemInterface.h"
@ -117,8 +122,11 @@ class QnnBackendManager {
void Split(std::vector<std::string>& split_string, const std::string& tokenized_string, const char separator);
Status ExtractBackendProfilingInfo();
Status ExtractProfilingSubEvents(QnnProfile_EventId_t profile_event_id, std::ofstream& outfile, bool backendSupportsExtendedEventData);
Status ExtractProfilingEvent(QnnProfile_EventId_t profile_event_id, const std::string& eventLevel, std::ofstream& outfile, bool backendSupportsExtendedEventData);
Status ExtractProfilingSubEvents(QnnProfile_EventId_t profile_event_id, std::ofstream& outfile,
bool backendSupportsExtendedEventData, bool tracelogging_provider_ep_enabled);
Status ExtractProfilingEvent(QnnProfile_EventId_t profile_event_id, const std::string& eventLevel,
std::ofstream& outfile, bool backendSupportsExtendedEventData,
bool tracelogging_provider_ep_enabled);
void SetQnnBackendType(uint32_t backend_id);
QnnBackendType GetQnnBackendType() { return qnn_backend_type_; }
@ -175,13 +183,25 @@ class QnnBackendManager {
return (backend_build_id == nullptr ? std::string("") : std::string(backend_build_id));
}
Status ExtractProfilingEventBasic(QnnProfile_EventId_t profile_event_id, const std::string& eventLevel, std::ofstream& outfile);
Status ExtractProfilingEventExtended(QnnProfile_EventId_t profile_event_id, const std::string& eventLevel, std::ofstream& outfile);
Status ExtractProfilingEventBasic(QnnProfile_EventId_t profile_event_id, const std::string& eventLevel,
std::ofstream& outfile, bool tracelogging_provider_ep_enabled);
Status ExtractProfilingEventExtended(QnnProfile_EventId_t profile_event_id, const std::string& eventLevel,
std::ofstream& outfile, bool tracelogging_provider_ep_enabled);
static const std::string& GetUnitString(QnnProfile_EventUnit_t unitType);
static const std::unordered_map<QnnProfile_EventUnit_t, std::string>& GetUnitStringMap();
static const std::string GetEventTypeString(QnnProfile_EventType_t eventType);
static const std::string ExtractQnnScalarValue(const Qnn_Scalar_t& scalar);
const char* QnnProfileErrorToString(QnnProfile_Error_t error);
#ifdef _WIN32
void LogQnnProfileEventAsTraceLogging(
uint64_t timestamp,
const std::string& message,
const std::string& qnnScalarValue,
const std::string& unit,
const std::string& timingSource,
const std::string& eventLevel,
const char* eventIdentifier);
#endif
private:
const std::string backend_path_;

View file

@ -4,12 +4,13 @@
#include "qnn_execution_provider.h"
#include <filesystem>
#include "core/providers/common.h"
#include "core/framework/compute_capability.h"
#include "core/graph/graph_viewer.h"
#include "core/session/onnxruntime_session_options_config_keys.h"
#include "core/session/onnxruntime_cxx_api.h"
#include "core/framework/kernel_registry.h"
#include "core/platform/env.h"
#include "core/providers/common.h"
#include "core/providers/partitioning_utils.h"
#include "core/providers/qnn/builder/op_builder_factory.h"
#include "core/providers/partitioning_utils.h"
@ -28,7 +29,7 @@ static void ParseProfilingLevel(std::string profiling_level_string,
profiling_level_string.end(),
profiling_level_string.begin(),
[](unsigned char c) { return static_cast<unsigned char>(std::tolower(c)); });
LOGS_DEFAULT(VERBOSE) << "profiling_level: " << profiling_level_string;
LOGS_DEFAULT(INFO) << "profiling_level: " << profiling_level_string;
if (profiling_level_string == "off") {
profiling_level = qnn::ProfilingLevel::OFF;
} else if (profiling_level_string == "basic") {
@ -146,9 +147,30 @@ QNNExecutionProvider::QNNExecutionProvider(const ProviderOptions& provider_optio
static const std::string PROFILING_LEVEL = "profiling_level";
qnn::ProfilingLevel profiling_level = qnn::ProfilingLevel::OFF;
auto profiling_level_pos = provider_options_map.find(PROFILING_LEVEL);
if (profiling_level_pos != provider_options_map.end()) {
ParseProfilingLevel(profiling_level_pos->second, profiling_level);
const Env& env = Env::Default();
auto& provider = env.GetTelemetryProvider();
if (provider.IsEnabled()) {
auto level = provider.Level();
auto keyword = provider.Keyword();
if ((keyword & static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Profiling)) != 0) {
if (level != 0) {
if (level == 5) {
LOGS_DEFAULT(INFO) << "Overriding profiling to basic based on ETW level: " << static_cast<int>(level);
ParseProfilingLevel("basic", profiling_level);
} else if (level < 5) {
LOGS_DEFAULT(INFO) << "QNN Profiler ETW level not supported below level 5. Level: "
<< static_cast<int>(level);
} else {
LOGS_DEFAULT(INFO) << "Overriding profiling to detailed based on ETW level: " << static_cast<int>(level);
ParseProfilingLevel("detailed", profiling_level);
}
}
}
} else {
auto profiling_level_pos = provider_options_map.find(PROFILING_LEVEL);
if (profiling_level_pos != provider_options_map.end()) {
ParseProfilingLevel(profiling_level_pos->second, profiling_level);
}
}
static const std::string RPC_CONTROL_LANTENCY = "rpc_control_latency";

View file

@ -69,6 +69,11 @@
#include "core/util/protobuf_parsing_utils.h"
#include "core/util/thread_utils.h"
#ifdef _WIN32
#include "core/platform/windows/logging/etw_sink.h"
#include "core/common/logging/sinks/composite_sink.h"
#endif
// custom ops are not available in a minimal build unless ORT_MINIMAL_BUILD_CUSTOM_OPS is set
#if !defined(ORT_MINIMAL_BUILD) || defined(ORT_MINIMAL_BUILD_CUSTOM_OPS)
#include "core/framework/customregistry.h"
@ -307,6 +312,7 @@ static Status FinalizeSessionOptions(const SessionOptions& user_provided_session
logging::Severity GetSeverity(const SessionOptions& session_options) {
logging::Severity severity = logging::Severity::kWARNING;
if (session_options.session_log_severity_level == -1) {
severity = logging::LoggingManager::DefaultLogger().GetSeverity();
} else {
@ -322,11 +328,17 @@ logging::Severity GetSeverity(const SessionOptions& session_options) {
void InferenceSession::SetLoggingManager(const SessionOptions& session_options,
const Environment& session_env) {
logging_manager_ = session_env.GetLoggingManager();
std::unique_ptr<logging::ISink> sink;
if (session_options.user_logging_function) {
std::unique_ptr<logging::ISink> user_sink = std::make_unique<UserLoggingSink>(session_options.user_logging_function,
session_options.user_logging_param);
user_logging_manager_ = std::make_unique<logging::LoggingManager>(std::move(user_sink),
GetSeverity(session_options),
sink = std::make_unique<UserLoggingSink>(session_options.user_logging_function,
session_options.user_logging_param);
auto sessionSeverity = GetSeverity(session_options);
auto etwOverrideSeverity = logging::OverrideLevelWithEtw(sessionSeverity);
sink = EnhanceLoggerWithEtw(std::move(sink), sessionSeverity, etwOverrideSeverity);
user_logging_manager_ = std::make_unique<logging::LoggingManager>(std::move(sink),
std::min(sessionSeverity, etwOverrideSeverity),
false,
logging::LoggingManager::InstanceType::Temporal,
&session_options.session_logid);
@ -467,6 +479,8 @@ void InferenceSession::TraceSessionOptions(const SessionOptions& session_options
#ifdef _WIN32
TraceLoggingWrite(telemetry_provider_handle,
"SessionOptions",
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Session)),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingUInt8(static_cast<UINT8>(session_options.execution_mode), "execution_mode"),
TraceLoggingUInt8(static_cast<UINT8>(session_options.execution_order), "execution_order"),
TraceLoggingBoolean(session_options.enable_profiling, "enable_profiling"),
@ -487,6 +501,8 @@ void InferenceSession::TraceSessionOptions(const SessionOptions& session_options
TraceLoggingWrite(
telemetry_provider_handle,
"SessionOptions_IntraOrtThreadPoolParams",
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Session)),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingInt32(session_options.intra_op_param.thread_pool_size, "thread_pool_size"),
TraceLoggingBoolean(session_options.intra_op_param.auto_set_affinity, "auto_set_affinity"),
TraceLoggingBoolean(session_options.intra_op_param.allow_spinning, "allow_spinning"),
@ -499,6 +515,8 @@ void InferenceSession::TraceSessionOptions(const SessionOptions& session_options
TraceLoggingWrite(
telemetry_provider_handle,
"SessionOptions_ConfigEntry",
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Session)),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingString(config_pair.first.c_str(), "Key"),
TraceLoggingString(config_pair.second.c_str(), "Value"));
}

View file

@ -39,23 +39,23 @@ OrtEnv* OrtEnv::GetInstance(const OrtEnv::LoggingManagerConstructionInfo& lm_inf
if (!p_instance_) {
std::unique_ptr<LoggingManager> lmgr;
std::string name = lm_info.logid;
if (lm_info.logging_function) {
std::unique_ptr<ISink> logger = std::make_unique<UserLoggingSink>(lm_info.logging_function,
lm_info.logger_param);
lmgr = std::make_unique<LoggingManager>(std::move(logger),
static_cast<Severity>(lm_info.default_warning_level),
false,
LoggingManager::InstanceType::Default,
&name);
} else {
auto sink = MakePlatformDefaultLogSink();
lmgr = std::make_unique<LoggingManager>(std::move(sink),
static_cast<Severity>(lm_info.default_warning_level),
false,
LoggingManager::InstanceType::Default,
&name);
std::unique_ptr<ISink> sink = nullptr;
if (lm_info.logging_function) {
sink = std::make_unique<UserLoggingSink>(lm_info.logging_function, lm_info.logger_param);
} else {
sink = MakePlatformDefaultLogSink();
}
auto etwOverrideSeverity = logging::OverrideLevelWithEtw(static_cast<Severity>(lm_info.default_warning_level));
sink = EnhanceLoggerWithEtw(std::move(sink), static_cast<Severity>(lm_info.default_warning_level),
etwOverrideSeverity);
lmgr = std::make_unique<LoggingManager>(std::move(sink),
std::min(static_cast<Severity>(lm_info.default_warning_level), etwOverrideSeverity),
false,
LoggingManager::InstanceType::Default,
&name);
std::unique_ptr<onnxruntime::Environment> env;
if (!tp_options) {
status = onnxruntime::Environment::Create(std::move(lmgr), env);

View file

@ -4,6 +4,7 @@
#include <string>
#include "core/common/common.h"
#include "core/common/logging/logging.h"
#include "core/framework/error_code_helper.h"
#include "core/framework/provider_options.h"
#include "core/providers/provider_factory_creators.h"
@ -13,6 +14,7 @@
#include "core/providers/openvino/openvino_provider_factory_creator.h"
#ifdef _WIN32
#include <winmeta.h>
#include "core/platform/tracing.h"
#endif
@ -75,6 +77,8 @@ ORT_API_STATUS_IMPL(OrtApis::SessionOptionsAppendExecutionProvider,
TraceLoggingWrite(
telemetry_provider_handle,
"ProviderOptionsAppendExecutionProvider",
TraceLoggingKeyword(static_cast<uint64_t>(onnxruntime::logging::ORTTraceLoggingKeyword::Session)),
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingString(provider_name, "ProviderName"),
TraceLoggingString(config_pair.first.c_str(), "Key"),
TraceLoggingString(config_pair.second.c_str(), "Value"));

View file

@ -156,7 +156,7 @@ TEST(LoggingTests, TestCompositeSink) {
EXPECT_CALL(*sink_ptr2, SendImpl(testing::_, testing::_, testing::_)).Times(1);
CompositeSink* sink = new CompositeSink();
sink->AddSink(std::unique_ptr<ISink>{sink_ptr1}).AddSink(std::unique_ptr<ISink>{sink_ptr2});
sink->AddSink(std::unique_ptr<ISink>{sink_ptr1}, min_log_level).AddSink(std::unique_ptr<ISink>{sink_ptr2}, min_log_level);
LoggingManager manager{std::unique_ptr<ISink>(sink), min_log_level, false, InstanceType::Temporal};
auto logger = manager.CreateLogger(logid);

View file

@ -1,10 +1,19 @@
## Validating ETW Sink unit test output
## About the ETW Sink
## Setup
Install Windows Performance Toolkit from <https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install>
You get to select components when installing, so can select just the performance toolkit.
The ETW Sink (ONNXRuntimeTraceLoggingProvider) allows ONNX semi-structured printf style logs to be output via ETW.
Overview of the steps is at <https://msdn.microsoft.com/en-us/library/windows/desktop/dn904629(v=vs.85).aspx> if you want more detail.
ETW makes it easy and useful to only enable and listen for events with great performance, and when you need them instead of only at compile time.
Therefore ONNX will preserve any existing loggers and log severity [provided at compile time](docs/FAQ.md?plain=1#L7).
However, when the provider is enabled a new ETW logger sink will also be added and the severity separately controlled via ETW dynamically.
- Provider GUID: 929DD115-1ECB-4CB5-B060-EBD4983C421D
- Keyword: Logs (0x2) keyword per [logging.h](include\onnxruntime\core\common\logging\logging.h)
- Level: 1-5 ([CRITICAL through VERBOSE](https://learn.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_descriptor)) [mapping](onnxruntime\core\platform\windows\logging\etw_sink.cc) to [ONNX severity](include\onnxruntime\core\common\logging\severity.h) in an intuitive manner
Notes:
- The ETW provider must be enabled prior to session creation, as that as when internal logging setup is complete
- Other structured ETW logs are output via the other Microsoft.ML.ONNXRuntime ETW provider. Both used together are recommended
## Capturing ETW trace output
@ -25,9 +34,17 @@ Run the ETW sink unit tests
Stop the ETW tracing
`<path to repo>\onnxruntime\test\platform\windows\logging> wpr -stop TraceCaptureFile.etl EtwSinkTest`
## View the output
## View the trace output
Open TraceCaptureFile.etl file Windows Performance Analyzer.
### Setup
- Install Windows Performance Analyzer (Preview) from the Windows Store - <https://www.microsoft.com/en-us/p/windows-performance-analyzer-preview/9n58qrw40dfw>
- Or from the ADK <https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install>
- You get to select components when installing, so can select just the performance toolkit.
- Overview of the steps is at <https://msdn.microsoft.com/en-us/library/windows/desktop/dn904629(v=vs.85).aspx> if you want more detail.
### Viewing
Open TraceCaptureFile.etl file in Windows Performance Analyzer.
Expand the "System Activity" dropdown in the left pane, and double-click "Generic Events".
That should open events in an Analysis window in the right pane. You should see an event

View file

@ -47,8 +47,8 @@ TEST(LoggingTests, TestEtwSink) {
/// </summary>
TEST(LoggingTests, TestEtwSinkCtor) {
CompositeSink* sinks = new CompositeSink();
sinks->AddSink(std::unique_ptr<ISink>(new EtwSink()))
.AddSink(std::unique_ptr<ISink>(new EtwSink()));
sinks->AddSink(std::unique_ptr<ISink>(new EtwSink()), Severity::kWARNING)
.AddSink(std::unique_ptr<ISink>(new EtwSink()), Severity::kWARNING);
LoggingManager manager{std::unique_ptr<ISink>{sinks},
Severity::kWARNING,

View file

@ -8,12 +8,12 @@
<Profiles>
<EventCollector Id="EventCollector_OrtTraceLoggingProvider"
Name="OrtTraceLoggingProviderCollector">
<BufferSize Value="65536" />
<BufferSize Value="1024" />
<Buffers Value="10" PercentageOfTotalMemory="true"/>
</EventCollector>
<EventProvider Id="EventProvider_OrtTraceLoggingProvider"
Name="3a26b1ff-7484-7484-7484-15261f42614d" />
Name="3a26b1ff-7484-7484-7484-15261f42614d" Level="5" />
<Profile Id="OrtTraceLoggingProvider.Verbose.File"
Name="OrtTraceLoggingProvider" Description="OrtTraceLoggingProvider"
LoggingMode="File" DetailLevel="Verbose">