diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 15718d3a7b834..e1e79a9402fe5 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -157,6 +157,12 @@ jobs: with: ndk-version: 28.0.13004108 + - name: Test static telemetry package dependencies on Linux and Android + shell: bash + run: | + test -n "$ANDROID_NDK_HOME" + python3 tools/ci_build/test_telemetry_package.py -v + - name: Setup CCache uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0 with: diff --git a/build.bat b/build.bat index 6dbe3acf3d1a4..d0c6cbcddd669 100644 --- a/build.bat +++ b/build.bat @@ -7,4 +7,4 @@ setlocal set PATH=C:\Program Files\Git\usr\bin;%PATH% rem Requires a Python install to be available in your PATH -python "%~dp0\tools\ci_build\build.py" --build_dir "%~dp0\build\Windows" --no_telemetry %* +python "%~dp0\tools\ci_build\build.py" --build_dir "%~dp0\build\Windows" %* diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 2ffa1d574ac29..3a3bda1dded41 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -172,9 +172,44 @@ option(onnxruntime_USE_WINML "Build with WinML support" OFF) option(onnxruntime_USE_ACL "Build with ACL support" OFF) option(onnxruntime_ENABLE_INSTRUMENT "Enable Instrument with Event Tracing for Windows (ETW)" OFF) option(onnxruntime_USE_TELEMETRY "Build with Telemetry" OFF) -# Optional 1DS ingestion token for non-Windows telemetry. Leave empty to compile in the encoded -# in-repo default from core/platform/posix/telemetry.cc; callers can override it at configure time. -set(ONNXRUNTIME_TELEMETRY_TENANT_TOKEN "" CACHE STRING "Override the compiled-in telemetry ingestion token (non-Windows)") +# AUTO selects 1DS on supported native platforms; TraceLogging requires an explicit Windows opt-in. +set(onnxruntime_TELEMETRY_BACKEND "AUTO" CACHE STRING "Telemetry backend: AUTO, 1DS, or WINDOWS") +set_property(CACHE onnxruntime_TELEMETRY_BACKEND PROPERTY STRINGS AUTO 1DS WINDOWS) +string(TOUPPER "${onnxruntime_TELEMETRY_BACKEND}" onnxruntime_TELEMETRY_BACKEND) +if(NOT onnxruntime_TELEMETRY_BACKEND MATCHES "^(AUTO|1DS|WINDOWS)$") + message(FATAL_ERROR + "Invalid onnxruntime_TELEMETRY_BACKEND='${onnxruntime_TELEMETRY_BACKEND}'. " + "Expected AUTO, 1DS, or WINDOWS.") +endif() + +# Deprecated compatibility alias for callers that adopted this option before the backend selector. +option(onnxruntime_USE_WINDOWS_TELEMETRY "Use Windows TraceLogging instead of 1DS telemetry" OFF) +if(onnxruntime_USE_WINDOWS_TELEMETRY) + set(onnxruntime_TELEMETRY_BACKEND "WINDOWS") +endif() + +if(onnxruntime_TELEMETRY_BACKEND STREQUAL "WINDOWS") + if(NOT WIN32) + message(FATAL_ERROR "The WINDOWS telemetry backend is only supported on Windows") + endif() + set(onnxruntime_USE_TELEMETRY ON CACHE BOOL "Build with Telemetry" FORCE) +endif() + +set(onnxruntime_USE_1DS_TELEMETRY OFF) +if(onnxruntime_USE_TELEMETRY) + if(onnxruntime_TELEMETRY_BACKEND STREQUAL "1DS" OR + onnxruntime_TELEMETRY_BACKEND STREQUAL "AUTO") + set(onnxruntime_USE_1DS_TELEMETRY ON) + endif() +endif() +if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" AND onnxruntime_USE_1DS_TELEMETRY) + message(WARNING "1DS telemetry is not supported for UWP builds. Telemetry will be disabled.") + set(onnxruntime_USE_TELEMETRY OFF CACHE BOOL "Build with Telemetry" FORCE) + set(onnxruntime_USE_1DS_TELEMETRY OFF) +endif() +# Optional 1DS ingestion token. Leave empty to compile in the encoded +# in-repo platform default; callers can override it at configure time. +set(ONNXRUNTIME_TELEMETRY_TENANT_TOKEN "" CACHE STRING "Override the compiled-in 1DS telemetry ingestion token") cmake_dependent_option(onnxruntime_USE_MIMALLOC "Override new/delete and arena allocator with mimalloc" OFF "WIN32;NOT onnxruntime_USE_CUDA;NOT onnxruntime_USE_OPENVINO" OFF) option(onnxruntime_USE_CANN "Build with CANN support" OFF) option(onnxruntime_USE_XNNPACK "Build with XNNPACK support. Provides an alternative math library on ARM, WebAssembly and x86." OFF) @@ -2018,41 +2053,7 @@ else() add_library(safeint_interface IMPORTED INTERFACE)\n\ target_include_directories(safeint_interface INTERFACE ${safeint_SOURCE_DIR})\n\ ") - if(onnxruntime_USE_TELEMETRY AND NOT WIN32 AND onnxruntime_TELEMETRY_USES_EXTERNAL_PACKAGE) - string(APPEND PROJECT_CONFIG_CONTENT - "find_dependency(MSTelemetry CONFIG)\n\ - ") - endif() - if(onnxruntime_USE_TELEMETRY AND APPLE - AND NOT onnxruntime_TELEMETRY_USES_EXTERNAL_PACKAGE) - string(APPEND PROJECT_CONFIG_CONTENT - "if(NOT TARGET MSTelemetry::sqlite_dependency)\n\ - add_library(MSTelemetry::sqlite_dependency INTERFACE IMPORTED)\n\ - set_property(TARGET MSTelemetry::sqlite_dependency PROPERTY INTERFACE_LINK_LIBRARIES sqlite3)\n\ - endif()\n\ - if(NOT TARGET MSTelemetry::zlib_dependency)\n\ - add_library(MSTelemetry::zlib_dependency INTERFACE IMPORTED)\n\ - set_property(TARGET MSTelemetry::zlib_dependency PROPERTY INTERFACE_LINK_LIBRARIES z)\n\ - endif()\n\ - ") - endif() - if(onnxruntime_USE_TELEMETRY AND NOT WIN32 AND NOT APPLE - AND NOT CMAKE_SYSTEM_NAME STREQUAL "Android" - AND NOT onnxruntime_TELEMETRY_USES_EXTERNAL_PACKAGE - AND NOT onnxruntime_BUILD_SHARED_LIB) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - # The embedded curl archive uses the platform Threads target for its resolver. - string(APPEND PROJECT_CONFIG_CONTENT - "find_dependency(Threads)\n\ - ") - else() - # Preserve the legacy dependency graph for any explicitly enabled non-Linux Unix target. - string(APPEND PROJECT_CONFIG_CONTENT - "find_dependency(CURL)\n\ - find_dependency(Threads)\n\ - ") - endif() - endif() + include("${CMAKE_CURRENT_LIST_DIR}/onnxruntime_telemetry_config.cmake") endif() string(APPEND PROJECT_CONFIG_CONTENT diff --git a/cmake/deps.txt b/cmake/deps.txt index 2bec4eafdc4d1..467f2c2d5d1a3 100644 --- a/cmake/deps.txt +++ b/cmake/deps.txt @@ -66,9 +66,9 @@ kleidiai;https://github.com/ARM-software/kleidiai/archive/refs/tags/v1.30.0.tar. # this entry will be updated to use refs/tags/ instead of the raw commit hash. kleidiai-qmx;https://github.com/qualcomm/kleidiai/archive/2f10c9a8d32f81ffeeb6d4885a29cc35d2b0da87.zip;5e855730a2d69057a569f43dd7532db3b2d2a05c vulkan_headers;https://codeload.github.com/KhronosGroup/Vulkan-Headers/tar.gz/refs/tags/v1.4.344;57bc528ef7c4a3f7bfbb59e64a187e3734bd29d8 -# cpp_client_telemetry (1DS SDK) for cross-platform telemetry on non-Windows platforms. -# Pinned to the stable release. -cpp_client_telemetry;https://github.com/microsoft/cpp_client_telemetry/archive/refs/tags/v3.10.267.1.zip;9d29169b8e51157d44a24a0d6cf6847260ea2efc +# cpp_client_telemetry (1DS SDK) for cross-platform telemetry, including vcpkg builds. +# This release fixes Windows Debug listener TLS leaks during DLL unload. +cpp_client_telemetry;https://github.com/microsoft/cpp_client_telemetry/archive/refs/tags/v3.10.279.1.zip;e97ee8f91c9348551d35873da33a18b4e50cf0db # Transitive 1DS Linux transport dependencies, retained here for dependency inventory generation. curl;https://github.com/curl/curl/releases/download/curl-8_21_0/curl-8.21.0.tar.xz;c3b4712175609f1e1662e716efd4fd48901e863d mbedtls;https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-3.6.7/mbedtls-3.6.7.tar.bz2;e892e98cff90cf7e0736c525bf357626a2513c4c diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 6368d7e798057..b7bc9aff89c08 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -8,8 +8,10 @@ include(external/helper_functions.cmake) file(STRINGS deps.txt ONNXRUNTIME_DEPS_LIST) foreach(ONNXRUNTIME_DEP IN LISTS ONNXRUNTIME_DEPS_LIST) # Lines start with "#" are comments, so skip them. - # cpp_client_telemetry is only needed for telemetry on non-Windows platforms, so skip if telemetry is not enabled or it's Windows platform. - if((NOT ONNXRUNTIME_DEP MATCHES "^#") AND ((NOT ONNXRUNTIME_DEP MATCHES "^cpp_client_telemetry") OR (onnxruntime_USE_TELEMETRY AND NOT WIN32))) + # cpp_client_telemetry is needed only when the 1DS backend is selected. + if((NOT ONNXRUNTIME_DEP MATCHES "^#") AND + ((NOT ONNXRUNTIME_DEP MATCHES "^cpp_client_telemetry") OR + onnxruntime_USE_1DS_TELEMETRY)) # The first column is name list(POP_FRONT ONNXRUNTIME_DEP ONNXRUNTIME_DEP_NAME) # The second column is URL @@ -1083,182 +1085,241 @@ if(onnxruntime_USE_SNPE) list(APPEND onnxruntime_EXTERNAL_LIBRARIES ${SNPE_NN_LIBS}) endif() -# 1DS SDK (cpp_client_telemetry) for cross-platform telemetry on non-Windows platforms -if(onnxruntime_USE_TELEMETRY AND NOT WIN32) +# 1DS SDK (cpp_client_telemetry) is the default cross-platform telemetry backend. +if(onnxruntime_USE_1DS_TELEMETRY) if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - message(FATAL_ERROR "onnxruntime_USE_TELEMETRY is not supported for WebAssembly/Emscripten builds: " + message(FATAL_ERROR "onnxruntime_USE_1DS_TELEMETRY / telemetry is not supported for WebAssembly/Emscripten builds: " "the 1DS telemetry SDK is excluded on Emscripten. Disable telemetry for WASM builds.") endif() - set(onnxruntime_TELEMETRY_USES_EXTERNAL_PACKAGE OFF) - if(onnxruntime_USE_VCPKG AND NOT ANDROID AND NOT TARGET MSTelemetry::mat) - # The telemetry manifest feature installs this package. Keep it required so a broken vcpkg - # integration cannot silently switch dependency models within a vcpkg build. - find_package(MSTelemetry CONFIG REQUIRED) + # Use the pinned GitHub SDK on every platform, independently of the vcpkg registry version. + set(_ort_requested_apple_architectures "${CMAKE_OSX_ARCHITECTURES}") + + # Android AARs require HttpClient_Android and its Java bridge. + # Use cpp_client_telemetry's canonical build options. The SDK keeps its + # build policy and dependency selection local to 1DS. + set(MATSDK_BUILD_HEADERS ON CACHE BOOL "Build 1DS SDK headers" FORCE) + set(MATSDK_BUILD_LIBRARY ON CACHE BOOL "Build 1DS SDK library" FORCE) + set(MATSDK_BUILD_TEST_TOOL OFF CACHE BOOL "Disable 1DS SDK test tool" FORCE) + set(MATSDK_BUILD_UNIT_TESTS OFF CACHE BOOL "Disable 1DS SDK unit tests" FORCE) + set(MATSDK_BUILD_FUNC_TESTS OFF CACHE BOOL "Disable 1DS SDK functional tests" FORCE) + set(MATSDK_BUILD_PRIVACYGUARD OFF CACHE BOOL "Disable 1DS privacy guard module" FORCE) + set(MATSDK_BUILD_CDS OFF CACHE BOOL "Disable 1DS CDS module" FORCE) + set(MATSDK_BUILD_LIVEEVENTINSPECTOR OFF CACHE BOOL "Disable 1DS live event inspector" FORCE) + set(MATSDK_BUILD_SIGNALS OFF CACHE BOOL "Disable 1DS signals module" FORCE) + set(MATSDK_BUILD_SANITIZER OFF CACHE BOOL "Disable 1DS sanitizer module" FORCE) + set(MATSDK_BUILD_AZMON OFF CACHE BOOL "Disable 1DS Azure Monitor module" FORCE) + set(MATSDK_BUILD_OBJC_WRAPPER OFF CACHE BOOL "Disable 1DS ObjC wrapper" FORCE) + set(MATSDK_BUILD_SWIFT_WRAPPER OFF CACHE BOOL "Disable 1DS Swift wrapper" FORCE) + set(MATSDK_BUILD_JNI_WRAPPER OFF CACHE BOOL "Disable 1DS JNI wrapper" FORCE) + set(MATSDK_BUILD_PACKAGE OFF CACHE BOOL "Disable 1DS package generation" FORCE) + set(MATSDK_BUILD_APPLE_HTTP ${APPLE} CACHE BOOL "Build the 1DS Apple HTTP client" FORCE) + set(MATSDK_DISABLE_LOGGING ON CACHE BOOL "Compile internal 1DS logging out" FORCE) + set(MATSDK_DISABLE_EXCEPTIONS OFF CACHE BOOL "Retain 1DS SDK exception support" FORCE) + set(MATSDK_USE_WININET OFF CACHE BOOL "Use WinHTTP for desktop Windows telemetry" FORCE) + # Desktop ORT supplies a hashed, generated device ID; mobile builds use the SDK's platform ID. + if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS") + set(MATSDK_ENABLE_DEVICE_ID ON CACHE BOOL "Use the 1DS platform device ID on mobile" FORCE) + else() + set(MATSDK_ENABLE_DEVICE_ID OFF CACHE BOOL "Disable native 1DS device ID collection on desktop" FORCE) endif() - if(onnxruntime_USE_VCPKG AND TARGET MSTelemetry::mat AND NOT ANDROID) - message(STATUS "Telemetry: using the vcpkg MSTelemetry::mat package") - set(onnxruntime_TELEMETRY_USES_EXTERNAL_PACKAGE ON) + set(MATSDK_ANDROID_USE_ROOM OFF CACHE BOOL "Use native SQLite for Android telemetry storage" FORCE) + set(MATSDK_ENABLE_CAPI_HTTP_CLIENT OFF CACHE BOOL "Disable the alternative Android C API HTTP client" FORCE) + set(MATSDK_CURL_TLS_BACKEND MBEDTLS CACHE STRING "Use mbedTLS for 1DS curl" FORCE) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT onnxruntime_USE_VCPKG) + set(MATSDK_CURL_PROVIDER FETCH CACHE STRING "Build the SDK's pinned curl with mbedTLS" FORCE) else() - set(_ort_requested_apple_architectures "${CMAKE_OSX_ARCHITECTURES}") - - # Android always uses this path, including vcpkg-based AAR builds. The vcpkg port selects - # HttpClient_Curl on Android, while the platform identity and transport used by the AAR require - # HttpClient_Android and its Java bridge. - # Use cpp_client_telemetry's canonical build options. The SDK keeps its - # build policy and dependency selection local to 1DS. - set(MATSDK_BUILD_HEADERS ON CACHE BOOL "Build 1DS SDK headers" FORCE) - set(MATSDK_BUILD_LIBRARY ON CACHE BOOL "Build 1DS SDK library" FORCE) - set(MATSDK_BUILD_TEST_TOOL OFF CACHE BOOL "Disable 1DS SDK test tool" FORCE) - set(MATSDK_BUILD_UNIT_TESTS OFF CACHE BOOL "Disable 1DS SDK unit tests" FORCE) - set(MATSDK_BUILD_FUNC_TESTS OFF CACHE BOOL "Disable 1DS SDK functional tests" FORCE) - set(MATSDK_BUILD_PRIVACYGUARD OFF CACHE BOOL "Disable 1DS privacy guard module" FORCE) - set(MATSDK_BUILD_CDS OFF CACHE BOOL "Disable 1DS CDS module" FORCE) - set(MATSDK_BUILD_LIVEEVENTINSPECTOR OFF CACHE BOOL "Disable 1DS live event inspector" FORCE) - set(MATSDK_BUILD_SIGNALS OFF CACHE BOOL "Disable 1DS signals module" FORCE) - set(MATSDK_BUILD_SANITIZER OFF CACHE BOOL "Disable 1DS sanitizer module" FORCE) - set(MATSDK_BUILD_AZMON OFF CACHE BOOL "Disable 1DS Azure Monitor module" FORCE) - set(MATSDK_BUILD_OBJC_WRAPPER OFF CACHE BOOL "Disable 1DS ObjC wrapper" FORCE) - set(MATSDK_BUILD_SWIFT_WRAPPER OFF CACHE BOOL "Disable 1DS Swift wrapper" FORCE) - set(MATSDK_BUILD_JNI_WRAPPER OFF CACHE BOOL "Disable 1DS JNI wrapper" FORCE) - set(MATSDK_BUILD_PACKAGE OFF CACHE BOOL "Disable 1DS package generation" FORCE) - set(MATSDK_DISABLE_LOGGING ON CACHE BOOL "Compile internal 1DS logging out" FORCE) - if(APPLE) - set(MATSDK_BUILD_APPLE_HTTP ON CACHE BOOL "Build the 1DS Apple HTTP client" FORCE) - endif() - set(MATSDK_CURL_TLS_BACKEND MBEDTLS CACHE STRING "Use mbedTLS for 1DS curl" FORCE) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(MATSDK_CURL_PROVIDER FETCH CACHE STRING "Build the SDK's pinned curl with mbedTLS" FORCE) - else() - set(MATSDK_CURL_PROVIDER SYSTEM CACHE STRING "Use the platform HTTP transport" FORCE) + set(MATSDK_CURL_PROVIDER SYSTEM CACHE STRING "Use the platform HTTP transport" FORCE) + endif() + if(APPLE) + set(MATSDK_SQLITE_PROVIDER SYSTEM CACHE STRING "Use Apple's system SQLite" FORCE) + set(MATSDK_ZLIB_PROVIDER SYSTEM CACHE STRING "Use Apple's system libz" FORCE) + # Supply the canonical system targets even when the SDK detects ORT's vcpkg toolchain. + if(NOT TARGET SQLite3::SQLite3) + add_library(SQLite3::SQLite3 INTERFACE IMPORTED) + target_link_libraries(SQLite3::SQLite3 INTERFACE sqlite3) endif() - if(APPLE) - set(MATSDK_SQLITE_PROVIDER SYSTEM CACHE STRING "Use Apple's system SQLite" FORCE) - set(MATSDK_ZLIB_PROVIDER SYSTEM CACHE STRING "Use Apple's system libz" FORCE) - else() - set(MATSDK_SQLITE_PROVIDER MINIMAL CACHE STRING "Build the SDK's minimal private SQLite" FORCE) - set(MATSDK_ZLIB_PROVIDER VENDORED CACHE STRING "Build the SDK's private zlib" FORCE) + if(NOT TARGET ZLIB::ZLIB) + add_library(ZLIB::ZLIB INTERFACE IMPORTED) + target_link_libraries(ZLIB::ZLIB INTERFACE z) endif() - # BUILD_SHARED_LIBS is a global that ORT's own targets read after this block, and the SDK selects - # mat's library type from it (lib/CMakeLists.txt). Save it, force static for the SDK, restore below. - set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}") - set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build 1DS SDK as static library" FORCE) + else() + set(MATSDK_SQLITE_PROVIDER MINIMAL CACHE STRING "Build the SDK's minimal private SQLite" FORCE) + set(MATSDK_ZLIB_PROVIDER VENDORED CACHE STRING "Build the SDK's private zlib" FORCE) + endif() + # BUILD_SHARED_LIBS is a global that ORT's own targets read after this block, and the SDK selects + # mat's library type from it (lib/CMakeLists.txt). Save it, force static for the SDK, restore below. + set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}") + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build 1DS SDK as static library" FORCE) + set(BUILD_SHARED_LIBS OFF) - # Android uses the Java transport; all other source builds use the SDK's - # canonical Apple/system or fetched mbedTLS transport selection. - set(MATSDK_ANDROID_HTTP_CLIENT JAVA CACHE STRING "Use the 1DS Java HTTP bridge on Android" FORCE) + # Android uses the Java transport; all other source builds use the SDK's + # canonical Apple/system or fetched mbedTLS transport selection. + set(MATSDK_ANDROID_HTTP_CLIENT JAVA CACHE STRING "Use the 1DS Java HTTP bridge on Android" FORCE) - onnxruntime_fetchcontent_declare( - cpp_client_telemetry - URL ${DEP_URL_cpp_client_telemetry} - URL_HASH SHA1=${DEP_SHA1_cpp_client_telemetry} - EXCLUDE_FROM_ALL - ) + onnxruntime_fetchcontent_declare( + cpp_client_telemetry + URL ${DEP_URL_cpp_client_telemetry} + URL_HASH SHA1=${DEP_SHA1_cpp_client_telemetry} + EXCLUDE_FROM_ALL + ) + block(PROPAGATE cpp_client_telemetry_SOURCE_DIR cpp_client_telemetry_BINARY_DIR) + # The SDK otherwise derives its version from the build date, not the release tag. + set(BUILD_VERSION "3.10.279.1") onnxruntime_fetchcontent_makeavailable(cpp_client_telemetry) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT onnxruntime_BUILD_SHARED_LIB) - # curl's imported mbedTLS helper is not exported with ORT's static package. - get_target_property(_ort_curl_link_libraries libcurl_static INTERFACE_LINK_LIBRARIES) - list(FILTER _ort_curl_link_libraries EXCLUDE REGEX "CURL::mbedtls") - set_target_properties(libcurl_static PROPERTIES - INTERFACE_LINK_LIBRARIES "${_ort_curl_link_libraries}") - target_link_libraries(libcurl_static PRIVATE mbedtls) - endif() - if(ANDROID) - target_compile_definitions(mat PRIVATE ANDROID_SUPPRESS_LOGCAT) - endif() + endblock() + if(WIN32 AND TARGET mat) + # CMake's Visual Studio generator otherwise forwards mat's BUILD_INTERFACE system include + # expression verbatim to MASM. ORT adds dependency includes to C/C++ targets separately. + set_property(TARGET mat PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "") + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND TARGET libcurl_static AND NOT onnxruntime_BUILD_SHARED_LIB) + # curl's imported mbedTLS helper is not exported with ORT's static package. + get_target_property(_ort_curl_link_libraries libcurl_static INTERFACE_LINK_LIBRARIES) + list(FILTER _ort_curl_link_libraries EXCLUDE REGEX "CURL::mbedtls") + set_target_properties(libcurl_static PROPERTIES + INTERFACE_LINK_LIBRARIES "${_ort_curl_link_libraries}") + target_link_libraries(libcurl_static PRIVATE mbedtls) + endif() + if(ANDROID) + target_compile_definitions(mat PRIVATE ANDROID_SUPPRESS_LOGCAT) + endif() - if(ANDROID) - string(CONCAT _ort_android_telemetry_java_source_dir - "${cpp_client_telemetry_SOURCE_DIR}/lib/android_build/maesdk/src/main/java/" - "com/microsoft/applications/events") - file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/android/telemetry-java") - set(_ort_android_telemetry_java_dir - "${CMAKE_BINARY_DIR}/android/telemetry-java/ai/onnxruntime/telemetry") - file(MAKE_DIRECTORY "${_ort_android_telemetry_java_dir}") - foreach(_ort_android_telemetry_java_file HttpClient.java HttpClientRequest.java) - file(READ - "${_ort_android_telemetry_java_source_dir}/${_ort_android_telemetry_java_file}" - _ort_android_telemetry_java_source) - string(REPLACE - "package com.microsoft.applications.events;" - "package ai.onnxruntime.telemetry;" - _ort_android_telemetry_java_source - "${_ort_android_telemetry_java_source}") - file(WRITE - "${_ort_android_telemetry_java_dir}/${_ort_android_telemetry_java_file}" + if(ANDROID) + string(CONCAT _ort_android_telemetry_java_source_dir + "${cpp_client_telemetry_SOURCE_DIR}/lib/android_build/maesdk/src/main/java/" + "com/microsoft/applications/events") + file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/android/telemetry-java") + set(_ort_android_telemetry_java_dir + "${CMAKE_BINARY_DIR}/android/telemetry-java/ai/onnxruntime/telemetry") + file(MAKE_DIRECTORY "${_ort_android_telemetry_java_dir}") + foreach(_ort_android_telemetry_java_file HttpClient.java HttpClientRequest.java) + file(READ + "${_ort_android_telemetry_java_source_dir}/${_ort_android_telemetry_java_file}" + _ort_android_telemetry_java_source) + string(REPLACE + "package com.microsoft.applications.events;" + "package ai.onnxruntime.telemetry;" + _ort_android_telemetry_java_source "${_ort_android_telemetry_java_source}") - endforeach() + file(WRITE + "${_ort_android_telemetry_java_dir}/${_ort_android_telemetry_java_file}" + "${_ort_android_telemetry_java_source}") + endforeach() - set(_ort_android_telemetry_resource_dir "${CMAKE_BINARY_DIR}/android/telemetry-resources/META-INF") - file(MAKE_DIRECTORY "${_ort_android_telemetry_resource_dir}") - configure_file( - "${cpp_client_telemetry_SOURCE_DIR}/LICENSE" - "${_ort_android_telemetry_resource_dir}/LICENSE-1DS" - COPYONLY) - endif() + set(_ort_android_telemetry_resource_dir "${CMAKE_BINARY_DIR}/android/telemetry-resources/META-INF") + file(MAKE_DIRECTORY "${_ort_android_telemetry_resource_dir}") + configure_file( + "${cpp_client_telemetry_SOURCE_DIR}/LICENSE" + "${_ort_android_telemetry_resource_dir}/LICENSE-1DS" + COPYONLY) + endif() - if(TARGET mat) - if(TARGET sqlite3_bundled) - # 1DS uses sqlite only for its narrow offline-event store. Keep the previous vcpkg - # size reductions and extension-loading hardening on the bundled replacement. - target_compile_definitions(sqlite3_bundled PRIVATE - SQLITE_OMIT_LOAD_EXTENSION - SQLITE_OMIT_DEPRECATED - SQLITE_OMIT_UTF16 - SQLITE_OMIT_PROGRESS_CALLBACK - SQLITE_OMIT_SHARED_CACHE - SQLITE_OMIT_GET_TABLE - SQLITE_OMIT_COMPLETE - SQLITE_OMIT_TCL_VARIABLE - SQLITE_DQS=0 - SQLITE_DEFAULT_MEMSTATUS=0 - SQLITE_DEFAULT_FOREIGN_KEYS=0 - ) - endif() - foreach(_ort_apple_dep mat sqlite3_bundled zlib_bundled) - if(TARGET ${_ort_apple_dep}) - if(APPLE AND _ort_requested_apple_architectures) - set_target_properties(${_ort_apple_dep} PROPERTIES - OSX_ARCHITECTURES "${_ort_requested_apple_architectures}" - XCODE_ATTRIBUTE_ARCHS "${_ort_requested_apple_architectures}") - endif() - get_target_property(_ort_apple_inc - ${_ort_apple_dep} INTERFACE_INCLUDE_DIRECTORIES) - if(_ort_apple_inc) - set_target_properties(${_ort_apple_dep} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "$") - endif() + if(TARGET mat) + if(TARGET sqlite3_bundled) + # 1DS uses sqlite only for its narrow offline-event store. Keep the previous vcpkg + # size reductions and extension-loading hardening on the bundled replacement. + # Memory accounting must remain enabled for the SDK's SQLite soft heap limit. + target_compile_definitions(sqlite3_bundled PRIVATE + SQLITE_OMIT_LOAD_EXTENSION + SQLITE_OMIT_DEPRECATED + SQLITE_OMIT_UTF16 + SQLITE_OMIT_PROGRESS_CALLBACK + SQLITE_OMIT_SHARED_CACHE + SQLITE_OMIT_GET_TABLE + SQLITE_OMIT_COMPLETE + SQLITE_OMIT_TCL_VARIABLE + SQLITE_DQS=0 + SQLITE_DEFAULT_FOREIGN_KEYS=0 + ) + endif() + foreach(_ort_apple_dep mat sqlite3_bundled zlib_bundled) + if(TARGET ${_ort_apple_dep}) + if(APPLE AND _ort_requested_apple_architectures) + set_target_properties(${_ort_apple_dep} PROPERTIES + OSX_ARCHITECTURES "${_ort_requested_apple_architectures}" + XCODE_ATTRIBUTE_ARCHS "${_ort_requested_apple_architectures}") endif() - endforeach() - # ORT enables -ffast-math globally, which conflicts with - # std::numeric_limits::infinity() in the 1DS SDK's bundled nlohmann/json.hpp. - # Also suppress warnings in the 1DS SDK code that ORT treats as errors. + get_target_property(_ort_apple_inc + ${_ort_apple_dep} INTERFACE_INCLUDE_DIRECTORIES) + if(_ort_apple_inc) + # Preserve individual build expressions; wrapping a semicolon-separated list nests + # expressions that ORT's MSVC /external:I handling cannot flatten. + set(_ort_build_includes "") + foreach(_ort_include IN LISTS _ort_apple_inc) + if(_ort_include MATCHES "^\\$") + endif() + endforeach() + set_target_properties(${_ort_apple_dep} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_ort_build_includes}") + endif() + endif() + endforeach() + # ORT enables -ffast-math globally, which conflicts with + # std::numeric_limits::infinity() in the 1DS SDK's bundled nlohmann/json.hpp. + # Also suppress warnings in the 1DS SDK code that ORT treats as errors. + if(NOT MSVC) target_compile_options(mat PRIVATE -fno-finite-math-only -Wno-unused-const-variable $<$:-Wno-reorder> $<$:-Wno-reorder-ctor> ) - # Vendored 1DS dependencies emit unavoidable narrowing warnings under Apple's warning policy. - # Keep the warning enabled for ORT sources while suppressing it only for third-party targets. - if(APPLE) - foreach(_ort_mat_tgt mat sqlite3_bundled zlib_bundled) - if(TARGET ${_ort_mat_tgt}) - target_compile_options(${_ort_mat_tgt} PRIVATE -Wno-shorten-64-to-32) - endif() - endforeach() - if(TARGET sqlite3_bundled) - target_compile_options(sqlite3_bundled PRIVATE -Wno-ambiguous-macro) + endif() + # Vendored 1DS dependencies emit unavoidable narrowing warnings under Apple's warning policy. + # Keep the warning enabled for ORT sources while suppressing it only for third-party targets. + if(APPLE) + foreach(_ort_mat_tgt mat sqlite3_bundled zlib_bundled) + if(TARGET ${_ort_mat_tgt}) + target_compile_options(${_ort_mat_tgt} PRIVATE -Wno-shorten-64-to-32) endif() - endif() - if(TARGET sqlite3_bundled - AND CMAKE_C_COMPILER_ID STREQUAL "GNU" - AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11) - target_compile_options(sqlite3_bundled PRIVATE -Wno-error=stringop-overread) + endforeach() + if(TARGET sqlite3_bundled) + target_compile_options(sqlite3_bundled PRIVATE -Wno-ambiguous-macro) endif() endif() - - set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}" CACHE BOOL "" FORCE) + if(TARGET sqlite3_bundled + AND CMAKE_C_COMPILER_ID STREQUAL "GNU" + AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11) + target_compile_options(sqlite3_bundled PRIVATE -Wno-error=stringop-overread) + endif() endif() + + foreach(_ort_1ds_cache_var + MATSDK_BUILD_HEADERS + MATSDK_BUILD_LIBRARY + MATSDK_BUILD_TEST_TOOL + MATSDK_BUILD_UNIT_TESTS + MATSDK_BUILD_FUNC_TESTS + MATSDK_BUILD_PRIVACYGUARD + MATSDK_BUILD_CDS + MATSDK_BUILD_LIVEEVENTINSPECTOR + MATSDK_BUILD_SIGNALS + MATSDK_BUILD_SANITIZER + MATSDK_BUILD_AZMON + MATSDK_BUILD_OBJC_WRAPPER + MATSDK_BUILD_SWIFT_WRAPPER + MATSDK_BUILD_JNI_WRAPPER + MATSDK_BUILD_PACKAGE + MATSDK_BUILD_APPLE_HTTP + MATSDK_DISABLE_LOGGING + MATSDK_DISABLE_EXCEPTIONS + MATSDK_USE_WININET + MATSDK_ENABLE_DEVICE_ID + MATSDK_ANDROID_USE_ROOM + MATSDK_ENABLE_CAPI_HTTP_CLIENT + MATSDK_ANDROID_HTTP_CLIENT + MATSDK_CURL_PROVIDER + MATSDK_CURL_TLS_BACKEND + MATSDK_SQLITE_PROVIDER + MATSDK_ZLIB_PROVIDER) + unset(${_ort_1ds_cache_var} CACHE) + endforeach() + + set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}" CACHE BOOL "" FORCE) + set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}") endif() FILE(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} ORT_BINARY_DIR) diff --git a/cmake/onnxruntime.cmake b/cmake/onnxruntime.cmake index d2146b2c049c9..a977141f34983 100644 --- a/cmake/onnxruntime.cmake +++ b/cmake/onnxruntime.cmake @@ -161,6 +161,7 @@ if(onnxruntime_BUILD_SHARED_LIB) endif() add_dependencies(onnxruntime onnxruntime_generate_def ${onnxruntime_EXTERNAL_DEPENDENCIES}) + set_property(TARGET onnxruntime APPEND PROPERTY LINK_DEPENDS "${SYMBOL_FILE}") target_include_directories(onnxruntime PRIVATE ${ONNXRUNTIME_ROOT} PUBLIC "$") diff --git a/cmake/onnxruntime_common.cmake b/cmake/onnxruntime_common.cmake index 405786b313c8d..6fbe650f431ad 100644 --- a/cmake/onnxruntime_common.cmake +++ b/cmake/onnxruntime_common.cmake @@ -23,9 +23,10 @@ set(onnxruntime_common_src_patterns "${ONNXRUNTIME_ROOT}/core/platform/path_lib.cc" "${ONNXRUNTIME_ROOT}/core/platform/scoped_resource.h" "${ONNXRUNTIME_ROOT}/core/platform/telemetry.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_strings.h" "${ONNXRUNTIME_ROOT}/core/platform/telemetry.cc" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry_sha256.h" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry_sha256.cc" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_sha256.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_sha256.cc" "${ONNXRUNTIME_ROOT}/core/platform/logging/make_platform_default_log_sink.h" "${ONNXRUNTIME_ROOT}/core/platform/logging/make_platform_default_log_sink.cc" "${ONNXRUNTIME_ROOT}/core/quantization/*.h" @@ -49,6 +50,12 @@ if(WIN32) "${ONNXRUNTIME_ROOT}/core/platform/windows/logging/*.h" "${ONNXRUNTIME_ROOT}/core/platform/windows/logging/*.cc" ) + if(onnxruntime_USE_1DS_TELEMETRY) + list(APPEND onnxruntime_common_src_patterns + "${ONNXRUNTIME_ROOT}/core/platform/windows/device_id.cc" + "${ONNXRUNTIME_ROOT}/core/platform/windows/telemetry_1ds.cc" + ) + endif() else() list(APPEND onnxruntime_common_src_patterns @@ -57,16 +64,11 @@ else() "${ONNXRUNTIME_ROOT}/core/platform/posix/stacktrace.cc" ) - # Telemetry for non-Windows platforms (enabled by USE_TELEMETRY) - if (onnxruntime_USE_TELEMETRY) + # 1DS telemetry sources for non-Windows platforms. + if(onnxruntime_USE_1DS_TELEMETRY) list(APPEND onnxruntime_common_src_patterns - "${ONNXRUNTIME_ROOT}/core/platform/posix/device_id.h" "${ONNXRUNTIME_ROOT}/core/platform/posix/device_id.cc" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry.h" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry.cc" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry_context.h" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry_no_throw.h" - "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry_sampling.h" + "${ONNXRUNTIME_ROOT}/core/platform/posix/telemetry_1ds.cc" ) endif() @@ -93,6 +95,17 @@ else() endif() endif() +if(onnxruntime_USE_1DS_TELEMETRY) + list(APPEND onnxruntime_common_src_patterns + "${ONNXRUNTIME_ROOT}/core/platform/device_id.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_1ds.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_1ds.cc" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_1ds_platform.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_context.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_no_throw.h" + "${ONNXRUNTIME_ROOT}/core/platform/telemetry_sampling.h") +endif() + # platform-specific device discovery files if (onnxruntime_DISABLE_DEVICE_DISCOVERY) list(APPEND onnxruntime_common_src_patterns @@ -169,6 +182,9 @@ if(WIN32) list(APPEND onnxruntime_DELAYLOAD_FLAGS "/DELAYLOAD:shell32.dll") endif() endif() + if(onnxruntime_USE_1DS_TELEMETRY) + target_link_libraries(onnxruntime_common PRIVATE iphlpapi) + endif() endif() if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") @@ -179,7 +195,8 @@ if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND CMAKE_SYSTEM_PROCESSOR MATCHES "x endif() if (onnxruntime_USE_TELEMETRY) - if(WIN32) + if(WIN32 AND NOT onnxruntime_USE_1DS_TELEMETRY) + target_compile_definitions(onnxruntime_common PUBLIC USE_WINDOWS_TELEMETRY) set(ONNXRUNTIME_TELEMETRY_CONFIG_HEADER "${ONNXRUNTIME_INCLUDE_DIR}/core/platform/windows/TraceLoggingConfigPrivate.h") if(EXISTS "${ONNXRUNTIME_TELEMETRY_CONFIG_HEADER}") @@ -188,7 +205,7 @@ if (onnxruntime_USE_TELEMETRY) PROPERTIES COMPILE_FLAGS "/FI${ONNXRUNTIME_TELEMETRY_CONFIG_HEADER}") endif() else() - target_compile_definitions(onnxruntime_common PRIVATE USE_POSIX_TELEMETRY) + target_compile_definitions(onnxruntime_common PUBLIC USE_1DS_TELEMETRY) # Optional tenant-token override written into a generated header in the build tree (kept off the # compiler command line, so the token never appears in compile_commands.json or build logs). It may be # supplied either as -DONNXRUNTIME_TELEMETRY_TENANT_TOKEN=... or via an @@ -275,42 +292,42 @@ if(CPUINFO_SUPPORTED) list(APPEND onnxruntime_EXTERNAL_LIBRARIES cpuinfo::cpuinfo) endif() -# Link telemetry library (1DS SDK) for non-Windows platforms -if(onnxruntime_USE_TELEMETRY AND NOT WIN32) - if(onnxruntime_TELEMETRY_USES_EXTERNAL_PACKAGE AND TARGET MSTelemetry::mat) - # The vcpkg package target propagates its include - # directories and transitive dependencies (curl/sqlite3/zlib/nlohmann-json), so no - # manual include paths or system libraries are required here. - target_link_libraries(onnxruntime_common PRIVATE MSTelemetry::mat) - list(APPEND onnxruntime_EXTERNAL_LIBRARIES MSTelemetry::mat) - elseif(TARGET mat) +# Link the default 1DS telemetry backend. +if(onnxruntime_USE_1DS_TELEMETRY) + if(TARGET mat) # Link mat directly. In a shared build its resolved dependency set is absorbed into # libonnxruntime; in a static build mat -- and the bundled static archives it links -- are shipped # and exported below so a downstream find_package(onnxruntime) resolves them. target_link_libraries(onnxruntime_common PRIVATE mat) list(APPEND onnxruntime_EXTERNAL_LIBRARIES mat) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND TARGET libcurl_static) - # Prevent shared-library consumers from re-exporting the embedded transport symbols. This does - # not namespace static symbols; static ORT consumers must not co-link another curl/mbedTLS copy. - string(CONCAT _onnxruntime_telemetry_build_exclude_libs - "LINKER:--exclude-libs=" - "$:" - "$:" - "$:" - "$:" - "$:" - "$") - string(CONCAT _onnxruntime_telemetry_install_exclude_libs - "LINKER:--exclude-libs=" - "$:" - "$:" - "$:" - "$:" - "$:" - "$") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID) + # Hide SDK/dependency archives without hiding ORT's archive-backed public API. + # This does not namespace static symbols or make duplicate static dependencies safe to co-link. + # Some GNU toolchains implicitly link versioned compatibility symbols from this archive. + set(_onnxruntime_telemetry_build_exclude_libs "libstdc++_nonshared.a") + set(_onnxruntime_telemetry_install_exclude_libs "libstdc++_nonshared.a") + foreach(_ort_telemetry_archive + mat + sqlite3_bundled + zlib_bundled + libcurl_static + mbedtls + mbedx509 + mbedcrypto + everest + p256m) + if(TARGET ${_ort_telemetry_archive}) + list(APPEND _onnxruntime_telemetry_build_exclude_libs + "$") + list(APPEND _onnxruntime_telemetry_install_exclude_libs + "$") + endif() + endforeach() + list(JOIN _onnxruntime_telemetry_build_exclude_libs ":" _onnxruntime_telemetry_build_exclude_libs) + list(JOIN _onnxruntime_telemetry_install_exclude_libs ":" _onnxruntime_telemetry_install_exclude_libs) target_link_options(onnxruntime_common INTERFACE - "$" - "$") + "$" + "$") endif() # mat propagates its public include dir as a normal (non-SYSTEM) include, so onnxruntime_common's # -Wall -Wextra -Werror would apply to the SDK's headers (they trip -Werror=unused-parameter in @@ -356,7 +373,7 @@ if(onnxruntime_USE_TELEMETRY AND NOT WIN32) endforeach() endif() else() - message(FATAL_ERROR "Telemetry enabled but no 1DS SDK target ('MSTelemetry::mat' or 'mat') was found") + message(FATAL_ERROR "Telemetry enabled but the pinned 1DS SDK target ('mat') was not found") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") # Every supported Linux telemetry path uses static curl/mbedTLS. Select a readable CA bundle diff --git a/cmake/onnxruntime_telemetry_config.cmake b/cmake/onnxruntime_telemetry_config.cmake new file mode 100644 index 0000000000000..5da4b449ddaa4 --- /dev/null +++ b/cmake/onnxruntime_telemetry_config.cmake @@ -0,0 +1,37 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# Append dependencies needed by the installed static package, before importing its targets. +if(NOT onnxruntime_USE_1DS_TELEMETRY OR onnxruntime_BUILD_SHARED_LIB) + return() +endif() + +if(APPLE) + string(APPEND PROJECT_CONFIG_CONTENT + "if(NOT TARGET MSTelemetry::sqlite_dependency)\n\ + add_library(MSTelemetry::sqlite_dependency INTERFACE IMPORTED)\n\ + set_property(TARGET MSTelemetry::sqlite_dependency PROPERTY INTERFACE_LINK_LIBRARIES sqlite3)\n\ + endif()\n\ + if(NOT TARGET MSTelemetry::zlib_dependency)\n\ + add_library(MSTelemetry::zlib_dependency INTERFACE IMPORTED)\n\ + set_property(TARGET MSTelemetry::zlib_dependency PROPERTY INTERFACE_LINK_LIBRARIES z)\n\ + endif()\n") +elseif(NOT WIN32) + # The SDK publicly links Threads on Linux and Android, including the Java transport. + string(APPEND PROJECT_CONFIG_CONTENT "find_dependency(Threads)\n") + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") + if(TARGET libcurl_static) + # mat's install interface names the SDK wrapper, not the exported curl archive. + set(_ort_installed_curl_target onnxruntime::libcurl_static) + else() + string(APPEND PROJECT_CONFIG_CONTENT "find_dependency(CURL)\n") + set(_ort_installed_curl_target CURL::libcurl) + endif() + string(APPEND PROJECT_CONFIG_CONTENT + "if(NOT TARGET MSTelemetry::curl_dependency)\n\ + add_library(MSTelemetry::curl_dependency INTERFACE IMPORTED)\n\ + set_property(TARGET MSTelemetry::curl_dependency PROPERTY INTERFACE_LINK_LIBRARIES ${_ort_installed_curl_target})\n\ + endif()\n") + unset(_ort_installed_curl_target) + endif() +endif() diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 9f80a78905840..43dd050e8e072 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -6,6 +6,10 @@ endif() set(TEST_SRC_DIR ${ONNXRUNTIME_ROOT}/test) set(TEST_INC_DIR ${ONNXRUNTIME_ROOT}) +if(NOT CMAKE_CROSSCOMPILING) + add_test(NAME onnxruntime_public_exports_test + COMMAND "${Python_EXECUTABLE}" "${REPO_ROOT}/tools/ci_build/test_gen_def.py") +endif() if (onnxruntime_ENABLE_TRAINING) list(APPEND TEST_INC_DIR ${ORTTRAINING_ROOT}) endif() @@ -467,6 +471,10 @@ if(WIN32) list(APPEND onnxruntime_test_framework_src_patterns "${TEST_SRC_DIR}/platform/windows/*.cc" "${TEST_SRC_DIR}/platform/windows/logging/*.cc" ) + if(onnxruntime_USE_1DS_TELEMETRY) + list(APPEND onnxruntime_test_framework_src_patterns + "${TEST_SRC_DIR}/platform/windows_telemetry/*.cc" ) + endif() endif() if(LINUX AND NOT onnxruntime_DISABLE_DEVICE_DISCOVERY) @@ -872,6 +880,11 @@ file(GLOB onnxruntime_test_framework_src CONFIGURE_DEPENDS ${onnxruntime_test_framework_src_patterns} ) +if(IOS) + # The ONNX test runner library is not built for iOS. + list(REMOVE_ITEM onnxruntime_test_framework_src "${TEST_SRC_DIR}/framework/onnx_test_loader_test.cc") +endif() + #This is a small wrapper library that shouldn't use any onnxruntime internal symbols(except onnxruntime_common). #Because it could dynamically link to onnxruntime. Otherwise you will have two copies of onnxruntime in the same #process and you won't know which one you are testing. diff --git a/cmake/vcpkg-configuration.json b/cmake/vcpkg-configuration.json index 0e2c6e1510bf1..9c5e36a1d178a 100644 --- a/cmake/vcpkg-configuration.json +++ b/cmake/vcpkg-configuration.json @@ -1,5 +1,5 @@ { - "$comment": "Baseline pinned to versions compatible with ORT's vcpkg tool. ORT's cpp-client-telemetry overlay supplies version 3.10.267.1 and replaces the upstream curl/OpenSSL transport with static curl/mbedTLS for POSIX telemetry.", + "$comment": "Baseline pinned to versions compatible with ORT's vcpkg tool. The 1DS SDK uses its GitHub source pin independently of this registry; Linux vcpkg builds still resolve curl/mbedTLS through vcpkg.", "default-registry": { "kind": "git", "repository": "https://github.com/Microsoft/vcpkg", diff --git a/cmake/vcpkg.json b/cmake/vcpkg.json index 3ade3c2510c35..eb9a3e9c8f2bd 100644 --- a/cmake/vcpkg.json +++ b/cmake/vcpkg.json @@ -103,12 +103,8 @@ "dependencies": [] }, "telemetry": { - "description": "Build with static 1DS telemetry support on non-Windows platforms", + "description": "Supply curl/mbedTLS transport for the source-built 1DS SDK on Linux", "dependencies": [ - { - "name": "cpp-client-telemetry", - "platform": "!windows & !android & !emscripten & !uwp" - }, { "name": "curl", "default-features": false, @@ -123,11 +119,6 @@ "pthreads" ], "platform": "linux" - }, - { - "name": "sqlite3", - "default-features": false, - "platform": "!windows & !android & !emscripten & !uwp" } ] } diff --git a/cmake/winml.cmake b/cmake/winml.cmake index 61d46120c0f4a..4ab4ef16c47aa 100644 --- a/cmake/winml.cmake +++ b/cmake/winml.cmake @@ -193,6 +193,7 @@ target_include_directories(winml_lib_telemetry PRIVATE ${winml_lib_telemetry_dir target_include_directories(winml_lib_telemetry PRIVATE ${winml_lib_common_dir}/inc) target_include_directories(winml_lib_telemetry PRIVATE ${ONNXRUNTIME_INCLUDE_DIR}/core/platform/windows) target_include_directories(winml_lib_telemetry PRIVATE ${REPO_ROOT}/winml) +target_include_directories(winml_lib_telemetry PRIVATE ${ONNXRUNTIME_ROOT}) target_include_directories(winml_lib_telemetry PRIVATE ${GSL_INCLUDE_DIR}) # Properties diff --git a/docs/Privacy.md b/docs/Privacy.md index 34f3b294aa4ef..f05b6ce9691e0 100644 --- a/docs/Privacy.md +++ b/docs/Privacy.md @@ -6,18 +6,32 @@ The software may collect information about you and your use of the software and *** ### Official Builds -ONNX Runtime collects trace events with the goal of improving product quality. On Windows, it uses the platform's built-in ETW telemetry system; on supported Linux architectures, macOS, Android, and iOS, it uses the cross-platform 1DS telemetry SDK that is built into ONNX Runtime. Targets without a supported telemetry provider, including WebAssembly, tvOS, visionOS, Mac Catalyst, AIX, and RISC-V, do not include telemetry. In all cases, collection is subject to user consent and handled following Microsoft's privacy practices. +ONNX Runtime collects trace events with the goal of improving product quality. For builds used in Windows apps and components like Windows ML, it uses the platform's built-in ETW telemetry system; for other Windows builds including our official releases, supported Linux architectures, macOS, Android, and iOS, it uses the cross-platform 1DS telemetry SDK that is built into ONNX Runtime. Targets without a supported telemetry provider, including WebAssembly, tvOS, visionOS, Mac Catalyst, AIX, and RISC-V, do not include telemetry. In all cases, collection is subject to user consent and handled following Microsoft's privacy practices. Telemetry is turned **ON** by default in the official builds ([see here](../README.md#binaries)). Both providers are accessed through ONNX Runtime's common telemetry interface (see [telemetry.h](../onnxruntime/core/platform/telemetry.h)). ### Private Builds -The build driver enables telemetry by default for supported native platforms. The standard Windows `build.bat` wrapper explicitly passes `--no_telemetry`, so private builds made with that wrapper perform no data collection. Targets without a supported provider and builds that disable C++ exceptions automatically exclude telemetry. For information on how to disable telemetry in other builds, see [Disabling Telemetry](#disabling-telemetry) below. +The build driver enables telemetry by default for supported native platforms. Targets without a supported provider and builds that disable C++ exceptions automatically exclude telemetry. For information on how to disable telemetry in other builds, see [Disabling Telemetry](#disabling-telemetry) below. + +Existing native Windows build scripts that pass `--use_telemetry` retain the +historical TraceLogging backend, with a deprecation warning. New scripts can use +`--use_windows_telemetry` explicitly. Application telemetry control APIs are unchanged. + +For direct CMake builds, `onnxruntime_USE_TELEMETRY=ON` with the default `AUTO` +backend selects 1DS on supported native platforms, including Windows. Windows +TraceLogging requires explicit `onnxruntime_TELEMETRY_BACKEND=WINDOWS` or +`onnxruntime_USE_WINDOWS_TELEMETRY=ON`. #### Technical Details -**Windows.** The Windows provider uses the [TraceLogging](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-about) API for its implementation. This enables ONNX Runtime trace events to be collected by the operating system, and based on user consent, this data may be periodically sent to Microsoft servers following GDPR and privacy regulations for anonymity and data access controls. Windows ML and ONNX Runtime C APIs allow Trace Logging to be turned on/off (see [API pages](../README.md#api-documentation) for details); there are equivalent APIs in the C#, Python, and Java language bindings as well. +Custom ONNX model metadata (`modelMetaData`) is not uploaded through 1DS. Arbitrary +metadata keys and values can contain sensitive information; size limits or path +redaction alone do not make them safe to transmit. Public model metadata APIs and +local Windows ETW capture-state events are unchanged. + +**Windows apps and components.** The Windows provider uses the [TraceLogging](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-about) API for its implementation. This enables ONNX Runtime trace events to be collected by the operating system, and based on user consent, this data may be periodically sent to Microsoft servers following GDPR and privacy regulations for anonymity and data access controls. Windows ML and ONNX Runtime C APIs allow Trace Logging to be turned on/off (see [API pages](../README.md#api-documentation) for details); there are equivalent APIs in the C#, Python, and Java language bindings as well. -**Non-Windows (Linux, macOS, Android, iOS).** These platforms use the cross-platform 1DS SDK (cpp_client_telemetry) to send the same trace events to Microsoft's telemetry backend over HTTPS. Based on user consent, this data is handled following GDPR and privacy regulations for anonymity and data access controls. ONNX Runtime C APIs allow 1DS to be turned on/off (see [API pages](../README.md#api-documentation) for details); there are equivalent APIs in the C#, Python, and Java language bindings as well. +**Other builds with telemetry (Linux, macOS, Android, iOS, Windows builds not made for Windows apps and components).** These platforms use the cross-platform 1DS SDK (cpp_client_telemetry) to send the same trace events to Microsoft's telemetry backend over HTTPS. Based on user consent, this data is handled following GDPR and privacy regulations for anonymity and data access controls. ONNX Runtime C APIs allow 1DS to be turned on/off (see [API pages](../README.md#api-documentation) for details); there are equivalent APIs in the C#, Python, and Java language bindings as well. For ways to disable telemetry, see the [Disabling Telemetry](#disabling-telemetry) section below. @@ -25,10 +39,26 @@ For ways to disable telemetry, see the [Disabling Telemetry](#disabling-telemetr Telemetry can be disabled in any of these ways: -- **Disable it at build time.** Pass `--no_telemetry` to `build.py` or `build.sh`. This omits the 1DS provider from non-Windows builds and disables the Microsoft telemetry configuration on Windows. The standard Windows `build.bat` wrapper does this automatically. Unsupported targets and exception-free builds never include telemetry. -- **Disable all telemetry at runtime (non-Windows).** Set `ORT_DISABLE_TELEMETRY=1` before ONNX Runtime initializes. This prevents the uploader, events, and persistent device identifier from being created for the process lifetime. -- **Disable non-essential events via the API.** The C API (and the C#, Python, and Java bindings) can suppress non-essential telemetry. ONNX Runtime may already have emitted a minimal initialization event before the API can be called. On **Windows**, ETW events are recorded only when an external trace session is collecting. +- **Disable it at build time.** Pass `--no_telemetry` to `build.py` or `build.sh`. This omits the 1DS provider from all builds and disables the Microsoft telemetry configuration on Windows. Unsupported targets and exception-free builds never include telemetry. +- **Disable all 1DS telemetry at runtime.** Set `ORT_DISABLE_TELEMETRY=1` before ONNX Runtime initializes. On all builds with 1DS telemetry, this prevents the uploader, events, and persistent device identifier from being created for the process lifetime. The legacy Windows TraceLogging backend does not use this environment variable. +- **Disable non-essential events via the API.** The C API (and the C#, Python, and Java bindings) can suppress non-essential telemetry. ONNX Runtime may already have emitted a minimal initialization event before the API can be called. On builds for **Windows apps and components**, ETW events are recorded only when an external trace session is collecting. + +All 1DS builds use the pinned GitHub SDK source, including builds that use vcpkg for +other dependencies. Desktop builds disable the SDK's native device-ID collection and +instead supply a hash of ORT's locally generated persistent identifier. Android and +iOS retain the SDK's platform device IDs. SDK logging is compiled out; source builds +retain exception support, use WinHTTP on Windows, and use Java HTTP with native SQLite +storage on Android. + +Telemetry-enabled static Linux builds depend on static curl and +mbedTLS. FetchContent-built static ORT packages include these archives; vcpkg-built +packages resolve these transport dependencies through vcpkg. Shared ORT libraries use +a public-symbol allowlist, so embedded telemetry dependencies are not exported. ELF +consumers also hide the SDK, bundled SQLite, zlib, curl, and mbedTLS archives, and +GNU's implicitly linked `libstdc++_nonshared.a` compatibility archive while preserving +ORT's public API exports. -Telemetry-enabled static Linux builds use static curl and mbedTLS. FetchContent packages include those archives; -vcpkg builds resolve them from the same vcpkg installation. A static consumer that links another curl or mbedTLS -copy into the same final binary must build ORT with `--no_telemetry` to avoid ordinary static-symbol collisions. +Applications linking a telemetry-enabled static Linux ORT build must not co-link +another copy of curl or mbedTLS. Symbol hiding does not namespace static archives, +so duplicate-symbol or archive-order conflicts can still occur. Build ORT with +`--no_telemetry` if your application needs its own curl or mbedTLS copy. diff --git a/include/onnxruntime/core/common/logging/capture.h b/include/onnxruntime/core/common/logging/capture.h index 7eae5585d5f46..1a8e8e7be42cf 100644 --- a/include/onnxruntime/core/common/logging/capture.h +++ b/include/onnxruntime/core/common/logging/capture.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "core/common/common.h" #include "core/common/code_location.h" @@ -98,6 +99,14 @@ class Capture { return stream_.str(); } +#ifdef _WIN32 + // Only the Windows ETW sink needs this API; Apple libc++ does not provide stream::view(). + // The view remains valid until the capture stream is modified or destroyed. + std::string_view MessageView() const noexcept { + return stream_.view(); + } +#endif + ~Capture(); private: diff --git a/onnxruntime/contrib_ops/cuda/bert/group_query_attention.cc b/onnxruntime/contrib_ops/cuda/bert/group_query_attention.cc index 0660308f85131..298d02cb05743 100644 --- a/onnxruntime/contrib_ops/cuda/bert/group_query_attention.cc +++ b/onnxruntime/contrib_ops/cuda/bert/group_query_attention.cc @@ -575,6 +575,7 @@ Status GroupQueryAttention::ComputeInternal(OpKernelContext* context) cons parameters.is_windowed_kv_cache)); bool is_inputs_quantized = (k_quant_type_ != KVQuantizationType::NONE) || (v_quant_type_ != KVQuantizationType::NONE); + constexpr bool is_int8 = std::is_same::value; constexpr bool is_fp8 = std::is_same::value; // Keep the original cache capacity in the shared workspace problem. Flash, MEA, and unfused diff --git a/onnxruntime/core/framework/execution_providers.h b/onnxruntime/core/framework/execution_providers.h index 761f48861c90f..999386d2c8db4 100644 --- a/onnxruntime/core/framework/execution_providers.h +++ b/onnxruntime/core/framework/execution_providers.h @@ -12,6 +12,8 @@ #include "core/framework/execution_provider.h" #include "core/graph/graph_viewer.h" #include "core/common/logging/logging.h" +#include "core/platform/telemetry_strings.h" +#include "core/platform/telemetry_redaction.h" #ifdef _WIN32 #include #include @@ -190,14 +192,10 @@ class ExecutionProviders { void LogProviderOptions(const std::string& provider_id, const ProviderOptions& options, bool capture_state) { const Env& env = Env::Default(); // Convert ProviderOptions to string for telemetry logging - std::string provider_options_str; - for (const auto& config_pair : options) { - if (!provider_options_str.empty()) { - provider_options_str += ","; - } - provider_options_str += config_pair.first + ":" + config_pair.second; - } - env.GetTelemetryProvider().LogProviderOptions(provider_id, provider_options_str, capture_state); + bool truncated = false; + const auto provider_options_str = telemetry_detail::FormatTelemetryMap(options, ",", ":", &truncated); + env.GetTelemetryProvider().LogProviderOptions( + provider_id, ScrubStringForTelemetry(provider_options_str, truncated), capture_state); } std::vector> exec_providers_; diff --git a/onnxruntime/core/platform/posix/device_id.h b/onnxruntime/core/platform/device_id.h similarity index 86% rename from onnxruntime/core/platform/posix/device_id.h rename to onnxruntime/core/platform/device_id.h index f4b9e4fc66cd2..6f01b4b8506d7 100644 --- a/onnxruntime/core/platform/posix/device_id.h +++ b/onnxruntime/core/platform/device_id.h @@ -19,6 +19,7 @@ enum class DeviceIdStatus { /** * Manages a persistent device identifier for telemetry purposes. * The device ID is stored in a platform-appropriate location: + * - Windows: HKCU\SOFTWARE\Microsoft\DeveloperTools\.onnxruntime, deviceid value * - macOS: ~/Library/Application Support/Microsoft/DeveloperTools/.onnxruntime/deviceid * - Linux: $XDG_CACHE_HOME (or ~/.cache) /Microsoft/DeveloperTools/.onnxruntime/deviceid * @@ -38,10 +39,12 @@ class DeviceId { std::string GetStatusString(); // Get the directory path for device ID / telemetry cache storage - // (macOS: ~/Library/Application Support/...; Linux: $XDG_CACHE_HOME or ~/.cache/...). + // (Windows: %LOCALAPPDATA%, %APPDATA%, or the user profile; macOS: + // ~/Library/Application Support/...; other POSIX: $XDG_CACHE_HOME or ~/.cache/...). static std::string GetStorageDirectory(); - // Same as GetStorageDirectory(), but also creates the directory tree (0700) if it does not exist. + // Same as GetStorageDirectory(), but also creates the directory tree if it does not exist. + // POSIX creates it with mode 0700; Windows uses the inherited directory ACL. // Returns "" if no suitable location is available. Use before writing into the directory (e.g. the // telemetry offline cache, which the 1DS SDK opens during initialization). static std::string EnsureStorageDirectory(); @@ -53,9 +56,6 @@ class DeviceId { void InitializeInternal(); - // Validate GUID format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) - static bool IsValidGUID(const std::string& str); - // Create directory tree recursively using platform APIs. static bool CreateDirectoryTree(const std::string& path, bool leaf = true); diff --git a/onnxruntime/core/platform/posix/android_telemetry_jni.cc b/onnxruntime/core/platform/posix/android_telemetry_jni.cc index 8685c63358796..c2c10e8cb8775 100644 --- a/onnxruntime/core/platform/posix/android_telemetry_jni.cc +++ b/onnxruntime/core/platform/posix/android_telemetry_jni.cc @@ -2,6 +2,29 @@ // Licensed under the MIT License. #include +#include "core/platform/telemetry_strings.h" + +namespace { +jstring BoundedTelemetryJniString(JNIEnv* env, jstring value, + size_t max_bytes = onnxruntime::kMaxTelemetryStringLength, bool truncate = true) { + if (value == nullptr) return nullptr; + const jsize length = env->GetStringLength(value); + if (env->ExceptionCheck()) return nullptr; + std::array buffer{}; + const jsize count = static_cast(std::min(static_cast(length), max_bytes + 1)); + env->GetStringRegion(value, 0, count, buffer.data()); + if (env->ExceptionCheck()) return nullptr; + const size_t prefix_length = onnxruntime::telemetry_detail::TelemetryUnicodePrefixLength( + buffer.data(), static_cast(count), max_bytes, true); + if (prefix_length == static_cast(length)) return value; + if (!truncate) { + const jclass exception = env->FindClass("java/lang/IllegalArgumentException"); + if (exception != nullptr) env->ThrowNew(exception, "Telemetry cache path exceeds the byte limit"); + return nullptr; + } + return env->NewString(buffer.data(), static_cast(prefix_length)); +} +} // namespace extern "C" { @@ -46,21 +69,33 @@ JNIEXPORT void JNICALL Java_ai_onnxruntime_telemetry_HttpClient_onPowerChange( JNIEXPORT void JNICALL Java_ai_onnxruntime_telemetry_HttpClient_setCacheFilePath( JNIEnv* env, jobject client, jstring path) { + path = BoundedTelemetryJniString(env, path, onnxruntime::telemetry_detail::kMaxTelemetryPathBytes, false); + if (env->ExceptionCheck()) return; Java_com_microsoft_applications_events_HttpClient_setCacheFilePath(env, client, path); } JNIEXPORT void JNICALL Java_ai_onnxruntime_telemetry_HttpClient_setDeviceInfo( JNIEnv* env, jobject client, jstring id, jstring manufacturer, jstring model) { + std::array values{id, manufacturer, model}; + for (auto& value : values) { + value = BoundedTelemetryJniString(env, value); + if (env->ExceptionCheck()) return; + } Java_com_microsoft_applications_events_HttpClient_setDeviceInfo( - env, client, id, manufacturer, model); + env, client, values[0], values[1], values[2]); } JNIEXPORT void JNICALL Java_ai_onnxruntime_telemetry_HttpClient_setSystemInfo( JNIEnv* env, jobject client, jstring app_id, jstring app_version, jstring app_language, jstring os_major_version, jstring os_full_version, jstring time_zone, jstring device_class) { + std::array values{ + app_id, app_version, app_language, os_major_version, os_full_version, time_zone, device_class}; + for (auto& value : values) { + value = BoundedTelemetryJniString(env, value); + if (env->ExceptionCheck()) return; + } Java_com_microsoft_applications_events_HttpClient_setSystemInfo( - env, client, app_id, app_version, app_language, os_major_version, os_full_version, time_zone, - device_class); + env, client, values[0], values[1], values[2], values[3], values[4], values[5], values[6]); } } // extern "C" diff --git a/onnxruntime/core/platform/posix/device_id.cc b/onnxruntime/core/platform/posix/device_id.cc index 453a3c018e36d..5025a6a833cb0 100644 --- a/onnxruntime/core/platform/posix/device_id.cc +++ b/onnxruntime/core/platform/posix/device_id.cc @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/platform/posix/device_id.h" +#include "core/platform/device_id.h" #include "core/common/common.h" +#include "core/common/logging/logging.h" #include "core/platform/telemetry_guid.h" +#include "core/platform/telemetry_environment.h" #include -#include #include #include #include @@ -34,8 +35,6 @@ namespace onnxruntime { namespace { -// ORT and OGA share this file and use the same flock-first protocol so corruption repair is -// serialized even when both libraries are loaded in one process. constexpr char kDeviceIdLockFileName[] = "deviceid.lock"; enum class DeviceIdReadResult { @@ -172,15 +171,6 @@ class ScopedDeviceIdFileLock { int fd_{-1}; }; -void TrimAsciiWhitespace(std::string& value) { - value.erase(std::find_if_not(value.rbegin(), value.rend(), - [](unsigned char c) { return std::isspace(c); }) - .base(), - value.end()); - value.erase(value.begin(), std::find_if_not(value.begin(), value.end(), - [](unsigned char c) { return std::isspace(c); })); -} - DeviceIdFileRead ReadDeviceIdFileNoFollow(int directory_fd, const char* file_name, size_t max_size) { int flags = O_RDONLY; #ifdef O_NOFOLLOW @@ -223,7 +213,7 @@ DeviceIdFileRead ReadDeviceIdFileNoFollow(int directory_fd, const char* file_nam if (total > max_size) return {DeviceIdReadResult::Invalid, {}}; std::string content(buffer.data(), total); - TrimAsciiWhitespace(content); + content = telemetry_detail::TrimAscii(content); return {DeviceIdReadResult::Read, std::move(content)}; } @@ -261,36 +251,28 @@ std::string DeviceId::GetStatusString() { } } -bool DeviceId::IsValidGUID(const std::string& str) { - if (str.length() != 36) return false; - - for (size_t i = 0; i < str.length(); ++i) { - char c = str[i]; - if (i == 8 || i == 13 || i == 18 || i == 23) { - if (c != '-') return false; - } else { - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - return false; - } - } - } - return true; -} - std::string DeviceId::GetStorageDirectory() { + const auto read_path = [](const char* name) { + auto value = telemetry_detail::ReadTelemetryEnvironment(name, telemetry_detail::kMaxTelemetryPathBytes); + if (!value && logging::LoggingManager::HasDefaultLogger()) { + LOGS_DEFAULT(WARNING) << "Ignoring oversized or unreadable telemetry storage environment variable " << name; + } + return value; + }; #if !defined(__APPLE__) // XDG requires absolute paths. Ignore relative values so telemetry state is never written below // the process working directory. - if (const char* xdg = std::getenv("XDG_CACHE_HOME"); xdg != nullptr && xdg[0] == '/') { - return std::string(xdg) + "/" + kDeviceIdDir; + if (const auto xdg = read_path("XDG_CACHE_HOME"); xdg && !xdg->empty() && (*xdg)[0] == '/') { + const std::string path = *xdg + "/" + kDeviceIdDir; + return path.size() <= telemetry_detail::kMaxTelemetryPathBytes ? path : std::string{}; } #endif // Prefer an absolute $HOME; fall back to the password database for contexts where HOME is unset // or invalid, e.g. system services/daemons under systemd/launchd. std::string home; - if (const char* h = std::getenv("HOME"); h != nullptr && h[0] == '/') { - home = h; + if (const auto h = read_path("HOME"); h && !h->empty() && (*h)[0] == '/') { + home = *h; } else { // getpwuid() returns a pointer to shared static storage and is not thread-safe; use the // reentrant getpwuid_r() with a caller-provided buffer so concurrent callers don't race. @@ -304,16 +286,20 @@ std::string DeviceId::GetStorageDirectory() { std::vector buf(pw_buffer_size); if (::getpwuid_r(::getuid(), &pwd, buf.data(), buf.size(), &result) == 0 && result != nullptr && result->pw_dir != nullptr && result->pw_dir[0] == '/') { - home = result->pw_dir; + const auto value = telemetry_detail::TelemetryCStringView(result->pw_dir, telemetry_detail::kMaxTelemetryPathBytes); + if (value.size() <= telemetry_detail::kMaxTelemetryPathBytes) { + home = value; + } } } if (home.empty()) return ""; #if defined(__APPLE__) - return home + "/Library/Application Support/" + kDeviceIdDir; + const std::string path = home + "/Library/Application Support/" + kDeviceIdDir; #else - return home + "/.cache/" + kDeviceIdDir; + const std::string path = home + "/.cache/" + kDeviceIdDir; #endif + return path.size() <= telemetry_detail::kMaxTelemetryPathBytes ? path : std::string{}; } std::string DeviceId::EnsureStorageDirectory() { @@ -392,7 +378,7 @@ void DeviceId::InitializeInternal() { // Try to read existing device ID const DeviceIdFileRead existing = ReadDeviceIdFileNoFollow(directory.Get(), kFileName, kMaxFileSize); - if (existing.result == DeviceIdReadResult::Read && IsValidGUID(existing.content)) { + if (existing.result == DeviceIdReadResult::Read && IsValidGuid(existing.content)) { device_id_ = existing.content; status_ = DeviceIdStatus::Existing; return; @@ -414,9 +400,9 @@ void DeviceId::InitializeInternal() { return; } - // Another ORT or OGA process may have repaired the shared file while this process waited. + // Another process may have repaired the shared file while this process waited. const DeviceIdFileRead repaired = ReadDeviceIdFileNoFollow(directory.Get(), kFileName, kMaxFileSize); - if (repaired.result == DeviceIdReadResult::Read && IsValidGUID(repaired.content)) { + if (repaired.result == DeviceIdReadResult::Read && IsValidGuid(repaired.content)) { device_id_ = repaired.content; status_ = DeviceIdStatus::Existing; return; @@ -474,7 +460,7 @@ void DeviceId::InitializeInternal() { // Another process won the first-run race. Its complete file was published atomically, // so use that value instead of allowing the persisted id to flap. const DeviceIdFileRead winner = ReadDeviceIdFileNoFollow(directory.Get(), kFileName, kMaxFileSize); - if (winner.result == DeviceIdReadResult::Read && IsValidGUID(winner.content)) { + if (winner.result == DeviceIdReadResult::Read && IsValidGuid(winner.content)) { device_id_ = winner.content; status_ = DeviceIdStatus::Existing; } else { diff --git a/onnxruntime/core/platform/posix/env.cc b/onnxruntime/core/platform/posix/env.cc index 8e60ab850abc5..2c03a137c4fe3 100644 --- a/onnxruntime/core/platform/posix/env.cc +++ b/onnxruntime/core/platform/posix/env.cc @@ -16,8 +16,8 @@ limitations under the License. #include "core/platform/env.h" -#ifdef USE_POSIX_TELEMETRY -#include "core/platform/posix/telemetry.h" +#ifdef USE_1DS_TELEMETRY +#include "core/platform/telemetry_1ds.h" #endif #include @@ -801,8 +801,8 @@ class PosixEnv : public Env { } private: -#ifdef USE_POSIX_TELEMETRY - PosixTelemetry telemetry_provider_; +#ifdef USE_1DS_TELEMETRY + OneDsTelemetry telemetry_provider_; #else Telemetry telemetry_provider_; #endif diff --git a/onnxruntime/core/platform/posix/telemetry_1ds.cc b/onnxruntime/core/platform/posix/telemetry_1ds.cc new file mode 100644 index 0000000000000..791eb76cc3a99 --- /dev/null +++ b/onnxruntime/core/platform/posix/telemetry_1ds.cc @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/platform/telemetry_1ds.h" +#include "core/platform/telemetry_1ds_platform.h" +#include "core/platform/telemetry_environment.h" +#include "core/common/logging/logging.h" + +#ifdef __APPLE__ +#include +#include +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace onnxruntime { +namespace { +std::string GetFileName(std::string_view path) { + const size_t separator = path.find_last_of("/\\"); + return telemetry_detail::BoundedTelemetryString(path.substr(separator == std::string_view::npos ? 0 : separator + 1)); +} + +#if defined(__linux__) || defined(__ANDROID__) +std::string ReadBoundedFile(const char* path, size_t max_bytes = telemetry_detail::kMaxTelemetryProbeBytes) { + std::ifstream input(path, std::ios::binary); + if (!input) { + return {}; + } + std::string buffer(max_bytes, '\0'); + input.read(buffer.data(), static_cast(buffer.size())); + buffer.resize(static_cast(input.gcount())); + return buffer; +} +#endif + +} // namespace + +namespace telemetry_internal { +const char* GetDefaultEncodedToken() { + return "eg8KQWRGDBBdD1YuWl9JahRaTFhZVX4NDUhgRF9MXlhIegxYHGpFXxJEXVd/V0NMa0FXWVEOA3hDCxw3RFhCUF8Edl0NVWRMWEM="; +} + +int32_t GetProcessorCount() { + auto n = sysconf(_SC_NPROCESSORS_ONLN); + if (n <= 0) { + return 0; + } + if (n > static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + return static_cast(n); +} + +std::string GetCachePath(const std::string& directory) { + return directory + "/onnxruntime.db"; +} +} // namespace telemetry_internal + +std::string OneDsTelemetry::GetPlatformInfo() const { +#if defined(__APPLE__) +#if TARGET_OS_IOS + return "iOS"; +#elif TARGET_OS_MAC + return "macOS"; +#else + return "Apple"; +#endif +#elif defined(__ANDROID__) + return "Android"; +#elif defined(__linux__) + return "Linux"; +#else + return "Unknown"; +#endif +} + +// --------------------------------------------------------------------------- +// Process / system info helpers for LogProcessInfo +// --------------------------------------------------------------------------- + +// Get detailed OS version string (e.g., "macOS 15.2", "Ubuntu 22.04 LTS") +std::string OneDsTelemetry::GetOsDescription() const { +#if defined(__APPLE__) + char version[64] = {}; + size_t len = sizeof(version); + if (sysctlbyname("kern.osproductversion", version, &len, nullptr, 0) == 0) { + version[sizeof(version) - 1] = '\0'; +#if TARGET_OS_IOS + return std::string("iOS ") + version; +#else + return std::string("macOS ") + version; +#endif + } + return GetPlatformInfo(); + +#elif defined(__ANDROID__) + // Read Android system properties via /system/build.prop + std::string release, sdk; + std::istringstream prop(ReadBoundedFile("/system/build.prop")); + if (prop) { + std::string line; + while (std::getline(prop, line)) { + if (line.rfind("ro.build.version.release=", 0) == 0) + release = telemetry_detail::BoundedTelemetryString(std::string_view(line).substr(25)); + else if (line.rfind("ro.build.version.sdk=", 0) == 0) + sdk = telemetry_detail::BoundedTelemetryString(std::string_view(line).substr(21)); + } + } + if (!release.empty()) { + std::string result = "Android "; + telemetry_detail::AppendTelemetryString(result, release); + if (!sdk.empty()) { + telemetry_detail::AppendTelemetryString(result, " (API "); + telemetry_detail::AppendTelemetryString(result, sdk); + telemetry_detail::AppendTelemetryString(result, ")"); + } + return result; + } + return "Android"; + +#elif defined(__linux__) + // Parse /etc/os-release for PRETTY_NAME (e.g., "Ubuntu 22.04.3 LTS") + std::istringstream os_release(ReadBoundedFile("/etc/os-release")); + if (os_release) { + std::string line; + while (std::getline(os_release, line)) { + if (line.rfind("PRETTY_NAME=", 0) == 0) { + std::string_view value = std::string_view(line).substr(12); + if (value.size() >= 2 && value.front() == '"' && value.back() == '"') { + value = value.substr(1, value.size() - 2); + } + return telemetry_detail::BoundedTelemetryString(value); + } + } + } + return "Linux"; + +#else + return "Unknown"; +#endif +} + +// Get the CPU brand string (e.g. "Intel(R) Core(TM) i7-10700K"). Empty when unavailable. +std::string OneDsTelemetry::GetCpuModel() const { +#if defined(__APPLE__) + // macOS/iOS expose the CPU brand string via sysctl. + char buf[256] = {0}; + size_t size = sizeof(buf); + if (sysctlbyname("machdep.cpu.brand_string", buf, &size, nullptr, 0) == 0) { + buf[sizeof(buf) - 1] = '\0'; + return std::string(buf); + } + return ""; + +#elif defined(__linux__) || defined(__ANDROID__) + // /proc/cpuinfo exposes the CPU brand as "model name" (x86) or "Hardware" (ARM). + std::istringstream cpuinfo(ReadBoundedFile("/proc/cpuinfo")); + std::string line; + std::string hardware; + while (std::getline(cpuinfo, line)) { + const size_t colon = line.find(':'); + if (colon == std::string::npos) { + continue; + } + std::string key = telemetry_detail::BoundedTelemetryString(std::string_view(line).substr(0, colon)); + while (!key.empty() && std::isspace(static_cast(key.back()))) { + key.pop_back(); + } + std::string_view value = std::string_view(line).substr(colon + 1); + const size_t start = value.find_first_not_of(" \t"); + value = (start == std::string::npos) ? std::string_view() : value.substr(start); + if (key == "model name") { + return telemetry_detail::BoundedTelemetryString(value); + } + if (hardware.empty() && key == "Hardware") { + hardware = telemetry_detail::BoundedTelemetryString(value); + } + } + return hardware; + +#else + return ""; +#endif +} + +// Coarse device class for the host: "Mobile" on Android/iOS, "Desktop" elsewhere. +std::string OneDsTelemetry::GetDeviceClass() const { +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + return "Mobile"; +#else + return "Desktop"; +#endif +} + +namespace { + +#if defined(__linux__) || defined(__ANDROID__) +bool FileExists(const char* path) { + std::ifstream input(path); + return input.good(); +} +#endif + +} // namespace + +telemetry_detail::HostEnvironmentInfo OneDsTelemetry::GetHostEnvironmentInfo() { + telemetry_detail::HostEnvironmentEvidence evidence; +#if defined(__linux__) || defined(__ANDROID__) + evidence.docker_marker = FileExists("/.dockerenv"); + evidence.podman_marker = FileExists("/run/.containerenv"); + const auto read_evidence = [](const char* name) { + const auto value = telemetry_detail::ReadTelemetryEnvironment(name); + if (!value) { + if (logging::LoggingManager::HasDefaultLogger()) { + LOGS_DEFAULT(WARNING) << "Ignoring oversized or unreadable telemetry environment evidence " << name; + } + return std::string{}; + } + return *value; + }; + evidence.kubernetes = !read_evidence("KUBERNETES_SERVICE_HOST").empty(); + evidence.aws_ecs = !read_evidence("ECS_CONTAINER_METADATA_URI").empty() || + !read_evidence("ECS_CONTAINER_METADATA_URI_V4").empty(); + evidence.generic_container = + telemetry_detail::IsTruthyCiValue(read_evidence("DOTNET_RUNNING_IN_CONTAINER")); + evidence.systemd_container = + ReadBoundedFile("/run/systemd/container") + read_evidence("container"); + evidence.cgroup = ReadBoundedFile("/proc/1/cgroup") + ReadBoundedFile("/proc/self/cgroup"); + evidence.cpu_info = ReadBoundedFile("/proc/cpuinfo"); + evidence.kernel_release = ReadBoundedFile("/proc/sys/kernel/osrelease"); + evidence.dmi = ReadBoundedFile("/sys/class/dmi/id/sys_vendor") + + ReadBoundedFile("/sys/class/dmi/id/product_name") + + ReadBoundedFile("/sys/class/dmi/id/board_vendor"); +#if defined(__ANDROID__) + const std::string android_properties = ReadBoundedFile("/system/build.prop"); + evidence.android_emulator = telemetry_detail::ContainsAscii(android_properties, "ro.kernel.qemu=1") || + telemetry_detail::ContainsAscii(android_properties, "ro.boot.qemu=1") || + telemetry_detail::ContainsAscii(android_properties, "ro.product.manufacturer=genymotion"); +#endif +#elif defined(__APPLE__) && !TARGET_OS_IOS + int is_virtual_machine = 0; + size_t size = sizeof(is_virtual_machine); + evidence.apple_virtual_machine = + sysctlbyname("kern.hv_vmm_present", &is_virtual_machine, &size, nullptr, 0) == 0 && + is_virtual_machine != 0; +#endif + return telemetry_detail::ClassifyHostEnvironment(evidence); +} + +std::string OneDsTelemetry::GetProcessName() { +#if defined(__APPLE__) + uint32_t path_size = 1024; + std::vector path(path_size); + if (_NSGetExecutablePath(path.data(), &path_size) != 0) { + if (path_size > telemetry_detail::kMaxTelemetryPathBytes) { + return {}; + } + path.resize(path_size); + if (_NSGetExecutablePath(path.data(), &path_size) != 0) { + return {}; + } + } + return GetFileName(path.data()); +#elif defined(__linux__) || defined(__ANDROID__) + std::istringstream cmdline(ReadBoundedFile("/proc/self/cmdline", telemetry_detail::kMaxTelemetryPathBytes + 1)); + std::string first_argument; + if (cmdline && std::getline(cmdline, first_argument, '\0') && + first_argument.size() <= telemetry_detail::kMaxTelemetryPathBytes) { + return GetFileName(first_argument); + } + return {}; +#else + return {}; +#endif +} + +// Get the CPU architecture the binary was compiled for +std::string OneDsTelemetry::GetArchitecture() { +#if defined(__x86_64__) + return "x86_64"; +#elif defined(__i386__) + return "x86"; +#elif defined(__aarch64__) + return "arm64"; +#elif defined(__arm__) + return "arm"; +#elif defined(__riscv) + return "riscv"; +#elif defined(__wasm__) + return "wasm"; +#else + return "unknown"; +#endif +} + +// Get total physical memory in MB +int64_t OneDsTelemetry::GetTotalMemoryMB() { +#if defined(__APPLE__) + int64_t mem = 0; + size_t len = sizeof(mem); + if (sysctlbyname("hw.memsize", &mem, &len, nullptr, 0) == 0) { + return mem / (1024 * 1024); + } + return 0; + +#elif defined(__linux__) || defined(__ANDROID__) + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + if (pages > 0 && page_size > 0) { + return static_cast(pages) * page_size / (1024 * 1024); + } + return 0; + +#else + return 0; +#endif +} + +void OneDsTelemetry::LogExecutionProviderEvent(LUID* adapter_luid) const { + (void)adapter_luid; +} + +void OneDsTelemetry::LogDriverInfoEvent( + std::string_view device_class, + const std::wstring_view& driver_names, + const std::wstring_view& driver_versions) const { + (void)device_class; + (void)driver_names; + (void)driver_versions; +} + +} // namespace onnxruntime diff --git a/onnxruntime/core/platform/posix/telemetry.cc b/onnxruntime/core/platform/telemetry_1ds.cc similarity index 65% rename from onnxruntime/core/platform/posix/telemetry.cc rename to onnxruntime/core/platform/telemetry_1ds.cc index 915c95e2f6726..3d9a9d04cbbd8 100644 --- a/onnxruntime/core/platform/posix/telemetry.cc +++ b/onnxruntime/core/platform/telemetry_1ds.cc @@ -1,22 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/platform/posix/telemetry.h" -#include "core/platform/posix/device_id.h" -#include "core/platform/posix/telemetry_context.h" -#include "core/platform/posix/telemetry_no_throw.h" -#include "core/platform/posix/telemetry_sampling.h" -#include "core/platform/posix/telemetry_sha256.h" +#include "core/platform/telemetry_1ds.h" +#include "core/platform/device_id.h" +#include "core/platform/telemetry_context.h" +#include "core/platform/telemetry_no_throw.h" +#include "core/platform/telemetry_sampling.h" +#include "core/platform/telemetry_sha256.h" #include "core/platform/telemetry_environment.h" #include "core/platform/telemetry_guid.h" +#include "core/platform/telemetry_1ds_platform.h" #include "core/platform/telemetry_redaction.h" #ifdef __APPLE__ #include -#include #endif +#if defined(ORT_TELEMETRY_USES_STATIC_CURL) #include +#endif + // 1DS SDK #include #include @@ -24,26 +27,9 @@ #include "http/HttpClient_Android.hpp" #endif -#include -#include - -#ifdef __APPLE__ -#include -#include -#endif - -#if defined(__linux__) || defined(__ANDROID__) -#include -#endif - #include -#include -#include -#include #include #include -#include -#include #include #include #include @@ -68,7 +54,7 @@ using namespace Microsoft::Applications::Events; namespace onnxruntime { -// PosixTelemetry can be constructed during early Env initialization (before logging registers a +// OneDsTelemetry can be constructed during early Env initialization (before logging registers a // default logger) and destroyed late at process exit, so only emit warnings when a default logger // exists, to avoid touching a missing/destroyed LoggingManager. #define ORT_TELEMETRY_WARN(stream_expr) \ @@ -79,17 +65,17 @@ namespace onnxruntime { } while (0) // Static member initialization -std::atomic PosixTelemetry::global_register_count_{0}; -std::mutex PosixTelemetry::global_mutex_; -std::shared_mutex PosixTelemetry::mutex_; -::Microsoft::Applications::Events::ILogManager* PosixTelemetry::log_manager_ = nullptr; -std::atomic<::Microsoft::Applications::Events::ILogger*> PosixTelemetry::logger_{nullptr}; -std::unique_ptr<::Microsoft::Applications::Events::ILogConfiguration> PosixTelemetry::config_; -std::atomic PosixTelemetry::enabled_{true}; -std::atomic PosixTelemetry::telemetry_disabled_{false}; -std::atomic PosixTelemetry::network_context_suppressed_{false}; -std::atomic PosixTelemetry::projection_{0}; -std::atomic PosixTelemetry::process_info_logged_{false}; +std::atomic OneDsTelemetry::global_register_count_{0}; +std::mutex OneDsTelemetry::global_mutex_; +std::shared_mutex OneDsTelemetry::mutex_; +::Microsoft::Applications::Events::ILogManager* OneDsTelemetry::log_manager_ = nullptr; +std::atomic<::Microsoft::Applications::Events::ILogger*> OneDsTelemetry::logger_{nullptr}; +std::unique_ptr<::Microsoft::Applications::Events::ILogConfiguration> OneDsTelemetry::config_; +std::atomic OneDsTelemetry::enabled_{true}; +std::atomic OneDsTelemetry::telemetry_disabled_{false}; +std::atomic OneDsTelemetry::network_context_suppressed_{false}; +std::atomic OneDsTelemetry::projection_{0}; +std::atomic OneDsTelemetry::process_info_logged_{false}; #if !defined(ORT_TELEMETRY_TENANT_TOKEN) namespace { @@ -125,8 +111,7 @@ std::string DecodeBase64(const std::string& encoded) { std::string GetToken() { static constexpr char kXorKey[] = "OnnxRuntime"; constexpr size_t klen = sizeof(kXorKey) - 1; - std::string decoded = DecodeBase64( - "eg8KQWRGDBBdD1YuWl9JahRaTFhZVX4NDUhgRF9MXlhIegxYHGpFXxJEXVd/V0NMa0FXWVEOA3hDCxw3RFhCUF8Edl0NVWRMWEM="); + std::string decoded = DecodeBase64(telemetry_internal::GetDefaultEncodedToken()); for (size_t i = 0; i < decoded.size(); ++i) { decoded[i] = static_cast(decoded[i] ^ kXorKey[i % klen]); } @@ -146,18 +131,6 @@ enum class EventPriority { // Helper class to build events with common properties class EventBuilder { private: - template - static InlinedVector SortedEntries(const Map& map) { - InlinedVector entries; - entries.reserve(map.size()); - for (const auto& entry : map) { - entries.push_back(&entry); - } - std::sort(entries.begin(), entries.end(), - [](const auto* lhs, const auto* rhs) { return lhs->first < rhs->first; }); - return entries; - } - EventProperties props_; public: @@ -176,13 +149,17 @@ class EventBuilder { return *this; } - EventBuilder& AddString(const char* key, const std::string& value) { + EventBuilder& AddString(const char* key, std::string_view value) { if (!value.empty()) { - props_.SetProperty(key, value); + props_.SetProperty(key, telemetry_detail::BoundedTelemetryString(value)); } return *this; } + EventBuilder& AddString(const char* key, const char* value) { + return AddString(key, telemetry_detail::TelemetryCStringView(value, kMaxTelemetryStringLength + 3)); + } + EventBuilder& AddInt32(const char* key, int32_t value) { props_.SetProperty(key, static_cast(value)); return *this; @@ -211,73 +188,28 @@ class EventBuilder { // Helper for vector to comma-separated string EventBuilder& AddStringList(const char* key, const std::vector& vec) { if (!vec.empty()) { - std::string result; - for (size_t i = 0; i < vec.size(); ++i) { - if (i > 0) result += ','; - result += vec[i]; - } - props_.SetProperty(key, result); + props_.SetProperty(key, telemetry_detail::JoinTelemetryStrings(vec)); } return *this; } - // Helper for map to key=value,key=value format - EventBuilder& AddIntMap(const char* key, const std::unordered_map& map) { - if (!map.empty()) { - std::string result; - bool first = true; - for (const auto* entry : SortedEntries(map)) { - const auto& [k, v] = *entry; - if (!first) result += ','; - result += k + '=' + std::to_string(v); - first = false; - } - props_.SetProperty(key, result); - } - return *this; - } - - // Helper for string map - EventBuilder& AddStringMap(const char* key, const std::unordered_map& map) { + template + EventBuilder& AddMap(const char* key, const Map& map) { if (!map.empty()) { - std::string result; - bool first = true; - for (const auto* entry : SortedEntries(map)) { - const auto& [k, v] = *entry; - if (!first) result += ','; - result += k + '=' + v; - first = false; - } - props_.SetProperty(key, result); + props_.SetProperty(key, telemetry_detail::FormatTelemetryMap(map)); } return *this; } // Helper for batch size duration map EventBuilder& AddBatchSizeDurations(const std::unordered_map& durations) { - std::string result; - for (const auto* entry : SortedEntries(durations)) { - const auto& [batch_size, duration] = *entry; - if (!result.empty()) { - result += ", "; - } - result += std::to_string(batch_size); - result += ": "; - result += std::to_string(duration); - } - props_.SetProperty("totalRunDurationPerBatchSize", result); + props_.SetProperty("totalRunDurationPerBatchSize", telemetry_detail::FormatTelemetryMap(durations, ", ", ": ")); return *this; } EventProperties Build() { return std::move(props_); } }; -// All Microsoft AI developer tools read the same UUID and derive the same upload identifier. -// The raw UUID is never transmitted. -[[maybe_unused]] static std::string HashDeviceId(const std::string& id) { - return telemetry_internal::Sha256::HashStringHex(id); -} - namespace { class PendingLogManager { @@ -311,16 +243,19 @@ class PendingLogManager { ILogManager* manager_; }; -const std::string& GetAppSessionGuid() { +const std::string& GetAppSessionGuidInternal() { static const std::string guid = GenerateGuidV4(); return guid; } #if defined(ORT_TELEMETRY_USES_STATIC_CURL) std::string GetCertificateAuthorityBundlePath() { - if (const char* ssl_cert_file = std::getenv("SSL_CERT_FILE"); - ssl_cert_file != nullptr && access(ssl_cert_file, R_OK) == 0) { - return ssl_cert_file; + const auto ssl_cert_file = + telemetry_detail::ReadTelemetryEnvironment("SSL_CERT_FILE", telemetry_detail::kMaxTelemetryPathBytes); + if (!ssl_cert_file) { + ORT_TELEMETRY_WARN("Ignoring oversized or unreadable telemetry SSL_CERT_FILE"); + } else if (!ssl_cert_file->empty() && access(ssl_cert_file->c_str(), R_OK) == 0) { + return *ssl_cert_file; } constexpr const char* kCertificateAuthorityBundlePaths[] = { @@ -342,22 +277,8 @@ std::string GetCertificateAuthorityBundlePath() { } #endif -template -void RunTelemetryOperation(const char* operation_name, Operation&& operation) noexcept { - auto warning = [operation_name](const char* message) { - if (message != nullptr) { - ORT_TELEMETRY_WARN("[Telemetry] " << operation_name << " failed: " << message); - } else { - ORT_TELEMETRY_WARN("[Telemetry] " << operation_name << " failed with an unknown exception"); - } - }; - - telemetry_internal::RunTelemetryOperationNoThrow( - std::forward(operation), warning); -} - bool PrepareSampledEvent(EventBuilder& event, uint32_t session_id) { - if (!telemetry_internal::ShouldSampleSession(GetAppSessionGuid(), session_id)) { + if (!telemetry_internal::ShouldSampleSession(GetAppSessionGuidInternal(), session_id)) { return false; } @@ -367,7 +288,7 @@ bool PrepareSampledEvent(EventBuilder& event, uint32_t session_id) { bool PrepareHighVolumeEvent(EventBuilder& event, uint32_t session_id) { if (!telemetry_internal::ShouldSampleSession( - GetAppSessionGuid(), session_id, + GetAppSessionGuidInternal(), session_id, telemetry_internal::kHighVolumeEventSampleRatePercent)) { return false; } @@ -376,36 +297,34 @@ bool PrepareHighVolumeEvent(EventBuilder& event, uint32_t session_id) { return true; } -bool PrepareProcessEvent(EventBuilder& event) { +bool PrepareSampledProcessEvent(EventBuilder& event) { if (!telemetry_internal::ShouldSampleSession( - GetAppSessionGuid(), 0, - telemetry_internal::kProcessEventSampleRatePercent)) { + GetAppSessionGuidInternal(), 0, + telemetry_internal::kOtherProcessEventSampleRatePercent)) { return false; } - event.SetPopsample(telemetry_internal::kProcessEventSampleRatePercent); + event.SetPopsample(telemetry_internal::kOtherProcessEventSampleRatePercent); return true; } -int32_t GetProcessorCount() { - auto n = sysconf(_SC_NPROCESSORS_ONLN); - if (n <= 0) { - return 0; - } - if (n > static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - return static_cast(n); -} +} // namespace -std::string GetFileName(std::string_view path) { - const size_t separator = path.find_last_of('/'); - return std::string(path.substr(separator == std::string_view::npos ? 0 : separator + 1)); +namespace telemetry_internal { +const std::string& GetAppSessionGuid() { + return GetAppSessionGuidInternal(); } +} // namespace telemetry_internal -} // namespace +void OneDsTelemetry::ReportFailure(const char* operation_name, const char* message) { + if (message != nullptr) { + ORT_TELEMETRY_WARN("[Telemetry] " << operation_name << " failed: " << message); + } else { + ORT_TELEMETRY_WARN("[Telemetry] " << operation_name << " failed with an unknown exception"); + } +} -PosixTelemetry::PosixTelemetry() { +OneDsTelemetry::OneDsTelemetry() { std::lock_guard lock(global_mutex_); // Always increment so destructor pairing is symmetric @@ -418,7 +337,11 @@ PosixTelemetry::PosixTelemetry() { } } -PosixTelemetry::~PosixTelemetry() { +OneDsTelemetry::OneDsTelemetry(const Telemetry& local_telemetry) : OneDsTelemetry() { + local_telemetry_ = &local_telemetry; +} + +OneDsTelemetry::~OneDsTelemetry() { std::lock_guard lock(global_mutex_); global_register_count_--; @@ -433,7 +356,7 @@ PosixTelemetry::~PosixTelemetry() { } } -void PosixTelemetry::LogEventAsync(Microsoft::Applications::Events::EventProperties&& props) const { +void OneDsTelemetry::LogEventAsync(Microsoft::Applications::Events::EventProperties&& props) const { // Hold a shared (reader) lock for the duration of the LogEvent call so the logger and its owning // log manager cannot be torn down underneath us: Initialize()/Shutdown() take this lock // exclusively. The shared lock still allows multiple threads to log concurrently. @@ -464,7 +387,7 @@ void PosixTelemetry::LogEventAsync(Microsoft::Applications::Events::EventPropert } } -void PosixTelemetry::Initialize() { +void OneDsTelemetry::Initialize() { std::unique_lock lock(mutex_); // Full suppression is process-wide and irreversible: never create the uploader, emit an event, or @@ -491,14 +414,20 @@ void PosixTelemetry::Initialize() { auto& config = *pending_config; config[CFG_STR_COLLECTOR_URL] = "https://mobile.events.data.microsoft.com/OneCollector/1.0"; - config[CFG_BOOL_ENABLE_TRACE] = false; // Disable SDK internal logging + config[CFG_BOOL_ENABLE_TRACE] = false; config[CFG_INT_TRACE_LEVEL_MASK] = 0; - config[CFG_INT_SDK_MODE] = SdkModeTypes::SdkModeTypes_CS; // Common Schema 4.0 mode + config[CFG_INT_SDK_MODE] = SdkModeTypes::SdkModeTypes_CS; +#if defined(_WIN32) + // The 1DS network detector leaves a netprofm.dll allocation at process exit. + config[CFG_BOOL_ENABLE_NET_DETECT] = false; +#endif #if defined(__APPLE__) - // Apple system SQLite is process-global. Multiple libraries may embed 1DS in the same process, - // so let SQLite initialize lazily and never let an individual SDK copy shut it down. + // System SQLite is shared with other libraries; let it manage its own lifetime. config["skipSqliteInitAndShutdown"] = "true"; #endif + // Persist pending events without blocking process teardown on uploads. + config[CFG_INT_MAX_TEARDOWN_TIME] = 0; + config[CFG_INT_RAM_QUEUE_SIZE] = 512 * 1024; #if defined(ORT_TELEMETRY_USES_STATIC_CURL) if (std::string ca_bundle = GetCertificateAuthorityBundlePath(); !ca_bundle.empty()) { config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO] = ca_bundle; @@ -506,25 +435,22 @@ void PosixTelemetry::Initialize() { ORT_TELEMETRY_WARN("No readable CA bundle was found; telemetry HTTPS uploads will be unavailable"); } #endif - // Do not block process teardown to upload; persisted events are sent on the next run. 0 keeps - // Shutdown non-blocking and avoids adding exit latency to host apps. - config[CFG_INT_MAX_TEARDOWN_TIME] = 0; - #if !defined(__ANDROID__) // Configure the desktop cache in the same directory as device ID storage. Android's Java // HttpClient supplies the app-private cache directory to 1DS. { std::string cache_dir = DeviceId::EnsureStorageDirectory(); if (!cache_dir.empty()) { - std::string cache_path = cache_dir + "/onnxruntime.db"; + std::string cache_path = telemetry_internal::GetCachePath(cache_dir); + if (cache_path.size() > telemetry_detail::kMaxTelemetryPathBytes) { + ORT_TELEMETRY_WARN("Telemetry is unavailable because the cache path exceeds the byte limit"); + return; + } config[CFG_STR_CACHE_FILE_PATH] = cache_path; } } #endif - // Configure RAM queue for async batching - config[CFG_INT_RAM_QUEUE_SIZE] = 512 * 1024; // 512KB RAM queue - // Create log manager via LogManagerProvider (recommended for production use, // per LogManager_Creation_and_Lifecycle_Management.md). status_t status; @@ -577,12 +503,13 @@ void PosixTelemetry::Initialize() { auto& device_id = DeviceId::Instance(); std::string raw_device_id = device_id.GetValue(); if (!raw_device_id.empty()) { - logger->GetSemanticContext()->SetDeviceId("c:" + HashDeviceId(raw_device_id)); + // All Microsoft AI developer tools derive the same upload identifier; never transmit the raw UUID. + logger->GetSemanticContext()->SetDeviceId("c:" + telemetry_internal::Sha256::HashStringHex(raw_device_id)); } #endif // Add only ORT version and process-wide correlation context. - logger->SetContext("AppSessionGuid", GetAppSessionGuid()); + logger->SetContext("AppSessionGuid", GetAppSessionGuidInternal()); logger->SetContext("LibraryVersion", ORT_VERSION); // Caller-framework label from the build-time ORT_CALLER_FRAMEWORK option; only stamped when a @@ -598,13 +525,13 @@ void PosixTelemetry::Initialize() { logger_.store(logger, std::memory_order_release); } -void PosixTelemetry::Shutdown() { +void OneDsTelemetry::Shutdown() { std::unique_lock lock(mutex_); // Clear the logger so concurrent LogEventAsync() readers (which take the shared lock) observe // nullptr and skip. enabled_ is intentionally left untouched: it reflects the user's // EnableTelemetryEvents()/DisableTelemetryEvents() opt-in state, so a later Initialize() (e.g. in - // tests or dynamic load/unload of the last instance) can resume telemetry without a forced re-enable. + // dynamic load/unload of the last instance) can resume telemetry without a forced re-enable. logger_ = nullptr; // Owned by log_manager_, will be destroyed with it if (log_manager_ && config_) { @@ -622,290 +549,42 @@ void PosixTelemetry::Shutdown() { } } -std::string PosixTelemetry::GetPlatformInfo() const { -#if defined(__APPLE__) -#if TARGET_OS_IOS - return "iOS"; -#elif TARGET_OS_MAC - return "macOS"; -#else - return "Apple"; -#endif -#elif defined(__ANDROID__) - return "Android"; -#elif defined(__linux__) - return "Linux"; -#else - return "Unknown"; -#endif -} - -// --------------------------------------------------------------------------- -// Process / system info helpers for LogProcessInfo -// --------------------------------------------------------------------------- - -// Get detailed OS version string (e.g., "macOS 15.2", "Ubuntu 22.04 LTS") -std::string PosixTelemetry::GetOsDescription() const { -#if defined(__APPLE__) - char version[64] = {}; - size_t len = sizeof(version); - if (sysctlbyname("kern.osproductversion", version, &len, nullptr, 0) == 0) { -#if TARGET_OS_IOS - return std::string("iOS ") + version; -#else - return std::string("macOS ") + version; -#endif - } - return GetPlatformInfo(); - -#elif defined(__ANDROID__) - // Read Android system properties via /system/build.prop - std::string release, sdk; - std::ifstream prop("/system/build.prop"); - if (prop.is_open()) { - std::string line; - while (std::getline(prop, line)) { - if (line.rfind("ro.build.version.release=", 0) == 0) - release = line.substr(25); - else if (line.rfind("ro.build.version.sdk=", 0) == 0) - sdk = line.substr(21); - } - } - if (!release.empty()) { - std::string result = "Android " + release; - if (!sdk.empty()) result += " (API " + sdk + ")"; - return result; - } - return "Android"; - -#elif defined(__linux__) - // Parse /etc/os-release for PRETTY_NAME (e.g., "Ubuntu 22.04.3 LTS") - std::ifstream os_release("/etc/os-release"); - if (os_release.is_open()) { - std::string line; - while (std::getline(os_release, line)) { - if (line.rfind("PRETTY_NAME=", 0) == 0) { - std::string value = line.substr(12); - if (value.size() >= 2 && value.front() == '"' && value.back() == '"') { - value = value.substr(1, value.size() - 2); - } - return value; - } - } - } - return "Linux"; - -#else - return "Unknown"; -#endif -} - -// Get the CPU brand string (e.g. "Intel(R) Core(TM) i7-10700K"). Empty when unavailable. -std::string PosixTelemetry::GetCpuModel() const { -#if defined(__APPLE__) - // macOS/iOS expose the CPU brand string via sysctl. - char buf[256] = {0}; - size_t size = sizeof(buf); - if (sysctlbyname("machdep.cpu.brand_string", buf, &size, nullptr, 0) == 0) { - return std::string(buf); - } - return ""; - -#elif defined(__linux__) || defined(__ANDROID__) - // /proc/cpuinfo exposes the CPU brand as "model name" (x86) or "Hardware" (ARM). - std::ifstream cpuinfo("/proc/cpuinfo"); - std::string line; - std::string hardware; - while (std::getline(cpuinfo, line)) { - const size_t colon = line.find(':'); - if (colon == std::string::npos) { - continue; - } - std::string key = line.substr(0, colon); - while (!key.empty() && std::isspace(static_cast(key.back()))) { - key.pop_back(); - } - std::string value = line.substr(colon + 1); - const size_t start = value.find_first_not_of(" \t"); - value = (start == std::string::npos) ? std::string() : value.substr(start); - if (key == "model name") { - return value; - } - if (hardware.empty() && key == "Hardware") { - hardware = value; - } - } - return hardware; - -#else - return ""; -#endif -} - -// Coarse device class for the host: "Mobile" on Android/iOS, "Desktop" elsewhere. -std::string PosixTelemetry::GetDeviceClass() const { -#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) - return "Mobile"; -#else - return "Desktop"; -#endif -} - -namespace { - -#if defined(__linux__) || defined(__ANDROID__) -std::string ReadBoundedFile(const char* path) { - constexpr size_t kMaxProbeBytes = 16 * 1024; - std::ifstream input(path, std::ios::binary); - if (!input) { - return {}; - } - - std::array buffer{}; - input.read(buffer.data(), static_cast(buffer.size())); - return std::string(buffer.data(), static_cast(input.gcount())); -} - -bool FileExists(const char* path) { - std::ifstream input(path); - return input.good(); -} -#endif - -} // namespace - -telemetry_detail::HostEnvironmentInfo PosixTelemetry::GetHostEnvironmentInfo() { - telemetry_detail::HostEnvironmentEvidence evidence; -#if defined(__linux__) || defined(__ANDROID__) - evidence.docker_marker = FileExists("/.dockerenv"); - evidence.podman_marker = FileExists("/run/.containerenv"); - evidence.kubernetes = !telemetry_detail::GetTelemetryEnv("KUBERNETES_SERVICE_HOST").empty(); - evidence.aws_ecs = !telemetry_detail::GetTelemetryEnv("ECS_CONTAINER_METADATA_URI").empty() || - !telemetry_detail::GetTelemetryEnv("ECS_CONTAINER_METADATA_URI_V4").empty(); - evidence.generic_container = - telemetry_detail::IsTruthyCiValue(telemetry_detail::GetTelemetryEnv("DOTNET_RUNNING_IN_CONTAINER")); - evidence.systemd_container = - ReadBoundedFile("/run/systemd/container") + telemetry_detail::GetTelemetryEnv("container"); - evidence.cgroup = ReadBoundedFile("/proc/1/cgroup") + ReadBoundedFile("/proc/self/cgroup"); - evidence.cpu_info = ReadBoundedFile("/proc/cpuinfo"); - evidence.kernel_release = ReadBoundedFile("/proc/sys/kernel/osrelease"); - evidence.dmi = ReadBoundedFile("/sys/class/dmi/id/sys_vendor") + - ReadBoundedFile("/sys/class/dmi/id/product_name") + - ReadBoundedFile("/sys/class/dmi/id/board_vendor"); -#if defined(__ANDROID__) - const std::string android_properties = ReadBoundedFile("/system/build.prop"); - evidence.android_emulator = telemetry_detail::ContainsAscii(android_properties, "ro.kernel.qemu=1") || - telemetry_detail::ContainsAscii(android_properties, "ro.boot.qemu=1") || - telemetry_detail::ContainsAscii(android_properties, "ro.product.manufacturer=genymotion"); -#endif -#elif defined(__APPLE__) && !TARGET_OS_IOS - int is_virtual_machine = 0; - size_t size = sizeof(is_virtual_machine); - evidence.apple_virtual_machine = - sysctlbyname("kern.hv_vmm_present", &is_virtual_machine, &size, nullptr, 0) == 0 && - is_virtual_machine != 0; -#endif - return telemetry_detail::ClassifyHostEnvironment(evidence); -} - -std::string PosixTelemetry::GetProcessName() { -#if defined(__APPLE__) - uint32_t path_size = 1024; - std::vector path(path_size); - if (_NSGetExecutablePath(path.data(), &path_size) != 0) { - path.resize(path_size); - if (_NSGetExecutablePath(path.data(), &path_size) != 0) { - return {}; - } - } - return GetFileName(path.data()); -#elif defined(__linux__) || defined(__ANDROID__) - std::ifstream cmdline("/proc/self/cmdline", std::ios::binary); - std::string first_argument; - if (cmdline && std::getline(cmdline, first_argument, '\0')) { - return GetFileName(first_argument); - } - return {}; -#else - return {}; -#endif -} - -// Get the CPU architecture the binary was compiled for -std::string PosixTelemetry::GetArchitecture() { -#if defined(__x86_64__) - return "x86_64"; -#elif defined(__i386__) - return "x86"; -#elif defined(__aarch64__) - return "arm64"; -#elif defined(__arm__) - return "arm"; -#elif defined(__riscv) - return "riscv"; -#elif defined(__wasm__) - return "wasm"; -#else - return "unknown"; -#endif -} - -// Get total physical memory in MB -int64_t PosixTelemetry::GetTotalMemoryMB() { -#if defined(__APPLE__) - int64_t mem = 0; - size_t len = sizeof(mem); - if (sysctlbyname("hw.memsize", &mem, &len, nullptr, 0) == 0) { - return mem / (1024 * 1024); - } - return 0; - -#elif defined(__linux__) || defined(__ANDROID__) - long pages = sysconf(_SC_PHYS_PAGES); - long page_size = sysconf(_SC_PAGE_SIZE); - if (pages > 0 && page_size > 0) { - return static_cast(pages) * page_size / (1024 * 1024); - } - return 0; - -#else - return 0; -#endif -} - -void PosixTelemetry::EnableTelemetryEvents() const { +void OneDsTelemetry::EnableTelemetryEvents() const { if (!telemetry_disabled_.load(std::memory_order_acquire)) { enabled_.store(true, std::memory_order_release); } } -void PosixTelemetry::DisableTelemetryEvents() const { +void OneDsTelemetry::DisableTelemetryEvents() const { enabled_.store(false, std::memory_order_release); } -void PosixTelemetry::SetLanguageProjection(uint32_t projection) const { - projection_ = projection; +void OneDsTelemetry::SetLanguageProjection(uint32_t projection) const { + RunTelemetryOperation("SetLanguageProjection", [&]() { + projection_ = projection; + if (local_telemetry_ != nullptr) { + local_telemetry_->SetLanguageProjection(projection); + } + }); } -bool PosixTelemetry::IsEnabled() const { +bool OneDsTelemetry::IsEnabled() const { // Reflect actual readiness: the opt-out flag AND a successfully-initialized logger. return enabled_.load(std::memory_order_acquire) && logger_.load(std::memory_order_acquire) != nullptr; } -unsigned char PosixTelemetry::Level() const { +unsigned char OneDsTelemetry::Level() const { // POSIX telemetry has no ETW-style level/keyword control plane; report zero. return 0; } -uint64_t PosixTelemetry::Keyword() const { +uint64_t OneDsTelemetry::Keyword() const { return 0; } -void PosixTelemetry::LogProcessInfo() const { +void OneDsTelemetry::LogProcessInfo() const { RunTelemetryOperation("LogProcessInfo", [&]() { - // Runtime API suppression leaves the uploader live, so ProcessInfo still fires. Full process - // suppression never creates a logger and returns here. - if (logger_.load(std::memory_order_acquire) == nullptr) { + if (!IsEnabled()) { return; } @@ -923,10 +602,6 @@ void PosixTelemetry::LogProcessInfo() const { #endif auto builder = EventBuilder("ProcessInfo", EventPriority::CRITICAL); - if (!PrepareProcessEvent(builder)) { - return; - } - const auto host_environment = GetHostEnvironmentInfo(); builder.AddString("runtimeVersion", ORT_VERSION) #if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) @@ -946,29 +621,29 @@ void PosixTelemetry::LogProcessInfo() const { .AddString("hostEnvironment", host_environment.environment_class) .AddString("environmentDetectionConfidence", host_environment.detection_confidence) .AddString("deviceIdScope", host_environment.device_id_scope) - .AddInt32("processorCount", GetProcessorCount()) + .AddInt32("processorCount", telemetry_internal::GetProcessorCount()) .AddInt64("totalMemoryMB", GetTotalMemoryMB()); LogEventAsync(builder.Build()); }); } -void PosixTelemetry::LogSessionCreationStart(uint32_t session_id) const { +void OneDsTelemetry::LogSessionCreationStart(uint32_t session_id) const { // Start/stop markers are retained by TraceLogging. 1DS completion events carry local durations. (void)session_id; } -void PosixTelemetry::LogEvaluationStop(uint32_t session_id) const { +void OneDsTelemetry::LogEvaluationStop(uint32_t session_id) const { // Per-run start/stop markers are useful for ETW tracing, but RuntimePerf already aggregates every // successful run for 1DS without putting synchronous telemetry work on the inference hot path. (void)session_id; } -void PosixTelemetry::LogEvaluationStart(uint32_t session_id) const { +void OneDsTelemetry::LogEvaluationStart(uint32_t session_id) const { (void)session_id; } -void PosixTelemetry::LogSessionCreation( +void OneDsTelemetry::LogSessionCreation( uint32_t session_id, int64_t ir_version, const std::string& model_producer_name, const std::string& model_producer_version, @@ -987,15 +662,22 @@ void PosixTelemetry::LogSessionCreation( const std::string& ep_versions, bool use_fp16, bool captureState) const { RunTelemetryOperation("LogSessionCreation", [&]() { + if (captureState) { + if (local_telemetry_ != nullptr && enabled_.load(std::memory_order_acquire) && + !telemetry_disabled_.load(std::memory_order_acquire)) { + local_telemetry_->LogSessionCreation( + session_id, ir_version, model_producer_name, model_producer_version, model_domain, + domain_to_version_map, model_file_name, model_graph_name, model_weight_type, + model_graph_hash, model_weight_hash, model_metadata, loadedFrom, execution_provider_ids, + hardware_device_types, hardware_vendor_ids, ep_versions, use_fp16, true); + } + return; + } if (!IsEnabled()) { return; } - // captureState is currently only triggered on Windows via ETW's EVENT_CONTROL_CODE_CAPTURE_STATE callback - // (LogAllSessions). Kept here for future compatibility if a similar mechanism is added for POSIX. - std::string event_name = captureState ? "SessionCreation_CaptureState" : "SessionCreation"; - - auto builder = EventBuilder(std::move(event_name), EventPriority::CRITICAL); + auto builder = EventBuilder("SessionCreation", EventPriority::CRITICAL); if (!PrepareSampledEvent(builder, session_id)) { return; } @@ -1005,13 +687,12 @@ void PosixTelemetry::LogSessionCreation( .AddString("modelProducerName", model_producer_name) .AddString("modelProducerVersion", model_producer_version) .AddString("modelDomain", model_domain) - .AddIntMap("domainToVersionMap", domain_to_version_map) + .AddMap("domainToVersionMap", domain_to_version_map) .AddString("modelFileName", model_file_name) .AddString("modelGraphName", model_graph_name) .AddString("modelWeightType", model_weight_type) .AddString("modelGraphHash", model_graph_hash) .AddString("modelWeightHash", model_weight_hash) - .AddStringMap("modelMetaData", model_metadata) .AddString("loadedFrom", loadedFrom) .AddStringList("executionProviderIds", execution_provider_ids) .AddString("hardwareDeviceTypes", hardware_device_types) @@ -1023,7 +704,7 @@ void PosixTelemetry::LogSessionCreation( }); } -void PosixTelemetry::LogCompileModelStart( +void OneDsTelemetry::LogCompileModelStart( uint32_t session_id, const std::string& input_source, const std::string& output_target, @@ -1055,7 +736,7 @@ void PosixTelemetry::LogCompileModelStart( }); } -void PosixTelemetry::LogCompileModelComplete( +void OneDsTelemetry::LogCompileModelComplete( uint32_t session_id, bool success, uint32_t error_code, @@ -1081,7 +762,7 @@ void PosixTelemetry::LogCompileModelComplete( }); } -void PosixTelemetry::LogRuntimeError( +void OneDsTelemetry::LogRuntimeError( uint32_t session_id, const common::Status& status, const char* file, const char* function, uint32_t line) const { RunTelemetryOperation("LogRuntimeError", [&]() { @@ -1091,16 +772,16 @@ void PosixTelemetry::LogRuntimeError( // __FILE__ may be an absolute build path that embeds developer/build directory names; emit only // the basename so remote telemetry doesn't leak usernames or local paths. - std::string_view file_view = file ? std::string_view{file} : std::string_view{}; + std::string_view file_view = telemetry_detail::TelemetryCStringView(file, telemetry_detail::kMaxTelemetryPathBytes); + if (file_view.size() > telemetry_detail::kMaxTelemetryPathBytes) { + file_view = {}; + } if (const size_t slash = file_view.find_last_of("/\\"); slash != std::string_view::npos) { file_view.remove_prefix(slash + 1); } const std::string scrubbed_file = ScrubStringForTelemetry(file_view); auto builder = EventBuilder("RuntimeError", EventPriority::HIGH); - if (!PrepareHighVolumeEvent(builder, session_id)) { - return; - } auto event = builder.AddUInt32("sessionId", session_id) .AddInt32("errorCode", static_cast(status.Code())) .AddInt32("errorCategory", static_cast(status.Category())) @@ -1114,7 +795,7 @@ void PosixTelemetry::LogRuntimeError( }); } -void PosixTelemetry::LogRuntimeInferenceError(uint32_t session_id, const common::Status& status, +void OneDsTelemetry::LogRuntimeInferenceError(uint32_t session_id, const common::Status& status, const std::string& ep_versions, const std::string& ep_device_types) const { RunTelemetryOperation("LogRuntimeInferenceError", [&]() { @@ -1123,9 +804,6 @@ void PosixTelemetry::LogRuntimeInferenceError(uint32_t session_id, const common: } auto builder = EventBuilder("RuntimeInferenceError", EventPriority::HIGH); - if (!PrepareHighVolumeEvent(builder, session_id)) { - return; - } auto event = builder.AddUInt32("sessionId", session_id) .AddInt32("errorCode", static_cast(status.Code())) .AddInt32("errorCategory", static_cast(status.Category())) @@ -1139,7 +817,7 @@ void PosixTelemetry::LogRuntimeInferenceError(uint32_t session_id, const common: }); } -void PosixTelemetry::LogRuntimePerf( +void OneDsTelemetry::LogRuntimePerf( uint32_t session_id, uint32_t total_runs_since_last, int64_t total_run_duration_since_last, const std::unordered_map& duration_per_batch_size) const { @@ -1162,26 +840,7 @@ void PosixTelemetry::LogRuntimePerf( }); } -void PosixTelemetry::LogExecutionProviderEvent(LUID* adapterLuid) const { - RunTelemetryOperation("LogExecutionProviderEvent", [&]() { - // Not applicable for non-Windows platforms (LUID is Windows-specific) - (void)adapterLuid; - }); -} - -void PosixTelemetry::LogDriverInfoEvent( - const std::string_view device_class, - const std::wstring_view& driver_names, - const std::wstring_view& driver_versions) const { - RunTelemetryOperation("LogDriverInfoEvent", [&]() { - // Not applicable for non-Windows platforms - (void)device_class; - (void)driver_names; - (void)driver_versions; - }); -} - -void PosixTelemetry::LogAutoEpSelection( +void OneDsTelemetry::LogAutoEpSelection( uint32_t session_id, const std::string& selection_policy, const std::vector& requested_execution_provider_ids, const std::vector& available_execution_provider_ids) const { @@ -1204,11 +863,22 @@ void PosixTelemetry::LogAutoEpSelection( }); } -void PosixTelemetry::LogModelLoadStart(uint32_t session_id) const { +void OneDsTelemetry::LogModelLoadStart(uint32_t session_id) const { (void)session_id; } -void PosixTelemetry::LogModelLoadEnd(uint32_t session_id, const common::Status& status, +void OneDsTelemetry::LogProviderOptions( + const std::string& provider_id, const std::string& provider_options, bool capture_state) const { + RunTelemetryOperation("LogProviderOptions", [&]() { + // Custom provider values are not safe to upload, even after filesystem-path redaction. + if (local_telemetry_ != nullptr && enabled_.load(std::memory_order_acquire) && + !telemetry_disabled_.load(std::memory_order_acquire)) { + local_telemetry_->LogProviderOptions(provider_id, provider_options, capture_state); + } + }); +} + +void OneDsTelemetry::LogModelLoadEnd(uint32_t session_id, const common::Status& status, int64_t duration_us) const { RunTelemetryOperation("LogModelLoadEnd", [&]() { if (!IsEnabled()) { @@ -1231,7 +901,7 @@ void PosixTelemetry::LogModelLoadEnd(uint32_t session_id, const common::Status& }); } -void PosixTelemetry::LogSessionCreationEnd(uint32_t session_id, const common::Status& status, +void OneDsTelemetry::LogSessionCreationEnd(uint32_t session_id, const common::Status& status, int64_t duration_us) const { RunTelemetryOperation("LogSessionCreationEnd", [&]() { if (!IsEnabled()) { @@ -1254,7 +924,7 @@ void PosixTelemetry::LogSessionCreationEnd(uint32_t session_id, const common::St }); } -void PosixTelemetry::LogEpDeviceUsage( +void OneDsTelemetry::LogEpDeviceUsage( uint32_t session_id, const std::string& ep_type, const std::string& hardware_device_type, @@ -1292,11 +962,11 @@ void PosixTelemetry::LogEpDeviceUsage( }); } -void PosixTelemetry::LogRegisterEpLibraryStart(const std::string& registration_name) const { +void OneDsTelemetry::LogRegisterEpLibraryStart(const std::string& registration_name) const { (void)registration_name; } -void PosixTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_name, +void OneDsTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_name, const common::Status& status, int64_t duration_us) const { RunTelemetryOperation("LogRegisterEpLibraryEnd", [&]() { @@ -1305,7 +975,7 @@ void PosixTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_nam } auto builder = EventBuilder("RegisterEpLibraryEnd", EventPriority::NORMAL); - if (!PrepareProcessEvent(builder)) { + if (!PrepareSampledProcessEvent(builder)) { return; } auto event = builder.AddString("registrationName", registration_name) @@ -1320,7 +990,7 @@ void PosixTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_nam }); } -void PosixTelemetry::LogRegisterEpLibraryWithLibPath(const std::string& registration_name, +void OneDsTelemetry::LogRegisterEpLibraryWithLibPath(const std::string& registration_name, const std::string& lib_path) const { RunTelemetryOperation("LogRegisterEpLibraryWithLibPath", [&]() { if (!IsEnabled()) { @@ -1329,7 +999,7 @@ void PosixTelemetry::LogRegisterEpLibraryWithLibPath(const std::string& registra const std::string scrubbed_lib_path = ScrubStringForTelemetry(lib_path); auto builder = EventBuilder("RegisterEpLibraryWithLibPath", EventPriority::NORMAL); - if (!PrepareProcessEvent(builder)) { + if (!PrepareSampledProcessEvent(builder)) { return; } auto event = builder.AddString("registrationName", registration_name) diff --git a/onnxruntime/core/platform/posix/telemetry.h b/onnxruntime/core/platform/telemetry_1ds.h similarity index 87% rename from onnxruntime/core/platform/posix/telemetry.h rename to onnxruntime/core/platform/telemetry_1ds.h index 1691f607d423d..2d0eabfd48dc2 100644 --- a/onnxruntime/core/platform/posix/telemetry.h +++ b/onnxruntime/core/platform/telemetry_1ds.h @@ -5,6 +5,7 @@ #include "core/platform/telemetry.h" #include "core/platform/telemetry_environment.h" +#include "core/platform/telemetry_no_throw.h" #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include // Forward declarations of 1DS SDK types @@ -32,12 +34,14 @@ namespace onnxruntime { * as the original WindowsTelemetry to provide consistent telemetry across all platforms. * * Configuration: - * - Telemetry is opt-in via build flags + * - Supported native build-driver targets enable telemetry unless --no_telemetry is passed. */ -class PosixTelemetry : public Telemetry { +class OneDsTelemetry : public Telemetry { public: - PosixTelemetry(); - ~PosixTelemetry() override; + OneDsTelemetry(); + // The local diagnostic provider must outlive this instance. + explicit OneDsTelemetry(const Telemetry& local_telemetry); + ~OneDsTelemetry() override; void EnableTelemetryEvents() const override; void DisableTelemetryEvents() const override; @@ -105,6 +109,10 @@ class PosixTelemetry : public Telemetry { const std::vector& requested_execution_provider_ids, const std::vector& available_execution_provider_ids) const override; + void LogProviderOptions(const std::string& provider_id, + const std::string& provider_options_string, + bool capture_state) const override; + void LogModelLoadStart(uint32_t session_id) const override; void LogModelLoadEnd(uint32_t session_id, const common::Status& status, int64_t duration_us) const override; @@ -132,6 +140,17 @@ class PosixTelemetry : public Telemetry { const std::string& lib_path) const override; private: + static void ReportFailure(const char* operation_name, const char* message); + + template + static void RunTelemetryOperation(const char* operation_name, Operation&& operation) noexcept { + telemetry_internal::RunTelemetryOperationNoThrow( + std::forward(operation), + [operation_name](const char* message) { ReportFailure(operation_name, message); }); + } + + const Telemetry* local_telemetry_ = nullptr; + // Initialize telemetry SDK logger void Initialize(); @@ -153,10 +172,10 @@ class PosixTelemetry : public Telemetry { // Safe async event logging. void LogEventAsync(::Microsoft::Applications::Events::EventProperties&& props) const; - // All shared telemetry state below is static: PosixTelemetry is a process-wide singleton whose + // All shared telemetry state below is static: OneDsTelemetry is a process-wide singleton whose // lifetime is gated by global_register_count_ (the first instance initializes the SDK, the last // tears it down), matching WindowsTelemetry. Keeping the SDK handles and state static ensures a - // single owner regardless of how many PosixTelemetry objects exist. + // single owner regardless of how many OneDsTelemetry objects exist. // Mutex for thread-safe init/shutdown of the shared SDK state. static std::shared_mutex mutex_; diff --git a/onnxruntime/core/platform/telemetry_1ds_platform.h b/onnxruntime/core/platform/telemetry_1ds_platform.h new file mode 100644 index 0000000000000..2cfdbd99dd0a8 --- /dev/null +++ b/onnxruntime/core/platform/telemetry_1ds_platform.h @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include + +namespace onnxruntime::telemetry_internal { + +const std::string& GetAppSessionGuid(); +const char* GetDefaultEncodedToken(); +int32_t GetProcessorCount(); +std::string GetCachePath(const std::string& directory); + +} // namespace onnxruntime::telemetry_internal diff --git a/onnxruntime/core/platform/posix/telemetry_context.h b/onnxruntime/core/platform/telemetry_context.h similarity index 91% rename from onnxruntime/core/platform/posix/telemetry_context.h rename to onnxruntime/core/platform/telemetry_context.h index cf36eff9960e5..012b8a3e7b989 100644 --- a/onnxruntime/core/platform/posix/telemetry_context.h +++ b/onnxruntime/core/platform/telemetry_context.h @@ -6,6 +6,7 @@ #include #include +#include "core/platform/telemetry_strings.h" namespace onnxruntime::telemetry_internal { // These PAL-populated fields are not needed by ORT. ext.os.locale is not populated by this SDK path. @@ -35,7 +36,7 @@ void SuppressUnneededCommonContext(SemanticContext& context) { template void SetApplicationNameFromProcessName(SemanticContext& context, const std::string& process_name) { if (!process_name.empty()) { - context.SetCommonField("AppInfo.Name", process_name); + context.SetCommonField("AppInfo.Name", telemetry_detail::BoundedTelemetryString(process_name)); } } diff --git a/onnxruntime/core/platform/telemetry_environment.h b/onnxruntime/core/platform/telemetry_environment.h index fcf4c2aaeaf77..21149318f1b2d 100644 --- a/onnxruntime/core/platform/telemetry_environment.h +++ b/onnxruntime/core/platform/telemetry_environment.h @@ -6,11 +6,15 @@ #include #include #include +#include #include #include +#include "core/platform/telemetry_strings.h" + #ifdef _WIN32 #include +#include "core/common/path_string.h" #endif namespace onnxruntime { @@ -35,28 +39,40 @@ inline constexpr std::array kCiEnvironmentVariableNames = { "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", // Azure DevOps }; -// Read an environment variable, returning an empty string when unset. -inline std::string GetTelemetryEnv(const char* name) { +// Unset/empty values succeed; oversized, unreadable, or unstable values return nullopt. +inline std::optional ReadTelemetryEnvironment( + const char* name, size_t max_bytes = kMaxTelemetryProbeBytes) { #ifdef _WIN32 - DWORD required_size = ::GetEnvironmentVariableA(name, nullptr, 0); - while (required_size != 0) { - std::string value(required_size, '\0'); - const DWORD written = ::GetEnvironmentVariableA(name, value.data(), required_size); + const auto wide_name = ToWideString(name); + ::SetLastError(ERROR_SUCCESS); + DWORD required_size = ::GetEnvironmentVariableW(wide_name.c_str(), nullptr, 0); + for (int attempt = 0; attempt < 3; ++attempt) { + if (required_size == 0) { + const DWORD error = ::GetLastError(); + if (error != ERROR_SUCCESS && error != ERROR_ENVVAR_NOT_FOUND) return std::nullopt; + return std::string{}; + } + if (required_size - 1 > max_bytes) return std::nullopt; + std::wstring value(required_size, L'\0'); + ::SetLastError(ERROR_SUCCESS); + const DWORD written = ::GetEnvironmentVariableW(wide_name.c_str(), value.data(), required_size); if (written == 0) { - return {}; + required_size = 0; + continue; } if (written < required_size) { value.resize(written); - return value; + if (TelemetryWideStringView(value, max_bytes).size() != value.size()) return std::nullopt; + return ToUTF8String(value); } - - // The value grew between calls. Windows returns its new required size, including the null. required_size = written; } - return {}; + return std::nullopt; #else const char* value = std::getenv(name); - return value != nullptr ? std::string(value) : std::string(); + const auto prefix = TelemetryCStringView(value, max_bytes); + if (prefix.size() > max_bytes) return std::nullopt; + return std::string(prefix); #endif } @@ -73,7 +89,7 @@ inline std::string_view TrimAscii(std::string_view s) { } inline std::string ToLowerAscii(std::string_view s) { - std::string out(s); + std::string out(TelemetryStringView(s, kMaxTelemetryProbeBytes)); for (char& c : out) { c = static_cast(std::tolower(static_cast(c))); } @@ -83,6 +99,7 @@ inline std::string ToLowerAscii(std::string_view s) { // A CI variable counts as present unless its (trimmed) value is empty or an explicit falsey token, so // that a runner exporting e.g. CI=false does not trip detection. inline bool IsTruthyCiValue(std::string_view value) { + if (value.size() > kMaxTelemetryProbeBytes) return true; const std::string v = ToLowerAscii(TrimAscii(value)); return !v.empty() && v != "0" && v != "false" && v != "no" && v != "off"; } @@ -119,8 +136,9 @@ inline bool ContainsAscii(std::string_view haystack, std::string_view needle) { // Classifies only positive evidence. "undetected" deliberately does not claim bare metal. inline HostEnvironmentInfo ClassifyHostEnvironment(const HostEnvironmentEvidence& evidence) { - const std::string container_name = ToLowerAscii(TrimAscii(evidence.systemd_container)); - const std::string combined_container_evidence = ToLowerAscii(evidence.cgroup + " " + container_name); + const std::string container_name = ToLowerAscii(TrimAscii(TelemetryStringView(evidence.systemd_container))); + const std::string combined_container_evidence = + ToLowerAscii(TelemetryStringView(evidence.cgroup, kMaxTelemetryProbeBytes)) + " " + container_name; const char* container_type = "none"; int container_confidence = 0; @@ -239,7 +257,8 @@ inline HostEnvironmentInfo ClassifyHostEnvironment(const HostEnvironmentEvidence // telemetry providers suppress all telemetry when this holds, matching Olive and Foundry Local. inline bool IsRunningInCI() { for (const char* name : telemetry_detail::kCiEnvironmentVariableNames) { - if (telemetry_detail::IsTruthyCiValue(telemetry_detail::GetTelemetryEnv(name))) { + const auto value = telemetry_detail::ReadTelemetryEnvironment(name); + if (!value || telemetry_detail::IsTruthyCiValue(*value)) { return true; } } @@ -250,15 +269,18 @@ inline bool IsRunningInCI() { // before creating any environment, so local (non-CI) test runs never initialize the telemetry uploader // or emit events. This is an internal harness signal, not a user-facing opt-out. inline bool IsRunningUnitTests() { - return telemetry_detail::IsTruthyCiValue(telemetry_detail::GetTelemetryEnv("ORT_RUNNING_UNIT_TESTS")); + const auto value = telemetry_detail::ReadTelemetryEnvironment("ORT_RUNNING_UNIT_TESTS"); + return !value || telemetry_detail::IsTruthyCiValue(*value); } // True if ORT_DISABLE_TELEMETRY is set to a truthy value (1/true/yes/on/y, case-insensitive). // The POSIX 1DS provider latches this full opt-out during initialization. Windows ETW retains its // separate API/trace-session control model and does not consult this environment variable. inline bool IsTelemetryDisabledByEnvironment() { + const auto input = telemetry_detail::ReadTelemetryEnvironment("ORT_DISABLE_TELEMETRY"); + if (!input) return true; const std::string value = telemetry_detail::ToLowerAscii( - telemetry_detail::TrimAscii(telemetry_detail::GetTelemetryEnv("ORT_DISABLE_TELEMETRY"))); + telemetry_detail::TrimAscii(*input)); return value == "1" || value == "true" || value == "yes" || value == "on" || value == "y"; } diff --git a/onnxruntime/core/platform/telemetry_guid.h b/onnxruntime/core/platform/telemetry_guid.h index c176f6de526e0..c8e280067f17d 100644 --- a/onnxruntime/core/platform/telemetry_guid.h +++ b/onnxruntime/core/platform/telemetry_guid.h @@ -7,9 +7,26 @@ #include #include #include +#include namespace onnxruntime { +inline bool IsValidGuid(std::string_view value) { + if (value.size() != 36) { + return false; + } + for (size_t i = 0; i < value.size(); ++i) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (value[i] != '-') return false; + } else if (!((value[i] >= '0' && value[i] <= '9') || + (value[i] >= 'a' && value[i] <= 'f') || + (value[i] >= 'A' && value[i] <= 'F'))) { + return false; + } + } + return true; +} + // Generate a random RFC 4122 version-4 UUID formatted as // "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" (36 lowercase-hex characters, hyphen-separated). // diff --git a/onnxruntime/core/platform/posix/telemetry_no_throw.h b/onnxruntime/core/platform/telemetry_no_throw.h similarity index 100% rename from onnxruntime/core/platform/posix/telemetry_no_throw.h rename to onnxruntime/core/platform/telemetry_no_throw.h diff --git a/onnxruntime/core/platform/telemetry_redaction.h b/onnxruntime/core/platform/telemetry_redaction.h index a772d63787ef6..1ae656d1dec08 100644 --- a/onnxruntime/core/platform/telemetry_redaction.h +++ b/onnxruntime/core/platform/telemetry_redaction.h @@ -8,10 +8,9 @@ #include #include -namespace onnxruntime { +#include "core/platform/telemetry_strings.h" -// Maximum transmitted telemetry-string length, applied after scrubbing to bound telemetry payload size. -inline constexpr size_t kMaxTelemetryStringLength = 40'960; +namespace onnxruntime { namespace telemetry_detail { @@ -90,18 +89,6 @@ inline size_t FindPathAnchor(std::string_view s) { return std::string_view::npos; } -inline void TruncateUtf8AtBoundary(std::string& s, size_t max_length) { - if (s.size() <= max_length) { - return; - } - - size_t end = max_length; - while (end > 0 && (static_cast(s[end]) & 0xC0) == 0x80) { - --end; - } - s.resize(end); -} - } // namespace telemetry_detail // Scrub filesystem paths out of a free-text telemetry string before transmission and cap its length. @@ -110,19 +97,31 @@ inline void TruncateUtf8AtBoundary(std::string& s, size_t max_length) { // frequently contain spaces, a per-token classifier is bypassable; instead everything from the first // path anchor to the end of the message is replaced with a single "[path]" placeholder, so no portion // of the path -- including a space-separated user name -- can survive. -inline std::string ScrubStringForTelemetry(std::string_view msg) { - const size_t anchor = telemetry_detail::FindPathAnchor(msg); - std::string out; - if (anchor == std::string_view::npos) { - out.assign(msg); - } else { - out.assign(msg.substr(0, anchor)); - out += "[path]"; +inline std::string ScrubStringForTelemetry(std::string_view msg, bool input_truncated = false) { + const bool truncated = input_truncated || msg.size() > telemetry_detail::kMaxTelemetryProbeBytes; + msg = msg.substr(0, telemetry_detail::kMaxTelemetryProbeBytes); + size_t anchor = telemetry_detail::FindPathAnchor(msg); + if (truncated) { + // An unseen suffix can complete a path anchor or an unfinished first path segment. + size_t uncertain = msg.find_first_of("/\\"); + if (uncertain == std::string_view::npos) uncertain = msg.size(); + while (uncertain > 0 && !std::isspace(static_cast(msg[uncertain - 1])) && + msg[uncertain - 1] != '"' && msg[uncertain - 1] != '\'') { + --uncertain; + } + anchor = (std::min)(anchor, uncertain); } - if (out.size() > kMaxTelemetryStringLength) { - telemetry_detail::TruncateUtf8AtBoundary(out, kMaxTelemetryStringLength); + if (anchor == std::string_view::npos) { + return telemetry_detail::BoundedTelemetryString(msg); } + std::string out = telemetry_detail::BoundedTelemetryString(msg.substr(0, anchor)); + telemetry_detail::AppendTelemetryString(out, "[path]"); return out; } +inline std::string ScrubStringForTelemetry(const char* msg) { + return ScrubStringForTelemetry( + telemetry_detail::TelemetryCStringView(msg, telemetry_detail::kMaxTelemetryProbeBytes)); +} + } // namespace onnxruntime diff --git a/onnxruntime/core/platform/posix/telemetry_sampling.h b/onnxruntime/core/platform/telemetry_sampling.h similarity index 91% rename from onnxruntime/core/platform/posix/telemetry_sampling.h rename to onnxruntime/core/platform/telemetry_sampling.h index 8168bf4a40175..684e844defcdf 100644 --- a/onnxruntime/core/platform/posix/telemetry_sampling.h +++ b/onnxruntime/core/platform/telemetry_sampling.h @@ -12,14 +12,14 @@ namespace onnxruntime::telemetry_internal { // 1DS popSample is metadata only; ShouldSampleSession performs the actual client-side sampling. inline constexpr double kModelSessionSampleRatePercent = 1.0; inline constexpr double kHighVolumeEventSampleRatePercent = 1.0; -inline constexpr double kProcessEventSampleRatePercent = 1.0; +inline constexpr double kOtherProcessEventSampleRatePercent = 1.0; static_assert(kModelSessionSampleRatePercent >= 0.0 && kModelSessionSampleRatePercent <= 100.0); static_assert(kHighVolumeEventSampleRatePercent >= 0.0 && kHighVolumeEventSampleRatePercent <= 100.0); -static_assert(kProcessEventSampleRatePercent >= 0.0 && - kProcessEventSampleRatePercent <= 100.0); +static_assert(kOtherProcessEventSampleRatePercent >= 0.0 && + kOtherProcessEventSampleRatePercent <= 100.0); inline uint64_t HashSamplingKey(std::string_view app_session_guid, uint32_t session_id) { uint64_t hash = 14695981039346656037ULL; diff --git a/onnxruntime/core/platform/posix/telemetry_sha256.cc b/onnxruntime/core/platform/telemetry_sha256.cc similarity index 99% rename from onnxruntime/core/platform/posix/telemetry_sha256.cc rename to onnxruntime/core/platform/telemetry_sha256.cc index 36826ea380061..2457ef6031be3 100644 --- a/onnxruntime/core/platform/posix/telemetry_sha256.cc +++ b/onnxruntime/core/platform/telemetry_sha256.cc @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/platform/posix/telemetry_sha256.h" +#include "core/platform/telemetry_sha256.h" #include #include diff --git a/onnxruntime/core/platform/posix/telemetry_sha256.h b/onnxruntime/core/platform/telemetry_sha256.h similarity index 100% rename from onnxruntime/core/platform/posix/telemetry_sha256.h rename to onnxruntime/core/platform/telemetry_sha256.h diff --git a/onnxruntime/core/platform/telemetry_strings.h b/onnxruntime/core/platform/telemetry_strings.h new file mode 100644 index 0000000000000..fd4d049e4e265 --- /dev/null +++ b/onnxruntime/core/platform/telemetry_strings.h @@ -0,0 +1,275 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace onnxruntime { + +inline constexpr size_t kMaxTelemetryStringLength = 1024; + +namespace telemetry_detail { + +inline constexpr size_t kMaxTelemetryCollectionEntries = 128; +inline constexpr size_t kMaxTelemetryProbeBytes = 16 * 1024; +inline constexpr size_t kMaxTelemetryPathBytes = 4096; + +inline std::string_view TelemetryStringView(std::string_view value, + size_t max_bytes = kMaxTelemetryStringLength) { + if (value.size() <= max_bytes) { + return value; + } + size_t end = max_bytes; + while (end > 0 && (static_cast(value[end]) & 0xC0) == 0x80) { + --end; + } + return value.substr(0, end); +} + +inline std::string_view TelemetryCStringView(const char* value, size_t max_bytes = kMaxTelemetryStringLength) { + if (value == nullptr) { + return {}; + } + size_t length = 0; + while (length <= max_bytes && value[length] != '\0') { + ++length; + } + return std::string_view(value, length); +} + +inline std::string_view TelemetryStringView(const char* value, + size_t max_bytes = kMaxTelemetryStringLength) { + return TelemetryStringView(TelemetryCStringView(value, max_bytes), max_bytes); +} + +// Invalid UTF-8 bytes become '?'; a valid codepoint that cannot fit is omitted in full. +inline bool AppendTelemetryString(std::string& output, std::string_view value, + size_t max_bytes = kMaxTelemetryStringLength) { + if (output.size() > max_bytes) { + output.resize(TelemetryStringView(std::string_view(output), max_bytes).size()); + } + size_t offset = 0; + while (offset < value.size() && output.size() < max_bytes) { + const auto lead = static_cast(value[offset]); + const size_t width = lead < 0x80 ? 1 : lead >= 0xC2 && lead <= 0xDF ? 2 + : lead >= 0xE0 && lead <= 0xEF ? 3 + : lead >= 0xF0 && lead <= 0xF4 ? 4 + : 0; + bool valid = width != 0 && width <= value.size() - offset; + for (size_t i = 1; valid && i < width; ++i) { + const auto byte = static_cast(value[offset + i]); + valid = (byte & 0xC0) == 0x80; + if (i == 1) { + valid = valid && !(lead == 0xE0 && byte < 0xA0) && + !(lead == 0xED && byte >= 0xA0) && + !(lead == 0xF0 && byte < 0x90) && + !(lead == 0xF4 && byte >= 0x90); + } + } + if (!valid) { + output += '?'; + ++offset; + } else { + if (width > max_bytes - output.size()) break; + output.append(value.data() + offset, width); + offset += width; + } + } + return offset == value.size(); +} + +// Commit a complete comma-separated row or leave every summary unchanged. +template +bool AppendTelemetryRow(const std::array& summaries, + const std::array& values, bool first) { + std::array staged; + for (size_t i = 0; i < Columns; ++i) { + staged[i] = *summaries[i]; + if ((!first && !AppendTelemetryString(staged[i], ",")) || + !AppendTelemetryString(staged[i], values[i])) { + return false; + } + } + for (size_t i = 0; i < Columns; ++i) { + summaries[i]->swap(staged[i]); + } + return true; +} + +inline std::string BoundedTelemetryString(std::string_view value, + size_t max_bytes = kMaxTelemetryStringLength) { + std::string result; + result.reserve((std::min)(value.size(), max_bytes)); + AppendTelemetryString(result, value, max_bytes); + return result; +} + +inline std::string BoundedTelemetryString(const char* value, + size_t max_bytes = kMaxTelemetryStringLength) { + if (max_bytes == 0) return {}; + // Look past the output boundary far enough to distinguish a full codepoint from malformed input. + const size_t probe_bytes = max_bytes + (std::min)(size_t{3}, (std::numeric_limits::max)() - max_bytes); + return BoundedTelemetryString(TelemetryCStringView(value, probe_bytes), max_bytes); +} + +// JNI encodes supplementary characters as two three-byte sequences, and NUL as two bytes. +template +size_t TelemetryUnicodePrefixLength( + const Char* value, size_t length, size_t max_bytes = kMaxTelemetryStringLength, bool modified_utf8 = false) { + size_t bytes = 0; + size_t end = 0; + while (end < length) { + uint32_t codepoint = static_cast(value[end]); + size_t width = 1; + if constexpr (sizeof(Char) == 2) { + if (codepoint >= 0xD800 && codepoint <= 0xDBFF && end + 1 < length) { + const uint32_t low = static_cast(value[end + 1]); + if (low >= 0xDC00 && low <= 0xDFFF) { + codepoint = 0x10000 + ((codepoint - 0xD800) << 10) + low - 0xDC00; + width = 2; + } + } + } + const size_t encoded_bytes = modified_utf8 && width == 2 ? 6 + : modified_utf8 && codepoint == 0 ? 2 + : codepoint <= 0x7F ? 1 + : codepoint <= 0x7FF ? 2 + : codepoint <= 0xFFFF ? 3 + : 4; + if (encoded_bytes > max_bytes - bytes) { + break; + } + bytes += encoded_bytes; + end += width; + } + return end; +} + +template +std::basic_string_view TelemetryUnicodeStringView( + std::basic_string_view value, size_t max_bytes = kMaxTelemetryStringLength, bool modified_utf8 = false) { + return value.substr(0, TelemetryUnicodePrefixLength(value.data(), value.size(), max_bytes, modified_utf8)); +} + +inline std::wstring_view TelemetryWideStringView(std::wstring_view value, + size_t max_bytes = kMaxTelemetryStringLength) { + return TelemetryUnicodeStringView(value, max_bytes); +} + +inline std::wstring BoundedTelemetryWideString(std::wstring_view value) { + return std::wstring(TelemetryWideStringView(value)); +} + +inline std::wstring_view TelemetryWideStringView(const wchar_t* value) { + if (value == nullptr) { + return {}; + } + size_t length = 0; + while (length <= kMaxTelemetryStringLength && value[length] != L'\0') { + ++length; + } + return TelemetryWideStringView(std::wstring_view(value, length)); +} + +inline std::string_view TelemetryStringValue(std::string_view value) { + // Preserve the suffix so the aggregate append can report truncation to path redaction. + return value; +} + +template , int> = 0> +std::string TelemetryStringValue(T value) { + return std::to_string(value); +} + +template +std::string JoinTelemetryStrings(const Range& values) { + std::string output; + size_t count = 0; + for (const auto& value : values) { + if (count++ == kMaxTelemetryCollectionEntries || + (count > 1 && !AppendTelemetryString(output, ",")) || + !AppendTelemetryString(output, value) || output.size() == kMaxTelemetryStringLength) { + break; + } + } + return output; +} + +template +std::string FormatTelemetryMap(const Map& values, std::string_view separator = ",", + std::string_view key_separator = "=", bool* truncated = nullptr) { + if (truncated != nullptr) *truncated = false; + std::array entries{}; + size_t count = 0; + for (const auto& entry : values) { + if (count == entries.size()) { + if (truncated != nullptr) *truncated = true; + break; + } + entries[count++] = &entry; + } + std::sort(entries.begin(), entries.begin() + count, [](const auto* lhs, const auto* rhs) { + if constexpr (std::is_arithmetic_v) { + return lhs->first < rhs->first; + } else { + return TelemetryStringView(std::string_view(lhs->first)) < TelemetryStringView(std::string_view(rhs->first)); + } + }); + std::string output; + for (size_t i = 0; i < count; ++i) { + if ((i != 0 && !AppendTelemetryString(output, separator)) || + !AppendTelemetryString(output, TelemetryStringValue(entries[i]->first)) || + !AppendTelemetryString(output, key_separator) || + !AppendTelemetryString(output, TelemetryStringValue(entries[i]->second)) || + output.size() == kMaxTelemetryStringLength) { + if (truncated != nullptr) *truncated = true; + break; + } + } + return output; +} + +// TraceLogging macros retain pointers across statements; temporaries cannot own these values. +class TelemetryStrings { + public: + const char* Utf8(std::string_view value) { + strings_.push_back(BoundedTelemetryString(value)); + return strings_.back().c_str(); + } + + const char* Utf8(const char* value) { + strings_.push_back(BoundedTelemetryString(value)); + return strings_.back().c_str(); + } + + const wchar_t* Wide(std::wstring_view value) { + wide_strings_.push_back(BoundedTelemetryWideString(value)); + return wide_strings_.back().c_str(); + } + + const wchar_t* Wide(const wchar_t* value) { + return Wide(TelemetryWideStringView(value)); + } + + TelemetryStrings() = default; + TelemetryStrings(const TelemetryStrings&) = delete; + TelemetryStrings& operator=(const TelemetryStrings&) = delete; + TelemetryStrings(TelemetryStrings&&) = delete; + TelemetryStrings& operator=(TelemetryStrings&&) = delete; + + private: + std::deque strings_; + std::deque wide_strings_; +}; + +} // namespace telemetry_detail +} // namespace onnxruntime diff --git a/onnxruntime/core/platform/windows/device_id.cc b/onnxruntime/core/platform/windows/device_id.cc new file mode 100644 index 0000000000000..840a8dd1eadb9 --- /dev/null +++ b/onnxruntime/core/platform/windows/device_id.cc @@ -0,0 +1,377 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/platform/device_id.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/platform/telemetry_guid.h" +#include "core/platform/telemetry_environment.h" +#include "core/platform/telemetry_strings.h" +#include "core/common/logging/logging.h" +#include "core/common/path_string.h" + +namespace onnxruntime { +namespace { + +constexpr char kDeviceIdRegistryKey[] = "SOFTWARE\\Microsoft\\DeveloperTools\\.onnxruntime"; +constexpr char kDeviceIdRegistryValue[] = "deviceid"; +constexpr wchar_t kDeviceIdDirectory[] = L"Microsoft\\DeveloperTools\\.onnxruntime"; +constexpr size_t kMaxDeviceIdSize = 256; + +enum class RegistryReadResult { + Missing, + Valid, + Invalid, + Failed, +}; + +struct RegistryRead { + RegistryReadResult result; + std::string value; +}; + +class ScopedWinHandle { + public: + explicit ScopedWinHandle(HANDLE handle = nullptr) : handle_(handle) {} + ~ScopedWinHandle() { + if (handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE) { + ::CloseHandle(handle_); + } + } + + ScopedWinHandle(const ScopedWinHandle&) = delete; + ScopedWinHandle& operator=(const ScopedWinHandle&) = delete; + + HANDLE Get() const { return handle_; } + + private: + HANDLE handle_; +}; + +class ScopedDeviceIdMutex { + public: + ScopedDeviceIdMutex() { + HANDLE token{}; + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) { + return; + } + ScopedWinHandle token_handle(token); + + DWORD size = 0; + ::GetTokenInformation(token_handle.Get(), TokenUser, nullptr, 0, &size); + if (size < sizeof(TOKEN_USER) || size > sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) { + return; + } + + std::vector token_info(size); + if (!::GetTokenInformation(token_handle.Get(), TokenUser, token_info.data(), size, &size)) { + return; + } + + const auto* token_user = reinterpret_cast(token_info.data()); + if (!::IsValidSid(token_user->User.Sid)) { + return; + } + + const DWORD sid_size = ::GetLengthSid(token_user->User.Sid); + const auto* sid_bytes = static_cast(token_user->User.Sid); + uint64_t sid_hash = 14695981039346656037ULL; + for (DWORD i = 0; i < sid_size; ++i) { + sid_hash ^= sid_bytes[i]; + sid_hash *= 1099511628211ULL; + } + + std::array mutex_name{}; + _snwprintf_s(mutex_name.data(), mutex_name.size(), _TRUNCATE, + L"Global\\Microsoft.DeveloperTools.OnnxRuntime.DeviceId.%016llx", + static_cast(sid_hash)); + handle_ = ::CreateMutexW(nullptr, FALSE, mutex_name.data()); + if (handle_ == nullptr) { + return; + } + + const DWORD wait_result = ::WaitForSingleObject(handle_, 1000); + acquired_ = wait_result == WAIT_OBJECT_0 || wait_result == WAIT_ABANDONED; + } + + ~ScopedDeviceIdMutex() { + if (acquired_) { + ::ReleaseMutex(handle_); + } + if (handle_ != nullptr) { + ::CloseHandle(handle_); + } + } + + ScopedDeviceIdMutex(const ScopedDeviceIdMutex&) = delete; + ScopedDeviceIdMutex& operator=(const ScopedDeviceIdMutex&) = delete; + + explicit operator bool() const { return acquired_; } + + private: + HANDLE handle_{}; + bool acquired_{}; +}; + +RegistryRead ReadDeviceIdRegistryValue() { + HKEY key{}; + const LSTATUS open_status = + ::RegOpenKeyExA(HKEY_CURRENT_USER, kDeviceIdRegistryKey, 0, KEY_READ | KEY_WOW64_64KEY, &key); + if (open_status == ERROR_FILE_NOT_FOUND) { + return {RegistryReadResult::Missing, {}}; + } + if (open_status != ERROR_SUCCESS) { + return {RegistryReadResult::Failed, {}}; + } + + std::array buffer{}; + DWORD type = 0; + DWORD size = static_cast(buffer.size()); + const LSTATUS query_status = ::RegQueryValueExA( + key, kDeviceIdRegistryValue, nullptr, &type, reinterpret_cast(buffer.data()), &size); + ::RegCloseKey(key); + + if (query_status == ERROR_FILE_NOT_FOUND) { + return {RegistryReadResult::Missing, {}}; + } + if (query_status == ERROR_MORE_DATA) { + return {RegistryReadResult::Invalid, {}}; + } + if (query_status != ERROR_SUCCESS) { + return {RegistryReadResult::Failed, {}}; + } + if (type != REG_SZ || size == 0 || size > buffer.size()) { + return {RegistryReadResult::Invalid, {}}; + } + + size_t value_size = size; + if (buffer[value_size - 1] == '\0') { + --value_size; + } + if (std::find(buffer.begin(), buffer.begin() + value_size, '\0') != buffer.begin() + value_size) { + return {RegistryReadResult::Invalid, {}}; + } + + std::string value(buffer.data(), value_size); + value = telemetry_detail::TrimAscii(value); + return {IsValidGuid(value) ? RegistryReadResult::Valid : RegistryReadResult::Invalid, + std::move(value)}; +} + +bool WriteDeviceIdRegistryValue(const std::string& value) { + HKEY key{}; + if (::RegCreateKeyExA(HKEY_CURRENT_USER, kDeviceIdRegistryKey, 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_WRITE | KEY_WOW64_64KEY, nullptr, &key, nullptr) != ERROR_SUCCESS) { + return false; + } + + const LSTATUS status = + ::RegSetValueExA(key, kDeviceIdRegistryValue, 0, REG_SZ, + reinterpret_cast(value.c_str()), + static_cast(value.size() + 1)); + ::RegCloseKey(key); + return status == ERROR_SUCCESS; +} + +std::wstring GetEnvironmentValue(const char* name) { + const auto value = telemetry_detail::ReadTelemetryEnvironment(name, telemetry_detail::kMaxTelemetryPathBytes); + if (!value) { + if (logging::LoggingManager::HasDefaultLogger()) { + LOGS_DEFAULT(WARNING) << "Ignoring oversized or unreadable telemetry storage environment variable " << name; + } + return {}; + } + return ToWideString(*value); +} + +std::filesystem::path GetAbsoluteEnvironmentPath(const char* name) { + std::filesystem::path path(GetEnvironmentValue(name)); + return path.is_absolute() ? path : std::filesystem::path{}; +} + +std::string WideToUtf8(std::wstring_view value) { + if (value.empty()) { + return {}; + } + if (value.size() > telemetry_detail::kMaxTelemetryPathBytes) { + if (logging::LoggingManager::HasDefaultLogger()) { + LOGS_DEFAULT(WARNING) << "Ignoring oversized telemetry storage path"; + } + return {}; + } + + const int required_size = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, value.data(), + static_cast(value.size()), nullptr, 0, nullptr, nullptr); + if (required_size == 0) { + return {}; + } + if (required_size > static_cast(telemetry_detail::kMaxTelemetryPathBytes)) { + if (logging::LoggingManager::HasDefaultLogger()) { + LOGS_DEFAULT(WARNING) << "Ignoring oversized telemetry storage path"; + } + return {}; + } + + std::string result(required_size, '\0'); + return ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, value.data(), static_cast(value.size()), + result.data(), required_size, nullptr, nullptr) == required_size + ? result + : std::string{}; +} + +std::wstring Utf8ToWide(std::string_view value) { + if (value.empty()) { + return {}; + } + + const int required_size = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.data(), static_cast(value.size()), nullptr, 0); + if (required_size == 0) { + return {}; + } + + std::wstring result(required_size, L'\0'); + return ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.data(), static_cast(value.size()), + result.data(), required_size) == required_size + ? result + : std::wstring{}; +} + +} // namespace + +DeviceId& DeviceId::Instance() { + static DeviceId instance; + return instance; +} + +std::string DeviceId::GetValue() { + std::lock_guard lock(mutex_); + InitializeInternal(); + return device_id_; +} + +DeviceIdStatus DeviceId::GetStatus() { + std::lock_guard lock(mutex_); + InitializeInternal(); + return status_; +} + +std::string DeviceId::GetStatusString() { + switch (GetStatus()) { + case DeviceIdStatus::New: + return "New"; + case DeviceIdStatus::Existing: + return "Existing"; + case DeviceIdStatus::Corrupted: + return "Corrupted"; + case DeviceIdStatus::Failed: + return "Failed"; + default: + return "Unknown"; + } +} + +std::string DeviceId::GetStorageDirectory() { + for (const char* variable : {"LOCALAPPDATA", "APPDATA"}) { + const std::filesystem::path app_data = GetAbsoluteEnvironmentPath(variable); + if (!app_data.empty()) { + return WideToUtf8((app_data / kDeviceIdDirectory).native()); + } + } + + std::filesystem::path home = GetAbsoluteEnvironmentPath("HOME"); + if (home.empty()) { + home = GetAbsoluteEnvironmentPath("USERPROFILE"); + } + if (home.empty()) { + const std::wstring home_drive = GetEnvironmentValue("HOMEDRIVE"); + const std::wstring home_path = GetEnvironmentValue("HOMEPATH"); + if (home_drive.size() + home_path.size() > telemetry_detail::kMaxTelemetryPathBytes) return {}; + const std::filesystem::path combined_home(home_drive + home_path); + if (combined_home.is_absolute()) { + home = combined_home; + } + } + return home.empty() + ? std::string{} + : WideToUtf8((home / L"AppData" / L"Local" / kDeviceIdDirectory).native()); +} + +std::string DeviceId::EnsureStorageDirectory() { + const std::string path = GetStorageDirectory(); + if (path.empty() || !CreateDirectoryTree(path)) { + return {}; + } + return path; +} + +bool DeviceId::CreateDirectoryTree(const std::string& path, bool /*leaf*/) { + const std::wstring wide_path = Utf8ToWide(path); + if (wide_path.empty()) { + return false; + } + + std::error_code error; + std::filesystem::create_directories(wide_path, error); + return !error && std::filesystem::is_directory(wide_path, error); +} + +void DeviceId::InitializeInternal() { + if (initialized_) { + return; + } + initialized_ = true; + + RegistryRead existing = ReadDeviceIdRegistryValue(); + if (existing.result == RegistryReadResult::Valid) { + device_id_ = std::move(existing.value); + status_ = DeviceIdStatus::Existing; + return; + } + if (existing.result == RegistryReadResult::Failed) { + device_id_ = GenerateGuidV4(); + status_ = DeviceIdStatus::Failed; + return; + } + const bool was_corrupted = existing.result == RegistryReadResult::Invalid; + + ScopedDeviceIdMutex lock; + if (!lock) { + device_id_ = GenerateGuidV4(); + status_ = DeviceIdStatus::Failed; + return; + } + + existing = ReadDeviceIdRegistryValue(); + if (existing.result == RegistryReadResult::Valid) { + device_id_ = std::move(existing.value); + status_ = DeviceIdStatus::Existing; + return; + } + if (existing.result == RegistryReadResult::Failed) { + device_id_ = GenerateGuidV4(); + status_ = DeviceIdStatus::Failed; + return; + } + + device_id_ = GenerateGuidV4(); + if (!WriteDeviceIdRegistryValue(device_id_)) { + status_ = DeviceIdStatus::Failed; + return; + } + status_ = (was_corrupted || existing.result == RegistryReadResult::Invalid) + ? DeviceIdStatus::Corrupted + : DeviceIdStatus::New; +} + +} // namespace onnxruntime diff --git a/onnxruntime/core/platform/windows/env.cc b/onnxruntime/core/platform/windows/env.cc index edd507bc55932..68bed629e922e 100644 --- a/onnxruntime/core/platform/windows/env.cc +++ b/onnxruntime/core/platform/windows/env.cc @@ -17,7 +17,6 @@ limitations under the License. #include "core/platform/windows/env.h" #include "core/platform/env_var.h" - #include #include #include diff --git a/onnxruntime/core/platform/windows/env.h b/onnxruntime/core/platform/windows/env.h index ba5f8f97ff260..f57d7d337496f 100644 --- a/onnxruntime/core/platform/windows/env.h +++ b/onnxruntime/core/platform/windows/env.h @@ -16,6 +16,9 @@ limitations under the License. #include "core/platform/env.h" #include "core/platform/windows/telemetry.h" +#ifdef USE_1DS_TELEMETRY +#include "core/platform/telemetry_1ds.h" +#endif #include "core/common/inlined_containers.h" #include #include @@ -142,7 +145,16 @@ class WindowsEnv : public Env { private: void InitializeCpuInfo(); typedef VOID(WINAPI* FnGetSystemTimePreciseAsFileTime)(LPFILETIME); +#if defined(USE_1DS_TELEMETRY) + // Keep the TraceLogging provider registered for local ETW diagnostics even when 1DS is the + // telemetry upload backend. + WindowsTelemetry windows_telemetry_provider_; + OneDsTelemetry telemetry_provider_{windows_telemetry_provider_}; +#elif defined(USE_WINDOWS_TELEMETRY) WindowsTelemetry telemetry_provider_; +#else + Telemetry telemetry_provider_; +#endif }; namespace internal { diff --git a/onnxruntime/core/platform/windows/logging/etw_sink.cc b/onnxruntime/core/platform/windows/logging/etw_sink.cc index 489cd19b11302..f1f5c714459f6 100644 --- a/onnxruntime/core/platform/windows/logging/etw_sink.cc +++ b/onnxruntime/core/platform/windows/logging/etw_sink.cc @@ -3,6 +3,8 @@ // EtwSink.h must come before the windows includes #include "core/platform/windows/logging/etw_sink.h" +#include "core/platform/telemetry_redaction.h" +#include "core/platform/telemetry_strings.h" #ifdef ETW_TRACE_LOGGING_SUPPORTED @@ -212,15 +214,26 @@ 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. + telemetry_detail::TelemetryStrings strings; + const auto& location = message.Location(); + const std::string_view file = location.file_and_path.size() <= telemetry_detail::kMaxTelemetryPathBytes + ? std::string_view(location.file_and_path) + : std::string_view{}; + std::string location_text = telemetry_detail::BoundedTelemetryString(file.substr(file.find_last_of("/\\") + 1)); + telemetry_detail::AppendTelemetryString(location_text, ":"); + telemetry_detail::AppendTelemetryString(location_text, std::to_string(location.line_num)); + telemetry_detail::AppendTelemetryString(location_text, " "); + telemetry_detail::AppendTelemetryString(location_text, ScrubStringForTelemetry(location.function)); #define ETW_EVENT_NAME "ONNXRuntimeLogEvent" #define TRACE_LOG_WRITE(level) \ TraceLoggingWrite(etw_provider_handle, ETW_EVENT_NAME, \ TraceLoggingKeyword(static_cast(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")) + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(logger_id)), "logger"), \ + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(message.Category())), \ + "category"), \ + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(location_text)), "location"), \ + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(message.MessageView())), "message")) const auto severity{message.Severity()}; diff --git a/onnxruntime/core/platform/windows/telemetry.cc b/onnxruntime/core/platform/windows/telemetry.cc index 0b84a4ee2d0ee..4310408f0360d 100644 --- a/onnxruntime/core/platform/windows/telemetry.cc +++ b/onnxruntime/core/platform/windows/telemetry.cc @@ -108,7 +108,8 @@ uint64_t GetTotalMemoryMB() { return memory_status.ullTotalPhys / (1024 * 1024); } -std::string ConvertWideStringToUtf8(const std::wstring& wide) { +std::string ConvertWideStringToUtf8(std::wstring_view wide) { + wide = telemetry_detail::TelemetryWideStringView(wide); if (wide.empty()) return {}; @@ -152,21 +153,25 @@ std::string GetServiceNamesFromCommandLine() { if (argv == nullptr) return {}; - std::wstring aggregated; + std::string aggregated; bool first = true; + size_t count = 0; for (int i = 0; i < argc - 1; ++i) { if ((_wcsicmp(argv[i], L"-s") == 0 || _wcsicmp(argv[i], L"-k") == 0)) { + if (count++ == telemetry_detail::kMaxTelemetryCollectionEntries) break; if (!first) { - aggregated.push_back(L','); + if (!telemetry_detail::AppendTelemetryString(aggregated, ",")) break; } - aggregated.append(argv[i + 1]); + const std::string value = ConvertWideStringToUtf8(telemetry_detail::TelemetryWideStringView(argv[i + 1])); + if (!telemetry_detail::AppendTelemetryString(aggregated, value) || + aggregated.size() == kMaxTelemetryStringLength) break; first = false; ++i; // skip the value we just consumed } } ::LocalFree(argv); - return ConvertWideStringToUtf8(aggregated); + return aggregated; #else // CommandLineToArgvW lives in shell32 and is only available on the desktop partition; the // svchost -s/-k service-name convention does not apply on non-desktop Windows (UWP/GDK). @@ -196,7 +201,8 @@ std::string GetServiceNamesForCurrentProcess() { return; } - if (bytes_needed == 0) { + // EnumServicesStatusEx supports at most a 256 KiB enumeration buffer. + if (bytes_needed == 0 || bytes_needed > 256 * 1024) { ::CloseServiceHandle(service_manager); service_names = GetServiceNamesFromCommandLine(); return; @@ -214,21 +220,26 @@ std::string GetServiceNamesForCurrentProcess() { } DWORD current_pid = ::GetCurrentProcessId(); - std::wstring aggregated; + std::string aggregated; bool first = true; + size_t count = 0; for (DWORD i = 0; i < services_returned; ++i) { if (services[i].ServiceStatusProcess.dwProcessId == current_pid) { + if (count++ == telemetry_detail::kMaxTelemetryCollectionEntries) break; if (!first) { - aggregated.push_back(L','); + if (!telemetry_detail::AppendTelemetryString(aggregated, ",")) break; } - aggregated.append(services[i].lpServiceName); + const std::string value = + ConvertWideStringToUtf8(telemetry_detail::TelemetryWideStringView(services[i].lpServiceName)); + if (!telemetry_detail::AppendTelemetryString(aggregated, value) || + aggregated.size() == kMaxTelemetryStringLength) break; first = false; } } ::CloseServiceHandle(service_manager); - service_names = ConvertWideStringToUtf8(aggregated); + service_names = std::move(aggregated); if (service_names.empty()) { service_names = GetServiceNamesFromCommandLine(); } @@ -373,6 +384,7 @@ void WindowsTelemetry::LogProcessInfo() const { const std::string cpu_model = GetCpuModel(); const uint32_t processor_count = GetProcessorCount(); const uint64_t total_memory_mb = GetTotalMemoryMB(); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "ProcessInfo", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -381,14 +393,14 @@ void WindowsTelemetry::LogProcessInfo() const { TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(cpu_model.c_str(), "cpuModel"), + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(cpu_model.c_str()), "cpuModel"), TraceLoggingUInt32(processor_count, "processorCount"), TraceLoggingUInt64(total_memory_mb, "totalMemoryMB"), TraceLoggingBool(IsDebuggerPresent(), "isDebuggerAttached"), TraceLoggingBool(isRedist, "isRedist"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName"), - TraceLoggingString(service_names.c_str(), "serviceNames")); + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName"), + TraceLoggingString(strings.Utf8(service_names.c_str()), "serviceNames")); process_info_logged = true; } @@ -397,6 +409,7 @@ void WindowsTelemetry::LogSessionCreationStart(uint32_t session_id) const { if (global_register_count_ == 0 || enabled_ == false) return; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "SessionCreationStart", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -404,8 +417,8 @@ void WindowsTelemetry::LogSessionCreationStart(uint32_t session_id) const { TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogEvaluationStop(uint32_t session_id) const { @@ -443,48 +456,14 @@ void WindowsTelemetry::LogSessionCreation(uint32_t session_id, int64_t ir_versio if (global_register_count_ == 0 || enabled_ == false) return; - // build the strings we need - - std::string domain_to_version_string; - bool first = true; - for (auto& i : domain_to_version_map) { - if (first) { - first = false; - } else { - domain_to_version_string += ','; - } - domain_to_version_string += i.first; - domain_to_version_string += '='; - domain_to_version_string += std::to_string(i.second); - } - - std::string model_metadata_string; - first = true; - for (auto& i : model_metadata) { - if (first) { - first = false; - } else { - model_metadata_string += ','; - } - model_metadata_string += i.first; - model_metadata_string += '='; - model_metadata_string += i.second; - } - - std::string execution_provider_string; - first = true; - for (auto& i : execution_provider_ids) { - if (first) { - first = false; - } else { - execution_provider_string += ','; - } - execution_provider_string += i; - } + const std::string domain_to_version_string = telemetry_detail::FormatTelemetryMap(domain_to_version_map); + const std::string model_metadata_string = telemetry_detail::FormatTelemetryMap(model_metadata); + const std::string execution_provider_string = telemetry_detail::JoinTelemetryStrings(execution_provider_ids); const std::string service_names = GetServiceNamesForCurrentProcess(); // Difference is MeasureEvent & isCaptureState, but keep in sync otherwise if (!captureState) { + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "SessionCreation", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -499,25 +478,26 @@ void WindowsTelemetry::LogSessionCreation(uint32_t session_id, int64_t ir_versio TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingInt64(ir_version, "irVersion"), TraceLoggingUInt32(projection_, "OrtProgrammingProjection"), - TraceLoggingString(model_producer_name.c_str(), "modelProducerName"), - TraceLoggingString(model_producer_version.c_str(), "modelProducerVersion"), - TraceLoggingString(model_domain.c_str(), "modelDomain"), + TraceLoggingString(strings.Utf8(model_producer_name.c_str()), "modelProducerName"), + TraceLoggingString(strings.Utf8(model_producer_version.c_str()), "modelProducerVersion"), + TraceLoggingString(strings.Utf8(model_domain.c_str()), "modelDomain"), TraceLoggingBool(use_fp16, "usefp16"), - TraceLoggingString(domain_to_version_string.c_str(), "domainToVersionMap"), - TraceLoggingString(model_file_name.c_str(), "modelFileName"), - TraceLoggingString(model_graph_name.c_str(), "modelGraphName"), - TraceLoggingString(model_weight_type.c_str(), "modelWeightType"), - TraceLoggingString(model_graph_hash.c_str(), "modelGraphHash"), - TraceLoggingString(model_weight_hash.c_str(), "modelWeightHash"), - TraceLoggingString(model_metadata_string.c_str(), "modelMetaData"), - TraceLoggingString(loaded_from.c_str(), "loadedFrom"), - TraceLoggingString(execution_provider_string.c_str(), "executionProviderIds"), - TraceLoggingString(hardware_device_types.c_str(), "hardwareDeviceTypes"), - TraceLoggingString(hardware_vendor_ids.c_str(), "hardwareVendorIds"), - TraceLoggingString(ep_versions.c_str(), "executionProviderVersions"), - TraceLoggingString(service_names.c_str(), "serviceNames"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(domain_to_version_string.c_str()), "domainToVersionMap"), + TraceLoggingString(strings.Utf8(model_file_name.c_str()), "modelFileName"), + TraceLoggingString(strings.Utf8(model_graph_name.c_str()), "modelGraphName"), + TraceLoggingString(strings.Utf8(model_weight_type.c_str()), "modelWeightType"), + TraceLoggingString(strings.Utf8(model_graph_hash.c_str()), "modelGraphHash"), + TraceLoggingString(strings.Utf8(model_weight_hash.c_str()), "modelWeightHash"), + TraceLoggingString(strings.Utf8(model_metadata_string.c_str()), "modelMetaData"), + TraceLoggingString(strings.Utf8(loaded_from.c_str()), "loadedFrom"), + TraceLoggingString(strings.Utf8(execution_provider_string.c_str()), "executionProviderIds"), + TraceLoggingString(strings.Utf8(hardware_device_types.c_str()), "hardwareDeviceTypes"), + TraceLoggingString(strings.Utf8(hardware_vendor_ids.c_str()), "hardwareVendorIds"), + TraceLoggingString(strings.Utf8(ep_versions.c_str()), "executionProviderVersions"), + TraceLoggingString(strings.Utf8(service_names.c_str()), "serviceNames"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } else { + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "SessionCreation_CaptureState", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -532,24 +512,24 @@ void WindowsTelemetry::LogSessionCreation(uint32_t session_id, int64_t ir_versio TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingInt64(ir_version, "irVersion"), TraceLoggingUInt32(projection_, "OrtProgrammingProjection"), - TraceLoggingString(model_producer_name.c_str(), "modelProducerName"), - TraceLoggingString(model_producer_version.c_str(), "modelProducerVersion"), - TraceLoggingString(model_domain.c_str(), "modelDomain"), + TraceLoggingString(strings.Utf8(model_producer_name.c_str()), "modelProducerName"), + TraceLoggingString(strings.Utf8(model_producer_version.c_str()), "modelProducerVersion"), + TraceLoggingString(strings.Utf8(model_domain.c_str()), "modelDomain"), TraceLoggingBool(use_fp16, "usefp16"), - TraceLoggingString(domain_to_version_string.c_str(), "domainToVersionMap"), - TraceLoggingString(model_file_name.c_str(), "modelFileName"), - TraceLoggingString(model_graph_name.c_str(), "modelGraphName"), - TraceLoggingString(model_weight_type.c_str(), "modelWeightType"), - TraceLoggingString(model_graph_hash.c_str(), "modelGraphHash"), - TraceLoggingString(model_weight_hash.c_str(), "modelWeightHash"), - TraceLoggingString(model_metadata_string.c_str(), "modelMetaData"), - TraceLoggingString(loaded_from.c_str(), "loadedFrom"), - TraceLoggingString(execution_provider_string.c_str(), "executionProviderIds"), - TraceLoggingString(hardware_device_types.c_str(), "hardwareDeviceTypes"), - TraceLoggingString(hardware_vendor_ids.c_str(), "hardwareVendorIds"), - TraceLoggingString(ep_versions.c_str(), "executionProviderVersions"), - TraceLoggingString(service_names.c_str(), "serviceNames"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(domain_to_version_string.c_str()), "domainToVersionMap"), + TraceLoggingString(strings.Utf8(model_file_name.c_str()), "modelFileName"), + TraceLoggingString(strings.Utf8(model_graph_name.c_str()), "modelGraphName"), + TraceLoggingString(strings.Utf8(model_weight_type.c_str()), "modelWeightType"), + TraceLoggingString(strings.Utf8(model_graph_hash.c_str()), "modelGraphHash"), + TraceLoggingString(strings.Utf8(model_weight_hash.c_str()), "modelWeightHash"), + TraceLoggingString(strings.Utf8(model_metadata_string.c_str()), "modelMetaData"), + TraceLoggingString(strings.Utf8(loaded_from.c_str()), "loadedFrom"), + TraceLoggingString(strings.Utf8(execution_provider_string.c_str()), "executionProviderIds"), + TraceLoggingString(strings.Utf8(hardware_device_types.c_str()), "hardwareDeviceTypes"), + TraceLoggingString(strings.Utf8(hardware_vendor_ids.c_str()), "hardwareVendorIds"), + TraceLoggingString(strings.Utf8(ep_versions.c_str()), "executionProviderVersions"), + TraceLoggingString(strings.Utf8(service_names.c_str()), "serviceNames"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } } @@ -564,17 +544,9 @@ void WindowsTelemetry::LogCompileModelStart(uint32_t session_id, if (global_register_count_ == 0 || enabled_ == false) return; - std::string execution_provider_string; - bool first = true; - for (const auto& ep_id : execution_provider_ids) { - if (first) { - first = false; - } else { - execution_provider_string += ','; - } - execution_provider_string += ep_id; - } + const std::string execution_provider_string = telemetry_detail::JoinTelemetryStrings(execution_provider_ids); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "CompileModelStart", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -584,15 +556,15 @@ void WindowsTelemetry::LogCompileModelStart(uint32_t session_id, // Telemetry info TraceLoggingUInt8(1, "schemaVersion"), TraceLoggingUInt32(session_id, "sessionId"), - TraceLoggingString(input_source.c_str(), "inputSource"), - TraceLoggingString(output_target.c_str(), "outputTarget"), + TraceLoggingString(strings.Utf8(input_source.c_str()), "inputSource"), + TraceLoggingString(strings.Utf8(output_target.c_str()), "outputTarget"), TraceLoggingUInt32(flags, "flags"), TraceLoggingInt32(graph_optimization_level, "graphOptimizationLevel"), TraceLoggingBool(embed_ep_context, "embedEpContext"), TraceLoggingBool(has_external_initializers_file, "hasExternalInitializersFile"), - TraceLoggingString(execution_provider_string.c_str(), "executionProviderIds"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(execution_provider_string.c_str()), "executionProviderIds"), + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogCompileModelComplete(uint32_t session_id, @@ -604,6 +576,7 @@ void WindowsTelemetry::LogCompileModelComplete(uint32_t session_id, return; const std::string scrubbed_error = ScrubStringForTelemetry(error_message); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "CompileModelComplete", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -616,8 +589,8 @@ void WindowsTelemetry::LogCompileModelComplete(uint32_t session_id, TraceLoggingBool(success, "success"), TraceLoggingUInt32(error_code, "errorCode"), TraceLoggingUInt32(error_category, "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogRuntimeError(uint32_t session_id, const common::Status& status, const char* file, @@ -626,13 +599,17 @@ void WindowsTelemetry::LogRuntimeError(uint32_t session_id, const common::Status return; const std::string scrubbed_error = ScrubStringForTelemetry(status.ErrorMessage()); - std::string_view file_view = file ? std::string_view{file} : std::string_view{}; + std::string_view file_view = telemetry_detail::TelemetryCStringView(file, telemetry_detail::kMaxTelemetryPathBytes); + if (file_view.size() > telemetry_detail::kMaxTelemetryPathBytes) { + file_view = {}; + } if (const size_t slash = file_view.find_last_of("/\\"); slash != std::string_view::npos) { file_view.remove_prefix(slash + 1); } const std::string scrubbed_file = ScrubStringForTelemetry(file_view); #ifdef _WIN32 HRESULT hr = common::StatusCodeToHRESULT(static_cast(status.Code())); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RuntimeError", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -645,13 +622,14 @@ void WindowsTelemetry::LogRuntimeError(uint32_t session_id, const common::Status TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingUInt32(status.Code(), "errorCode"), TraceLoggingUInt32(status.Category(), "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(scrubbed_file.c_str(), "file"), - TraceLoggingString(function, "function"), + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(scrubbed_file.c_str()), "file"), + TraceLoggingString(strings.Utf8(function), "function"), TraceLoggingInt32(line, "line"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); #else + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RuntimeError", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -663,12 +641,12 @@ void WindowsTelemetry::LogRuntimeError(uint32_t session_id, const common::Status TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingUInt32(status.Code(), "errorCode"), TraceLoggingUInt32(status.Category(), "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(scrubbed_file.c_str(), "file"), - TraceLoggingString(function, "function"), + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(scrubbed_file.c_str()), "file"), + TraceLoggingString(strings.Utf8(function), "function"), TraceLoggingInt32(line, "line"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); #endif } @@ -679,6 +657,7 @@ void WindowsTelemetry::LogRuntimeInferenceError(uint32_t session_id, const commo return; const std::string scrubbed_error = ScrubStringForTelemetry(status.ErrorMessage()); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RuntimeInferenceError", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -690,11 +669,11 @@ void WindowsTelemetry::LogRuntimeInferenceError(uint32_t session_id, const commo TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingUInt32(status.Code(), "errorCode"), TraceLoggingUInt32(status.Category(), "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(ep_versions.c_str(), "executionProviderVersions"), - TraceLoggingString(ep_device_types.c_str(), "executionProviderDeviceTypes"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(ep_versions.c_str()), "executionProviderVersions"), + TraceLoggingString(strings.Utf8(ep_device_types.c_str()), "executionProviderDeviceTypes"), + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogRuntimePerf(uint32_t session_id, uint32_t total_runs_since_last, int64_t total_run_duration_since_last, @@ -702,17 +681,10 @@ void WindowsTelemetry::LogRuntimePerf(uint32_t session_id, uint32_t total_runs_s if (global_register_count_ == 0 || enabled_ == false) return; - // Convert duration_per_batch_size to a formatted string - std::string total_duration_per_batch_size; - for (const auto& entry : duration_per_batch_size) { - if (!total_duration_per_batch_size.empty()) { - total_duration_per_batch_size += ", "; - } - total_duration_per_batch_size += std::to_string(entry.first); - total_duration_per_batch_size += ": "; - total_duration_per_batch_size += std::to_string(entry.second); - } + const std::string total_duration_per_batch_size = + telemetry_detail::FormatTelemetryMap(duration_per_batch_size, ", ", ": "); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RuntimePerf", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -723,9 +695,9 @@ void WindowsTelemetry::LogRuntimePerf(uint32_t session_id, uint32_t total_runs_s TraceLoggingUInt32(session_id, "sessionId"), TraceLoggingUInt32(total_runs_since_last, "totalRuns"), TraceLoggingInt64(total_run_duration_since_last, "totalRunDuration"), - TraceLoggingString(total_duration_per_batch_size.c_str(), "totalRunDurationPerBatchSize"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(total_duration_per_batch_size.c_str()), "totalRunDurationPerBatchSize"), + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogEpDeviceUsage(uint32_t session_id, @@ -742,6 +714,7 @@ void WindowsTelemetry::LogEpDeviceUsage(uint32_t session_id, if (global_register_count_ == 0 || enabled_ == false) return; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "EpDeviceUsage", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -753,18 +726,18 @@ void WindowsTelemetry::LogEpDeviceUsage(uint32_t session_id, // schemaVersion 1: added epVersion, runtimeVersion TraceLoggingUInt8(1, "schemaVersion"), TraceLoggingUInt32(session_id, "sessionId"), - TraceLoggingString(ep_type.c_str(), "executionProviderType"), - TraceLoggingString(hardware_device_type.c_str(), "hardwareDeviceType"), + TraceLoggingString(strings.Utf8(ep_type.c_str()), "executionProviderType"), + TraceLoggingString(strings.Utf8(hardware_device_type.c_str()), "hardwareDeviceType"), TraceLoggingUInt32(hardware_vendor_id, "hardwareVendorId"), TraceLoggingUInt32(hardware_device_id, "hardwareDeviceId"), - TraceLoggingString(hardware_vendor.c_str(), "hardwareVendor"), - TraceLoggingString(ep_vendor.c_str(), "epVendor"), - TraceLoggingString(ep_version.c_str(), "epVersion"), + TraceLoggingString(strings.Utf8(hardware_vendor.c_str()), "hardwareVendor"), + TraceLoggingString(strings.Utf8(ep_vendor.c_str()), "epVendor"), + TraceLoggingString(strings.Utf8(ep_version.c_str()), "epVersion"), TraceLoggingInt32(assigned_node_count, "assignedNodeCount"), TraceLoggingUInt32(total_runs_since_last, "totalRunsSinceLast"), TraceLoggingInt64(total_run_duration_since_last, "totalRunDurationSinceLast"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogExecutionProviderEvent(LUID* adapterLuid) const { @@ -785,6 +758,7 @@ void WindowsTelemetry::LogDriverInfoEvent(const std::string_view device_class, c if (global_register_count_ == 0 || enabled_ == false) return; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "DriverInfo", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -793,9 +767,9 @@ void WindowsTelemetry::LogDriverInfoEvent(const std::string_view device_class, c TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), - TraceLoggingString(device_class.data(), "deviceClass"), - TraceLoggingWideString(driver_names.data(), "driverNames"), - TraceLoggingWideString(driver_versions.data(), "driverVersions")); + TraceLoggingString(strings.Utf8(device_class), "deviceClass"), + TraceLoggingWideString(strings.Wide(driver_names), "driverNames"), + TraceLoggingWideString(strings.Wide(driver_versions), "driverVersions")); } void WindowsTelemetry::LogAutoEpSelection(uint32_t session_id, const std::string& selection_policy, @@ -804,30 +778,12 @@ void WindowsTelemetry::LogAutoEpSelection(uint32_t session_id, const std::string if (global_register_count_ == 0 || enabled_ == false) return; - // Build requested execution provider string - std::string requested_execution_provider_string; - bool first = true; - for (const auto& ep_id : requested_execution_provider_ids) { - if (first) { - first = false; - } else { - requested_execution_provider_string += ','; - } - requested_execution_provider_string += ep_id; - } - - // Build available execution provider string - std::string available_execution_provider_string; - first = true; - for (const auto& ep_id : available_execution_provider_ids) { - if (first) { - first = false; - } else { - available_execution_provider_string += ','; - } - available_execution_provider_string += ep_id; - } + const std::string requested_execution_provider_string = + telemetry_detail::JoinTelemetryStrings(requested_execution_provider_ids); + const std::string available_execution_provider_string = + telemetry_detail::JoinTelemetryStrings(available_execution_provider_ids); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "EpAutoSelection", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -838,18 +794,21 @@ void WindowsTelemetry::LogAutoEpSelection(uint32_t session_id, const std::string // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), TraceLoggingUInt32(session_id, "sessionId"), - TraceLoggingString(selection_policy.c_str(), "selectionPolicy"), - TraceLoggingString(requested_execution_provider_string.c_str(), "requestedExecutionProviderIds"), - TraceLoggingString(available_execution_provider_string.c_str(), "availableExecutionProviderIds"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(selection_policy.c_str()), "selectionPolicy"), + TraceLoggingString(strings.Utf8(requested_execution_provider_string.c_str()), "requestedExecutionProviderIds"), + TraceLoggingString(strings.Utf8(available_execution_provider_string.c_str()), "availableExecutionProviderIds"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } -void WindowsTelemetry::LogProviderOptions(const std::string& provider_id, const std::string& provider_options_string, bool captureState) const { +void WindowsTelemetry::LogProviderOptions(const std::string& provider_id, + const std::string& provider_options_string, + bool capture_state) const { if (global_register_count_ == 0 || enabled_ == false) return; // Difference is MeasureEvent & isCaptureState, but keep in sync otherwise - if (!captureState) { + if (!capture_state) { + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "ProviderOptions", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -859,10 +818,11 @@ void WindowsTelemetry::LogProviderOptions(const std::string& provider_id, const TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), - TraceLoggingString(provider_id.c_str(), "providerId"), - TraceLoggingString(provider_options_string.c_str(), "providerOptions"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(provider_id.c_str()), "providerId"), + TraceLoggingString(strings.Utf8(provider_options_string.c_str()), "providerOptions"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } else { + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "ProviderOptions_CaptureState", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -872,9 +832,9 @@ void WindowsTelemetry::LogProviderOptions(const std::string& provider_id, const TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), - TraceLoggingString(provider_id.c_str(), "providerId"), - TraceLoggingString(provider_options_string.c_str(), "providerOptions"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(provider_id.c_str()), "providerId"), + TraceLoggingString(strings.Utf8(provider_options_string.c_str()), "providerOptions"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } } @@ -882,6 +842,7 @@ void WindowsTelemetry::LogModelLoadStart(uint32_t session_id) const { if (global_register_count_ == 0 || enabled_ == false) return; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "ModelLoadStart", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -891,8 +852,8 @@ void WindowsTelemetry::LogModelLoadStart(uint32_t session_id) const { // Telemetry info TraceLoggingUInt8(1, "schemaVersion"), TraceLoggingUInt32(session_id, "sessionId"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogModelLoadEnd(uint32_t session_id, const common::Status& status, @@ -902,6 +863,7 @@ void WindowsTelemetry::LogModelLoadEnd(uint32_t session_id, const common::Status return; const std::string scrubbed_error = status.IsOK() ? std::string() : ScrubStringForTelemetry(status.ErrorMessage()); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "ModelLoadEnd", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -914,8 +876,8 @@ void WindowsTelemetry::LogModelLoadEnd(uint32_t session_id, const common::Status TraceLoggingBool(status.IsOK(), "isSuccess"), TraceLoggingUInt32(status.Code(), "errorCode"), TraceLoggingUInt32(status.Category(), "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogSessionCreationEnd(uint32_t session_id, @@ -926,6 +888,7 @@ void WindowsTelemetry::LogSessionCreationEnd(uint32_t session_id, return; const std::string scrubbed_error = status.IsOK() ? std::string() : ScrubStringForTelemetry(status.ErrorMessage()); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "SessionCreationEnd", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -938,8 +901,8 @@ void WindowsTelemetry::LogSessionCreationEnd(uint32_t session_id, TraceLoggingBool(status.IsOK(), "isSuccess"), TraceLoggingUInt32(status.Code(), "errorCode"), TraceLoggingUInt32(status.Category(), "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogRegisterEpLibraryWithLibPath(const std::string& registration_name, @@ -947,6 +910,7 @@ void WindowsTelemetry::LogRegisterEpLibraryWithLibPath(const std::string& regist if (global_register_count_ == 0 || enabled_ == false) return; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RegisterEpLibraryWithLibPath", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -955,15 +919,16 @@ void WindowsTelemetry::LogRegisterEpLibraryWithLibPath(const std::string& regist TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), - TraceLoggingString(registration_name.c_str(), "registrationName"), - TraceLoggingString(lib_path.c_str(), "libPath"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(registration_name.c_str()), "registrationName"), + TraceLoggingString(strings.Utf8(lib_path.c_str()), "libPath"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogRegisterEpLibraryStart(const std::string& registration_name) const { if (global_register_count_ == 0 || enabled_ == false) return; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RegisterEpLibraryStart", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -972,9 +937,9 @@ void WindowsTelemetry::LogRegisterEpLibraryStart(const std::string& registration TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(1, "schemaVersion"), - TraceLoggingString(registration_name.c_str(), "registrationName"), - TraceLoggingString(ORT_VERSION, "runtimeVersion"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(registration_name.c_str()), "registrationName"), + TraceLoggingString(strings.Utf8(ORT_VERSION), "runtimeVersion"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } void WindowsTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_name, @@ -985,6 +950,7 @@ void WindowsTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_n return; const std::string scrubbed_error = status.IsOK() ? std::string() : ScrubStringForTelemetry(status.ErrorMessage()); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "RegisterEpLibraryEnd", TraceLoggingBool(true, "UTCReplace_AppSessionGuid"), @@ -993,12 +959,12 @@ void WindowsTelemetry::LogRegisterEpLibraryEnd(const std::string& registration_n TraceLoggingLevel(WINEVENT_LEVEL_INFO), // Telemetry info TraceLoggingUInt8(0, "schemaVersion"), - TraceLoggingString(registration_name.c_str(), "registrationName"), + TraceLoggingString(strings.Utf8(registration_name.c_str()), "registrationName"), TraceLoggingBool(status.IsOK(), "isSuccess"), TraceLoggingUInt32(status.Code(), "errorCode"), TraceLoggingUInt32(status.Category(), "errorCategory"), - TraceLoggingString(scrubbed_error.c_str(), "errorMessage"), - TraceLoggingString(ORT_CALLER_FRAMEWORK, "frameworkName")); + TraceLoggingString(strings.Utf8(scrubbed_error.c_str()), "errorMessage"), + TraceLoggingString(strings.Utf8(ORT_CALLER_FRAMEWORK), "frameworkName")); } } // namespace onnxruntime diff --git a/onnxruntime/core/platform/windows/telemetry_1ds.cc b/onnxruntime/core/platform/windows/telemetry_1ds.cc new file mode 100644 index 0000000000000..88ad276a796a0 --- /dev/null +++ b/onnxruntime/core/platform/windows/telemetry_1ds.cc @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include + +#include "core/platform/telemetry_1ds.h" +#include "core/platform/telemetry_1ds_platform.h" +#include "core/platform/windows/telemetry.h" +#include "core/platform/telemetry_sampling.h" +#include "core/platform/telemetry_environment.h" +#include "core/platform/telemetry_redaction.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/common/common.h" + +using namespace Microsoft::Applications::Events; + +namespace onnxruntime { +namespace { + +EventProperties NewEvent(std::string name) { + EventProperties event(std::move(name)); + event.SetLatency(EventLatency_Normal); + event.SetPopsample(100.0); + event.SetLevel(DIAG_LEVEL_REQUIRED); + return event; +} + +bool PrepareSampledProcessEvent(EventProperties& event) { + if (!telemetry_internal::ShouldSampleSession( + telemetry_internal::GetAppSessionGuid(), 0, + telemetry_internal::kOtherProcessEventSampleRatePercent)) { + return false; + } + event.SetPopsample(telemetry_internal::kOtherProcessEventSampleRatePercent); + return true; +} + +} // namespace + +namespace telemetry_internal { + +const char* GetDefaultEncodedToken() { + return "fllXSmJHWBYMX1cqWldIYU0KTA0IVnpWD0lkRloSWwhILF5WGmBDDUdEC1csC0NMZhZYWVEPBH9DWEhlR15MCl1UfgwPVWVDX0U="; +} + +int32_t GetProcessorCount() { + const DWORD count = ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + return count > static_cast(std::numeric_limits::max()) + ? std::numeric_limits::max() + : static_cast(count); +} + +std::string GetCachePath(const std::string& directory) { + return directory + "\\onnxruntime.db"; +} + +} // namespace telemetry_internal + +std::string OneDsTelemetry::GetPlatformInfo() const { + return "Windows"; +} + +std::string OneDsTelemetry::GetOsDescription() const { + using RtlGetVersionFn = LONG(WINAPI*)(PRTL_OSVERSIONINFOW); + const HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll"); + const auto rtl_get_version = + ntdll == nullptr ? nullptr + : reinterpret_cast(::GetProcAddress(ntdll, "RtlGetVersion")); + if (rtl_get_version != nullptr) { + RTL_OSVERSIONINFOW version_info{}; + version_info.dwOSVersionInfoSize = sizeof(version_info); + if (rtl_get_version(&version_info) == 0) { + std::array description{}; + std::snprintf(description.data(), description.size(), "Windows %lu.%lu (Build %lu)", + version_info.dwMajorVersion, version_info.dwMinorVersion, + version_info.dwBuildNumber); + return description.data(); + } + } + return "Windows"; +} + +std::string OneDsTelemetry::GetCpuModel() const { + HKEY key{}; + if (::RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + 0, KEY_READ, &key) != ERROR_SUCCESS) { + return {}; + } + + char cpu_model[256]{}; + DWORD value_type = REG_SZ; + DWORD size = sizeof(cpu_model); + const LSTATUS status = ::RegQueryValueExA( + key, "ProcessorNameString", nullptr, &value_type, + reinterpret_cast(cpu_model), &size); + ::RegCloseKey(key); + if (status != ERROR_SUCCESS || value_type != REG_SZ || size == 0) { + return {}; + } + cpu_model[sizeof(cpu_model) - 1] = '\0'; + return cpu_model; +} + +std::string OneDsTelemetry::GetDeviceClass() const { + return "Desktop"; +} + +telemetry_detail::HostEnvironmentInfo OneDsTelemetry::GetHostEnvironmentInfo() { + return telemetry_detail::ClassifyHostEnvironment({}); +} + +std::string OneDsTelemetry::GetProcessName() { + std::vector path(MAX_PATH); + for (;;) { + const DWORD length = ::GetModuleFileNameW(nullptr, path.data(), static_cast(path.size())); + if (length == 0) { + return {}; + } + if (length < path.size()) { + std::wstring_view file_name(path.data(), length); + if (const size_t separator = file_name.find_last_of(L"/\\"); separator != std::wstring_view::npos) { + file_name.remove_prefix(separator + 1); + } + std::string process_name = ToUTF8String(telemetry_detail::BoundedTelemetryWideString(file_name)); + constexpr std::string_view executable_extension = ".exe"; + if (process_name.size() > executable_extension.size() && + std::equal(executable_extension.begin(), executable_extension.end(), + process_name.end() - executable_extension.size(), + [](char lhs, char rhs) { + return std::tolower(static_cast(lhs)) == + std::tolower(static_cast(rhs)); + })) { + process_name.resize(process_name.size() - executable_extension.size()); + } + return process_name; + } + if (path.size() >= 32768) { + return {}; + } + path.resize(std::min(path.size() * 2, 32768)); + } +} + +std::string OneDsTelemetry::GetArchitecture() { +#if defined(_M_X64) && !defined(_M_ARM64EC) + return "x86_64"; +#elif defined(_M_IX86) + return "x86"; +#elif defined(_M_ARM64) || defined(_M_ARM64EC) + return "arm64"; +#elif defined(_M_ARM) + return "arm"; +#else + return "unknown"; +#endif +} + +int64_t OneDsTelemetry::GetTotalMemoryMB() { + MEMORYSTATUSEX memory_status{}; + memory_status.dwLength = sizeof(memory_status); + if (::GlobalMemoryStatusEx(&memory_status) != 0) { + return static_cast(memory_status.ullTotalPhys / (1024 * 1024)); + } + return 0; +} + +void OneDsTelemetry::LogExecutionProviderEvent(LUID* adapter_luid) const { + RunTelemetryOperation("LogExecutionProviderEvent", [&]() { + if (!IsEnabled() || adapter_luid == nullptr) { + return; + } + auto event = NewEvent("ExecutionProviderEvent"); + event.SetProperty("adapterLuidLowPart", static_cast(adapter_luid->LowPart)); + event.SetProperty("adapterLuidHighPart", static_cast(static_cast(adapter_luid->HighPart))); + if (PrepareSampledProcessEvent(event)) { + LogEventAsync(std::move(event)); + } + }); +} + +void OneDsTelemetry::LogDriverInfoEvent( + std::string_view device_class, const std::wstring_view& driver_names, + const std::wstring_view& driver_versions) const { + RunTelemetryOperation("LogDriverInfoEvent", [&]() { + if (!IsEnabled()) { + return; + } + auto event = NewEvent("DriverInfo"); + event.SetProperty("schemaVersion", int64_t{0}); + event.SetProperty("deviceClass", telemetry_detail::BoundedTelemetryString(device_class)); + event.SetProperty("driverNames", ToUTF8String(telemetry_detail::BoundedTelemetryWideString(driver_names))); + event.SetProperty("driverVersions", ToUTF8String(telemetry_detail::BoundedTelemetryWideString(driver_versions))); + if (PrepareSampledProcessEvent(event)) { + LogEventAsync(std::move(event)); + } + }); +} + +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/openvino/ov_tracing.cc b/onnxruntime/core/providers/openvino/ov_tracing.cc index 79109552f3df6..f3f7d5e48742b 100644 --- a/onnxruntime/core/providers/openvino/ov_tracing.cc +++ b/onnxruntime/core/providers/openvino/ov_tracing.cc @@ -1,6 +1,7 @@ // Copyright (c) Intel Corporation. All rights reserved. // Licensed under the MIT License. #include "core/providers/openvino/ov_tracing.h" +#include "core/platform/telemetry_strings.h" #ifdef _WIN32 #include @@ -24,10 +25,11 @@ TRACELOGGING_DEFINE_PROVIDER( #endif namespace { -std::string EscapeJsonString(const std::string& input) { +std::string EscapeJsonString(std::string_view input) { + input = onnxruntime::telemetry_detail::TelemetryStringView(input); std::string escaped; // Reserve extra space for escaping - escaped.reserve(input.size() + input.size() / 5); + escaped.reserve(input.size() * 6); for (char c : input) { switch (c) { @@ -134,48 +136,46 @@ UINT64 OVTracing::Keyword() const { void OVTracing::LogAllRuntimeOptions(uint32_t session_id, const SessionContext& ctx) const { if (!IsEnabled()) return; + telemetry_detail::TelemetryStrings strings; // Log OpenVINO SDK version separately TraceLoggingWrite(ov_tracing_provider_handle, "OV.SDK.Version", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingUInt32(session_id, "session_id"), - TraceLoggingString(ctx.openvino_sdk_version.c_str(), "openvino_sdk_version")); + TraceLoggingString(strings.Utf8(ctx.openvino_sdk_version), "openvino_sdk_version")); constexpr std::string_view provider_prefix = "ep.openvinoexecutionprovider."; - std::ostringstream provider_opts; - std::ostringstream session_opts; + std::string provider_opts = "{"; + std::string session_opts = "{"; bool provider_first = true; bool session_first = true; - provider_opts << "{"; - session_opts << "{"; - // Segregate options based on prefix + size_t option_count = 0; for (const auto& [key, value] : ctx.runtime_config.options) { + if (option_count++ == telemetry_detail::kMaxTelemetryCollectionEntries) break; if (!value.empty()) { - if (key.starts_with(provider_prefix)) { - // Provider option - if (!provider_first) provider_opts << ","; - provider_opts << "\"" << key << "\":\"" << EscapeJsonString(value) << "\""; - provider_first = false; - } else { - // Session option - if (!session_first) session_opts << ","; - session_opts << "\"" << key << "\":\"" << EscapeJsonString(value) << "\""; - session_first = false; + auto& output = key.starts_with(provider_prefix) ? provider_opts : session_opts; + auto& first = key.starts_with(provider_prefix) ? provider_first : session_first; + const std::string entry = "\"" + EscapeJsonString(key) + "\":\"" + EscapeJsonString(value) + "\""; + // Keep complete JSON entries and reserve space for the closing brace. + if (entry.size() + (first ? 0 : 1) + 1 <= kMaxTelemetryStringLength - output.size()) { + if (!first) output += ","; + output += entry; + first = false; } } } - provider_opts << "}"; - session_opts << "}"; + provider_opts += "}"; + session_opts += "}"; // Log provider options only if there are any if (!provider_first) { TraceLoggingWrite(ov_tracing_provider_handle, "OVEP.Provider.Options", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingUInt32(session_id, "session_id"), - TraceLoggingString(provider_opts.str().c_str(), "provider_options")); + TraceLoggingString(provider_opts.c_str(), "provider_options")); } // Log session options only if there are any @@ -183,7 +183,7 @@ void OVTracing::LogAllRuntimeOptions(uint32_t session_id, const SessionContext& TraceLoggingWrite(ov_tracing_provider_handle, "OVEP.Session.Options", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingUInt32(session_id, "session_id"), - TraceLoggingString(session_opts.str().c_str(), "session_options")); + TraceLoggingString(session_opts.c_str(), "session_options")); } } diff --git a/onnxruntime/core/providers/qnn/qnn_telemetry.cc b/onnxruntime/core/providers/qnn/qnn_telemetry.cc index b2c8350bfe8ca..fc75a7bf2eb46 100644 --- a/onnxruntime/core/providers/qnn/qnn_telemetry.cc +++ b/onnxruntime/core/providers/qnn/qnn_telemetry.cc @@ -2,6 +2,8 @@ // Licensed under the MIT License. #include "core/providers/qnn/qnn_telemetry.h" +#include "core/platform/telemetry_redaction.h" +#include "core/platform/telemetry_strings.h" #ifdef _WIN32 #if !BUILD_QNN_EP_STATIC_LIB @@ -143,18 +145,20 @@ void QnnTelemetry::LogQnnProfileEvent(uint64_t timestamp, const std::string& timingSource, const std::string& eventLevel, const char* eventIdentifier) const { + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite( telemetry_provider_handle, "QNNProfilingEvent", TraceLoggingKeyword(static_cast(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")); + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(message)), "Message"), + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(qnnScalarValue)), "Value"), + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(unit)), "Unit of Measurement"), + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(timingSource)), "Timing Source"), + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(eventLevel)), "Event Level"), + TraceLoggingString(strings.Utf8(ScrubStringForTelemetry(eventIdentifier)), + "Event Identifier")); } void QnnTelemetry::RegisterInternalCallback(const EtwInternalCallback& callback) { diff --git a/onnxruntime/core/session/inference_session.cc b/onnxruntime/core/session/inference_session.cc index 81db0872897f5..9d80a191ae0da 100644 --- a/onnxruntime/core/session/inference_session.cc +++ b/onnxruntime/core/session/inference_session.cc @@ -68,6 +68,7 @@ #include "core/optimizer/transpose_optimization/ort_optimizer_utils.h" #include "core/platform/Barrier.h" #include "core/platform/threadpool.h" +#include "core/platform/telemetry_strings.h" #ifdef _WIN32 #include "core/platform/tracing.h" #include @@ -776,8 +777,11 @@ void InferenceSession::TraceSessionOptions(const SessionOptions& session_options LOGS(logger, INFO) << session_options; #if defined(_WIN32) && defined(ONNXRUNTIME_ENABLE_INSTRUMENT) - std::string optimized_model_filepath = ORT_TSTR_CONVERT_TO_PRINTABLE_STRING(session_options.optimized_model_filepath); - std::string profile_file_prefix = ORT_TSTR_CONVERT_TO_PRINTABLE_STRING(session_options.profile_file_prefix); + const std::string optimized_model_filepath = + ToUTF8String(telemetry_detail::BoundedTelemetryWideString(session_options.optimized_model_filepath.native())); + const std::string profile_file_prefix = + ToUTF8String(telemetry_detail::BoundedTelemetryWideString(session_options.profile_file_prefix)); + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite(telemetry_provider_handle, "SessionOptions", @@ -791,7 +795,7 @@ void InferenceSession::TraceSessionOptions(const SessionOptions& session_options TraceLoggingBoolean(session_options.enable_mem_reuse, "enable_mem_reuse"), TraceLoggingBoolean(session_options.enable_cpu_mem_arena, "enable_cpu_mem_arena"), TraceLoggingString(profile_file_prefix.c_str(), "profile_file_prefix"), - TraceLoggingString(session_options.session_logid.c_str(), "session_logid"), + TraceLoggingString(strings.Utf8(session_options.session_logid), "session_logid"), TraceLoggingInt8(static_cast(session_options.session_log_severity_level), "session_log_severity_level"), TraceLoggingInt8(static_cast(session_options.session_log_verbosity_level), "session_log_verbosity_level"), TraceLoggingUInt32(session_options.max_num_graph_transformation_steps, "max_num_graph_transformation_steps"), @@ -811,18 +815,21 @@ void InferenceSession::TraceSessionOptions(const SessionOptions& session_options TraceLoggingBoolean(session_options.intra_op_param.allow_spinning, "allow_spinning"), TraceLoggingInt32(session_options.intra_op_param.dynamic_block_base_, "dynamic_block_base_"), TraceLoggingUInt32(session_options.intra_op_param.stack_size, "stack_size"), - TraceLoggingString(!session_options.intra_op_param.affinity_str.empty() ? session_options.intra_op_param.affinity_str.c_str() : "", "affinity_str"), + TraceLoggingString(strings.Utf8(session_options.intra_op_param.affinity_str), "affinity_str"), TraceLoggingBoolean(session_options.intra_op_param.set_denormal_as_zero, "set_denormal_as_zero"), TraceLoggingBoolean(captureState, "isCaptureState")); + size_t config_count = 0; for (const auto& config_pair : session_options.config_options.configurations) { + if (config_count++ == telemetry_detail::kMaxTelemetryCollectionEntries) break; + telemetry_detail::TelemetryStrings config_strings; TraceLoggingWrite( telemetry_provider_handle, "SessionOptions_ConfigEntry", TraceLoggingKeyword(static_cast(onnxruntime::logging::ORTTraceLoggingKeyword::Session)), TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingString(config_pair.first.c_str(), "Key"), - TraceLoggingString(config_pair.second.c_str(), "Value"), + TraceLoggingString(config_strings.Utf8(config_pair.first), "Key"), + TraceLoggingString(config_strings.Utf8(config_pair.second), "Value"), TraceLoggingBoolean(captureState, "isCaptureState")); } #endif @@ -1202,7 +1209,7 @@ common::Status InferenceSession::LoadWithLoader(std::function telemetry_lock(telemetry_mutex_); ++telemetry_.total_runs_since_last_; telemetry_.total_run_duration_since_last_ += TimeDiffMicroSeconds(tp); - telemetry_.duration_per_batch_size_[batch_size] += TimeDiffMicroSeconds(tp); + auto batch_entry = telemetry_.duration_per_batch_size_.find(batch_size); + if (batch_entry != telemetry_.duration_per_batch_size_.end()) { + batch_entry->second += TimeDiffMicroSeconds(tp); + } else if (telemetry_.duration_per_batch_size_.size() < telemetry_detail::kMaxTelemetryCollectionEntries) { + telemetry_.duration_per_batch_size_.emplace(batch_size, TimeDiffMicroSeconds(tp)); + } // Emit RuntimePerf on scheduled interval if ((TimeDiffMicroSeconds(telemetry_.time_sent_last_) > telemetry_.runtime_perf_interval_)) { @@ -4620,11 +4632,16 @@ void InferenceSession::PopulateEpDeviceInfo(const onnxruntime::Graph& graph) { // cannot disambiguate node-to-device assignment here — every device row for the // same EP type will carry the same total count. Downstream aggregation should // use MAX (not SUM) across device rows to avoid double counting. - std::unordered_map nodes_per_ep; + std::unordered_map nodes_per_ep; for (const auto& node : graph.Nodes()) { - const auto& ep = node.GetExecutionProviderType(); + const auto ep = telemetry_detail::TelemetryStringView(std::string_view(node.GetExecutionProviderType())); if (!ep.empty()) { - nodes_per_ep[ep]++; + const auto entry = nodes_per_ep.find(ep); + if (entry != nodes_per_ep.end()) { + ++entry->second; + } else if (nodes_per_ep.size() < telemetry_detail::kMaxTelemetryCollectionEntries) { + nodes_per_ep.emplace(ep, 1); + } } } @@ -4632,33 +4649,35 @@ void InferenceSession::PopulateEpDeviceInfo(const onnxruntime::Graph& graph) { // falling back to OrtDevice for legacy EPs that were not created via the V2 // OrtEpDevice path. for (const auto& provider : execution_providers_) { + if (telemetry_.ep_device_info_.size() == telemetry_detail::kMaxTelemetryCollectionEntries) break; if (!provider) { continue; } - const std::string& ep_type = provider->Type(); + const auto ep_type = telemetry_detail::TelemetryStringView(std::string_view(provider->Type())); const int assigned_nodes = nodes_per_ep.count(ep_type) ? nodes_per_ep[ep_type] : 0; const auto& ep_devices = provider->GetEpDevices(); if (!ep_devices.empty()) { // V2 path: full hardware metadata available via OrtEpDevice / OrtHardwareDevice. for (const OrtEpDevice* ep_device : ep_devices) { + if (telemetry_.ep_device_info_.size() == telemetry_detail::kMaxTelemetryCollectionEntries) break; if (!ep_device) { continue; } Telemetry::EpDeviceInfo entry; - entry.ep_type = ep_type; - entry.ep_vendor = ep_device->ep_vendor; + entry.ep_type = telemetry_detail::BoundedTelemetryString(ep_type); + entry.ep_vendor = telemetry_detail::BoundedTelemetryString(ep_device->ep_vendor); auto it = ep_device->ep_metadata.Entries().find(kOrtEpDevice_EpMetadataKey_Version); if (it != ep_device->ep_metadata.Entries().end()) { - entry.ep_version = it->second; + entry.ep_version = telemetry_detail::BoundedTelemetryString(it->second); } if (ep_device->device != nullptr) { entry.hardware_device_type = HardwareDeviceTypeToString(ep_device->device->type); entry.vendor_id = ep_device->device->vendor_id; entry.device_id = ep_device->device->device_id; - entry.vendor = ep_device->device->vendor; + entry.vendor = telemetry_detail::BoundedTelemetryString(ep_device->device->vendor); } else { entry.hardware_device_type = "UNKNOWN"; } @@ -4672,7 +4691,7 @@ void InferenceSession::PopulateEpDeviceInfo(const onnxruntime::Graph& graph) { // empty vendor / device_id of 0. const OrtDevice& device = provider->GetDevice(); Telemetry::EpDeviceInfo entry; - entry.ep_type = ep_type; + entry.ep_type = telemetry_detail::BoundedTelemetryString(ep_type); entry.hardware_device_type = OrtDeviceTypeToString(device.Type()); entry.vendor_id = device.Vendor(); entry.device_id = 0; @@ -4686,27 +4705,23 @@ void InferenceSession::PopulateEpDeviceInfo(const onnxruntime::Graph& graph) { // Build the comma-separated summaries used to enrich SessionCreation. Order // matches execution_providers_.GetIds() iteration order so consumers can join // by position against the existing executionProviderIds field. - std::ostringstream types_oss; - std::ostringstream vendor_ids_oss; - std::ostringstream versions_oss; bool first = true; for (const auto& entry : telemetry_.ep_device_info_) { - if (!first) { - types_oss << ','; - vendor_ids_oss << ','; - versions_oss << ','; + // Format vendor IDs as hex for readability (PCI IDs are conventionally hex). + std::ostringstream vendor_id; + vendor_id << "0x" << std::hex << std::uppercase << std::setw(4) + << std::setfill('0') << entry.vendor_id + << std::dec << std::nouppercase << std::setfill(' '); + if (!telemetry_detail::AppendTelemetryRow( + std::array{&telemetry_.ep_device_types_summary_, &telemetry_.ep_device_vendor_ids_summary_, + &telemetry_.ep_versions_summary_}, + std::array{entry.hardware_device_type, vendor_id.str(), + entry.ep_type + ":" + entry.ep_version}, + first)) { + break; } first = false; - types_oss << entry.hardware_device_type; - // Format vendor IDs as hex for readability (PCI IDs are conventionally hex). - vendor_ids_oss << "0x" << std::hex << std::uppercase << std::setw(4) - << std::setfill('0') << entry.vendor_id - << std::dec << std::nouppercase << std::setfill(' '); - versions_oss << entry.ep_type << ':' << entry.ep_version; - } - telemetry_.ep_device_types_summary_ = types_oss.str(); - telemetry_.ep_device_vendor_ids_summary_ = vendor_ids_oss.str(); - telemetry_.ep_versions_summary_ = versions_oss.str(); + } } void InferenceSession::LogSessionCreationTelemetry(const onnxruntime::Graph& graph, @@ -4714,8 +4729,21 @@ void InferenceSession::LogSessionCreationTelemetry(const onnxruntime::Graph& gra const std::string& model_graph_hash, const std::string& model_weight_hash) { const Env& env = Env::Default(); - std::filesystem::path model_path = graph.ModelPath(); - std::string model_file_name = PathToUTF8String(model_path.filename().native()); + const auto& model_path = graph.ModelPath().native(); +#ifdef _WIN32 + const auto path_view = telemetry_detail::TelemetryWideStringView(model_path, telemetry_detail::kMaxTelemetryPathBytes); + const auto file_name = path_view.size() == model_path.size() + ? std::filesystem::path(path_view).filename().native() + : std::wstring{}; + std::string model_file_name = ToUTF8String(telemetry_detail::BoundedTelemetryWideString(file_name)); +#else + const auto path_view = + telemetry_detail::TelemetryStringView(std::string_view(model_path), telemetry_detail::kMaxTelemetryPathBytes); + const auto file_name = path_view.size() == model_path.size() + ? std::filesystem::path(path_view).filename().native() + : std::string{}; + std::string model_file_name = telemetry_detail::BoundedTelemetryString(file_name); +#endif bool model_has_fp16_inputs = ModelHasFP16Inputs(graph); // Populate per-(EP, hardware-device) telemetry data captured once at session diff --git a/onnxruntime/core/session/provider_policy_context.cc b/onnxruntime/core/session/provider_policy_context.cc index 5090c13436cda..d123cac009a92 100644 --- a/onnxruntime/core/session/provider_policy_context.cc +++ b/onnxruntime/core/session/provider_policy_context.cc @@ -4,6 +4,7 @@ #if !defined(ORT_MINIMAL_BUILD) #include "core/session/provider_policy_context.h" +#include "core/platform/telemetry_strings.h" #include #include @@ -222,19 +223,21 @@ Status ProviderPolicyContext::LogTelemetry(InferenceSession& sess, const std::vector& execution_devices, const std::vector& devices_selected) { std::vector requested_ep_ids; - requested_ep_ids.reserve(devices_selected.size()); + requested_ep_ids.reserve(std::min(devices_selected.size(), telemetry_detail::kMaxTelemetryCollectionEntries)); for (const auto* device : devices_selected) { + if (requested_ep_ids.size() == telemetry_detail::kMaxTelemetryCollectionEntries) break; if (device != nullptr) { - requested_ep_ids.push_back(device->ep_name); + requested_ep_ids.push_back(telemetry_detail::BoundedTelemetryString(device->ep_name)); } } // Extract available execution provider IDs std::vector available_ep_ids; - available_ep_ids.reserve(execution_devices.size()); + available_ep_ids.reserve(std::min(execution_devices.size(), telemetry_detail::kMaxTelemetryCollectionEntries)); for (const auto* device : execution_devices) { - available_ep_ids.push_back(device->ep_name); + if (available_ep_ids.size() == telemetry_detail::kMaxTelemetryCollectionEntries) break; + available_ep_ids.push_back(telemetry_detail::BoundedTelemetryString(device->ep_name)); } std::string policy_type; diff --git a/onnxruntime/core/session/provider_registration.cc b/onnxruntime/core/session/provider_registration.cc index c68ad570fbc44..9fef7a611075e 100644 --- a/onnxruntime/core/session/provider_registration.cc +++ b/onnxruntime/core/session/provider_registration.cc @@ -11,6 +11,7 @@ #include "core/framework/error_code_helper.h" #include "core/framework/provider_options.h" #include "core/graph/constants.h" +#include "core/platform/telemetry_strings.h" #include "core/providers/provider_factory_creators.h" #include "core/session/abi_session_options_impl.h" #include "core/session/onnxruntime_c_api.h" @@ -137,15 +138,18 @@ ORT_API_STATUS_IMPL(OrtApis::SessionOptionsAppendExecutionProvider, } #if defined(_WIN32) && defined(ONNXRUNTIME_ENABLE_INSTRUMENT) + size_t option_count = 0; for (const auto& config_pair : provider_options) { + if (option_count++ == telemetry_detail::kMaxTelemetryCollectionEntries) break; + telemetry_detail::TelemetryStrings strings; TraceLoggingWrite( telemetry_provider_handle, "ProviderOptionsAppendExecutionProvider", TraceLoggingKeyword(static_cast(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")); + TraceLoggingString(strings.Utf8(provider_name), "ProviderName"), + TraceLoggingString(strings.Utf8(config_pair.first), "Key"), + TraceLoggingString(strings.Utf8(config_pair.second), "Value")); } #endif diff --git a/onnxruntime/test/framework/onnx_test_loader_test.cc b/onnxruntime/test/framework/onnx_test_loader_test.cc new file mode 100644 index 0000000000000..2681f1020e18c --- /dev/null +++ b/onnxruntime/test/framework/onnx_test_loader_test.cc @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "core/graph/onnx_protobuf.h" +#include "core/session/onnxruntime_cxx_api.h" +#include "test/onnx/callback.h" +#include "test/onnx/mem_buffer.h" +#include "test/onnx/tensorprotoutils.h" + +namespace onnxruntime { +namespace test { +namespace { + +using ElementTypes = std::pair; + +class OnnxTestLoaderTest : public testing::TestWithParam { + protected: + void CheckTensor(bool raw_data) { + const auto [proto_type, api_type] = GetParam(); + EXPECT_EQ(CApiElementTypeFromProtoType(proto_type), api_type); + + constexpr int64_t element_count = 5; + const size_t byte_count = api_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_INT2 || + api_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT2 + ? 2 + : 5; + const std::array data{0x12, 0x34, 0x56, 0x78, 0x7f}; + ONNX_NAMESPACE::TensorProto proto; + proto.set_data_type(proto_type); + proto.add_dims(element_count); + if (raw_data) { + proto.set_raw_data(data.data(), byte_count); + } else { + for (size_t i = 0; i < byte_count; ++i) { + proto.add_int32_data(data[i]); + } + } + + std::array buffer{}; + auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + MemBuffer memory(buffer.data(), byte_count, *memory_info); + Ort::Value value{nullptr}; + OrtCallback deleter; + auto status = TensorProtoToMLValue(proto, memory, value, deleter); + ASSERT_TRUE(status.IsOK()) << status.ErrorMessage(); + EXPECT_EQ(deleter.f, nullptr); + + const auto tensor_info = value.GetTensorTypeAndShapeInfo(); + EXPECT_EQ(tensor_info.GetElementType(), api_type); + EXPECT_EQ(tensor_info.GetElementCount(), static_cast(element_count)); + ASSERT_EQ(tensor_info.GetDimensionsCount(), 1u); + EXPECT_EQ(tensor_info.GetShape()[0], element_count); + EXPECT_EQ(std::memcmp(value.GetTensorRawData(), data.data(), byte_count), 0); + } +}; + +TEST_P(OnnxTestLoaderTest, RawDataPreservesElementTypeAndPayload) { + CheckTensor(true); +} + +TEST_P(OnnxTestLoaderTest, Int32DataPreservesElementTypeAndPayload) { + CheckTensor(false); +} + +constexpr ElementTypes kElementTypes[] = { + {ONNX_NAMESPACE::TensorProto_DataType_UINT8, ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8}, + {ONNX_NAMESPACE::TensorProto_DataType_INT2, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT2}, + {ONNX_NAMESPACE::TensorProto_DataType_UINT2, ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT2}, +#if !defined(DISABLE_FLOAT8_TYPES) + {ONNX_NAMESPACE::TensorProto_DataType_FLOAT8E8M0, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT8E8M0}, +#endif +}; + +INSTANTIATE_TEST_SUITE_P(TensorProto, OnnxTestLoaderTest, testing::ValuesIn(kElementTypes)); + +} // namespace +} // namespace test +} // namespace onnxruntime diff --git a/onnxruntime/test/onnx/tensorprotoutils.cc b/onnxruntime/test/onnx/tensorprotoutils.cc index 5c8d8f9fd8f23..1b3346426ca9d 100644 --- a/onnxruntime/test/onnx/tensorprotoutils.cc +++ b/onnxruntime/test/onnx/tensorprotoutils.cc @@ -647,6 +647,7 @@ ONNXTensorElementDataType CApiElementTypeFromProtoType(int type) { CASE_TYPE(FLOAT8E4M3FNUZ) CASE_TYPE(FLOAT8E5M2) CASE_TYPE(FLOAT8E5M2FNUZ) + CASE_TYPE(FLOAT8E8M0) CASE_TYPE(FLOAT4E2M1) CASE_TYPE(UINT4) CASE_TYPE(INT4) @@ -748,7 +749,7 @@ Status TensorProtoToMLValue(const onnx::TensorProto& tensor_proto, const MemBuff } std::vector tensor_shape_vec = GetTensorShapeFromTensorProto(tensor_proto); // Note: We permit an empty tensor_shape_vec, and treat it as a scalar (a tensor of size 1). - value = Ort::Value::CreateTensor(&allocator, tensor_data, m.GetLen(), tensor_shape_vec.data(), tensor_shape_vec.size(), (ONNXTensorElementDataType)tensor_proto.data_type()); + value = Ort::Value::CreateTensor(&allocator, tensor_data, m.GetLen(), tensor_shape_vec.data(), tensor_shape_vec.size(), ele_type); return Status::OK(); } diff --git a/onnxruntime/test/platform/posix/device_id_test.cc b/onnxruntime/test/platform/posix/device_id_test.cc index 41bd27fc8ee99..0deb1dbbaa666 100644 --- a/onnxruntime/test/platform/posix/device_id_test.cc +++ b/onnxruntime/test/platform/posix/device_id_test.cc @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/platform/posix/device_id.h" +#include "core/platform/device_id.h" #include #include @@ -118,5 +118,24 @@ TEST(DeviceIdDeathTest, RepairsCorruptedFile) { EXPECT_NE(persisted, "corrupted"); } +TEST(DeviceIdDeathTest, LoadsExistingFileWithWhitespace) { + ScopedTestDirectory test_dir{"existing"}; + const fs::path home = test_dir.Path() / "home"; + ScopedEnvironmentVariables environment{ + EnvVarMap{{"HOME", home.string()}, {"XDG_CACHE_HOME", nullopt}}}; + const fs::path storage_dir = DeviceId::GetStorageDirectory(); + fs::create_directories(storage_dir); + constexpr std::string_view kExistingId = "11111111-2222-4333-8444-555555555555"; + std::ofstream(storage_dir / "deviceid") << " \t" << kExistingId << "\r\n"; + + EXPECT_EXIT( + { + const bool passed = DeviceId::Instance().GetStatus() == DeviceIdStatus::Existing && + DeviceId::Instance().GetValue() == kExistingId; + std::_Exit(passed ? EXIT_SUCCESS : EXIT_FAILURE); + }, + ::testing::ExitedWithCode(EXIT_SUCCESS), ""); +} + } // namespace } // namespace onnxruntime::test diff --git a/onnxruntime/test/platform/telemetry_context_test.cc b/onnxruntime/test/platform/telemetry_context_test.cc index d9d6057c1fced..e030ebaaf857b 100644 --- a/onnxruntime/test/platform/telemetry_context_test.cc +++ b/onnxruntime/test/platform/telemetry_context_test.cc @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/platform/posix/telemetry_context.h" -#include "core/platform/posix/telemetry_sha256.h" +#include "core/platform/telemetry_context.h" +#include "core/platform/telemetry_sha256.h" #include #include @@ -37,19 +37,17 @@ TEST(TelemetryContextTest, SuppressesUnneededCommonContext) { EXPECT_EQ(context.Fields().count("AppInfo.Name"), 0); } -TEST(TelemetryContextTest, SetsApplicationNameFromProcessName) { - RecordingSemanticContext context; - telemetry_internal::SetApplicationNameFromProcessName(context, "onnxruntime_test_all"); - - ASSERT_EQ(context.Fields().size(), 1); - EXPECT_EQ(context.Fields().at("AppInfo.Name"), "onnxruntime_test_all"); -} - -TEST(TelemetryContextTest, PreservesSdkApplicationNameFallbackWhenProcessNameIsUnavailable) { - RecordingSemanticContext context; - telemetry_internal::SetApplicationNameFromProcessName(context, ""); - - EXPECT_TRUE(context.Fields().empty()); +TEST(TelemetryContextTest, SetsBoundedApplicationNameOrPreservesSdkFallback) { + const std::string names[] = {"", "onnxruntime_test_all", std::string(kMaxTelemetryStringLength + 1, 'a')}; + for (const auto& name : names) { + SCOPED_TRACE(name.size()); + RecordingSemanticContext context; + context.SetCommonField("AppInfo.Name", "sdk fallback"); + telemetry_internal::SetApplicationNameFromProcessName(context, name); + ASSERT_EQ(context.Fields().size(), 1); + EXPECT_EQ(context.Fields().at("AppInfo.Name"), + name.empty() ? "sdk fallback" : name.substr(0, kMaxTelemetryStringLength)); + } } TEST(TelemetryContextTest, SuppressesNetworkContextAfterProcessInfo) { diff --git a/onnxruntime/test/platform/telemetry_environment_test.cc b/onnxruntime/test/platform/telemetry_environment_test.cc index 6fc135abc1c79..32d8e3e225c70 100644 --- a/onnxruntime/test/platform/telemetry_environment_test.cc +++ b/onnxruntime/test/platform/telemetry_environment_test.cc @@ -9,69 +9,85 @@ namespace onnxruntime { namespace test { -TEST(TelemetryEnvironmentTest, IsTruthyCiValue) { - using telemetry_detail::IsTruthyCiValue; - // Any non-empty, non-falsey value counts as present. - EXPECT_TRUE(IsTruthyCiValue("1")); - EXPECT_TRUE(IsTruthyCiValue("true")); - EXPECT_TRUE(IsTruthyCiValue("TRUE")); - EXPECT_TRUE(IsTruthyCiValue("yes")); - EXPECT_TRUE(IsTruthyCiValue(" 1 ")); - EXPECT_TRUE(IsTruthyCiValue("anything")); - - EXPECT_FALSE(IsTruthyCiValue("")); - EXPECT_FALSE(IsTruthyCiValue(" ")); - EXPECT_FALSE(IsTruthyCiValue("0")); - EXPECT_FALSE(IsTruthyCiValue("false")); - EXPECT_FALSE(IsTruthyCiValue("FALSE")); - EXPECT_FALSE(IsTruthyCiValue("no")); - EXPECT_FALSE(IsTruthyCiValue("off")); +TEST(TelemetryEnvironmentTest, BoundsValuesAndRejectsOversizedPaths) { + const std::string large(20000, 'a'); + ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_TEST_TELEMETRY_VALUE", large}}}; + EXPECT_FALSE(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE")); + EXPECT_FALSE(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", 4096)); + EXPECT_EQ(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", large.size()), large); + EXPECT_FALSE(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", large.size() - 1)); } -TEST(TelemetryEnvironmentTest, EnvVarOptOut) { - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_DISABLE_TELEMETRY", "1"}}}; - EXPECT_TRUE(IsTelemetryDisabledByEnvironment()); - } - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_DISABLE_TELEMETRY", "TRUE"}}}; - EXPECT_TRUE(IsTelemetryDisabledByEnvironment()); - } - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_DISABLE_TELEMETRY", "0"}}}; - EXPECT_FALSE(IsTelemetryDisabledByEnvironment()); - } - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_DISABLE_TELEMETRY", "random"}}}; - EXPECT_FALSE(IsTelemetryDisabledByEnvironment()); - } - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_DISABLE_TELEMETRY", nullopt}}}; - EXPECT_FALSE(IsTelemetryDisabledByEnvironment()); - } +TEST(TelemetryEnvironmentTest, RejectsEnvironmentValuesOverUtf8ByteBudget) { + const std::string prefix = std::string(1021, 'a') + "\xe2\x82\xac"; + ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_TEST_TELEMETRY_VALUE", prefix + "tail"}}}; +#ifdef _WIN32 + const std::wstring wide = std::wstring(1021, L'a') + L"\u20actail"; + ASSERT_NE(::SetEnvironmentVariableW(L"ORT_TEST_TELEMETRY_VALUE", wide.c_str()), 0); +#endif + EXPECT_FALSE(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", prefix.size())); + EXPECT_EQ(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", prefix.size() + 4), + prefix + "tail"); } -TEST(TelemetryEnvironmentTest, CiDetectionSuppresses) { - // Only the positive direction is asserted so the test is deterministic whether or not it itself - // runs in a CI environment. APPVEYOR is not part of ORT's own CI, so save/restore stays clean. - ScopedEnvironmentVariables env_vars{EnvVarMap{{"APPVEYOR", "true"}}}; - EXPECT_TRUE(IsRunningInCI()); +TEST(TelemetryEnvironmentTest, DistinguishesAbsentEmptyAndRejectedValues) { + for (const auto& value : {std::optional{}, std::optional{""}}) { + ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_TEST_TELEMETRY_VALUE", value}}}; + EXPECT_EQ(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", 0), ""); + } + ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_TEST_TELEMETRY_VALUE", "x"}}}; + EXPECT_FALSE(telemetry_detail::ReadTelemetryEnvironment("ORT_TEST_TELEMETRY_VALUE", 0)); } -TEST(TelemetryEnvironmentTest, RunningUnitTestsSuppresses) { - // The unit-test entry point sets ORT_RUNNING_UNIT_TESTS process-wide; save/restore so this test can - // exercise both directions without leaking state to siblings. - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_RUNNING_UNIT_TESTS", "1"}}}; +TEST(TelemetryEnvironmentTest, SuppressionFlagsAreNotTruncatedAndOversizedValuesFailClosed) { + const std::string padded = std::string(kMaxTelemetryStringLength, ' ') + "true"; + const std::string oversized(telemetry_detail::kMaxTelemetryProbeBytes + 1, ' '); + for (const auto& value : {padded, oversized}) { + ScopedEnvironmentVariables env_vars{ + EnvVarMap{{"APPVEYOR", value}, {"ORT_RUNNING_UNIT_TESTS", value}, {"ORT_DISABLE_TELEMETRY", value}}}; + EXPECT_TRUE(IsRunningInCI()); EXPECT_TRUE(IsRunningUnitTests()); + EXPECT_TRUE(IsTelemetryDisabledByEnvironment()); } - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_RUNNING_UNIT_TESTS", "0"}}}; - EXPECT_FALSE(IsRunningUnitTests()); - } - { - ScopedEnvironmentVariables env_vars{EnvVarMap{{"ORT_RUNNING_UNIT_TESTS", nullopt}}}; - EXPECT_FALSE(IsRunningUnitTests()); + EXPECT_TRUE(telemetry_detail::IsTruthyCiValue(oversized)); +} + +TEST(TelemetryEnvironmentTest, SuppressionFlagsHonorValuesIndependentlyOfRunnerEnvironment) { + EnvVarMap cleared_ci; + for (const char* name : telemetry_detail::kCiEnvironmentVariableNames) { + cleared_ci.emplace(name, nullopt); + } + ScopedEnvironmentVariables runner_environment{cleared_ci}; + const struct { + optional value; + bool ci_or_unit_test; + bool opt_out; + } cases[] = { + {nullopt, false, false}, + {"", false, false}, + {" ", false, false}, + {"0", false, false}, + {"false", false, false}, + {"FALSE", false, false}, + {"no", false, false}, + {"off", false, false}, + {"1", true, true}, + {"true", true, true}, + {"TRUE", true, true}, + {"yes", true, true}, + {"on", true, true}, + {"y", true, true}, + {" 1 ", true, true}, + {"anything", true, false}, + }; + for (const auto& [value, ci_or_unit_test, opt_out] : cases) { + SCOPED_TRACE(value.value_or("")); + ScopedEnvironmentVariables env_vars{ + EnvVarMap{{"APPVEYOR", value}, {"ORT_RUNNING_UNIT_TESTS", value}, {"ORT_DISABLE_TELEMETRY", value}}}; + EXPECT_EQ(IsRunningInCI(), ci_or_unit_test); + EXPECT_EQ(IsRunningUnitTests(), ci_or_unit_test); + EXPECT_EQ(IsTelemetryDisabledByEnvironment(), opt_out); + if (value) EXPECT_EQ(telemetry_detail::IsTruthyCiValue(*value), ci_or_unit_test); } } diff --git a/onnxruntime/test/platform/telemetry_guid_test.cc b/onnxruntime/test/platform/telemetry_guid_test.cc new file mode 100644 index 0000000000000..107ee8e8c5b1b --- /dev/null +++ b/onnxruntime/test/platform/telemetry_guid_test.cc @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/platform/telemetry_guid.h" + +#include "gtest/gtest.h" + +namespace onnxruntime::test { + +TEST(TelemetryGuidTest, ValidatesPersistedGuidFormat) { + for (const char* value : {"11111111-2222-4333-8444-555555555555", + "01234567-89ab-cdef-ABCD-0123456789AB"}) { + EXPECT_TRUE(IsValidGuid(value)) << value; + } + for (const char* value : {"", "corrupted", "11111111-2222-4333-8444-55555555555", + "11111111-2222-4333-8444-5555555555555", + "11111111_2222-4333-8444-555555555555", + "11111111-2222-4333-8444-55555555555g"}) { + EXPECT_FALSE(IsValidGuid(value)) << value; + } + const std::string embedded_null("11111111-2222-4333-8444-555555555555\0junk", 41); + EXPECT_FALSE(IsValidGuid(embedded_null)); +} + +TEST(TelemetryGuidTest, GeneratesVersionFourGuid) { + const auto guid = GenerateGuidV4(); + ASSERT_TRUE(IsValidGuid(guid)); + EXPECT_EQ(guid[14], '4'); + EXPECT_NE(std::string_view("89ab").find(guid[19]), std::string_view::npos); +} + +} // namespace onnxruntime::test diff --git a/onnxruntime/test/platform/telemetry_no_throw_test.cc b/onnxruntime/test/platform/telemetry_no_throw_test.cc index f3e3c2e1ad448..06ead31524397 100644 --- a/onnxruntime/test/platform/telemetry_no_throw_test.cc +++ b/onnxruntime/test/platform/telemetry_no_throw_test.cc @@ -1,69 +1,51 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "gtest/gtest.h" +#include "core/platform/telemetry_no_throw.h" #include -#include -#include "core/platform/posix/telemetry_no_throw.h" +#include "gtest/gtest.h" namespace onnxruntime::test { -#ifndef ORT_NO_EXCEPTIONS - -TEST(TelemetryNoThrowTest, EventConstructionExceptionDoesNotEscape) { - std::string warning; - - EXPECT_NO_THROW(telemetry_internal::RunTelemetryOperationNoThrow( - []() { - throw std::runtime_error("event construction failed"); - }, - [&](const char* message) { - warning = message; - })); - - EXPECT_EQ(warning, "event construction failed"); +TEST(TelemetryNoThrowTest, SuccessfulOperationsDoNotWarn) { + int calls = 0; + EXPECT_TRUE(telemetry_internal::TryTelemetryOperationNoThrow([&]() { ++calls; })); + telemetry_internal::RunTelemetryOperationNoThrow( + [&]() { ++calls; }, [](const char*) { ADD_FAILURE() << "successful operation warned"; }); + EXPECT_EQ(calls, 2); } -TEST(TelemetryNoThrowTest, SdkEmissionUnknownExceptionDoesNotEscape) { - struct ThrowingSdkLogger { - void LogEvent(int) const { - throw 42; - } - } logger; - bool warned = false; - - EXPECT_NO_THROW(telemetry_internal::RunTelemetryOperationNoThrow( - [&]() { - const int event = 1; - logger.LogEvent(event); - }, - [&](const char* message) { - warned = true; - EXPECT_EQ(message, nullptr); - })); +#ifndef ORT_NO_EXCEPTIONS - EXPECT_TRUE(warned); +TEST(TelemetryNoThrowTest, ReportsStandardAndUnknownExceptionsWithoutEscaping) { + for (bool standard_exception : {false, true}) { + SCOPED_TRACE(standard_exception); + const auto operation = [standard_exception]() { + if (standard_exception) throw std::runtime_error("event failed"); + throw 42; + }; + int warnings = 0; + EXPECT_NO_THROW(telemetry_internal::RunTelemetryOperationNoThrow( + operation, [&](const char* message) { + ++warnings; + if (standard_exception) { + ASSERT_NE(message, nullptr); + EXPECT_STREQ(message, "event failed"); + } else { + EXPECT_EQ(message, nullptr); + } + })); + EXPECT_EQ(warnings, 1); + EXPECT_FALSE(telemetry_internal::TryTelemetryOperationNoThrow(operation)); + } } TEST(TelemetryNoThrowTest, DiagnosticExceptionDoesNotEscape) { EXPECT_NO_THROW(telemetry_internal::RunTelemetryOperationNoThrow( - []() { - throw std::runtime_error("event failed"); - }, - [](const char*) { - throw std::runtime_error("diagnostic failed"); - })); -} - -TEST(TelemetryNoThrowTest, BestEffortOperationReportsSuccess) { - EXPECT_TRUE(telemetry_internal::TryTelemetryOperationNoThrow([]() {})); -} - -TEST(TelemetryNoThrowTest, BestEffortOperationContainsFailure) { - EXPECT_FALSE(telemetry_internal::TryTelemetryOperationNoThrow( - []() { throw std::runtime_error("best-effort failure"); })); + []() { throw std::runtime_error("event failed"); }, + [](const char*) { throw std::runtime_error("diagnostic failed"); })); } #endif // ORT_NO_EXCEPTIONS diff --git a/onnxruntime/test/platform/telemetry_redaction_test.cc b/onnxruntime/test/platform/telemetry_redaction_test.cc index 2ebda575f0430..49e0d6fb0b9c0 100644 --- a/onnxruntime/test/platform/telemetry_redaction_test.cc +++ b/onnxruntime/test/platform/telemetry_redaction_test.cc @@ -4,128 +4,120 @@ #include "core/platform/telemetry_redaction.h" #include +#include #include "gtest/gtest.h" -namespace onnxruntime { -namespace test { - -// The scrubber is anchor-based: it finds the FIRST filesystem-path anchor (drive prefix, UNC prefix, -// home prefix, a relative Windows path with >=2 '\'-delimited segments, or a >=2-segment POSIX path) and -// replaces everything from that anchor to the end of the message with a single "[path]" placeholder. -// Redacting to end-of-message -- rather than classifying each whitespace-delimited token -- is what makes -// a space-separated user name (C:\Users\First Last\...) impossible to leak. - -TEST(TelemetryRedactionTest, EmptyAndNoPath) { - EXPECT_EQ(ScrubStringForTelemetry(""), ""); - EXPECT_EQ(ScrubStringForTelemetry("no path here"), "no path here"); - EXPECT_EQ(ScrubStringForTelemetry("error code 13"), "error code 13"); -} - -TEST(TelemetryRedactionTest, AbsolutePosixAndHomePathsFullyRedacted) { - // An anchor at the very start collapses the whole string to "[path]"; nothing (not even the parent - // directory or file name) is retained, so a user name can never survive. - EXPECT_EQ(ScrubStringForTelemetry("/home/alice/model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("/home/alice/proj/rn/model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("/data/models/rn/model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("/data/models/secret/"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("~/.config/app/x"), "[path]"); -} - -TEST(TelemetryRedactionTest, WindowsDriveAndUncFullyRedacted) { - EXPECT_EQ(ScrubStringForTelemetry("C:\\Users\\bob\\model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("C:/Users\\bob\\model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("\\\\server\\share\\dir\\weights.bin"), "[path]"); -} - -TEST(TelemetryRedactionTest, EmbeddedPathRedactedToEndOfMessage) { - // Everything before the anchor is kept verbatim; everything from the anchor onward (including any - // trailing prose after the path) is replaced, because a path may contain spaces. - EXPECT_EQ(ScrubStringForTelemetry("Load model from /home/alice/models/foo.onnx failed"), - "Load model from [path]"); - EXPECT_EQ(ScrubStringForTelemetry("Load C:\\proj\\bin\\m.onnx failed"), "Load [path]"); - EXPECT_EQ(ScrubStringForTelemetry("open D:/data/secret/model.onnx"), "open [path]"); - EXPECT_EQ(ScrubStringForTelemetry("from \\\\server\\share\\dir\\weights.bin done"), "from [path]"); -} - -TEST(TelemetryRedactionTest, SpacedUsernameFullyRedacted) { - // A space inside a path used to split it into separate tokens and let the trailing name half - // ("Last") leak. Anchoring on the drive prefix and redacting to end-of-message removes both halves. - const std::string spaced = ScrubStringForTelemetry("Load C:\\Users\\First Last\\model.onnx failed"); - EXPECT_EQ(spaced, "Load [path]"); - EXPECT_EQ(spaced.find("First"), std::string::npos); - EXPECT_EQ(spaced.find("Last"), std::string::npos); -} - -TEST(TelemetryRedactionTest, HomeUsernameNeverLeaksAcrossVariants) { - // Case, mixed/duplicate separators, "." segments, and embedding in a larger token must never leave - // the user name in the output. Exact output varies with where the first anchor lands, so the - // security-relevant invariant is asserted directly: the user name is absent and a redaction occurred. - const char* inputs[] = { - "C:\\UsErS\\alice\\model.onnx", - "C:\\Users/alice\\model.onnx", - "/UsErS/alice/model.onnx", - "C:\\Users/alice/proj\\model.onnx", - "/home//alice/model.onnx", - "C:\\Users\\\\alice\\model.onnx", - "/home/./alice/model.onnx", - "input:/home/alice/secret/m.onnx", - "file:///home/alice/secret/model.onnx", - "Users\\alice\\model.onnx", - "at proj\\alice\\weights\\m.onnx", - "alice/models/phi3.onnx", - "at alice/models/phi3.onnx", +namespace onnxruntime::test { + +TEST(TelemetryRedactionTest, RedactsPathsToEndOfMessage) { + const struct { + const char* input; + const char* expected; + } cases[] = { + {"/home/alice/model.onnx", "[path]"}, + {"/data/models/secret/", "[path]"}, + {"~/.config/app/x", "[path]"}, + {"~\\config\\app", "[path]"}, + {"C:\\Users\\bob\\model.onnx", "[path]"}, + {"C:/Users\\bob\\model.onnx", "[path]"}, + {"\\\\server\\share\\dir\\weights.bin", "[path]"}, + {"Load model from /home/alice/models/foo.onnx failed", "Load model from [path]"}, + {"Load C:\\proj\\bin\\m.onnx failed", "Load [path]"}, + {"open D:/data/secret/model.onnx", "open [path]"}, + {"from \\\\server\\share\\dir\\weights.bin done", "from [path]"}, + {"Load C:\\Users\\First Last\\model.onnx failed", "Load [path]"}, + {"C:\\UsErS\\alice\\model.onnx", "[path]"}, + {"C:\\Users/alice\\model.onnx", "[path]"}, + {"/UsErS/alice/model.onnx", "[path]"}, + {"C:\\Users/alice/proj\\model.onnx", "[path]"}, + {"/home//alice/model.onnx", "[path]"}, + {"C:\\Users\\\\alice\\model.onnx", "[path]"}, + {"/home/./alice/model.onnx", "[path]"}, + {"at proj\\alice\\weights\\m.onnx", "at [path]"}, + {"alice/models/phi3.onnx", "[path]"}, + {"at alice/models/phi3.onnx", "at [path]"}, + {"a/b/c", "[path]"}, + {"x/y/z/", "[path]"}, + {"a\\b\\c", "[path]"}, + {"alice\\models\\phi3.onnx", "[path]"}, + {"Users\\alice\\model.onnx", "[path]"}, + {"Load Users\\bob\\m.onnx failed", "Load [path]"}, + {"Users\\First Last\\model.onnx", "[path]"}, + {"Load Users\\First Last\\m.onnx failed", "Load [path]"}, }; - for (const char* in : inputs) { - const std::string out = ScrubStringForTelemetry(in); - EXPECT_EQ(out.find("alice"), std::string::npos) << "user name leaked for input: " << in; - EXPECT_NE(out.find("[path]"), std::string::npos) << "no redaction happened for input: " << in; + for (const auto& [input, expected] : cases) { + SCOPED_TRACE(input); + EXPECT_EQ(ScrubStringForTelemetry(input), expected); + EXPECT_EQ(ScrubStringForTelemetry(std::string_view(input)), expected); + } + // URI schemes can resemble drive prefixes; their retained prefix is not part of the privacy contract. + for (const char* input : {"input:/home/alice/secret/m.onnx", "file:///home/alice/secret/model.onnx"}) { + SCOPED_TRACE(input); + const auto output = ScrubStringForTelemetry(input); + EXPECT_EQ(output.find("alice"), std::string::npos); + EXPECT_NE(output.find("[path]"), std::string::npos); } } -TEST(TelemetryRedactionTest, MultiSegmentRelativePathReplaced) { - // A relative token with 2+ "/x" segments is a path anchor; the character(s) before the leading - // slash are kept and the rest is redacted. - EXPECT_EQ(ScrubStringForTelemetry("a/b/c"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("x/y/z/"), "[path]"); -} - -TEST(TelemetryRedactionTest, RelativeWindowsPathReplaced) { - // A drive-less relative Windows path (>= 2 '\'-delimited segments) has no C:\ / UNC / home prefix to - // anchor on, but is still a path: it anchors at the first backslash and redacts to end-of-message, so - // the user name in the second segment cannot leak. - EXPECT_EQ(ScrubStringForTelemetry("a\\b\\c"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("alice\\models\\phi3.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("Users\\alice\\model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("Load Users\\bob\\m.onnx failed"), "Load [path]"); - EXPECT_EQ(ScrubStringForTelemetry("Users\\First Last\\model.onnx"), "[path]"); - EXPECT_EQ(ScrubStringForTelemetry("Load Users\\First Last\\m.onnx failed"), "Load [path]"); +TEST(TelemetryRedactionTest, PreservesNonPaths) { + for (const char* input : {"", "no path here", "error code 13", "models/foo.onnx", + "ratio 3/4 and/or", "domain\\user", "read\\write access"}) { + SCOPED_TRACE(input); + EXPECT_EQ(ScrubStringForTelemetry(input), input); + } } -TEST(TelemetryRedactionTest, SingleSegmentAndNonPathSlashesKept) { - // A single "/x" segment is not enough to anchor a path, so ordinary text with slashes is preserved. - EXPECT_EQ(ScrubStringForTelemetry("models/foo.onnx"), "models/foo.onnx"); - EXPECT_EQ(ScrubStringForTelemetry("ratio 3/4 and/or"), "ratio 3/4 and/or"); - // A single backslash is likewise not a path anchor, so a Windows account name (DOMAIN\user) and other - // one-backslash tokens are kept verbatim rather than over-redacted. - EXPECT_EQ(ScrubStringForTelemetry("domain\\user"), "domain\\user"); - EXPECT_EQ(ScrubStringForTelemetry("read\\write access"), "read\\write access"); +TEST(TelemetryRedactionTest, BoundsAndSanitizesOutput) { + EXPECT_EQ(ScrubStringForTelemetry(std::string(kMaxTelemetryStringLength + 1, 'x')), + std::string(kMaxTelemetryStringLength, 'x')); + const std::string euro = "\xE2\x82\xAC"; + const std::string prefix(kMaxTelemetryStringLength - 1, 'x'); + EXPECT_EQ(ScrubStringForTelemetry(prefix + euro), prefix); + const std::string exact = std::string(kMaxTelemetryStringLength - euro.size(), 'x') + euro; + EXPECT_EQ(ScrubStringForTelemetry(exact), exact); + EXPECT_EQ(ScrubStringForTelemetry("\x80\xC0\xAF"), "???"); } -TEST(TelemetryRedactionTest, LengthIsCappedAfterScrub) { - const std::string long_msg(kMaxTelemetryStringLength + 1, 'x'); - EXPECT_EQ(ScrubStringForTelemetry(long_msg).size(), kMaxTelemetryStringLength); - EXPECT_LE(ScrubStringForTelemetry("short").size(), kMaxTelemetryStringLength); +TEST(TelemetryRedactionTest, PreservesPathEvidenceAcrossIntakeLimits) { + for (const char separator : {'/', '\\'}) { + const std::string path = "Load alice" + std::string(1, separator) + + std::string(2000, 'x') + separator + "model"; + EXPECT_EQ(ScrubStringForTelemetry(path), "Load [path]"); + const std::string oversized = "alice" + std::string(1, separator) + + std::string(telemetry_detail::kMaxTelemetryProbeBytes, 'x') + + separator + "model"; + const auto view = telemetry_detail::TelemetryCStringView( + oversized.c_str(), telemetry_detail::kMaxTelemetryProbeBytes); + EXPECT_EQ(view.size(), telemetry_detail::kMaxTelemetryProbeBytes + 1); + EXPECT_EQ(ScrubStringForTelemetry(view), "[path]"); + EXPECT_EQ(ScrubStringForTelemetry(oversized.c_str()), "[path]"); + } + EXPECT_EQ(ScrubStringForTelemetry(std::string(telemetry_detail::kMaxTelemetryProbeBytes + 1, 'a')), "[path]"); + EXPECT_EQ(ScrubStringForTelemetry("Load alice" + std::string(telemetry_detail::kMaxTelemetryProbeBytes, 'x') + + "/models/file"), + "Load [path]"); + EXPECT_EQ(ScrubStringForTelemetry("Load alice", true), "Load [path]"); + EXPECT_EQ(ScrubStringForTelemetry(nullptr), ""); + const std::string harmless = "ratio 3/4 " + std::string(2000, 'x'); + EXPECT_EQ(ScrubStringForTelemetry(harmless), harmless.substr(0, kMaxTelemetryStringLength)); } -TEST(TelemetryRedactionTest, LengthCapDoesNotSplitUtf8Codepoint) { - const std::string euro = "\xE2\x82\xAC"; - const std::string partial_tail = std::string(kMaxTelemetryStringLength - 1, 'x') + euro; - EXPECT_EQ(ScrubStringForTelemetry(partial_tail), std::string(kMaxTelemetryStringLength - 1, 'x')); - - const std::string exact_boundary = std::string(kMaxTelemetryStringLength - euro.size(), 'x') + euro; - EXPECT_EQ(ScrubStringForTelemetry(exact_boundary), exact_boundary); +TEST(TelemetryRedactionTest, ScrubsBeforePointerStorageAndLocationTruncation) { + const std::string message = "Using backend path: C:\\Users\\First Last\\QnnHtp.dll"; + telemetry_detail::TelemetryStrings strings; + const char* stored_message = strings.Utf8(ScrubStringForTelemetry(message)); + const char* stored_identifier = strings.Utf8(ScrubStringForTelemetry("\\\\server\\share\\model.bin")); + const char* stored_category = strings.Utf8(ScrubStringForTelemetry("backend /home/alice/qnn")); + EXPECT_STREQ(stored_message, "Using backend path: [path]"); + EXPECT_STREQ(stored_identifier, "[path]"); + EXPECT_STREQ(stored_category, "backend [path]"); + EXPECT_EQ(message, "Using backend path: C:\\Users\\First Last\\QnnHtp.dll"); + + const std::string function = "function alice/" + std::string(kMaxTelemetryStringLength, 'x') + "/model"; + std::string location = "qnn_execution_provider.cc:377 "; + telemetry_detail::AppendTelemetryString(location, ScrubStringForTelemetry(function)); + EXPECT_EQ(location, "qnn_execution_provider.cc:377 function [path]"); } -} // namespace test -} // namespace onnxruntime +} // namespace onnxruntime::test diff --git a/onnxruntime/test/platform/telemetry_sampling_test.cc b/onnxruntime/test/platform/telemetry_sampling_test.cc index f5bfac2b5e427..d2c235b95011f 100644 --- a/onnxruntime/test/platform/telemetry_sampling_test.cc +++ b/onnxruntime/test/platform/telemetry_sampling_test.cc @@ -1,47 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/platform/posix/telemetry_sampling.h" +#include "core/platform/telemetry_sampling.h" #include "gtest/gtest.h" namespace onnxruntime::test { namespace { -TEST(TelemetrySamplingTest, SessionDecisionIsStable) { - constexpr std::string_view app_session_guid = "00000000-0000-0000-0000-000000000001"; - constexpr uint32_t session_id = 42; - - const bool decision = telemetry_internal::ShouldSampleSession( - app_session_guid, session_id, telemetry_internal::kHighVolumeEventSampleRatePercent); - for (int i = 0; i < 100; ++i) { - EXPECT_EQ(telemetry_internal::ShouldSampleSession( - app_session_guid, session_id, - telemetry_internal::kHighVolumeEventSampleRatePercent), - decision); - } -} - TEST(TelemetrySamplingTest, HonorsBoundaryRates) { EXPECT_FALSE(telemetry_internal::ShouldSampleSession("guid", 1, 0.0)); EXPECT_TRUE(telemetry_internal::ShouldSampleSession("guid", 1, 100.0)); } -TEST(TelemetrySamplingTest, UsesOnePercentRates) { - EXPECT_EQ(telemetry_internal::kModelSessionSampleRatePercent, 1.0); - EXPECT_EQ(telemetry_internal::kHighVolumeEventSampleRatePercent, 1.0); - EXPECT_EQ(telemetry_internal::kProcessEventSampleRatePercent, 1.0); -} - -TEST(TelemetrySamplingTest, HighVolumeRateSamplesExpectedFraction) { +TEST(TelemetrySamplingTest, SessionDecisionsAreStableAndSampleExpectedFraction) { constexpr uint32_t session_count = 100000; uint32_t sampled_count = 0; for (uint32_t session_id = 0; session_id < session_count; ++session_id) { - sampled_count += telemetry_internal::ShouldSampleSession( - "00000000-0000-0000-0000-000000000001", session_id, - telemetry_internal::kHighVolumeEventSampleRatePercent) - ? 1 - : 0; + const bool sampled = telemetry_internal::ShouldSampleSession( + "00000000-0000-0000-0000-000000000001", session_id, 1.0); + ASSERT_EQ(telemetry_internal::ShouldSampleSession( + "00000000-0000-0000-0000-000000000001", session_id, 1.0), + sampled) + << session_id; + sampled_count += sampled ? 1 : 0; } EXPECT_GT(sampled_count, 900u); diff --git a/onnxruntime/test/platform/telemetry_strings_test.cc b/onnxruntime/test/platform/telemetry_strings_test.cc new file mode 100644 index 0000000000000..395565fb1c3df --- /dev/null +++ b/onnxruntime/test/platform/telemetry_strings_test.cc @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/platform/telemetry_strings.h" +#include "core/platform/telemetry_redaction.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace onnxruntime::test { +using namespace telemetry_detail; + +TEST(TelemetryStringsTest, CapsByteLengthsAndPreservesTighterLimits) { + for (size_t size : {size_t{1023}, size_t{1024}, size_t{1025}, size_t{1000000}}) { + const std::string input(size, 'x'); + EXPECT_EQ(BoundedTelemetryString(input).size(), std::min(size, size_t{1024})); + EXPECT_EQ(BoundedTelemetryString(input, 256).size(), 256); + EXPECT_EQ(BoundedTelemetryString(input.c_str()).size(), std::min(size, size_t{1024})); + } + EXPECT_TRUE(BoundedTelemetryString(nullptr).empty()); + EXPECT_TRUE(BoundedTelemetryString("value", 0).empty()); + const char non_terminated[] = {'a', 'b', 'c'}; + EXPECT_EQ(BoundedTelemetryString(std::string_view(non_terminated, 2)), "ab"); +} + +TEST(TelemetryStringsTest, DoesNotSplitUtf8Characters) { + for (const std::string character : {"\xc2\xa2", "\xe2\x82\xac", "\xf0\x9f\x98\x80"}) { + const std::string exact = std::string(1024 - character.size(), 'a') + character; + EXPECT_EQ(BoundedTelemetryString(exact + "z"), exact); + EXPECT_EQ(BoundedTelemetryString((exact + "z").c_str()), exact); + for (size_t prefix_size = 1025 - character.size(); prefix_size < 1024; ++prefix_size) { + const std::string prefix(prefix_size, 'a'); + EXPECT_EQ(BoundedTelemetryString(prefix + character), prefix); + EXPECT_EQ(BoundedTelemetryString((prefix + character).c_str()), prefix); + } + } +} + +TEST(TelemetryStringsTest, BoundsWideStringsByUtf8Bytes) { + const std::wstring exact = std::wstring(1021, L'a') + L"\u20ac"; + EXPECT_EQ(TelemetryWideStringView(exact + L"x"), exact); + EXPECT_EQ(TelemetryWideStringView(std::wstring(1022, L'a') + L"\u20ac").size(), 1022); + const std::wstring supplementary = L"\U0001f600"; + const std::wstring pair_exact = std::wstring(1020, L'a') + supplementary; + EXPECT_EQ(TelemetryWideStringView(pair_exact + L"x"), pair_exact); + EXPECT_EQ(TelemetryWideStringView(std::wstring(1021, L'a') + supplementary).size(), 1021); + EXPECT_TRUE(TelemetryWideStringView(static_cast(nullptr)).empty()); +} + +TEST(TelemetryStringsTest, SanitizesMalformedUtf8BeforeStorageAndTransmission) { + const std::string malformed = "\x80\xC0\xAF\xED\xA0\x80\xF4\x90\x80\x80"; + EXPECT_EQ(BoundedTelemetryString(malformed), "??????????"); + EXPECT_EQ(BoundedTelemetryString(malformed.c_str()), "??????????"); + const std::string continuations(2000, '\x80'); + EXPECT_EQ(BoundedTelemetryString(continuations), std::string(1024, '?')); + EXPECT_EQ(BoundedTelemetryString(continuations.c_str()), std::string(1024, '?')); + EXPECT_EQ(BoundedTelemetryString("\xF0\x9F"), "??"); + std::string output = "prefix "; + EXPECT_TRUE(AppendTelemetryString(output, malformed)); + EXPECT_EQ(output, "prefix ??????????"); + EXPECT_EQ(JoinTelemetryStrings(std::vector{malformed, "valid"}), "??????????,valid"); + TelemetryStrings strings; + EXPECT_STREQ(strings.Utf8(malformed), "??????????"); +} + +TEST(TelemetryStringsTest, BoundsJniModifiedUtf8AndKeepsSurrogatePairs) { + const std::u16string supplementary = u"\U0001f600"; + const std::u16string exact = std::u16string(1018, u'a') + supplementary; + EXPECT_EQ(TelemetryUnicodeStringView(std::u16string_view(exact), 1024, true), exact); + const std::u16string over = std::u16string(1019, u'a') + supplementary; + EXPECT_EQ(TelemetryUnicodeStringView(std::u16string_view(over), 1024, true).size(), 1019); + EXPECT_EQ(TelemetryUnicodeStringView(std::u16string_view(over), 1024, false).size(), over.size()); + const std::u16string nul = std::u16string(1023, u'a') + u'\0'; + EXPECT_EQ(TelemetryUnicodeStringView(std::u16string_view(nul), 1024, true).size(), 1023); + const std::u16string euro = std::u16string(1022, u'a') + u'\u20ac'; + EXPECT_EQ(TelemetryUnicodeStringView(std::u16string_view(euro), 1024, true).size(), 1022); +} + +TEST(TelemetryStringsTest, BoundsJniUnsignedShortCodeUnitsWithoutCharTraits) { + const std::array value{0x61, 0xD83D, 0xDE00, 0}; + EXPECT_EQ(TelemetryUnicodePrefixLength(value.data(), value.size(), 6, true), size_t{1}); + EXPECT_EQ(TelemetryUnicodePrefixLength(value.data(), value.size(), 7, true), size_t{3}); + EXPECT_EQ(TelemetryUnicodePrefixLength(value.data(), value.size(), 8, true), size_t{3}); + EXPECT_EQ(TelemetryUnicodePrefixLength(value.data(), value.size(), 9, true), value.size()); +} + +TEST(TelemetryStringsTest, AppendDoesNotExceedBudgetOrSplitCharacters) { + std::string output(1023, 'a'); + EXPECT_FALSE(AppendTelemetryString(output, "\xc2\xa2")); + EXPECT_EQ(output.size(), 1023); + EXPECT_TRUE(AppendTelemetryString(output, "b")); + EXPECT_FALSE(AppendTelemetryString(output, "c")); + EXPECT_EQ(output.size(), 1024); + EXPECT_TRUE(AppendTelemetryString(output, "")); + output.assign(100000, 'a'); + EXPECT_FALSE(AppendTelemetryString(output, "b")); + EXPECT_EQ(output.size(), 1024); +} + +TEST(TelemetryStringsTest, BoundsAggregatesAndCollectionCounts) { + const std::vector empty_values(100000); + EXPECT_EQ(JoinTelemetryStrings(empty_values), std::string(127, ',')); + EXPECT_EQ(JoinTelemetryStrings(std::vector{"a", "b"}), "a,b"); + EXPECT_EQ(JoinTelemetryStrings(std::vector{std::string(100000, 'x'), "b"}).size(), 1024); + const std::map values{{"b", "two"}, {"a", "one"}}; + EXPECT_EQ(FormatTelemetryMap(values), "a=one,b=two"); + EXPECT_EQ(FormatTelemetryMap(values, ",", ":"), "a:one,b:two"); + const std::map large{{std::string(100000, 'x'), std::string(100000, 'y')}}; + EXPECT_EQ(FormatTelemetryMap(large).size(), 1024); + std::map many; + for (int i = 0; i < 1000; ++i) many.emplace(std::to_string(i), ""); + bool truncated = false; + const auto formatted = FormatTelemetryMap(many, ",", "=", &truncated); + EXPECT_TRUE(truncated); + EXPECT_EQ(std::count(formatted.begin(), formatted.end(), '='), 128); +} + +TEST(TelemetryStringsTest, AppendsCompleteAlignedRows) { + std::array summaries; + const std::array outputs{&summaries[0], &summaries[1], &summaries[2]}; + EXPECT_TRUE(AppendTelemetryRow(outputs, std::array{"CPU", "0x0000", "CPUExecutionProvider:"}, + true)); + EXPECT_TRUE(AppendTelemetryRow(outputs, std::array{"GPU", "0x10DE", "CUDAExecutionProvider:1"}, + false)); + EXPECT_EQ(summaries[0], "CPU,GPU"); + EXPECT_EQ(summaries[1], "0x0000,0x10DE"); + EXPECT_EQ(summaries[2], "CPUExecutionProvider:,CUDAExecutionProvider:1"); +} + +TEST(TelemetryStringsTest, RejectsPartialRowsWithoutChangingAnySummary) { + const std::array row{"CPU", "0x0000", "CPUExecutionProvider:"}; + for (size_t full_column = 0; full_column < row.size(); ++full_column) { + for (size_t size : {kMaxTelemetryStringLength, kMaxTelemetryStringLength - 1, + kMaxTelemetryStringLength - row[full_column].size()}) { + SCOPED_TRACE(full_column); + SCOPED_TRACE(size); + std::array summaries{"type", "vendor", "version"}; + summaries[full_column].assign(size, 'x'); + const auto before = summaries; + EXPECT_FALSE(AppendTelemetryRow(std::array{&summaries[0], &summaries[1], &summaries[2]}, row, false)); + EXPECT_EQ(summaries, before); + } + } +} + +TEST(TelemetryStringsTest, RejectsOversizedFirstRowsAndIncompleteUtf8Rows) { + for (size_t column = 0; column < 3; ++column) { + SCOPED_TRACE(column); + std::array summaries; + std::array row{"CPU", "0x0000", "CPUExecutionProvider:"}; + row[column].assign(kMaxTelemetryStringLength + 1, 'x'); + const std::array values{row[0], row[1], row[2]}; + EXPECT_FALSE(AppendTelemetryRow(std::array{&summaries[0], &summaries[1], &summaries[2]}, values, true)); + EXPECT_EQ(summaries, (std::array{})); + + summaries[column].assign(kMaxTelemetryStringLength - 2, 'x'); + row[column] = "\xe2\x82\xac"; + const auto before = summaries; + EXPECT_FALSE(AppendTelemetryRow(std::array{&summaries[0], &summaries[1], &summaries[2]}, + std::array{row[0], row[1], row[2]}, false)); + EXPECT_EQ(summaries, before); + } +} + +TEST(TelemetryStringsTest, AcceptsExactBudgetRowsAndNormalizesMalformedUtf8) { + std::array summaries; + const std::array outputs{&summaries[0], &summaries[1], &summaries[2]}; + EXPECT_TRUE(AppendTelemetryRow(outputs, std::array{"", "\x80", "version"}, true)); + EXPECT_EQ(summaries, (std::array{"", "?", "version"})); + const std::array row{ + std::string(kMaxTelemetryStringLength - 1, 't'), + std::string(kMaxTelemetryStringLength - 2, 'v'), + std::string(kMaxTelemetryStringLength - 8, 'r')}; + EXPECT_TRUE(AppendTelemetryRow(outputs, std::array{row[0], row[1], row[2]}, false)); + for (const auto& summary : summaries) { + EXPECT_EQ(summary.size(), kMaxTelemetryStringLength); + EXPECT_EQ(std::count(summary.begin(), summary.end(), ','), 1); + } +} + +TEST(TelemetryStringsTest, RetainsPointersAcrossAdditionalFields) { + TelemetryStrings strings; + const char* first = strings.Utf8("first"); + const wchar_t* wide_first = strings.Wide(L"first"); + for (int i = 0; i < 128; ++i) { + EXPECT_EQ(std::string_view(strings.Utf8(std::string(10000, 'a'))).size(), 1024); + strings.Wide(std::wstring(10000, L'a')); + } + EXPECT_STREQ(first, "first"); + EXPECT_STREQ(wide_first, L"first"); +} + +TEST(TelemetryStringsTest, PropagatesMapTruncationToRedaction) { + const std::map options{{"cache", "alice/" + std::string(2000, 'a') + "/model"}}; + bool truncated = false; + const auto formatted = FormatTelemetryMap(options, ",", ":", &truncated); + EXPECT_TRUE(truncated); + EXPECT_EQ(ScrubStringForTelemetry(formatted, truncated), "[path]"); + FormatTelemetryMap(std::map{{"a", "b"}}, ",", ":", &truncated); + EXPECT_FALSE(truncated); +} + +TEST(TelemetryStringsTest, PropagatesUtf8FieldTruncationWithUnusedAggregateBudget) { + const std::string prefix = "alice/" + std::string(kMaxTelemetryStringLength - 9, 'x'); + const std::map options{{"a", prefix + "\xf0\x9f\x98\x80/model"}}; + bool truncated = false; + const auto formatted = FormatTelemetryMap(options, ",", ":", &truncated); + EXPECT_LT(formatted.size(), kMaxTelemetryStringLength); + EXPECT_TRUE(truncated); + EXPECT_EQ(ScrubStringForTelemetry(formatted, truncated), "[path]"); + + const std::map keys{{prefix + "\xf0\x9f\x98\x80/model", ""}}; + const auto formatted_keys = FormatTelemetryMap(keys, "", "", &truncated); + EXPECT_LT(formatted_keys.size(), kMaxTelemetryStringLength); + EXPECT_TRUE(truncated); + EXPECT_EQ(ScrubStringForTelemetry(formatted_keys, truncated), "[path]"); +} +} // namespace onnxruntime::test diff --git a/onnxruntime/test/platform/windows_telemetry/device_id_test.cc b/onnxruntime/test/platform/windows_telemetry/device_id_test.cc new file mode 100644 index 0000000000000..116747368ea72 --- /dev/null +++ b/onnxruntime/test/platform/windows_telemetry/device_id_test.cc @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "core/platform/device_id.h" + +#include + +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "core/common/common.h" +#include "core/platform/telemetry_guid.h" +#include "core/platform/telemetry_strings.h" +#include "test/util/include/scoped_env_vars.h" + +namespace onnxruntime::test { +namespace { + +namespace fs = std::filesystem; + +constexpr char kDeviceIdRegistryKey[] = "SOFTWARE\\Microsoft\\DeveloperTools\\.onnxruntime"; +constexpr char kDeviceIdRegistryValue[] = "deviceid"; + +fs::path Utf8Path(const std::string& value) { + return fs::path(std::u8string(value.begin(), value.end())); +} + +class ScopedRegistryOverride { + public: + ScopedRegistryOverride() + : path_("SOFTWARE\\Microsoft\\onnxruntime-tests\\device-id-" + + std::to_string(::GetCurrentProcessId())) { + HKEY writable_key{}; + ORT_ENFORCE(::RegCreateKeyExA(HKEY_CURRENT_USER, path_.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, nullptr, &writable_key, nullptr) == ERROR_SUCCESS); + ORT_ENFORCE(::RegCloseKey(writable_key) == ERROR_SUCCESS); + ORT_ENFORCE(::RegOpenKeyExA(HKEY_CURRENT_USER, path_.c_str(), 0, KEY_ALL_ACCESS, &key_) == ERROR_SUCCESS); + ORT_ENFORCE(::RegOverridePredefKey(HKEY_CURRENT_USER, key_) == ERROR_SUCCESS); + overridden_ = true; + } + + ~ScopedRegistryOverride() { + Reset(); + } + + ScopedRegistryOverride(const ScopedRegistryOverride&) = delete; + ScopedRegistryOverride& operator=(const ScopedRegistryOverride&) = delete; + + void WriteValue(DWORD type, const void* data, DWORD size) { + HKEY device_id_key{}; + ASSERT_EQ(::RegCreateKeyExA(HKEY_CURRENT_USER, kDeviceIdRegistryKey, 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, nullptr, &device_id_key, nullptr), + ERROR_SUCCESS); + ASSERT_EQ(::RegSetValueExA(device_id_key, kDeviceIdRegistryValue, 0, type, + static_cast(data), size), + ERROR_SUCCESS); + ASSERT_EQ(::RegCloseKey(device_id_key), ERROR_SUCCESS); + } + + std::string ReadValue() { + HKEY device_id_key{}; + if (::RegOpenKeyExA(HKEY_CURRENT_USER, kDeviceIdRegistryKey, 0, KEY_READ, &device_id_key) != ERROR_SUCCESS) { + return {}; + } + + std::vector value(512); + DWORD type = 0; + DWORD size = static_cast(value.size()); + const LSTATUS status = ::RegQueryValueExA( + device_id_key, kDeviceIdRegistryValue, nullptr, &type, + reinterpret_cast(value.data()), &size); + ::RegCloseKey(device_id_key); + if (status != ERROR_SUCCESS || type != REG_SZ || size == 0) { + return {}; + } + + value.back() = '\0'; + return value.data(); + } + + void Reset() { + if (overridden_) { + EXPECT_EQ(::RegOverridePredefKey(HKEY_CURRENT_USER, nullptr), ERROR_SUCCESS); + overridden_ = false; + } + if (key_ != nullptr) { + EXPECT_EQ(::RegCloseKey(key_), ERROR_SUCCESS); + key_ = nullptr; + } + const LSTATUS delete_status = ::RegDeleteTreeA(HKEY_CURRENT_USER, path_.c_str()); + EXPECT_TRUE(delete_status == ERROR_SUCCESS || delete_status == ERROR_FILE_NOT_FOUND); + } + + private: + std::string path_; + HKEY key_{}; + bool overridden_{}; +}; + +class ScopedWideEnvironmentVariable { + public: + ScopedWideEnvironmentVariable(const wchar_t* name, const std::wstring& value) : name_(name) { + const DWORD required_size = ::GetEnvironmentVariableW(name_.c_str(), nullptr, 0); + if (required_size != 0) { + original_value_.resize(required_size, L'\0'); + const DWORD value_size = + ::GetEnvironmentVariableW(name_.c_str(), original_value_.data(), required_size); + ORT_ENFORCE(value_size != 0 && value_size < required_size); + original_value_.resize(value_size); + was_defined_ = true; + } + ORT_ENFORCE(::SetEnvironmentVariableW(name_.c_str(), value.c_str())); + } + + ~ScopedWideEnvironmentVariable() { + EXPECT_TRUE(::SetEnvironmentVariableW(name_.c_str(), + was_defined_ ? original_value_.c_str() : nullptr)); + } + + ScopedWideEnvironmentVariable(const ScopedWideEnvironmentVariable&) = delete; + ScopedWideEnvironmentVariable& operator=(const ScopedWideEnvironmentVariable&) = delete; + + private: + std::wstring name_; + std::wstring original_value_; + bool was_defined_{}; +}; + +TEST(DeviceIdWindowsTest, FallsBackToAbsoluteAppData) { + const fs::path app_data = fs::temp_directory_path() / "ort_device_id_app_data"; + ScopedEnvironmentVariables environment{ + EnvVarMap{{"LOCALAPPDATA", nullopt}, + {"APPDATA", app_data.string()}, + {"HOME", nullopt}, + {"USERPROFILE", nullopt}}}; + + EXPECT_EQ(Utf8Path(DeviceId::GetStorageDirectory()), + app_data / "Microsoft" / "DeveloperTools" / ".onnxruntime"); +} + +TEST(DeviceIdWindowsTest, FallsBackToUserProfileWhenAppDataIsUnavailable) { + const fs::path user_profile = fs::temp_directory_path() / "ort_device_id_user_profile"; + ScopedEnvironmentVariables environment{ + EnvVarMap{{"LOCALAPPDATA", "relative-local"}, + {"APPDATA", "relative-roaming"}, + {"HOME", "relative-home"}, + {"USERPROFILE", user_profile.string()}}}; + + EXPECT_EQ(Utf8Path(DeviceId::GetStorageDirectory()), + user_profile / "AppData" / "Local" / "Microsoft" / "DeveloperTools" / ".onnxruntime"); +} + +TEST(DeviceIdWindowsTest, FallsBackToHomeDriveAndPath) { + const fs::path home = fs::temp_directory_path() / "ort_device_id_home"; + const std::wstring home_native = home.native(); + const std::wstring drive = home.root_name().native(); + ScopedEnvironmentVariables environment{ + EnvVarMap{{"LOCALAPPDATA", nullopt}, + {"APPDATA", nullopt}, + {"HOME", nullopt}, + {"USERPROFILE", nullopt}}}; + ScopedWideEnvironmentVariable home_drive(L"HOMEDRIVE", drive); + ScopedWideEnvironmentVariable home_path(L"HOMEPATH", home_native.substr(drive.size())); + + EXPECT_EQ(Utf8Path(DeviceId::GetStorageDirectory()), + home / "AppData" / "Local" / "Microsoft" / "DeveloperTools" / ".onnxruntime"); +} + +TEST(DeviceIdWindowsTest, CreatesUnicodeStorageDirectory) { + const fs::path app_data = fs::temp_directory_path() / L"ort_device_id_\u6d4b\u8bd5"; + ScopedWideEnvironmentVariable local_app_data(L"LOCALAPPDATA", app_data.native()); + + const fs::path storage_dir = Utf8Path(DeviceId::EnsureStorageDirectory()); + EXPECT_EQ(storage_dir, app_data / "Microsoft" / "DeveloperTools" / ".onnxruntime"); + EXPECT_TRUE(fs::is_directory(storage_dir)); + + std::error_code error; + fs::remove_all(app_data, error); + EXPECT_FALSE(error); +} + +TEST(DeviceIdWindowsTest, RejectsOversizedStoragePathsWithoutTruncation) { + const std::wstring drive = fs::temp_directory_path().root_name().native() + L"\\"; + const std::wstring paths[] = { + drive + std::wstring(telemetry_detail::kMaxTelemetryPathBytes, L'a'), + drive + std::wstring(telemetry_detail::kMaxTelemetryPathBytes / 3, L'\u20ac'), + }; + for (const auto& app_data : paths) { + ScopedWideEnvironmentVariable local_app_data(L"LOCALAPPDATA", app_data); + ScopedEnvironmentVariables environment{ + EnvVarMap{{"APPDATA", nullopt}, {"HOME", nullopt}, {"USERPROFILE", nullopt}, {"HOMEDRIVE", nullopt}, {"HOMEPATH", nullopt}}}; + EXPECT_TRUE(DeviceId::GetStorageDirectory().empty()); + } +} + +TEST(DeviceIdWindowsTest, IncludesStorageSuffixInPathBudget) { + const std::wstring drive = fs::temp_directory_path().root_name().native() + L"\\"; + const std::wstring suffix = L"\\Microsoft\\DeveloperTools\\.onnxruntime"; + const std::wstring app_data = drive + std::wstring( + telemetry_detail::kMaxTelemetryPathBytes - drive.size() - suffix.size(), L'a'); + ScopedEnvironmentVariables environment{ + EnvVarMap{{"APPDATA", nullopt}, {"HOME", nullopt}, {"USERPROFILE", nullopt}, {"HOMEDRIVE", nullopt}, {"HOMEPATH", nullopt}}}; + for (const auto& extra : {std::wstring{}, std::wstring{L"a"}}) { + ScopedWideEnvironmentVariable local_app_data(L"LOCALAPPDATA", app_data + extra); + const std::string storage = DeviceId::GetStorageDirectory(); + if (extra.empty()) { + EXPECT_EQ(storage.size(), telemetry_detail::kMaxTelemetryPathBytes); + EXPECT_EQ(Utf8Path(storage), fs::path(app_data + suffix)); + } else { + EXPECT_TRUE(storage.empty()); + } + } +} + +TEST(DeviceIdWindowsDeathTest, CreatesMissingRegistryValue) { + EXPECT_EXIT( + { + ScopedRegistryOverride registry; + const std::string value = DeviceId::Instance().GetValue(); + const bool passed = DeviceId::Instance().GetStatus() == DeviceIdStatus::New && + IsValidGuid(value) && + registry.ReadValue() == value; + registry.Reset(); + std::_Exit(passed ? EXIT_SUCCESS : EXIT_FAILURE); + }, + ::testing::ExitedWithCode(EXIT_SUCCESS), ""); +} + +TEST(DeviceIdWindowsDeathTest, LoadsExistingRegistryValue) { + constexpr char kExistingId[] = "11111111-2222-4333-8444-555555555555"; + for (const auto& stored : {std::string(kExistingId), std::string(" \t") + kExistingId + "\r\n"}) { + SCOPED_TRACE(stored.size()); + EXPECT_EXIT( + { + ScopedRegistryOverride registry; + registry.WriteValue(REG_SZ, stored.c_str(), static_cast(stored.size() + 1)); + const std::string value = DeviceId::Instance().GetValue(); + const bool passed = DeviceId::Instance().GetStatus() == DeviceIdStatus::Existing && + value == kExistingId && + registry.ReadValue() == stored; + registry.Reset(); + std::_Exit(passed ? EXIT_SUCCESS : EXIT_FAILURE); + }, + ::testing::ExitedWithCode(EXIT_SUCCESS), ""); + } +} + +TEST(DeviceIdWindowsDeathTest, RepairsMalformedRegistryValues) { + const std::string malformed_values[] = { + "corrupted", + std::string(512, 'a'), + std::string("11111111-2222-4333-8444-555555555555\0junk", 41), + }; + for (const auto& malformed : malformed_values) { + SCOPED_TRACE(malformed.size()); + EXPECT_EXIT( + { + ScopedRegistryOverride registry; + registry.WriteValue(REG_SZ, malformed.c_str(), static_cast(malformed.size() + 1)); + const std::string value = DeviceId::Instance().GetValue(); + const bool passed = DeviceId::Instance().GetStatus() == DeviceIdStatus::Corrupted && + IsValidGuid(value) && + registry.ReadValue() == value; + registry.Reset(); + std::_Exit(passed ? EXIT_SUCCESS : EXIT_FAILURE); + }, + ::testing::ExitedWithCode(EXIT_SUCCESS), ""); + } +} + +} // namespace +} // namespace onnxruntime::test diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py index 25d08695fcfde..1bcdae465d400 100644 --- a/tools/ci_build/build.py +++ b/tools/ci_build/build.py @@ -268,7 +268,7 @@ def generate_vcpkg_install_options(build_dir, args): vcpkg_install_options.append("--x-feature=webnn-ep") if args.use_xnnpack: vcpkg_install_options.append("--x-feature=xnnpack-ep") - if args.use_telemetry and not is_windows() and not args.android and not args.build_wasm: + if args.use_telemetry and not args.use_windows_telemetry and not args.android and not args.build_wasm: vcpkg_install_options.append("--x-feature=telemetry") overlay_triplets_dir = None @@ -462,16 +462,18 @@ def generate_build_tree( disable_optional_type = "optional" in types_to_disable disable_sparse_tensors = "sparsetensor" in types_to_disable disable_string_type = "string" in types_to_disable - # VitisAI and OpenVINO providers currently only support the full protobuf option. Resolve this once: the # vcpkg triplets (which decide how the ONNX port is built) and the CMake configure must agree, otherwise # ONNX and ONNX Runtime end up with different protobuf runtimes in the same binary. use_full_protobuf = bool( args.use_full_protobuf or args.use_openvino or args.use_vitisai or args.gen_doc or args.enable_generic_interface ) + # Select 1DS by default, including on Windows; TraceLogging requires explicit opt-in. + telemetry_backend = "WINDOWS" if args.use_windows_telemetry else "1DS" - # Telemetry uses ETW on Windows and 1DS on other supported native platforms. cmake_args.append("-Donnxruntime_USE_TELEMETRY=" + ("ON" if args.use_telemetry else "OFF")) + cmake_args.append("-Donnxruntime_TELEMETRY_BACKEND=" + telemetry_backend) + cmake_args.append("-Donnxruntime_USE_WINDOWS_TELEMETRY=" + ("ON" if args.use_windows_telemetry else "OFF")) if is_windows(): cmake_args += [ "-Donnxruntime_USE_DML=" + ("ON" if args.use_dml else "OFF"), diff --git a/tools/ci_build/build_args.py b/tools/ci_build/build_args.py index 341c1f4d0cfda..8b97e1e568bf4 100644 --- a/tools/ci_build/build_args.py +++ b/tools/ci_build/build_args.py @@ -887,12 +887,24 @@ def add_other_feature_args(parser: argparse.ArgumentParser) -> None: help="Build ORT shared lib with compatible bridge for primary EPs (TRT, OV, QNN, VitisAI), excludes tests.", ) # Telemetry arguments (cross-platform) - parser.add_argument( + telemetry_group = parser.add_mutually_exclusive_group() + telemetry_group.add_argument( "--no_telemetry", dest="use_telemetry", action="store_false", help="Disable telemetry. Telemetry is enabled by default for supported native builds.", ) + telemetry_group.add_argument( + "--use_telemetry", + dest="use_telemetry_legacy", + action="store_true", + help=argparse.SUPPRESS, + ) + telemetry_group.add_argument( + "--use_windows_telemetry", + action="store_true", + help="Use the legacy Windows TraceLogging telemetry backend instead of 1DS.", + ) def is_cross_compiling(args: argparse.Namespace) -> bool: @@ -1033,8 +1045,24 @@ def convert_arg_line_to_args(self, arg_line: str) -> list[str]: if args.build_wasm_static_lib: args.build_wasm = True + if args.use_telemetry_legacy: + warnings.warn( + "--use_telemetry is deprecated because telemetry is enabled by default. " + "For native Windows targets it retains its historical TraceLogging behavior; use " + "--use_windows_telemetry to request that backend explicitly.", + FutureWarning, + stacklevel=2, + ) + if not target_supports_telemetry(args): + parser.error("--use_telemetry requires a telemetry-capable target") + args.use_telemetry = True + if is_windows() and not args.android: + args.use_windows_telemetry = True + if not target_supports_telemetry(args): args.use_telemetry = False + if args.use_windows_telemetry and (not is_windows() or args.android or not target_supports_telemetry(args)): + parser.error("--use_windows_telemetry requires a telemetry-capable native Windows target") # Handle WASM exception logic if args.enable_wasm_api_exception_catching: diff --git a/tools/ci_build/github/windows/set_telemetry_var.ps1 b/tools/ci_build/github/windows/set_telemetry_var.ps1 index 481e3410360f5..026fdce8758c8 100644 --- a/tools/ci_build/github/windows/set_telemetry_var.ps1 +++ b/tools/ci_build/github/windows/set_telemetry_var.ps1 @@ -1,15 +1,20 @@ +param([switch]$DisableTelemetry) + # The first part of the expression in the following "if" statement is necessary, otherwise it will report an error, # "You cannot call a method on a null-valued expression.", when the env variable does not exist. -if (-not [string]::IsNullOrEmpty( $Env:TELEMETRYGUID) -and $Env:TELEMETRYGUID.StartsWith('"')) { +if ($DisableTelemetry) { + Write-Host "##vso[task.setvariable variable=TelemetryOption]--no_telemetry" + Write-Host "Telemetry is disabled." +} elseif (-not [string]::IsNullOrEmpty( $Env:TELEMETRYGUID) -and $Env:TELEMETRYGUID.StartsWith('"')) { $length = $Env:TELEMETRYGUID.length # See https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-traceloggingoptiongroup # The value of the env variable must have quotes, because we do not add the quotes here. $fileContent = "#define TraceLoggingOptionMicrosoftTelemetry() \ TraceLoggingOptionGroup("+$Env:TELEMETRYGUID.substring(1, $length-2)+")" New-Item -Path "include\onnxruntime\core\platform\windows\TraceLoggingConfigPrivate.h" -ItemType "file" -Value "$fileContent" -Force - Write-Host "##vso[task.setvariable variable=TelemetryOption]" - Write-Host "Telemetry is enabled." + Write-Host "##vso[task.setvariable variable=TelemetryOption]--use_windows_telemetry" + Write-Host "UTC telemetry is enabled." } else { - Write-Host "##vso[task.setvariable variable=TelemetryOption]--no_telemetry" - Write-Host "Telemetry is disabled." -} \ No newline at end of file + Write-Host "##vso[task.setvariable variable=TelemetryOption]" + Write-Host "1DS telemetry is enabled." +} diff --git a/tools/ci_build/test_build_args.py b/tools/ci_build/test_build_args.py index a98eaaaed1974..ffcd3680b65dc 100644 --- a/tools/ci_build/test_build_args.py +++ b/tools/ci_build/test_build_args.py @@ -30,11 +30,60 @@ def test_native_supported_targets_enable_telemetry_by_default(self): with self.subTest(platform_name=platform_name): args = self._parse(platform_name=platform_name) self.assertTrue(args.use_telemetry) + self.assertFalse(args.use_windows_telemetry) def test_no_telemetry_disables_supported_target(self): args = self._parse("--no_telemetry", platform_name="linux") self.assertFalse(args.use_telemetry) + def test_windows_telemetry_backend_is_explicit(self): + args = self._parse("--use_windows_telemetry", platform_name="windows") + self.assertTrue(args.use_telemetry) + self.assertTrue(args.use_windows_telemetry) + + def test_legacy_use_telemetry_retains_windows_backend(self): + with self.assertWarnsRegex(FutureWarning, "--use_telemetry is deprecated"): + args = self._parse("--use_telemetry", platform_name="windows") + + self.assertTrue(args.use_telemetry) + self.assertTrue(args.use_windows_telemetry) + + def test_legacy_use_telemetry_retains_1ds_elsewhere(self): + with self.assertWarnsRegex(FutureWarning, "--use_telemetry is deprecated"): + args = self._parse("--use_telemetry", platform_name="linux") + + self.assertTrue(args.use_telemetry) + self.assertFalse(args.use_windows_telemetry) + + def test_legacy_use_telemetry_selects_1ds_for_android_on_every_host(self): + for platform_name in ("windows", "linux", "macos"): + with ( + self.subTest(platform_name=platform_name), + self.assertWarnsRegex(FutureWarning, "--use_telemetry is deprecated"), + ): + args = self._parse("--android", "--use_telemetry", platform_name=platform_name) + self.assertTrue(args.use_telemetry) + self.assertFalse(args.use_windows_telemetry) + + def test_windows_telemetry_backend_is_rejected_elsewhere(self): + with self.assertRaises(SystemExit): + self._parse("--use_windows_telemetry", platform_name="linux") + + def test_windows_telemetry_backend_is_rejected_for_unsupported_targets(self): + for arguments in ( + ("--android",), + ("--build_wasm",), + ("--minimal_build", "--disable_exceptions"), + ): + with self.subTest(arguments=arguments), self.assertRaises(SystemExit): + self._parse("--use_windows_telemetry", *arguments, platform_name="windows") + + def test_telemetry_backend_and_opt_out_are_mutually_exclusive(self): + with self.assertRaises(SystemExit): + self._parse("--use_windows_telemetry", "--no_telemetry", platform_name="windows") + with self.assertRaises(SystemExit): + self._parse("--use_telemetry", "--no_telemetry", platform_name="windows") + def test_unsupported_targets_disable_telemetry(self): cases = ( (("--build_wasm",), "linux", "x86_64"), diff --git a/tools/ci_build/test_gen_def.py b/tools/ci_build/test_gen_def.py new file mode 100644 index 0000000000000..f68ac0a280cb0 --- /dev/null +++ b/tools/ci_build/test_gen_def.py @@ -0,0 +1,198 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + + +class TestPublicExports(unittest.TestCase): + @unittest.skipUnless(sys.platform.startswith("linux"), "Requires an ELF linker and dynamic loader") + def test_embedded_curl_is_not_exported_or_interposed(self): + repo_root = Path(__file__).resolve().parents[2] + public_symbols = { + line.strip() + for line in (repo_root / "onnxruntime" / "core" / "providers" / "cpu" / "symbols.txt") + .read_text(encoding="utf-8") + .splitlines() + if line.strip() + } + with tempfile.TemporaryDirectory(prefix="ort curl exports ") as directory: + root = Path(directory) + symbol_file = root / "onnxruntime.lds" + subprocess.run( + [ + sys.executable, + str(repo_root / "tools" / "ci_build" / "gen_def.py"), + "--src_root", + str(repo_root / "onnxruntime"), + "--version_file", + str(repo_root / "VERSION_NUMBER"), + "--output", + str(symbol_file), + "--output_source", + str(root / "generated_source.c"), + "--style", + "gcc", + "--config", + "cpu", + ], + check=True, + capture_output=True, + text=True, + timeout=30, + ) + (root / "curl.c").write_text( + "int curl_easy_init(void) { return 42; }\n" + "int Curl_private(void) { return 1; }\n" + "int mbedtls_private(void) { return 2; }\n", + encoding="utf-8", + ) + (root / "host.c").write_text("int curl_easy_init(void) { return -1; }\n", encoding="utf-8") + (root / "runtime.c").write_text( + "extern int curl_easy_init(void);\n" + "int OrtGetApiBase(void) { return curl_easy_init(); }\n" + + "".join( + f"int {symbol}(void) {{ return 0; }}\n" for symbol in sorted(public_symbols - {"OrtGetApiBase"}) + ), + encoding="utf-8", + ) + for command in ( + ["cc", "-fPIC", "-c", "curl.c", "-o", "curl.o"], + ["ar", "rcs", "libcustom-transport.a", "curl.o"], + ["cc", "-shared", "-fPIC", "host.c", "-o", "libhost.so"], + ["cc", "-shared", "-fPIC", "runtime.c", "libcustom-transport.a", "-o", "libunrestricted.so"], + [ + "cc", + "-shared", + "-fPIC", + "runtime.c", + "libcustom-transport.a", + f"-Wl,--version-script={symbol_file}", + "-o", + "libonnxruntime.so", + ], + ): + subprocess.run(command, cwd=root, check=True, capture_output=True, text=True, timeout=30) + symbols = subprocess.run( + ["nm", "-D", "--defined-only", "libonnxruntime.so"], + cwd=root, + check=True, + capture_output=True, + text=True, + timeout=30, + ).stdout + exported_functions = { + fields[2].split("@")[0] + for line in symbols.splitlines() + if len(fields := line.split()) == 3 and fields[1] == "T" + } + self.assertEqual(exported_functions, public_symbols) + self.assertNotIn("curl_easy_init", symbols) + self.assertNotIn("Curl_private", symbols) + self.assertNotIn("mbedtls_private", symbols) + interposition_test = ( + "import ctypes; " + "host = ctypes.CDLL('./libhost.so', mode=ctypes.RTLD_GLOBAL); " + "unrestricted = ctypes.CDLL('./libunrestricted.so'); " + "runtime = ctypes.CDLL('./libonnxruntime.so'); " + "assert host.curl_easy_init() == -1; " + "assert unrestricted.OrtGetApiBase() == -1; " + "assert runtime.OrtGetApiBase() == 42" + ) + subprocess.run( + [ + sys.executable, + "-c", + interposition_test, + ], + cwd=root, + check=True, + capture_output=True, + text=True, + timeout=30, + ) + + def test_public_exports_exclude_private_dependencies(self): + repo_root = Path(__file__).resolve().parents[2] + providers_root = repo_root / "onnxruntime" / "core" / "providers" + providers = sorted(path.parent.name for path in providers_root.glob("*/symbols.txt")) + public_symbols = set() + for provider in providers: + public_symbols.update( + line.strip() + for line in (providers_root / provider / "symbols.txt").read_text(encoding="utf-8").splitlines() + if line.strip() + ) + android_symbols = { + line.strip() + for line in (repo_root / "onnxruntime" / "core" / "platform" / "posix" / "android_telemetry_symbols.txt") + .read_text(encoding="utf-8") + .splitlines() + if line.strip() + } + self.assertIn("OrtGetApiBase", public_symbols) + self.assertTrue(android_symbols) + self.assertFalse(any(symbol.startswith(("curl_", "Curl_", "mbedtls_")) for symbol in public_symbols)) + + with tempfile.TemporaryDirectory(prefix="ort public exports ") as directory: + output = Path(directory) / "exports" + source = Path(directory) / "generated_source.c" + for style in ("gcc", "vc", "xcode", "aix"): + for android in (False, True): + with self.subTest(style=style, android=android): + command = [ + sys.executable, + str(repo_root / "tools" / "ci_build" / "gen_def.py"), + "--src_root", + str(repo_root / "onnxruntime"), + "--version_file", + str(repo_root / "VERSION_NUMBER"), + "--output", + str(output), + "--output_source", + str(source), + "--style", + style, + "--config", + *providers, + ] + if android: + command.extend( + [ + "--extra_symbol_file", + str( + repo_root + / "onnxruntime" + / "core" + / "platform" + / "posix" + / "android_telemetry_symbols.txt" + ), + ] + ) + subprocess.run(command, check=True, capture_output=True, text=True, timeout=30) + contents = output.read_text(encoding="utf-8") + expected_symbols = public_symbols | android_symbols if android else public_symbols + if style == "gcc": + global_section, local_section = contents.split(" local:\n") + actual_symbols = { + line.strip().removesuffix(";") + for line in global_section.split(" global:\n")[1].splitlines() + } + self.assertEqual(local_section, " *;\n}; \n") + elif style == "vc": + actual_symbols = {line.split()[0] for line in contents.splitlines()[2:]} + elif style == "xcode": + actual_symbols = {line.removeprefix("_") for line in contents.splitlines()} + else: + actual_symbols = set(contents.splitlines()) + self.assertEqual(actual_symbols, expected_symbols) + for symbol in android_symbols: + self.assertNotIn(symbol, source.read_text(encoding="utf-8")) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/ci_build/test_telemetry_package.py b/tools/ci_build/test_telemetry_package.py new file mode 100644 index 0000000000000..187f21e6b380e --- /dev/null +++ b/tools/ci_build/test_telemetry_package.py @@ -0,0 +1,402 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +"""Install and consume a minimal static package with the 1DS SDK's link interfaces. + +The fixture isolates ORT's package configuration from unrelated dependencies. Android +uses the NDK when ANDROID_NDK_HOME is set; host builds exercise the Linux export graph. +""" + +import json +import os +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + +_ROOT = Path(__file__).resolve().parents[2] +_CONFIG = _ROOT / "cmake" / "onnxruntime_telemetry_config.cmake" + +_PRODUCER = """ +cmake_minimum_required(VERSION 3.28) +project(telemetry_package C) +include(GNUInstallDirs) +find_package(Threads REQUIRED) +add_library(mat STATIC mat.c) +target_link_libraries(mat PUBLIC Threads::Threads) +add_library(onnxruntime_common STATIC common.c) +target_link_libraries(onnxruntime_common PRIVATE mat) +set(export_targets onnxruntime_common mat) +if(TEST_PLATFORM STREQUAL "Linux") + add_library(libcurl_static STATIC curl.c) + target_link_libraries(libcurl_static PUBLIC Threads::Threads) + target_compile_definitions(mat PRIVATE USE_CURL) + target_link_libraries(mat PRIVATE + "$" + "$") + list(APPEND export_targets libcurl_static) +endif() +set(onnxruntime_USE_1DS_TELEMETRY ON) +set(onnxruntime_BUILD_SHARED_LIB OFF) +set(PROJECT_CONFIG_CONTENT "include(CMakeFindDependencyMacro)\\n") +# Select the package dependency graph without changing the host compiler/toolchain. +function(append_telemetry_config) + set(WIN32 FALSE) + set(APPLE FALSE) + set(CMAKE_SYSTEM_NAME "${TEST_PLATFORM}") + include("${TELEMETRY_CONFIG}") + set(PROJECT_CONFIG_CONTENT "${PROJECT_CONFIG_CONTENT}" PARENT_SCOPE) +endfunction() +append_telemetry_config() +string(APPEND PROJECT_CONFIG_CONTENT + "include(\\"\\${CMAKE_CURRENT_LIST_DIR}/onnxruntimeTargets.cmake\\")\\n") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/onnxruntimeConfig.cmake" "${PROJECT_CONFIG_CONTENT}") +install(TARGETS ${export_targets} EXPORT onnxruntimeTargets ARCHIVE DESTINATION lib) +install(EXPORT onnxruntimeTargets NAMESPACE onnxruntime:: DESTINATION lib/cmake/onnxruntime) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/onnxruntimeConfig.cmake" DESTINATION lib/cmake/onnxruntime) +""" + +_CONSUMER = """ +cmake_minimum_required(VERSION 3.28) +project(telemetry_consumer C) +if(PREEXISTING_CURL) + add_library(MSTelemetry::curl_dependency INTERFACE IMPORTED) + set_property(TARGET MSTelemetry::curl_dependency PROPERTY + INTERFACE_LINK_LIBRARIES onnxruntime::libcurl_static) + set_property(TARGET MSTelemetry::curl_dependency PROPERTY TEST_PRESERVED TRUE) +endif() +find_package(onnxruntime CONFIG REQUIRED) +find_package(onnxruntime CONFIG REQUIRED) +if(NOT TARGET Threads::Threads) + message(FATAL_ERROR "The installed SDK requires Threads::Threads") +endif() +if(TEST_PLATFORM STREQUAL "Linux") + get_target_property(curl_link MSTelemetry::curl_dependency INTERFACE_LINK_LIBRARIES) + if(NOT curl_link STREQUAL "onnxruntime::libcurl_static") + message(FATAL_ERROR "The installed SDK must use the exported curl archive") + endif() + if(PREEXISTING_CURL) + get_target_property(preserved MSTelemetry::curl_dependency TEST_PRESERVED) + if(NOT preserved) + message(FATAL_ERROR "A caller's existing dependency target was replaced") + endif() + endif() +elseif(TARGET MSTelemetry::curl_dependency) + message(FATAL_ERROR "The Android Java transport must not require curl") +endif() +add_executable(consumer main.c) +target_link_libraries(consumer PRIVATE onnxruntime::onnxruntime_common) +enable_testing() +add_test(NAME consumer COMMAND consumer) +""" + + +class TelemetryPackageTest(unittest.TestCase): + def test_windows_network_detector_policy(self): + source = (_ROOT / "onnxruntime" / "core" / "platform" / "telemetry_1ds.cc").read_text(encoding="utf-8") + initialization = source[source.index("void OneDsTelemetry::Initialize()") :] + configuration = initialization[: initialization.index("// Create log manager via LogManagerProvider")] + start = configuration.index("#if defined(_WIN32)") + policy = configuration[start : configuration.index("#endif", start) + len("#endif")] + with tempfile.TemporaryDirectory(prefix="ort-telemetry-network-policy-") as temporary: + root = Path(temporary) + (root / "policy.inc").write_text(policy, encoding="utf-8") + (root / "main.cc").write_text( + textwrap.dedent( + """ + int main() { + constexpr int CFG_BOOL_ENABLE_NET_DETECT = 0; + bool config[] = {true}; + #include "policy.inc" + #if defined(_WIN32) + constexpr bool expected_net_detect = false; + #else + constexpr bool expected_net_detect = true; + #endif + return config[CFG_BOOL_ENABLE_NET_DETECT] != expected_net_detect; + } + """ + ), + encoding="utf-8", + ) + (root / "CMakeLists.txt").write_text( + "cmake_minimum_required(VERSION 3.28)\n" + "project(telemetry_network_policy CXX)\n" + "enable_testing()\n" + "add_executable(network_policy main.cc)\n" + "add_test(NAME network_policy COMMAND network_policy)\n", + encoding="utf-8", + ) + build = root / "build" + self._run( + "cmake", + "-S", + str(root), + "-B", + str(build), + ) + self._run("cmake", "--build", str(build), "--config", "Debug") + self._run("ctest", "--test-dir", str(build), "-C", "Debug", "--output-on-failure") + + def test_direct_cmake_backend_selection(self): + cmake = (_ROOT / "cmake" / "CMakeLists.txt").read_text(encoding="utf-8") + start = cmake.index("option(onnxruntime_USE_TELEMETRY ") + selector = cmake[start : cmake.index("# Optional 1DS ingestion token", start)] + cases = ( + ("Windows", "ON", None, "OFF", "ON", "ON"), + ("Windows", "ON", "AUTO", "OFF", "ON", "ON"), + ("Linux", "ON", None, "OFF", "ON", "ON"), + ("Darwin", "ON", "AUTO", "OFF", "ON", "ON"), + ("Windows", "OFF", None, "OFF", "OFF", "OFF"), + ("Windows", "ON", "1ds", "OFF", "ON", "ON"), + ("Windows", "OFF", "WINDOWS", "OFF", "ON", "OFF"), + ("Windows", "OFF", None, "ON", "ON", "OFF"), + ("WindowsStore", "ON", "AUTO", "OFF", "OFF", "OFF"), + ("WindowsStore", "ON", "WINDOWS", "OFF", "ON", "OFF"), + ) + with tempfile.TemporaryDirectory(prefix="ort-telemetry-backend-") as temporary: + script = Path(temporary) / "selector.cmake" + for platform_name, enabled, backend, legacy, expected_enabled, expected_1ds in cases: + with self.subTest(platform_name=platform_name, enabled=enabled, backend=backend, legacy=legacy): + setup = ( + f'set(CMAKE_SYSTEM_NAME "{platform_name}")\n' + f"set(WIN32 {'TRUE' if platform_name.startswith('Windows') else 'FALSE'})\n" + f'set(onnxruntime_USE_TELEMETRY {enabled} CACHE BOOL "")\n' + f'set(onnxruntime_USE_WINDOWS_TELEMETRY {legacy} CACHE BOOL "")\n' + ) + if backend is not None: + setup += f'set(onnxruntime_TELEMETRY_BACKEND "{backend}" CACHE STRING "")\n' + script.write_text( + setup + + selector + + f'\nif(NOT onnxruntime_USE_TELEMETRY STREQUAL "{expected_enabled}" OR\n' + + f' NOT onnxruntime_USE_1DS_TELEMETRY STREQUAL "{expected_1ds}")\n' + + ' message(FATAL_ERROR "Unexpected telemetry backend")\nendif()\n', + encoding="utf-8", + ) + self._run("cmake", "-P", str(script)) + + for platform_name, backend, error in ( + ("Linux", "WINDOWS", "only supported on Windows"), + ("Windows", "invalid", "Expected AUTO, 1DS, or WINDOWS"), + ): + with self.subTest(platform_name=platform_name, backend=backend): + script.write_text( + f'set(CMAKE_SYSTEM_NAME "{platform_name}")\n' + f"set(WIN32 {'TRUE' if platform_name == 'Windows' else 'FALSE'})\n" + f'set(onnxruntime_TELEMETRY_BACKEND "{backend}" CACHE STRING "")\n' + selector, + encoding="utf-8", + ) + result = subprocess.run( + ["cmake", "-P", str(script)], capture_output=True, text=True, check=False, timeout=30 + ) + self.assertNotEqual(result.returncode, 0) + self.assertIn(error, " ".join(result.stderr.split())) + + def test_source_sdk_selection_with_and_without_vcpkg(self): + external = (_ROOT / "cmake" / "external" / "onnxruntime_external_deps.cmake").read_text(encoding="utf-8") + telemetry = external[external.index("# 1DS SDK (cpp_client_telemetry)") :] + telemetry = telemetry[: telemetry.index("FILE(TO_NATIVE_PATH")] + dependency = next( + line + for line in (_ROOT / "cmake" / "deps.txt").read_text(encoding="utf-8").splitlines() + if line.startswith("cpp_client_telemetry;") + ) + _, url, sha1 = dependency.split(";") + with tempfile.TemporaryDirectory(prefix="ort-telemetry-source-") as temporary: + root = Path(temporary) + (root / "telemetry.cmake").write_text(telemetry, encoding="utf-8") + (root / "mat.c").write_text("int mat_value(void) { return 42; }\n", encoding="utf-8") + (root / "CMakeLists.txt").write_text( + textwrap.dedent( + """ + cmake_minimum_required(VERSION 3.28) + project(telemetry_source C) + add_library(mat STATIC mat.c) + add_library(sqlite3_bundled STATIC mat.c) + function(onnxruntime_fetchcontent_declare name) + cmake_parse_arguments(SDK "" "URL;URL_HASH" "" ${ARGN}) + if(NOT name STREQUAL "cpp_client_telemetry" OR + NOT SDK_URL STREQUAL DEP_URL_cpp_client_telemetry OR + NOT SDK_URL_HASH STREQUAL "SHA1=${DEP_SHA1_cpp_client_telemetry}") + message(FATAL_ERROR "The SDK must use the verified GitHub source pin") + endif() + endfunction() + macro(onnxruntime_fetchcontent_makeavailable name) + if(BUILD_SHARED_LIBS) + message(FATAL_ERROR "The source SDK must be embedded as a static library") + endif() + if(NOT BUILD_VERSION STREQUAL expected_sdk_version) + message(FATAL_ERROR "SDK version metadata must match the source pin") + endif() + if(NOT MATSDK_CURL_PROVIDER STREQUAL expected_curl) + message(FATAL_ERROR "Unexpected curl provider for ${platform}/${use_vcpkg}") + endif() + set_property(GLOBAL PROPERTY sdk_source_selected TRUE) + endmacro() + function(check_source platform use_vcpkg expected_curl) + set(onnxruntime_USE_1DS_TELEMETRY ON) + set(onnxruntime_BUILD_SHARED_LIB ON) + set(BUILD_SHARED_LIBS ON) + set(BUILD_VERSION caller-version) + set(onnxruntime_USE_VCPKG "${use_vcpkg}") + set(CMAKE_SYSTEM_NAME "${platform}") + set(WIN32 FALSE) + set(APPLE FALSE) + set(ANDROID FALSE) + set_property(GLOBAL PROPERTY sdk_source_selected FALSE) + if(platform STREQUAL "Windows") + set(WIN32 TRUE) + elseif(platform STREQUAL "Darwin") + set(APPLE TRUE) + endif() + include("${CMAKE_CURRENT_SOURCE_DIR}/telemetry.cmake") + get_property(sdk_source_selected GLOBAL PROPERTY sdk_source_selected) + if(NOT sdk_source_selected OR NOT BUILD_SHARED_LIBS OR + NOT BUILD_VERSION STREQUAL "caller-version") + message(FATAL_ERROR "SDK source selection or shared-library restoration failed") + endif() + if(DEFINED CACHE{MATSDK_CURL_PROVIDER}) + message(FATAL_ERROR "SDK options must not remain in the parent cache") + endif() + get_target_property(sqlite_definitions sqlite3_bundled COMPILE_DEFINITIONS) + if("SQLITE_DEFAULT_MEMSTATUS=0" IN_LIST sqlite_definitions) + message(FATAL_ERROR "Disabling SQLite memory accounting defeats the SDK's soft heap limit") + endif() + endfunction() + check_source(Windows OFF SYSTEM) + check_source(Windows ON SYSTEM) + check_source(Linux OFF FETCH) + check_source(Linux ON SYSTEM) + check_source(Darwin OFF SYSTEM) + check_source(Darwin ON SYSTEM) + """ + ), + encoding="utf-8", + ) + self._run( + "cmake", + "-S", + str(root), + "-B", + str(root / "build"), + f"-DDEP_URL_cpp_client_telemetry={url}", + f"-DDEP_SHA1_cpp_client_telemetry={sha1}", + f"-Dexpected_sdk_version={url.rsplit('/v', 1)[-1].removesuffix('.zip')}", + ) + + def test_vcpkg_telemetry_feature_installs_transport_not_sdk(self): + manifest = json.loads((_ROOT / "cmake" / "vcpkg.json").read_text(encoding="utf-8")) + dependencies = manifest["features"]["telemetry"]["dependencies"] + self.assertEqual({dependency["name"] for dependency in dependencies}, {"curl", "mbedtls"}) + self.assertTrue(all(dependency["platform"] == "linux" for dependency in dependencies)) + + def test_production_include_without_module_search_path(self): + includes = [ + line.strip() + for line in (_ROOT / "cmake" / "CMakeLists.txt").read_text(encoding="utf-8").splitlines() + if line.strip().startswith("include(") and "onnxruntime_telemetry_config" in line + ] + self.assertEqual(len(includes), 1) + with tempfile.TemporaryDirectory(prefix="ort-telemetry-include-") as temporary: + root = Path(temporary) + shutil.copyfile(_CONFIG, root / _CONFIG.name) + script = root / "include.cmake" + script.write_text( + 'set(CMAKE_MODULE_PATH "")\n' + "set(onnxruntime_USE_1DS_TELEMETRY ON)\n" + "set(WIN32 FALSE)\nset(APPLE FALSE)\nset(CMAKE_SYSTEM_NAME Android)\n" + + includes[0] + + '\nif(NOT PROJECT_CONFIG_CONTENT STREQUAL "find_dependency(Threads)\\n")\n' + ' message(FATAL_ERROR "The installed SDK dependency was not added")\nendif()\n', + encoding="utf-8", + ) + self._run("cmake", "-P", str(script)) + + def _run(self, *command): + result = subprocess.run(command, capture_output=True, text=True, check=False, timeout=180) + self.assertEqual(result.returncode, 0, f"{command}\n{result.stdout}\n{result.stderr}") + + def _install_and_consume(self, platform_name, *, android=False, preexisting_curl=False): + with tempfile.TemporaryDirectory(prefix="ort-telemetry-package-") as temporary: + root = Path(temporary) + producer = root / "producer" + consumer = root / "consumer" + producer.mkdir() + consumer.mkdir() + (producer / "CMakeLists.txt").write_text(textwrap.dedent(_PRODUCER), encoding="utf-8") + (producer / "common.c").write_text( + "int mat_value(void);\nint ort_value(void) { return mat_value(); }\n", encoding="utf-8" + ) + (producer / "mat.c").write_text( + "#ifdef USE_CURL\nint curl_value(void);\n" + "int mat_value(void) { return curl_value(); }\n" + "#else\nint mat_value(void) { return 42; }\n#endif\n", + encoding="utf-8", + ) + (producer / "curl.c").write_text("int curl_value(void) { return 42; }\n", encoding="utf-8") + (consumer / "CMakeLists.txt").write_text(textwrap.dedent(_CONSUMER), encoding="utf-8") + (consumer / "main.c").write_text( + "int ort_value(void);\nint main(void) { return ort_value() == 42 ? 0 : 1; }\n", encoding="utf-8" + ) + + configure = [f"-DTEST_PLATFORM={platform_name}", "-DCMAKE_BUILD_TYPE=Release"] + if android: + ndk = Path(os.environ["ANDROID_NDK_HOME"]) + configure += [ + "-G", + "Ninja", + f"-DCMAKE_TOOLCHAIN_FILE={ndk / 'build/cmake/android.toolchain.cmake'}", + "-DANDROID_ABI=arm64-v8a", + "-DANDROID_PLATFORM=android-28", + ] + ninja = shutil.which("ninja") + self.assertIsNotNone(ninja, "Ninja must be on PATH for the Android cross-link") + configure.append(f"-DCMAKE_MAKE_PROGRAM={ninja}") + + install = root / "install" + self._run( + "cmake", + "-S", + str(producer), + "-B", + str(root / "producer-build"), + f"-DTELEMETRY_CONFIG={_CONFIG}", + f"-DCMAKE_INSTALL_PREFIX={install}", + *configure, + ) + self._run("cmake", "--build", str(root / "producer-build"), "--config", "Release") + self._run("cmake", "--install", str(root / "producer-build"), "--config", "Release") + self._run( + "cmake", + "-S", + str(consumer), + "-B", + str(root / "consumer-build"), + f"-DCMAKE_PREFIX_PATH={install}", + f"-DCMAKE_FIND_ROOT_PATH={install}", + f"-DPREEXISTING_CURL={'ON' if preexisting_curl else 'OFF'}", + *configure, + ) + self._run("cmake", "--build", str(root / "consumer-build"), "--config", "Release") + if not android: + self._run("ctest", "--test-dir", str(root / "consumer-build"), "-C", "Release", "--output-on-failure") + + def test_bundled_linux_static_install_and_consume(self): + for preexisting_curl in (False, True): + with self.subTest(preexisting_curl=preexisting_curl): + self._install_and_consume("Linux", preexisting_curl=preexisting_curl) + + def test_bundled_android_java_static_install_and_consume(self): + self._install_and_consume("Android") + + @unittest.skipUnless(os.environ.get("ANDROID_NDK_HOME"), "Set ANDROID_NDK_HOME for an Android cross-link") + def test_bundled_android_ndk_static_install_and_consume(self): + self._install_and_consume("Android", android=True) + + +if __name__ == "__main__": + unittest.main() diff --git a/winml/lib/Common/inc/WinMLTelemetryHelper.h b/winml/lib/Common/inc/WinMLTelemetryHelper.h index c0f6ef39d6744..a8d3826514a03 100644 --- a/winml/lib/Common/inc/WinMLTelemetryHelper.h +++ b/winml/lib/Common/inc/WinMLTelemetryHelper.h @@ -83,7 +83,7 @@ class WinMLTelemetryHelper { void LogApiUsage(const char* name); void LogWinMLShutDown(); void LogWinMLSuspended(); - void LogRuntimeError(HRESULT hr, std::string message, PCSTR file, PCSTR function, int line); + void LogRuntimeError(HRESULT hr, const std::string& message, PCSTR file, PCSTR function, int line); void LogRuntimeError(HRESULT hr, PCSTR message, PCSTR file, PCSTR function, int line); void LogRegisterOperatorKernel(const char* name, const char* domain, int execution_type); void RegisterOperatorSetSchema( diff --git a/winml/lib/Telemetry/WinMLTelemetryHelper.cpp b/winml/lib/Telemetry/WinMLTelemetryHelper.cpp index ee8ef617c4519..8d30aa10ffa59 100644 --- a/winml/lib/Telemetry/WinMLTelemetryHelper.cpp +++ b/winml/lib/Telemetry/WinMLTelemetryHelper.cpp @@ -8,6 +8,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "lib/Telemetry/pch.h" +#include "core/platform/telemetry_strings.h" WinMLTelemetryHelper::WinMLTelemetryHelper() : provider_(winml_trace_logging_provider) { } @@ -18,6 +19,7 @@ WinMLTelemetryHelper::~WinMLTelemetryHelper() { void WinMLTelemetryHelper::LogApiUsage(const char* name) { if (!telemetry_enabled_) return; + onnxruntime::telemetry_detail::TelemetryStrings strings; WinMLTraceLoggingWrite( provider_, "ApiUsage", @@ -26,14 +28,14 @@ void WinMLTelemetryHelper::LogApiUsage(const char* name) { //Telemetry info TraceLoggingUInt8(WINML_TLM_EXPERIMENTAL_API_VERSION, "experimentalSchemaVersion"), // named dimension override info - TraceLoggingString(name, "name"), + TraceLoggingString(strings.Utf8(name), "name"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES) ); } void WinMLTelemetryHelper::LogWinMLShutDown() { - std::string message = BINARY_NAME; - message += " is unloaded"; + std::string message = onnxruntime::telemetry_detail::BoundedTelemetryString(BINARY_NAME); + onnxruntime::telemetry_detail::AppendTelemetryString(message, " is unloaded"); WinMLTraceLoggingWrite( provider_, "WinMLShutDown", @@ -54,6 +56,7 @@ void WinMLTelemetryHelper::LogWinMLSuspended() { void WinMLTelemetryHelper::LogRuntimeError(HRESULT hr, PCSTR message, PCSTR file, PCSTR function, int line) { if (!telemetry_enabled_) return; + onnxruntime::telemetry_detail::TelemetryStrings strings; WinMLTraceLoggingWrite( provider_, @@ -64,16 +67,18 @@ void WinMLTelemetryHelper::LogRuntimeError(HRESULT hr, PCSTR message, PCSTR file TraceLoggingUInt8(WINML_TLM_RUNTIME_ERROR_VERSION, "schemaVersion"), // Error Info TraceLoggingHResult(hr, "hResult"), - TraceLoggingString(message, "errormessage"), - TraceLoggingString(file, "file"), - TraceLoggingString(function, "function"), + TraceLoggingString(strings.Utf8(message), "errormessage"), + TraceLoggingString(strings.Utf8(file), "file"), + TraceLoggingString(strings.Utf8(function), "function"), TraceLoggingInt32(line, "line"), TraceLoggingInt32(runtime_session_id_, "runtimeSessionId"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES) ); } -void WinMLTelemetryHelper::LogRuntimeError(HRESULT hr, std::string message, PCSTR file, PCSTR function, int line) { +void WinMLTelemetryHelper::LogRuntimeError( + HRESULT hr, const std::string& message, PCSTR file, PCSTR function, int line +) { LogRuntimeError(hr, message.c_str(), file, function, line); } @@ -85,6 +90,7 @@ bool WinMLTelemetryHelper::IsMeasureSampled() { void WinMLTelemetryHelper::LogRegisterOperatorKernel(const char* name, const char* domain, int execution_type) { if (!telemetry_enabled_) return; + onnxruntime::telemetry_detail::TelemetryStrings strings; WinMLTraceLoggingWrite( provider_, @@ -94,8 +100,8 @@ void WinMLTelemetryHelper::LogRegisterOperatorKernel(const char* name, const cha // Telemetry info TraceLoggingUInt8(WINML_TLM_RUNTIME_ERROR_VERSION, "schemaVersion"), //op kernel info - TraceLoggingString(name, "name"), - TraceLoggingString(domain, "domain"), + TraceLoggingString(strings.Utf8(name), "name"), + TraceLoggingString(strings.Utf8(domain), "domain"), TraceLoggingInt32(execution_type, "executionType"), TraceLoggingInt32(runtime_session_id_, "runtimeSessionId"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES) @@ -112,6 +118,7 @@ void WinMLTelemetryHelper::RegisterOperatorSetSchema( ) { if (!telemetry_enabled_) return; + onnxruntime::telemetry_detail::TelemetryStrings strings; WinMLTraceLoggingWrite( provider_, @@ -121,7 +128,7 @@ void WinMLTelemetryHelper::RegisterOperatorSetSchema( // Telemetry info TraceLoggingUInt8(WINML_TLM_RUNTIME_ERROR_VERSION, "schemaVersion"), //op kernel info - TraceLoggingString(name, "name"), + TraceLoggingString(strings.Utf8(name), "name"), TraceLoggingInt32(input_count, "inputCount"), //stats TraceLoggingInt32(output_count, "outputCount"), TraceLoggingInt32(type_constraint_count, "typeConstraintCount"), @@ -168,6 +175,7 @@ void WinMLTelemetryHelper::SetIntraOpThreadSpinning(bool allow_spinning) { void WinMLTelemetryHelper::SetNamedDimensionOverride(winrt::hstring name, uint32_t value) { if (!telemetry_enabled_) return; + onnxruntime::telemetry_detail::TelemetryStrings strings; WinMLTraceLoggingWrite( provider_, "SetNamedDimensionOverride", @@ -176,7 +184,7 @@ void WinMLTelemetryHelper::SetNamedDimensionOverride(winrt::hstring name, uint32 //Telemetry info TraceLoggingUInt8(WINML_TLM_NAMED_DIMENSION_OVERRIDE_VERSION, "schemaVersion"), // named dimension override info - TraceLoggingWideString(name.c_str(), "dimensionName"), + TraceLoggingWideString(strings.Wide(std::wstring_view(name.c_str(), name.size())), "dimensionName"), TraceLoggingInt32(value, "overrideValue"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES) );