|
| 1 | +// Copyright (c) Intel Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT License. |
| 3 | +#include "core/providers/openvino/ov_tracing.h" |
| 4 | + |
| 5 | +#ifdef _WIN32 |
| 6 | +#include <windows.h> |
| 7 | +#ifdef _MSC_VER |
| 8 | +#pragma warning(push) |
| 9 | +#pragma warning(disable : 26440) |
| 10 | +#endif |
| 11 | +#include <TraceLoggingProvider.h> |
| 12 | +#include <winmeta.h> |
| 13 | +#include "core/platform/windows/TraceLoggingConfig.h" |
| 14 | + |
| 15 | +TRACELOGGING_DEFINE_PROVIDER( |
| 16 | + ov_tracing_provider_handle, |
| 17 | + "Intel.ML.ONNXRuntime.OpenVINO", |
| 18 | + // {"b5a8c2e1-4d7f-4a3b-9c2e-1f8e5a6b7c9d"} |
| 19 | + (0xb5a8c2e1, 0x4d7f, 0x4a3b, 0x9c, 0x2e, 0x1f, 0x8e, 0x5a, 0x6b, 0x7c, 0x9d), |
| 20 | + TraceLoggingOptionMicrosoftTelemetry()); |
| 21 | + |
| 22 | +#ifdef _MSC_VER |
| 23 | +#pragma warning(pop) |
| 24 | +#endif |
| 25 | + |
| 26 | +namespace { |
| 27 | +std::string EscapeJsonString(const std::string& input) { |
| 28 | + std::string escaped; |
| 29 | + // Reserve extra space for escaping |
| 30 | + escaped.reserve(input.size() + input.size() / 5); |
| 31 | + |
| 32 | + for (char c : input) { |
| 33 | + switch (c) { |
| 34 | + case '\"': |
| 35 | + escaped += "\\\""; |
| 36 | + break; |
| 37 | + case '\\': |
| 38 | + escaped += "\\\\"; |
| 39 | + break; |
| 40 | + case '\b': |
| 41 | + escaped += "\\b"; |
| 42 | + break; |
| 43 | + case '\f': |
| 44 | + escaped += "\\f"; |
| 45 | + break; |
| 46 | + case '\n': |
| 47 | + escaped += "\\n"; |
| 48 | + break; |
| 49 | + case '\r': |
| 50 | + escaped += "\\r"; |
| 51 | + break; |
| 52 | + case '\t': |
| 53 | + escaped += "\\t"; |
| 54 | + break; |
| 55 | + default: |
| 56 | + if (static_cast<unsigned char>(c) < 0x20) { |
| 57 | + char unicode_escape[7]; |
| 58 | + sprintf_s(unicode_escape, sizeof(unicode_escape), "\\u%04x", static_cast<unsigned char>(c)); |
| 59 | + escaped += unicode_escape; |
| 60 | + } else { |
| 61 | + escaped += c; |
| 62 | + } |
| 63 | + break; |
| 64 | + } |
| 65 | + } |
| 66 | + return escaped; |
| 67 | +} |
| 68 | +} // namespace |
| 69 | + |
| 70 | +namespace onnxruntime { |
| 71 | +namespace openvino_ep { |
| 72 | + |
| 73 | +std::mutex OVTracing::mutex_; |
| 74 | +std::mutex OVTracing::provider_change_mutex_; |
| 75 | +uint32_t OVTracing::global_register_count_ = 0; |
| 76 | +bool OVTracing::enabled_ = true; |
| 77 | +UCHAR OVTracing::level_ = 0; |
| 78 | +UINT64 OVTracing::keyword_ = 0; |
| 79 | +std::vector<const OVTracing::EtwInternalCallback*> OVTracing::callbacks_; |
| 80 | +std::mutex OVTracing::callbacks_mutex_; |
| 81 | + |
| 82 | +OVTracing::OVTracing() { |
| 83 | + std::lock_guard<std::mutex> lock(mutex_); |
| 84 | + if (global_register_count_ == 0) { |
| 85 | + HRESULT hr = TraceLoggingRegisterEx(ov_tracing_provider_handle, ORT_TL_EtwEnableCallback, nullptr); |
| 86 | + if (SUCCEEDED(hr)) { |
| 87 | + global_register_count_ += 1; |
| 88 | + } |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +OVTracing::~OVTracing() noexcept { |
| 93 | + // Clean up TraceLogging, only hold mutex_ |
| 94 | + try { |
| 95 | + std::lock_guard<std::mutex> lock(mutex_); |
| 96 | + if (global_register_count_ > 0) { |
| 97 | + global_register_count_ -= 1; |
| 98 | + if (global_register_count_ == 0) { |
| 99 | + TraceLoggingUnregister(ov_tracing_provider_handle); |
| 100 | + } |
| 101 | + } |
| 102 | + } catch (...) { |
| 103 | + // Suppress exceptions in destructor |
| 104 | + } |
| 105 | + |
| 106 | + // Clean up callbacks, only hold callbacks_mutex_ |
| 107 | + try { |
| 108 | + std::lock_guard<std::mutex> lock_callbacks(callbacks_mutex_); |
| 109 | + callbacks_.clear(); |
| 110 | + } catch (...) { |
| 111 | + // Suppress exceptions in destructor |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +OVTracing& OVTracing::Instance() { |
| 116 | + static OVTracing instance; |
| 117 | + return instance; |
| 118 | +} |
| 119 | + |
| 120 | +bool OVTracing::IsEnabled() const { |
| 121 | + std::lock_guard<std::mutex> lock(provider_change_mutex_); |
| 122 | + return enabled_; |
| 123 | +} |
| 124 | + |
| 125 | +UCHAR OVTracing::Level() const { |
| 126 | + std::lock_guard<std::mutex> lock(provider_change_mutex_); |
| 127 | + return level_; |
| 128 | +} |
| 129 | + |
| 130 | +UINT64 OVTracing::Keyword() const { |
| 131 | + std::lock_guard<std::mutex> lock(provider_change_mutex_); |
| 132 | + return keyword_; |
| 133 | +} |
| 134 | + |
| 135 | +void OVTracing::LogAllRuntimeOptions(uint32_t session_id, const SessionContext& ctx) const { |
| 136 | + if (!IsEnabled()) return; |
| 137 | + |
| 138 | + // Log OpenVINO SDK version separately |
| 139 | + TraceLoggingWrite(ov_tracing_provider_handle, "OV.SDK.Version", |
| 140 | + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), |
| 141 | + TraceLoggingUInt32(session_id, "session_id"), |
| 142 | + TraceLoggingString(ctx.openvino_sdk_version.c_str(), "openvino_sdk_version")); |
| 143 | + |
| 144 | + constexpr std::string_view provider_prefix = "ep.openvinoexecutionprovider."; |
| 145 | + std::ostringstream provider_opts; |
| 146 | + std::ostringstream session_opts; |
| 147 | + bool provider_first = true; |
| 148 | + bool session_first = true; |
| 149 | + |
| 150 | + provider_opts << "{"; |
| 151 | + session_opts << "{"; |
| 152 | + |
| 153 | + // Segregate options based on prefix |
| 154 | + for (const auto& [key, value] : ctx.runtime_config.options) { |
| 155 | + if (!value.empty()) { |
| 156 | + if (key.starts_with(provider_prefix)) { |
| 157 | + // Provider option |
| 158 | + if (!provider_first) provider_opts << ","; |
| 159 | + provider_opts << "\"" << key << "\":\"" << EscapeJsonString(value) << "\""; |
| 160 | + provider_first = false; |
| 161 | + } else { |
| 162 | + // Session option |
| 163 | + if (!session_first) session_opts << ","; |
| 164 | + session_opts << "\"" << key << "\":\"" << EscapeJsonString(value) << "\""; |
| 165 | + session_first = false; |
| 166 | + } |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + provider_opts << "}"; |
| 171 | + session_opts << "}"; |
| 172 | + |
| 173 | + // Log provider options only if there are any |
| 174 | + if (!provider_first) { |
| 175 | + TraceLoggingWrite(ov_tracing_provider_handle, "OVEP.Provider.Options", |
| 176 | + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), |
| 177 | + TraceLoggingUInt32(session_id, "session_id"), |
| 178 | + TraceLoggingString(provider_opts.str().c_str(), "provider_options")); |
| 179 | + } |
| 180 | + |
| 181 | + // Log session options only if there are any |
| 182 | + if (!session_first) { |
| 183 | + TraceLoggingWrite(ov_tracing_provider_handle, "OVEP.Session.Options", |
| 184 | + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), |
| 185 | + TraceLoggingUInt32(session_id, "session_id"), |
| 186 | + TraceLoggingString(session_opts.str().c_str(), "session_options")); |
| 187 | + } |
| 188 | +} |
| 189 | + |
| 190 | +void OVTracing::RegisterInternalCallback(const EtwInternalCallback& callback) { |
| 191 | + std::lock_guard<std::mutex> lock_callbacks(callbacks_mutex_); |
| 192 | + callbacks_.push_back(&callback); |
| 193 | +} |
| 194 | + |
| 195 | +void OVTracing::UnregisterInternalCallback(const EtwInternalCallback& callback) { |
| 196 | + std::lock_guard<std::mutex> lock_callbacks(callbacks_mutex_); |
| 197 | + auto new_end = std::remove_if(callbacks_.begin(), callbacks_.end(), |
| 198 | + [&callback](const EtwInternalCallback* ptr) { |
| 199 | + return ptr == &callback; |
| 200 | + }); |
| 201 | + callbacks_.erase(new_end, callbacks_.end()); |
| 202 | +} |
| 203 | + |
| 204 | +void NTAPI OVTracing::ORT_TL_EtwEnableCallback( |
| 205 | + _In_ LPCGUID SourceId, _In_ ULONG IsEnabled, _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, |
| 206 | + _In_ ULONGLONG MatchAllKeyword, _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData, _In_opt_ PVOID CallbackContext) { |
| 207 | + { |
| 208 | + std::lock_guard<std::mutex> lock(provider_change_mutex_); |
| 209 | + enabled_ = (IsEnabled != 0); |
| 210 | + level_ = Level; |
| 211 | + keyword_ = MatchAnyKeyword; |
| 212 | + } |
| 213 | + // Release lock before invoking callbacks to prevent deadlock |
| 214 | + InvokeCallbacks(SourceId, IsEnabled, Level, MatchAnyKeyword, MatchAllKeyword, FilterData, CallbackContext); |
| 215 | +} |
| 216 | + |
| 217 | +void OVTracing::InvokeCallbacks(LPCGUID SourceId, ULONG IsEnabled, UCHAR Level, ULONGLONG MatchAnyKeyword, |
| 218 | + ULONGLONG MatchAllKeyword, PEVENT_FILTER_DESCRIPTOR FilterData, PVOID CallbackContext) { |
| 219 | + std::lock_guard<std::mutex> lock_callbacks(callbacks_mutex_); |
| 220 | + for (const auto& callback : callbacks_) { |
| 221 | + (*callback)(SourceId, IsEnabled, Level, MatchAnyKeyword, MatchAllKeyword, FilterData, CallbackContext); |
| 222 | + } |
| 223 | +} |
| 224 | + |
| 225 | +} // namespace openvino_ep |
| 226 | +} // namespace onnxruntime |
| 227 | + |
| 228 | +#endif // defined(_WIN32) |
0 commit comments