From 2b8c7bb4035d305bf6a5c957b7136d5f2d2556bb Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Thu, 27 Aug 2026 13:20:33 -0700 Subject: [PATCH 01/16] Update cpuinfo with thread-safe deinitialization --- cmake/deps.txt | 2 +- .../external/onnxruntime_external_deps.cmake | 11 +- .../cpuinfo/enable_deinit_refcounting.patch | 586 ++++++++++++++++++ .../cpuinfo/enable_deinit_refcounting.patch | 586 ++++++++++++++++++ cmake/vcpkg-ports/cpuinfo/portfile.cmake | 5 +- 5 files changed, 1185 insertions(+), 5 deletions(-) create mode 100644 cmake/patches/cpuinfo/enable_deinit_refcounting.patch create mode 100644 cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch diff --git a/cmake/deps.txt b/cmake/deps.txt index 026988ffbd520..a7662e443cf67 100644 --- a/cmake/deps.txt +++ b/cmake/deps.txt @@ -50,7 +50,7 @@ protoc_mac_universal;https://github.com/protocolbuffers/protobuf/releases/downlo psimd;https://github.com/Maratyszcza/psimd/archive/072586a71b55b7f8c584153d223e95687148a900.zip;1f5454b01f06f9656b77e4a5e2e31d7422487013 pthreadpool;https://github.com/google/pthreadpool/archive/dcc9f28589066af0dbd4555579281230abbf74dd.zip;533a77943203ef15ca608bcd9dbe2c94da7451d2 pybind11;https://github.com/pybind/pybind11/archive/refs/tags/v3.0.2.zip;a064e663b4d7a337ac291d1bef7337ef4e60a1ae -pytorch_cpuinfo;https://github.com/pytorch/cpuinfo/archive/4628dc060ce4e82345dc166bbac875609db4ff69.zip;e58d4b47c16a982111c897e669ae4f1821a393d7 +pytorch_cpuinfo;https://github.com/pytorch/cpuinfo/archive/66ee79c038d70dad9f08705b2c9b3e58f6d8f512.zip;6e2be17c110e4f4184b059303c722b3c5bd080a6 re2;https://github.com/google/re2/archive/refs/tags/2024-07-02.zip;646e1728269cde7fcef990bf4a8e87b047882e88 safeint;https://github.com/dcleblanc/SafeInt/archive/refs/tags/3.0.28.zip;23f252040ff6cb9f1fd18575b32fa8fb5928daac tensorboard;https://github.com/tensorflow/tensorboard/archive/373eb09e4c5d2b3cc2493f0949dc4be6b6a45e81.zip;67b833913605a4f3f499894ab11528a702c2b381 diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 117af707d2ebf..7dd301dc9b67e 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -373,7 +373,9 @@ if (CPUINFO_SUPPORTED) PATCH_COMMAND ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && # https://github.com/pytorch/cpuinfo/pull/324 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch FIND_PACKAGE_ARGS NAMES cpuinfo ) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") @@ -385,7 +387,9 @@ if (CPUINFO_SUPPORTED) EXCLUDE_FROM_ALL PATCH_COMMAND # https://github.com/microsoft/onnxruntime/issues/10038 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch FIND_PACKAGE_ARGS NAMES cpuinfo ) else() @@ -394,6 +398,9 @@ if (CPUINFO_SUPPORTED) URL ${DEP_URL_pytorch_cpuinfo} URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch FIND_PACKAGE_ARGS NAMES cpuinfo ) endif() diff --git a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch new file mode 100644 index 0000000000000..c7febd3da6ae6 --- /dev/null +++ b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch @@ -0,0 +1,586 @@ +diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c +index 48c9f4a050..cb3170f279 100644 +--- a/src/arm/linux/init.c ++++ b/src/arm/linux/init.c +@@ -1001,3 +1001,43 @@ cleanup: + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + } ++ ++void cpuinfo_arm_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ /* cpuinfo_packages points to static storage (&package) — do not free */ ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); ++ cpuinfo_linux_cpu_to_uarch_index_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++} +diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c +index 76224b9d40..9a9fb4276c 100644 +--- a/src/arm/mach/init.c ++++ b/src/arm/mach/init.c +@@ -873,3 +873,32 @@ cleanup: + free(l2); + free(l3); + } ++ ++void cpuinfo_arm_mach_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++} +diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c +index 815ecb770a..2e44f8aa05 100644 +--- a/src/arm/windows/init-by-logical-sys-info.c ++++ b/src/arm/windows/init-by-logical-sys-info.c +@@ -372,6 +372,40 @@ clean_up: + return result; + } + ++BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ /* Caches are allocated as a single contiguous block starting at l1i */ ++ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ return TRUE; ++} ++ + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { + uint32_t nr_of_processors = 0; + +diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c +index 528ae3279f..fcf219df15 100644 +--- a/src/arm/windows/init.c ++++ b/src/arm/windows/init.c +@@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV + set_cpuinfo_isa_fields(); + + chip_info = get_system_info_from_registry(); ++ const bool chip_info_from_registry = chip_info != NULL; + if (chip_info == NULL) { + chip_info = &woa_chip_unknown; + } + + cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); + ++ if (chip_info_from_registry) { ++ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); ++ HeapFree(GetProcessHeap(), 0, chip_info); ++ } ++ + return true; + } + +@@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); + if (midr_qword == 0) { + cpuinfo_log_error("Registry read error for MIDR value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // MIDR is only 32 bits, so we need to cast it to uint32_t +@@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); + if (frequency_mhz == 0) { + cpuinfo_log_error("Registry read error for frequency value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // Convert MHz to Hz +@@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); + if (chip_info == NULL) { + cpuinfo_log_error("Heap allocation error for chip_info"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + + // set chip_info fields + chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); + + cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); +diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h +index d84b26a89c..9c1588872d 100644 +--- a/src/cpuinfo/internal-api.h ++++ b/src/cpuinfo/internal-api.h +@@ -45,6 +45,9 @@ extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; + extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max; + extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map; + extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map; ++#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 ++extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; ++#endif + #endif + + CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void); +@@ -62,6 +65,23 @@ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); + CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void); + CPUINFO_PRIVATE void cpuinfo_emscripten_init(void); + ++/* Platform-specific deinitialization functions. ++ * Each frees the globals allocated by its corresponding init function. */ ++CPUINFO_PRIVATE void cpuinfo_x86_mach_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_x86_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_x86_freebsd_deinit(void); ++#if defined(_WIN32) || defined(__CYGWIN__) ++#if CPUINFO_ARCH_ARM64 ++CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); ++#else ++CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); ++#endif ++#endif ++CPUINFO_PRIVATE void cpuinfo_arm_mach_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_arm_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_riscv_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_emscripten_deinit(void); ++ + CPUINFO_PRIVATE uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor); + + typedef void (*cpuinfo_processor_callback)(uint32_t); +diff --git a/src/emscripten/init.c b/src/emscripten/init.c +index c239324375..6c8210f2c2 100644 +--- a/src/emscripten/init.c ++++ b/src/emscripten/init.c +@@ -286,3 +286,35 @@ cleanup: + free(l1d); + free(l2); + } ++ ++void cpuinfo_emscripten_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ /* cpuinfo_packages points to static storage (&static_package) — do not free */ ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ /* L1i, L1d, L2 are dynamically allocated; L3 may point to static ++ * storage (&static_x86_l3) so only free levels that were calloc'd */ ++ free(cpuinfo_cache[cpuinfo_cache_level_1i]); ++ free(cpuinfo_cache[cpuinfo_cache_level_1d]); ++ free(cpuinfo_cache[cpuinfo_cache_level_2]); ++ /* cpuinfo_cache[cpuinfo_cache_level_3] may be &static_x86_l3 — do not free */ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/init.c b/src/init.c +index 81d5721c44..e3cd9217a9 100644 +--- a/src/init.c ++++ b/src/init.c +@@ -13,55 +13,98 @@ + #endif + + #if defined(_WIN32) || defined(__CYGWIN__) +-static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT; ++static SRWLOCK cpuinfo_lifecycle_lock = SRWLOCK_INIT; ++#define CPUINFO_LIFECYCLE_LOCK() AcquireSRWLockExclusive(&cpuinfo_lifecycle_lock) ++#define CPUINFO_LIFECYCLE_UNLOCK() ReleaseSRWLockExclusive(&cpuinfo_lifecycle_lock) + #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) +-static pthread_once_t init_guard = PTHREAD_ONCE_INIT; ++static pthread_mutex_t cpuinfo_lifecycle_lock = PTHREAD_MUTEX_INITIALIZER; ++#define CPUINFO_LIFECYCLE_LOCK() pthread_mutex_lock(&cpuinfo_lifecycle_lock) ++#define CPUINFO_LIFECYCLE_UNLOCK() pthread_mutex_unlock(&cpuinfo_lifecycle_lock) + #else +-static bool init_guard = false; ++/* Single-threaded build (e.g. Emscripten without pthreads): no lock needed. */ ++#define CPUINFO_LIFECYCLE_LOCK() \ ++ do { \ ++ } while (0) ++#define CPUINFO_LIFECYCLE_UNLOCK() \ ++ do { \ ++ } while (0) + #endif ++static unsigned int cpuinfo_init_refcount = 0; + + bool CPUINFO_ABI cpuinfo_initialize(void) { ++ CPUINFO_LIFECYCLE_LOCK(); ++ if (cpuinfo_init_refcount == 0) { + #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 + #if defined(__MACH__) && defined(__APPLE__) +- pthread_once(&init_guard, &cpuinfo_x86_mach_init); ++ cpuinfo_x86_mach_init(); + #elif defined(__FreeBSD__) +- pthread_once(&init_guard, &cpuinfo_x86_freebsd_init); ++ cpuinfo_x86_freebsd_init(); + #elif defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_x86_linux_init); ++ cpuinfo_x86_linux_init(); + #elif defined(_WIN32) || defined(__CYGWIN__) +- InitOnceExecuteOnce(&init_guard, &cpuinfo_x86_windows_init, NULL, NULL); ++ cpuinfo_x86_windows_init(NULL, NULL, NULL); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 + #if defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_arm_linux_init); ++ cpuinfo_arm_linux_init(); + #elif defined(__MACH__) && defined(__APPLE__) +- pthread_once(&init_guard, &cpuinfo_arm_mach_init); ++ cpuinfo_arm_mach_init(); + #elif defined(_WIN32) +- InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL); ++ cpuinfo_arm_windows_init(NULL, NULL, NULL); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + #if defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_riscv_linux_init); ++ cpuinfo_riscv_linux_init(); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD +-#if defined(__EMSCRIPTEN_PTHREADS__) +- pthread_once(&init_guard, &cpuinfo_emscripten_init); +-#else +- if (!init_guard) { + cpuinfo_emscripten_init(); +- } +- init_guard = true; +-#endif + #else +- cpuinfo_log_error("processor architecture is not supported in cpuinfo"); ++ cpuinfo_log_error("processor architecture is not supported in cpuinfo"); + #endif +- return cpuinfo_is_initialized; ++ } ++ if (cpuinfo_is_initialized) { ++ cpuinfo_init_refcount++; ++ } ++ const bool initialized = cpuinfo_is_initialized; ++ CPUINFO_LIFECYCLE_UNLOCK(); ++ return initialized; + } + +-void CPUINFO_ABI cpuinfo_deinitialize(void) {} ++void CPUINFO_ABI cpuinfo_deinitialize(void) { ++ CPUINFO_LIFECYCLE_LOCK(); ++ if (cpuinfo_init_refcount != 0 && --cpuinfo_init_refcount == 0) { ++ cpuinfo_is_initialized = false; ++#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 ++#if defined(__MACH__) && defined(__APPLE__) ++ cpuinfo_x86_mach_deinit(); ++#elif defined(__FreeBSD__) ++ cpuinfo_x86_freebsd_deinit(); ++#elif defined(__linux__) ++ cpuinfo_x86_linux_deinit(); ++#elif defined(_WIN32) || defined(__CYGWIN__) ++ cpuinfo_x86_windows_deinit(NULL, NULL, NULL); ++#endif ++#elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 ++#if defined(__linux__) ++ cpuinfo_arm_linux_deinit(); ++#elif defined(__MACH__) && defined(__APPLE__) ++ cpuinfo_arm_mach_deinit(); ++#elif defined(_WIN32) ++ cpuinfo_arm_windows_deinit(NULL, NULL, NULL); ++#endif ++#elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 ++#if defined(__linux__) ++ cpuinfo_riscv_linux_deinit(); ++#endif ++#elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD ++ cpuinfo_emscripten_deinit(); ++#endif ++ } ++ CPUINFO_LIFECYCLE_UNLOCK(); ++} +diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c +index 45168c7e66..3da8c3ffd5 100644 +--- a/src/riscv/linux/init.c ++++ b/src/riscv/linux/init.c +@@ -618,3 +618,36 @@ cleanup: + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + } ++ ++void cpuinfo_riscv_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); ++ cpuinfo_linux_cpu_to_uarch_index_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++} +diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c +index 797fa24b95..ba30a5d9c8 100644 +--- a/src/x86/freebsd/init.c ++++ b/src/x86/freebsd/init.c +@@ -396,3 +396,30 @@ cleanup: + free(l3); + free(l4); + } ++ ++void cpuinfo_x86_freebsd_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c +index d2b2d47514..62e1187e06 100644 +--- a/src/x86/linux/init.c ++++ b/src/x86/linux/init.c +@@ -676,3 +676,38 @@ cleanup: + free(linux_cpu_to_processor_map); + free(linux_cpu_to_core_map); + } ++ ++void cpuinfo_x86_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c +index b8ea6047ac..9009e830ab 100644 +--- a/src/x86/mach/init.c ++++ b/src/x86/mach/init.c +@@ -378,3 +378,30 @@ cleanup: + free(l3); + free(l4); + } ++ ++void cpuinfo_x86_mach_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c +index 7380bb46d5..f1401beb60 100644 +--- a/src/x86/windows/init.c ++++ b/src/x86/windows/init.c +@@ -665,5 +665,39 @@ cleanup: + if (l4 != NULL) { + HeapFree(heap, 0, l4); + } ++ if (processor_infos != NULL) { ++ HeapFree(heap, 0, processor_infos); ++ } ++ return TRUE; ++} ++ ++BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ HeapFree(heap, 0, cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++ + return TRUE; + } diff --git a/cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch b/cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch new file mode 100644 index 0000000000000..c7febd3da6ae6 --- /dev/null +++ b/cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch @@ -0,0 +1,586 @@ +diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c +index 48c9f4a050..cb3170f279 100644 +--- a/src/arm/linux/init.c ++++ b/src/arm/linux/init.c +@@ -1001,3 +1001,43 @@ cleanup: + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + } ++ ++void cpuinfo_arm_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ /* cpuinfo_packages points to static storage (&package) — do not free */ ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); ++ cpuinfo_linux_cpu_to_uarch_index_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++} +diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c +index 76224b9d40..9a9fb4276c 100644 +--- a/src/arm/mach/init.c ++++ b/src/arm/mach/init.c +@@ -873,3 +873,32 @@ cleanup: + free(l2); + free(l3); + } ++ ++void cpuinfo_arm_mach_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++} +diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c +index 815ecb770a..2e44f8aa05 100644 +--- a/src/arm/windows/init-by-logical-sys-info.c ++++ b/src/arm/windows/init-by-logical-sys-info.c +@@ -372,6 +372,40 @@ clean_up: + return result; + } + ++BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ /* Caches are allocated as a single contiguous block starting at l1i */ ++ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ return TRUE; ++} ++ + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { + uint32_t nr_of_processors = 0; + +diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c +index 528ae3279f..fcf219df15 100644 +--- a/src/arm/windows/init.c ++++ b/src/arm/windows/init.c +@@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV + set_cpuinfo_isa_fields(); + + chip_info = get_system_info_from_registry(); ++ const bool chip_info_from_registry = chip_info != NULL; + if (chip_info == NULL) { + chip_info = &woa_chip_unknown; + } + + cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); + ++ if (chip_info_from_registry) { ++ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); ++ HeapFree(GetProcessHeap(), 0, chip_info); ++ } ++ + return true; + } + +@@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); + if (midr_qword == 0) { + cpuinfo_log_error("Registry read error for MIDR value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // MIDR is only 32 bits, so we need to cast it to uint32_t +@@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); + if (frequency_mhz == 0) { + cpuinfo_log_error("Registry read error for frequency value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // Convert MHz to Hz +@@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { + chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); + if (chip_info == NULL) { + cpuinfo_log_error("Heap allocation error for chip_info"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + + // set chip_info fields + chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); + + cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); +diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h +index d84b26a89c..9c1588872d 100644 +--- a/src/cpuinfo/internal-api.h ++++ b/src/cpuinfo/internal-api.h +@@ -45,6 +45,9 @@ extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; + extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max; + extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map; + extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map; ++#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 ++extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; ++#endif + #endif + + CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void); +@@ -62,6 +65,23 @@ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); + CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void); + CPUINFO_PRIVATE void cpuinfo_emscripten_init(void); + ++/* Platform-specific deinitialization functions. ++ * Each frees the globals allocated by its corresponding init function. */ ++CPUINFO_PRIVATE void cpuinfo_x86_mach_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_x86_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_x86_freebsd_deinit(void); ++#if defined(_WIN32) || defined(__CYGWIN__) ++#if CPUINFO_ARCH_ARM64 ++CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); ++#else ++CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); ++#endif ++#endif ++CPUINFO_PRIVATE void cpuinfo_arm_mach_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_arm_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_riscv_linux_deinit(void); ++CPUINFO_PRIVATE void cpuinfo_emscripten_deinit(void); ++ + CPUINFO_PRIVATE uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor); + + typedef void (*cpuinfo_processor_callback)(uint32_t); +diff --git a/src/emscripten/init.c b/src/emscripten/init.c +index c239324375..6c8210f2c2 100644 +--- a/src/emscripten/init.c ++++ b/src/emscripten/init.c +@@ -286,3 +286,35 @@ cleanup: + free(l1d); + free(l2); + } ++ ++void cpuinfo_emscripten_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ /* cpuinfo_packages points to static storage (&static_package) — do not free */ ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ /* L1i, L1d, L2 are dynamically allocated; L3 may point to static ++ * storage (&static_x86_l3) so only free levels that were calloc'd */ ++ free(cpuinfo_cache[cpuinfo_cache_level_1i]); ++ free(cpuinfo_cache[cpuinfo_cache_level_1d]); ++ free(cpuinfo_cache[cpuinfo_cache_level_2]); ++ /* cpuinfo_cache[cpuinfo_cache_level_3] may be &static_x86_l3 — do not free */ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/init.c b/src/init.c +index 81d5721c44..e3cd9217a9 100644 +--- a/src/init.c ++++ b/src/init.c +@@ -13,55 +13,98 @@ + #endif + + #if defined(_WIN32) || defined(__CYGWIN__) +-static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT; ++static SRWLOCK cpuinfo_lifecycle_lock = SRWLOCK_INIT; ++#define CPUINFO_LIFECYCLE_LOCK() AcquireSRWLockExclusive(&cpuinfo_lifecycle_lock) ++#define CPUINFO_LIFECYCLE_UNLOCK() ReleaseSRWLockExclusive(&cpuinfo_lifecycle_lock) + #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) +-static pthread_once_t init_guard = PTHREAD_ONCE_INIT; ++static pthread_mutex_t cpuinfo_lifecycle_lock = PTHREAD_MUTEX_INITIALIZER; ++#define CPUINFO_LIFECYCLE_LOCK() pthread_mutex_lock(&cpuinfo_lifecycle_lock) ++#define CPUINFO_LIFECYCLE_UNLOCK() pthread_mutex_unlock(&cpuinfo_lifecycle_lock) + #else +-static bool init_guard = false; ++/* Single-threaded build (e.g. Emscripten without pthreads): no lock needed. */ ++#define CPUINFO_LIFECYCLE_LOCK() \ ++ do { \ ++ } while (0) ++#define CPUINFO_LIFECYCLE_UNLOCK() \ ++ do { \ ++ } while (0) + #endif ++static unsigned int cpuinfo_init_refcount = 0; + + bool CPUINFO_ABI cpuinfo_initialize(void) { ++ CPUINFO_LIFECYCLE_LOCK(); ++ if (cpuinfo_init_refcount == 0) { + #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 + #if defined(__MACH__) && defined(__APPLE__) +- pthread_once(&init_guard, &cpuinfo_x86_mach_init); ++ cpuinfo_x86_mach_init(); + #elif defined(__FreeBSD__) +- pthread_once(&init_guard, &cpuinfo_x86_freebsd_init); ++ cpuinfo_x86_freebsd_init(); + #elif defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_x86_linux_init); ++ cpuinfo_x86_linux_init(); + #elif defined(_WIN32) || defined(__CYGWIN__) +- InitOnceExecuteOnce(&init_guard, &cpuinfo_x86_windows_init, NULL, NULL); ++ cpuinfo_x86_windows_init(NULL, NULL, NULL); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 + #if defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_arm_linux_init); ++ cpuinfo_arm_linux_init(); + #elif defined(__MACH__) && defined(__APPLE__) +- pthread_once(&init_guard, &cpuinfo_arm_mach_init); ++ cpuinfo_arm_mach_init(); + #elif defined(_WIN32) +- InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL); ++ cpuinfo_arm_windows_init(NULL, NULL, NULL); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + #if defined(__linux__) +- pthread_once(&init_guard, &cpuinfo_riscv_linux_init); ++ cpuinfo_riscv_linux_init(); + #else +- cpuinfo_log_error("operating system is not supported in cpuinfo"); ++ cpuinfo_log_error("operating system is not supported in cpuinfo"); + #endif + #elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD +-#if defined(__EMSCRIPTEN_PTHREADS__) +- pthread_once(&init_guard, &cpuinfo_emscripten_init); +-#else +- if (!init_guard) { + cpuinfo_emscripten_init(); +- } +- init_guard = true; +-#endif + #else +- cpuinfo_log_error("processor architecture is not supported in cpuinfo"); ++ cpuinfo_log_error("processor architecture is not supported in cpuinfo"); + #endif +- return cpuinfo_is_initialized; ++ } ++ if (cpuinfo_is_initialized) { ++ cpuinfo_init_refcount++; ++ } ++ const bool initialized = cpuinfo_is_initialized; ++ CPUINFO_LIFECYCLE_UNLOCK(); ++ return initialized; + } + +-void CPUINFO_ABI cpuinfo_deinitialize(void) {} ++void CPUINFO_ABI cpuinfo_deinitialize(void) { ++ CPUINFO_LIFECYCLE_LOCK(); ++ if (cpuinfo_init_refcount != 0 && --cpuinfo_init_refcount == 0) { ++ cpuinfo_is_initialized = false; ++#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 ++#if defined(__MACH__) && defined(__APPLE__) ++ cpuinfo_x86_mach_deinit(); ++#elif defined(__FreeBSD__) ++ cpuinfo_x86_freebsd_deinit(); ++#elif defined(__linux__) ++ cpuinfo_x86_linux_deinit(); ++#elif defined(_WIN32) || defined(__CYGWIN__) ++ cpuinfo_x86_windows_deinit(NULL, NULL, NULL); ++#endif ++#elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 ++#if defined(__linux__) ++ cpuinfo_arm_linux_deinit(); ++#elif defined(__MACH__) && defined(__APPLE__) ++ cpuinfo_arm_mach_deinit(); ++#elif defined(_WIN32) ++ cpuinfo_arm_windows_deinit(NULL, NULL, NULL); ++#endif ++#elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 ++#if defined(__linux__) ++ cpuinfo_riscv_linux_deinit(); ++#endif ++#elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD ++ cpuinfo_emscripten_deinit(); ++#endif ++ } ++ CPUINFO_LIFECYCLE_UNLOCK(); ++} +diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c +index 45168c7e66..3da8c3ffd5 100644 +--- a/src/riscv/linux/init.c ++++ b/src/riscv/linux/init.c +@@ -618,3 +618,36 @@ cleanup: + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + } ++ ++void cpuinfo_riscv_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ free(cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); ++ cpuinfo_linux_cpu_to_uarch_index_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++} +diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c +index 797fa24b95..ba30a5d9c8 100644 +--- a/src/x86/freebsd/init.c ++++ b/src/x86/freebsd/init.c +@@ -396,3 +396,30 @@ cleanup: + free(l3); + free(l4); + } ++ ++void cpuinfo_x86_freebsd_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c +index d2b2d47514..62e1187e06 100644 +--- a/src/x86/linux/init.c ++++ b/src/x86/linux/init.c +@@ -676,3 +676,38 @@ cleanup: + free(linux_cpu_to_processor_map); + free(linux_cpu_to_core_map); + } ++ ++void cpuinfo_x86_linux_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ free(cpuinfo_linux_cpu_to_processor_map); ++ cpuinfo_linux_cpu_to_processor_map = NULL; ++ ++ free(cpuinfo_linux_cpu_to_core_map); ++ cpuinfo_linux_cpu_to_core_map = NULL; ++ ++ cpuinfo_linux_cpu_max = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c +index b8ea6047ac..9009e830ab 100644 +--- a/src/x86/mach/init.c ++++ b/src/x86/mach/init.c +@@ -378,3 +378,30 @@ cleanup: + free(l3); + free(l4); + } ++ ++void cpuinfo_x86_mach_deinit(void) { ++ free(cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ free(cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ free(cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ free(cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ free(cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++} +diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c +index 7380bb46d5..f1401beb60 100644 +--- a/src/x86/windows/init.c ++++ b/src/x86/windows/init.c +@@ -665,5 +665,39 @@ cleanup: + if (l4 != NULL) { + HeapFree(heap, 0, l4); + } ++ if (processor_infos != NULL) { ++ HeapFree(heap, 0, processor_infos); ++ } ++ return TRUE; ++} ++ ++BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ HeapFree(heap, 0, cpuinfo_cache[lvl]); ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; ++ + return TRUE; + } diff --git a/cmake/vcpkg-ports/cpuinfo/portfile.cmake b/cmake/vcpkg-ports/cpuinfo/portfile.cmake index 9140a233e2ccd..1062ccba4b8b3 100644 --- a/cmake/vcpkg-ports/cpuinfo/portfile.cmake +++ b/cmake/vcpkg-ports/cpuinfo/portfile.cmake @@ -6,12 +6,13 @@ endif() vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO pytorch/cpuinfo - REF 4628dc060ce4e82345dc166bbac875609db4ff69 - SHA512 db7a93279f2f6daaf825fbd8552935d8ed671d276b65ad614e11f722b6a6848e663850d65180d33b554d67ef1a36aae842feb368699f90be8f21172a1af1924e + REF 66ee79c038d70dad9f08705b2c9b3e58f6d8f512 + SHA512 ec1df3a03e52f50f221fc5f25251b7181316b59b32be61f11dec440ef245bc2070d0e16579ae8fd2748711f120c914f4479619ce1bd6b0bc9dbe462a8526298f HEAD_REF main PATCHES patch_cpuinfo_h_for_arm64ec.patch patch_vcpkg_arm64ec_support.patch # https://github.com/pytorch/cpuinfo/pull/324 + enable_deinit_refcounting.patch # https://github.com/pytorch/cpuinfo/pull/400 ) vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS From 1664116066827104518bfd3db2049260a5d1032d Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Thu, 27 Aug 2026 13:43:51 -0700 Subject: [PATCH 02/16] Share cpuinfo deinitialization patch command --- .../external/onnxruntime_external_deps.cmake | 50 +++++++------------ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 7dd301dc9b67e..cc61bf226c002 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -363,47 +363,33 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") + # https://github.com/pytorch/cpuinfo/pull/400 + set(ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch) if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/324 + set(ONNXRUNTIME_CPUINFO_PATCH_COMMAND ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && - # https://github.com/pytorch/cpuinfo/pull/324 ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + ${ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND}) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/microsoft/onnxruntime/issues/10038 + # https://github.com/microsoft/onnxruntime/issues/10038 + set(ONNXRUNTIME_CPUINFO_PATCH_COMMAND ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + ${ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND}) else() - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + set(ONNXRUNTIME_CPUINFO_PATCH_COMMAND ${ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND}) endif() + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND ${ONNXRUNTIME_CPUINFO_PATCH_COMMAND} + FIND_PACKAGE_ARGS NAMES cpuinfo + ) set(ONNXRUNTIME_CPUINFO_PROJ pytorch_cpuinfo) onnxruntime_fetchcontent_makeavailable(${ONNXRUNTIME_CPUINFO_PROJ}) if(TARGET cpuinfo::cpuinfo AND NOT TARGET cpuinfo) From 1cab52ff3f8cd00b364146586d90e693fd3d9819 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Thu, 27 Aug 2026 14:30:29 -0700 Subject: [PATCH 03/16] Fix cpuinfo patch application --- cmake/external/onnxruntime_external_deps.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index cc61bf226c002..1762e8d5503df 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -365,7 +365,8 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") # https://github.com/pytorch/cpuinfo/pull/400 set(ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch) + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch) if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") # https://github.com/pytorch/cpuinfo/pull/324 From 7fe410771f5f5f97cd2669cfe9fbeefa7b63dabe Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Thu, 27 Aug 2026 15:44:02 -0700 Subject: [PATCH 04/16] Test cpuinfo cleanup on ORT DLL unload --- cmake/onnxruntime_unittests.cmake | 14 ++ .../test/shared_lib/cpuinfo_dlopen_test.cc | 155 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index a64661c58aee5..c88d155436eab 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -1994,6 +1994,20 @@ endif() add_test(NAME onnxruntime_shared_lib_dlopen_test COMMAND onnxruntime_shared_lib_dlopen_test WORKING_DIRECTORY $) set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES FOLDER "ONNXRuntimeTest") + if(CPUINFO_SUPPORTED AND NOT onnxruntime_MINIMAL_BUILD) + onnxruntime_add_executable( + onnxruntime_shared_lib_cpuinfo_dlopen_test + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test.cc) + add_dependencies(onnxruntime_shared_lib_cpuinfo_dlopen_test ${all_dependencies} onnxruntime) + add_test( + NAME onnxruntime_shared_lib_cpuinfo_dlopen_test + COMMAND onnxruntime_shared_lib_cpuinfo_dlopen_test + WORKING_DIRECTORY $) + set_target_properties( + onnxruntime_shared_lib_cpuinfo_dlopen_test + PROPERTIES FOLDER "ONNXRuntimeTest") + endif() + if (MSVC) # set VS debugger working directory to the test program's directory set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY $) diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc new file mode 100644 index 0000000000000..33d682ef63028 --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include +#include +#include + +#include "onnxruntime_c_api.h" + +namespace { + +// cpuinfo's Windows backend allocates its global topology data directly from the process heap. +struct ProcessHeapSnapshot { + size_t busy_block_count = 0; + size_t busy_bytes = 0; +}; + +bool CaptureProcessHeapSnapshot(ProcessHeapSnapshot& snapshot) { + HANDLE process_heap = GetProcessHeap(); + if (!HeapLock(process_heap)) { + return false; + } + + PROCESS_HEAP_ENTRY entry{}; + while (HeapWalk(process_heap, &entry)) { + if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { + ++snapshot.busy_block_count; + snapshot.busy_bytes += entry.cbData; + } + } + + const DWORD error = GetLastError(); + const bool unlocked = HeapUnlock(process_heap) != FALSE; + return error == ERROR_NO_MORE_ITEMS && unlocked; +} + +bool CheckStatus(const OrtApi& ort_api, OrtStatus* status) { + if (status == nullptr) { + return true; + } + + std::cerr << "ONNX Runtime error: " << ort_api.GetErrorMessage(status) << std::endl; + ort_api.ReleaseStatus(status); + return false; +} + +bool LoadQueryHardwareAndUnload() { + HMODULE ort_library = LoadLibraryW(L"onnxruntime.dll"); + if (ort_library == nullptr) { + std::cerr << "LoadLibraryW failed with error " << GetLastError() << std::endl; + return false; + } + + using OrtGetApiBaseFunction = const OrtApiBase*(ORT_API_CALL*)(); + const auto ort_get_api_base = + reinterpret_cast(GetProcAddress(ort_library, "OrtGetApiBase")); + if (ort_get_api_base == nullptr) { + std::cerr << "GetProcAddress failed with error " << GetLastError() << std::endl; + FreeLibrary(ort_library); + return false; + } + + const OrtApiBase* ort_api_base = ort_get_api_base(); + if (ort_api_base == nullptr) { + std::cerr << "OrtGetApiBase returned null" << std::endl; + FreeLibrary(ort_library); + return false; + } + + const OrtApi* ort_api = ort_api_base->GetApi(ORT_API_VERSION); + OrtEnv* env = nullptr; + bool success = ort_api != nullptr; + + if (success) { + success = CheckStatus(*ort_api, ort_api->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "CpuinfoDlopenTest", &env)); + } + if (success) { + size_t num_devices = 0; + success = CheckStatus(*ort_api, ort_api->GetNumHardwareDevices(env, &num_devices)); + if (success && num_devices == 0) { + std::cerr << "ONNX Runtime reported no hardware devices" << std::endl; + success = false; + } + } + if (env != nullptr) { + ort_api->ReleaseEnv(env); + } + + if (!FreeLibrary(ort_library)) { + std::cerr << "FreeLibrary failed with error " << GetLastError() << std::endl; + return false; + } + + if (GetModuleHandleW(L"onnxruntime.dll") != nullptr) { + std::cerr << "onnxruntime.dll remained loaded after FreeLibrary" << std::endl; + return false; + } + + return success; +} + +} // namespace + +int wmain() { + constexpr size_t kWarmupCycles = 2; + constexpr size_t kMeasuredCycles = 3; + + for (size_t cycle = 0; cycle < kWarmupCycles; ++cycle) { + if (!LoadQueryHardwareAndUnload()) { + return EXIT_FAILURE; + } + } + + std::array snapshots; + if (!CaptureProcessHeapSnapshot(snapshots[0])) { + std::cerr << "Failed to capture the initial process heap snapshot" << std::endl; + return EXIT_FAILURE; + } + + for (size_t cycle = 0; cycle < kMeasuredCycles; ++cycle) { + if (!LoadQueryHardwareAndUnload()) { + return EXIT_FAILURE; + } + if (!CaptureProcessHeapSnapshot(snapshots[cycle + 1])) { + std::cerr << "Failed to capture process heap snapshot " << cycle + 1 << std::endl; + return EXIT_FAILURE; + } + } + + bool block_count_grew_each_cycle = true; + bool allocated_bytes_grew_each_cycle = true; + // Ignore one-time loader caching and detect the repeated growth caused by unreleased cpuinfo globals. + for (size_t cycle = 0; cycle < kMeasuredCycles; ++cycle) { + block_count_grew_each_cycle = + block_count_grew_each_cycle && + snapshots[cycle + 1].busy_block_count > snapshots[cycle].busy_block_count; + allocated_bytes_grew_each_cycle = + allocated_bytes_grew_each_cycle && + snapshots[cycle + 1].busy_bytes > snapshots[cycle].busy_bytes; + } + + if (block_count_grew_each_cycle && allocated_bytes_grew_each_cycle) { + std::cerr << "Process heap grew after every ONNX Runtime DLL load/unload cycle:" + << " blocks " << snapshots[0].busy_block_count << " -> " + << snapshots[kMeasuredCycles].busy_block_count + << ", bytes " << snapshots[0].busy_bytes << " -> " + << snapshots[kMeasuredCycles].busy_bytes << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} From 883a2e00138c54625c833dcf0be3428b394e0a3e Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Thu, 27 Aug 2026 15:58:13 -0700 Subject: [PATCH 05/16] Add cpuinfo refcount regression test --- cmake/onnxruntime_unittests.cmake | 11 +++++ .../test/shared_lib/cpuinfo_refcount_test.cc | 44 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index c88d155436eab..ed12a668b48ca 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -1995,6 +1995,17 @@ endif() set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES FOLDER "ONNXRuntimeTest") if(CPUINFO_SUPPORTED AND NOT onnxruntime_MINIMAL_BUILD) + onnxruntime_add_executable( + onnxruntime_cpuinfo_refcount_test + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_refcount_test.cc) + target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo) + add_test( + NAME onnxruntime_cpuinfo_refcount_test + COMMAND onnxruntime_cpuinfo_refcount_test) + set_target_properties( + onnxruntime_cpuinfo_refcount_test + PROPERTIES FOLDER "ONNXRuntimeTest") + onnxruntime_add_executable( onnxruntime_shared_lib_cpuinfo_dlopen_test ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test.cc) diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc new file mode 100644 index 0000000000000..616afa18bbc1b --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include + +int main() { + if (!cpuinfo_initialize()) { + std::cerr << "The first cpuinfo_initialize call failed" << std::endl; + return EXIT_FAILURE; + } + + if (!cpuinfo_initialize()) { + std::cerr << "The second cpuinfo_initialize call failed" << std::endl; + cpuinfo_deinitialize(); + return EXIT_FAILURE; + } + + const uint32_t processor_count = cpuinfo_get_processors_count(); + const cpuinfo_processor* processors = cpuinfo_get_processors(); + if (processor_count == 0 || processors == nullptr) { + std::cerr << "cpuinfo did not expose valid processor data" << std::endl; + cpuinfo_deinitialize(); + cpuinfo_deinitialize(); + return EXIT_FAILURE; + } + + cpuinfo_deinitialize(); + + const bool first_consumer_release_preserved_state = + cpuinfo_get_processors_count() == processor_count && + cpuinfo_get_processors() == processors; + + cpuinfo_deinitialize(); + + if (!first_consumer_release_preserved_state) { + std::cerr << "cpuinfo released shared state while another consumer was still active" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} From 10da89e0a4f37077e9990ec8493b30e99a75d515 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Thu, 27 Aug 2026 18:14:37 -0700 Subject: [PATCH 06/16] Simplify cpuinfo patch integration --- .../external/onnxruntime_external_deps.cmake | 54 +- cmake/external/xnnpack.cmake | 3 +- cmake/onnxruntime_unittests.cmake | 29 +- .../cpuinfo/enable_deinit_refcounting.patch | 90 ++- ...elease_cpuinfo_after_hardware_config.patch | 46 ++ .../cpuinfo/enable_deinit_refcounting.patch | 586 ------------------ cmake/vcpkg-ports/cpuinfo/portfile.cmake | 3 +- cmake/vcpkg-ports/xnnpack/portfile.cmake | 1 + .../test/common/cpuinfo_refcount_test.cc | 135 ++++ .../test/shared_lib/cpuinfo_dlopen_test.cc | 24 + .../test/shared_lib/cpuinfo_refcount_test.cc | 44 -- 11 files changed, 340 insertions(+), 675 deletions(-) create mode 100644 cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch delete mode 100644 cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch create mode 100644 onnxruntime/test/common/cpuinfo_refcount_test.cc delete mode 100644 onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 1762e8d5503df..c98c62213133c 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -363,34 +363,50 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") - # https://github.com/pytorch/cpuinfo/pull/400 - set(ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch) if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") - # https://github.com/pytorch/cpuinfo/pull/324 - set(ONNXRUNTIME_CPUINFO_PATCH_COMMAND + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && + # https://github.com/pytorch/cpuinfo/pull/324 ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && - ${ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND}) + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + FIND_PACKAGE_ARGS NAMES cpuinfo + ) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") - # https://github.com/microsoft/onnxruntime/issues/10038 - set(ONNXRUNTIME_CPUINFO_PATCH_COMMAND + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/microsoft/onnxruntime/issues/10038 ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && - ${ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND}) + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + FIND_PACKAGE_ARGS NAMES cpuinfo + ) else() - set(ONNXRUNTIME_CPUINFO_PATCH_COMMAND ${ONNXRUNTIME_CPUINFO_DEINIT_PATCH_COMMAND}) + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + FIND_PACKAGE_ARGS NAMES cpuinfo + ) endif() - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND ${ONNXRUNTIME_CPUINFO_PATCH_COMMAND} - FIND_PACKAGE_ARGS NAMES cpuinfo - ) set(ONNXRUNTIME_CPUINFO_PROJ pytorch_cpuinfo) onnxruntime_fetchcontent_makeavailable(${ONNXRUNTIME_CPUINFO_PROJ}) if(TARGET cpuinfo::cpuinfo AND NOT TARGET cpuinfo) diff --git a/cmake/external/xnnpack.cmake b/cmake/external/xnnpack.cmake index 571283c33c713..223f99001b916 100644 --- a/cmake/external/xnnpack.cmake +++ b/cmake/external/xnnpack.cmake @@ -84,7 +84,8 @@ endif() onnxruntime_fetchcontent_declare(googlexnnpack URL ${DEP_URL_googlexnnpack} URL_HASH SHA1=${DEP_SHA1_googlexnnpack} - PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch + PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch && + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/release_cpuinfo_after_hardware_config.patch EXCLUDE_FROM_ALL FIND_PACKAGE_ARGS NAMES xnnpack ) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index ed12a668b48ca..6b32ab88e163c 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -1986,6 +1986,19 @@ endif() set_target_properties(ep_weight_sharing_ctx_gen PROPERTIES FOLDER "ONNXRuntimeTest") endif() + if (CPUINFO_SUPPORTED AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + onnxruntime_add_executable( + onnxruntime_cpuinfo_refcount_test + ${TEST_SRC_DIR}/common/cpuinfo_refcount_test.cc) + target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) + add_test( + NAME onnxruntime_cpuinfo_refcount_test + COMMAND onnxruntime_cpuinfo_refcount_test) + set_target_properties( + onnxruntime_cpuinfo_refcount_test + PROPERTIES FOLDER "ONNXRuntimeTest") + endif() + # shared lib if (onnxruntime_BUILD_SHARED_LIB) if(WIN32) @@ -1995,21 +2008,15 @@ endif() set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES FOLDER "ONNXRuntimeTest") if(CPUINFO_SUPPORTED AND NOT onnxruntime_MINIMAL_BUILD) - onnxruntime_add_executable( - onnxruntime_cpuinfo_refcount_test - ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_refcount_test.cc) - target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo) - add_test( - NAME onnxruntime_cpuinfo_refcount_test - COMMAND onnxruntime_cpuinfo_refcount_test) - set_target_properties( - onnxruntime_cpuinfo_refcount_test - PROPERTIES FOLDER "ONNXRuntimeTest") - onnxruntime_add_executable( onnxruntime_shared_lib_cpuinfo_dlopen_test ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test.cc) add_dependencies(onnxruntime_shared_lib_cpuinfo_dlopen_test ${all_dependencies} onnxruntime) + if(onnxruntime_USE_XNNPACK) + target_compile_definitions( + onnxruntime_shared_lib_cpuinfo_dlopen_test + PRIVATE ORT_CPUINFO_DLOPEN_TEST_USE_XNNPACK) + endif() add_test( NAME onnxruntime_shared_lib_cpuinfo_dlopen_test COMMAND onnxruntime_shared_lib_cpuinfo_dlopen_test diff --git a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch index c7febd3da6ae6..0cc10d075fc7f 100644 --- a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch +++ b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch @@ -1,5 +1,5 @@ diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c -index 48c9f4a050..cb3170f279 100644 +index 48c9f4a050412220b5c817814b643fc4850c7626..cb3170f279474ee344dd61480eab2831f2d027b9 100644 --- a/src/arm/linux/init.c +++ b/src/arm/linux/init.c @@ -1001,3 +1001,43 @@ cleanup: @@ -47,7 +47,7 @@ index 48c9f4a050..cb3170f279 100644 + cpuinfo_linux_cpu_max = 0; +} diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c -index 76224b9d40..9a9fb4276c 100644 +index 76224b9d40eb5cd047f1353e81b5999196d3b881..9a9fb4276c531687bf7984e2bd41152ed91e94a6 100644 --- a/src/arm/mach/init.c +++ b/src/arm/mach/init.c @@ -873,3 +873,32 @@ cleanup: @@ -84,10 +84,28 @@ index 76224b9d40..9a9fb4276c 100644 + cpuinfo_max_cache_size = 0; +} diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c -index 815ecb770a..2e44f8aa05 100644 +index 815ecb770a5bc5c48c7dff049f50b111b427954a..f0d66223f0d189776659248f525200ebdcda0116 100644 --- a/src/arm/windows/init-by-logical-sys-info.c +++ b/src/arm/windows/init-by-logical-sys-info.c -@@ -372,6 +372,40 @@ clean_up: +@@ -14,6 +14,8 @@ + + #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) + ++static uint32_t cache_counters[MAX_NR_OF_CACHES]; ++ + /* Call chain: + * cpu_info_init_by_logical_sys_info + * read_packages_for_processors +@@ -126,6 +128,8 @@ bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, co + uint32_t nr_of_uarchs = 0; + bool result = false; + ++ memset(cache_counters, 0, sizeof(cache_counters)); ++ + HANDLE heap = GetProcessHeap(); + + /* 1. Count available logical processor groups and processors */ +@@ -372,6 +376,40 @@ clean_up: return result; } @@ -128,8 +146,54 @@ index 815ecb770a..2e44f8aa05 100644 static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { uint32_t nr_of_processors = 0; +@@ -606,11 +644,6 @@ static bool parse_relation_cache_info( + uint32_t* numbers_of_caches, + const uint32_t* global_proc_index_per_group, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { +- static uint32_t l1i_counter = 0; +- static uint32_t l1d_counter = 0; +- static uint32_t l2_counter = 0; +- static uint32_t l3_counter = 0; +- + /* Count cache types for allocation at first. */ + if (caches == NULL) { + switch (info->Cache.Level) { +@@ -665,12 +698,12 @@ static bool parse_relation_cache_info( + case 1: + switch (info->Cache.Type) { + case CacheInstruction: +- current_cache = l1i_base + l1i_counter; +- l1i_counter++; ++ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; ++ cache_counters[cpuinfo_cache_level_1i]++; + break; + case CacheData: +- current_cache = l1d_base + l1d_counter; +- l1d_counter++; ++ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; ++ cache_counters[cpuinfo_cache_level_1d]++; + break; + case CacheUnified: + break; +@@ -681,12 +714,12 @@ static bool parse_relation_cache_info( + } + break; + case 2: +- current_cache = l2_base + l2_counter; +- l2_counter++; ++ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; ++ cache_counters[cpuinfo_cache_level_2]++; + break; + case 3: +- current_cache = l3_base + l3_counter; +- l3_counter++; ++ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; ++ cache_counters[cpuinfo_cache_level_3]++; + break; + } + current_cache->size = info->Cache.CacheSize; diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c -index 528ae3279f..fcf219df15 100644 +index 528ae3279f5e979409ac07de6cee833691e034cd..fcf219df153419249211be4fdf014f50fc5198da 100644 --- a/src/arm/windows/init.c +++ b/src/arm/windows/init.c @@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV @@ -182,7 +246,7 @@ index 528ae3279f..fcf219df15 100644 cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h -index d84b26a89c..9c1588872d 100644 +index d84b26a89cfc38e726216688e490f50799f31455..9c1588872da413535f2fb68fb96d6106b420ac49 100644 --- a/src/cpuinfo/internal-api.h +++ b/src/cpuinfo/internal-api.h @@ -45,6 +45,9 @@ extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; @@ -220,7 +284,7 @@ index d84b26a89c..9c1588872d 100644 typedef void (*cpuinfo_processor_callback)(uint32_t); diff --git a/src/emscripten/init.c b/src/emscripten/init.c -index c239324375..6c8210f2c2 100644 +index c2393243755816fb341f892b7b092a58891603c4..6c8210f2c23ddc0f0efde954d80fcf974aba74fb 100644 --- a/src/emscripten/init.c +++ b/src/emscripten/init.c @@ -286,3 +286,35 @@ cleanup: @@ -260,7 +324,7 @@ index c239324375..6c8210f2c2 100644 + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} diff --git a/src/init.c b/src/init.c -index 81d5721c44..e3cd9217a9 100644 +index 81d5721c446f363cefcbb39d52507a2c07298c35..e3cd9217a9acfc9615f97a1aa6bb66d96f373910 100644 --- a/src/init.c +++ b/src/init.c @@ -13,55 +13,98 @@ @@ -387,7 +451,7 @@ index 81d5721c44..e3cd9217a9 100644 + CPUINFO_LIFECYCLE_UNLOCK(); +} diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c -index 45168c7e66..3da8c3ffd5 100644 +index 45168c7e66b80e7f8a712a6afe7bab379a07b95b..3da8c3ffd59f02bebb0193b832d67ddf0841cab3 100644 --- a/src/riscv/linux/init.c +++ b/src/riscv/linux/init.c @@ -618,3 +618,36 @@ cleanup: @@ -428,7 +492,7 @@ index 45168c7e66..3da8c3ffd5 100644 + cpuinfo_linux_cpu_max = 0; +} diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c -index 797fa24b95..ba30a5d9c8 100644 +index 797fa24b95a55e0503459dd2fdaf8476f060a631..ba30a5d9c89c209115f6797d9b52263c7271e17f 100644 --- a/src/x86/freebsd/init.c +++ b/src/x86/freebsd/init.c @@ -396,3 +396,30 @@ cleanup: @@ -463,7 +527,7 @@ index 797fa24b95..ba30a5d9c8 100644 + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c -index d2b2d47514..62e1187e06 100644 +index d2b2d47514c8cc75b502c78b1d1176ae49fe3304..62e1187e0631b791c378d23963d63e10095a12be 100644 --- a/src/x86/linux/init.c +++ b/src/x86/linux/init.c @@ -676,3 +676,38 @@ cleanup: @@ -506,7 +570,7 @@ index d2b2d47514..62e1187e06 100644 + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c -index b8ea6047ac..9009e830ab 100644 +index b8ea6047ac043fc067190013e719188b50aca3f5..9009e830ab4ac15610af72e700266b800d88e0fc 100644 --- a/src/x86/mach/init.c +++ b/src/x86/mach/init.c @@ -378,3 +378,30 @@ cleanup: @@ -541,7 +605,7 @@ index b8ea6047ac..9009e830ab 100644 + cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; +} diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c -index 7380bb46d5..f1401beb60 100644 +index 7380bb46d5f2ee96e98b77fd378be9d70de813ee..f1401beb60de989503402be98fdbac4c20909c1b 100644 --- a/src/x86/windows/init.c +++ b/src/x86/windows/init.c @@ -665,5 +665,39 @@ cleanup: diff --git a/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch new file mode 100644 index 0000000000000..8aea6bfc4d156 --- /dev/null +++ b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch @@ -0,0 +1,46 @@ +diff --git a/src/configs/hardware-config.c b/src/configs/hardware-config.c +index b4e942aa6955360e3ea85619d549410e65ab7a9c..6cf6e9a213b6f72c94940ff815e11f70a658c419 100644 +--- a/src/configs/hardware-config.c ++++ b/src/configs/hardware-config.c +@@ -343,6 +343,7 @@ static void init_hardware_config(void) { + xnn_log_info("cpu_get_uarch(%i): 0x%x", i, cpuinfo_get_uarch(i)->uarch); + } + #endif // XNN_MAX_UARCH_TYPES > 1 ++ cpuinfo_deinitialize(); + #else + xnn_log_warning("Unable to determine L1/L2 data cache properties."); + #endif // XNN_ENABLE_CPUINFO +@@ -358,21 +359,33 @@ const struct xnn_hardware_config* xnn_init_hardware_config() { + #if XNN_ARCH_ARM + if (!cpuinfo_has_arm_v6()) { + xnn_log_debug("unsupported hardware: ARMv6 not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + + if (!cpuinfo_has_arm_vfpv2() && !cpuinfo_has_arm_vfpv3()) { + xnn_log_debug("unsupported hardware: VFP FPU not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + #endif // XNN_ARCH_ARM + #if XNN_ARCH_X86 + if (!cpuinfo_has_x86_sse2()) { + xnn_log_debug("unsupported hardware: SSE2 not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + #endif // XNN_ARCH_X86 + + XNN_INIT_ONCE(hardware); ++ #if !XNN_PLATFORM_WEB && !XNN_ARCH_RISCV && !XNN_ARCH_PPC64 && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return &hardware_config; + } diff --git a/cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch b/cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch deleted file mode 100644 index c7febd3da6ae6..0000000000000 --- a/cmake/vcpkg-ports/cpuinfo/enable_deinit_refcounting.patch +++ /dev/null @@ -1,586 +0,0 @@ -diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c -index 48c9f4a050..cb3170f279 100644 ---- a/src/arm/linux/init.c -+++ b/src/arm/linux/init.c -@@ -1001,3 +1001,43 @@ cleanup: - free(linux_cpu_to_core_map); - free(linux_cpu_to_uarch_index_map); - } -+ -+void cpuinfo_arm_linux_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ /* cpuinfo_packages points to static storage (&package) — do not free */ -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ free(cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ free(cpuinfo_cache[lvl]); -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ free(cpuinfo_linux_cpu_to_processor_map); -+ cpuinfo_linux_cpu_to_processor_map = NULL; -+ -+ free(cpuinfo_linux_cpu_to_core_map); -+ cpuinfo_linux_cpu_to_core_map = NULL; -+ -+ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); -+ cpuinfo_linux_cpu_to_uarch_index_map = NULL; -+ -+ cpuinfo_linux_cpu_max = 0; -+} -diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c -index 76224b9d40..9a9fb4276c 100644 ---- a/src/arm/mach/init.c -+++ b/src/arm/mach/init.c -@@ -873,3 +873,32 @@ cleanup: - free(l2); - free(l3); - } -+ -+void cpuinfo_arm_mach_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ free(cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ free(cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ free(cpuinfo_cache[lvl]); -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+} -diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c -index 815ecb770a..2e44f8aa05 100644 ---- a/src/arm/windows/init-by-logical-sys-info.c -+++ b/src/arm/windows/init-by-logical-sys-info.c -@@ -372,6 +372,40 @@ clean_up: - return result; - } - -+BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { -+ HANDLE heap = GetProcessHeap(); -+ -+ HeapFree(heap, 0, cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ /* Caches are allocated as a single contiguous block starting at l1i */ -+ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ return TRUE; -+} -+ - static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { - uint32_t nr_of_processors = 0; - -diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c -index 528ae3279f..fcf219df15 100644 ---- a/src/arm/windows/init.c -+++ b/src/arm/windows/init.c -@@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV - set_cpuinfo_isa_fields(); - - chip_info = get_system_info_from_registry(); -+ const bool chip_info_from_registry = chip_info != NULL; - if (chip_info == NULL) { - chip_info = &woa_chip_unknown; - } - - cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); - -+ if (chip_info_from_registry) { -+ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); -+ HeapFree(GetProcessHeap(), 0, chip_info); -+ } -+ - return true; - } - -@@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); - if (midr_qword == 0) { - cpuinfo_log_error("Registry read error for MIDR value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // MIDR is only 32 bits, so we need to cast it to uint32_t -@@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); - if (frequency_mhz == 0) { - cpuinfo_log_error("Registry read error for frequency value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // Convert MHz to Hz -@@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); - if (chip_info == NULL) { - cpuinfo_log_error("Heap allocation error for chip_info"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - - // set chip_info fields - chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); - - cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); -diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h -index d84b26a89c..9c1588872d 100644 ---- a/src/cpuinfo/internal-api.h -+++ b/src/cpuinfo/internal-api.h -@@ -45,6 +45,9 @@ extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; - extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max; - extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map; - extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map; -+#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 -+extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; -+#endif - #endif - - CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void); -@@ -62,6 +65,23 @@ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); - CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void); - CPUINFO_PRIVATE void cpuinfo_emscripten_init(void); - -+/* Platform-specific deinitialization functions. -+ * Each frees the globals allocated by its corresponding init function. */ -+CPUINFO_PRIVATE void cpuinfo_x86_mach_deinit(void); -+CPUINFO_PRIVATE void cpuinfo_x86_linux_deinit(void); -+CPUINFO_PRIVATE void cpuinfo_x86_freebsd_deinit(void); -+#if defined(_WIN32) || defined(__CYGWIN__) -+#if CPUINFO_ARCH_ARM64 -+CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); -+#else -+CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context); -+#endif -+#endif -+CPUINFO_PRIVATE void cpuinfo_arm_mach_deinit(void); -+CPUINFO_PRIVATE void cpuinfo_arm_linux_deinit(void); -+CPUINFO_PRIVATE void cpuinfo_riscv_linux_deinit(void); -+CPUINFO_PRIVATE void cpuinfo_emscripten_deinit(void); -+ - CPUINFO_PRIVATE uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor); - - typedef void (*cpuinfo_processor_callback)(uint32_t); -diff --git a/src/emscripten/init.c b/src/emscripten/init.c -index c239324375..6c8210f2c2 100644 ---- a/src/emscripten/init.c -+++ b/src/emscripten/init.c -@@ -286,3 +286,35 @@ cleanup: - free(l1d); - free(l2); - } -+ -+void cpuinfo_emscripten_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ /* cpuinfo_packages points to static storage (&static_package) — do not free */ -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ /* L1i, L1d, L2 are dynamically allocated; L3 may point to static -+ * storage (&static_x86_l3) so only free levels that were calloc'd */ -+ free(cpuinfo_cache[cpuinfo_cache_level_1i]); -+ free(cpuinfo_cache[cpuinfo_cache_level_1d]); -+ free(cpuinfo_cache[cpuinfo_cache_level_2]); -+ /* cpuinfo_cache[cpuinfo_cache_level_3] may be &static_x86_l3 — do not free */ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; -+} -diff --git a/src/init.c b/src/init.c -index 81d5721c44..e3cd9217a9 100644 ---- a/src/init.c -+++ b/src/init.c -@@ -13,55 +13,98 @@ - #endif - - #if defined(_WIN32) || defined(__CYGWIN__) --static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT; -+static SRWLOCK cpuinfo_lifecycle_lock = SRWLOCK_INIT; -+#define CPUINFO_LIFECYCLE_LOCK() AcquireSRWLockExclusive(&cpuinfo_lifecycle_lock) -+#define CPUINFO_LIFECYCLE_UNLOCK() ReleaseSRWLockExclusive(&cpuinfo_lifecycle_lock) - #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) --static pthread_once_t init_guard = PTHREAD_ONCE_INIT; -+static pthread_mutex_t cpuinfo_lifecycle_lock = PTHREAD_MUTEX_INITIALIZER; -+#define CPUINFO_LIFECYCLE_LOCK() pthread_mutex_lock(&cpuinfo_lifecycle_lock) -+#define CPUINFO_LIFECYCLE_UNLOCK() pthread_mutex_unlock(&cpuinfo_lifecycle_lock) - #else --static bool init_guard = false; -+/* Single-threaded build (e.g. Emscripten without pthreads): no lock needed. */ -+#define CPUINFO_LIFECYCLE_LOCK() \ -+ do { \ -+ } while (0) -+#define CPUINFO_LIFECYCLE_UNLOCK() \ -+ do { \ -+ } while (0) - #endif -+static unsigned int cpuinfo_init_refcount = 0; - - bool CPUINFO_ABI cpuinfo_initialize(void) { -+ CPUINFO_LIFECYCLE_LOCK(); -+ if (cpuinfo_init_refcount == 0) { - #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 - #if defined(__MACH__) && defined(__APPLE__) -- pthread_once(&init_guard, &cpuinfo_x86_mach_init); -+ cpuinfo_x86_mach_init(); - #elif defined(__FreeBSD__) -- pthread_once(&init_guard, &cpuinfo_x86_freebsd_init); -+ cpuinfo_x86_freebsd_init(); - #elif defined(__linux__) -- pthread_once(&init_guard, &cpuinfo_x86_linux_init); -+ cpuinfo_x86_linux_init(); - #elif defined(_WIN32) || defined(__CYGWIN__) -- InitOnceExecuteOnce(&init_guard, &cpuinfo_x86_windows_init, NULL, NULL); -+ cpuinfo_x86_windows_init(NULL, NULL, NULL); - #else -- cpuinfo_log_error("operating system is not supported in cpuinfo"); -+ cpuinfo_log_error("operating system is not supported in cpuinfo"); - #endif - #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 - #if defined(__linux__) -- pthread_once(&init_guard, &cpuinfo_arm_linux_init); -+ cpuinfo_arm_linux_init(); - #elif defined(__MACH__) && defined(__APPLE__) -- pthread_once(&init_guard, &cpuinfo_arm_mach_init); -+ cpuinfo_arm_mach_init(); - #elif defined(_WIN32) -- InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL); -+ cpuinfo_arm_windows_init(NULL, NULL, NULL); - #else -- cpuinfo_log_error("operating system is not supported in cpuinfo"); -+ cpuinfo_log_error("operating system is not supported in cpuinfo"); - #endif - #elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 - #if defined(__linux__) -- pthread_once(&init_guard, &cpuinfo_riscv_linux_init); -+ cpuinfo_riscv_linux_init(); - #else -- cpuinfo_log_error("operating system is not supported in cpuinfo"); -+ cpuinfo_log_error("operating system is not supported in cpuinfo"); - #endif - #elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD --#if defined(__EMSCRIPTEN_PTHREADS__) -- pthread_once(&init_guard, &cpuinfo_emscripten_init); --#else -- if (!init_guard) { - cpuinfo_emscripten_init(); -- } -- init_guard = true; --#endif - #else -- cpuinfo_log_error("processor architecture is not supported in cpuinfo"); -+ cpuinfo_log_error("processor architecture is not supported in cpuinfo"); - #endif -- return cpuinfo_is_initialized; -+ } -+ if (cpuinfo_is_initialized) { -+ cpuinfo_init_refcount++; -+ } -+ const bool initialized = cpuinfo_is_initialized; -+ CPUINFO_LIFECYCLE_UNLOCK(); -+ return initialized; - } - --void CPUINFO_ABI cpuinfo_deinitialize(void) {} -+void CPUINFO_ABI cpuinfo_deinitialize(void) { -+ CPUINFO_LIFECYCLE_LOCK(); -+ if (cpuinfo_init_refcount != 0 && --cpuinfo_init_refcount == 0) { -+ cpuinfo_is_initialized = false; -+#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 -+#if defined(__MACH__) && defined(__APPLE__) -+ cpuinfo_x86_mach_deinit(); -+#elif defined(__FreeBSD__) -+ cpuinfo_x86_freebsd_deinit(); -+#elif defined(__linux__) -+ cpuinfo_x86_linux_deinit(); -+#elif defined(_WIN32) || defined(__CYGWIN__) -+ cpuinfo_x86_windows_deinit(NULL, NULL, NULL); -+#endif -+#elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 -+#if defined(__linux__) -+ cpuinfo_arm_linux_deinit(); -+#elif defined(__MACH__) && defined(__APPLE__) -+ cpuinfo_arm_mach_deinit(); -+#elif defined(_WIN32) -+ cpuinfo_arm_windows_deinit(NULL, NULL, NULL); -+#endif -+#elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 -+#if defined(__linux__) -+ cpuinfo_riscv_linux_deinit(); -+#endif -+#elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD -+ cpuinfo_emscripten_deinit(); -+#endif -+ } -+ CPUINFO_LIFECYCLE_UNLOCK(); -+} -diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c -index 45168c7e66..3da8c3ffd5 100644 ---- a/src/riscv/linux/init.c -+++ b/src/riscv/linux/init.c -@@ -618,3 +618,36 @@ cleanup: - free(linux_cpu_to_core_map); - free(linux_cpu_to_uarch_index_map); - } -+ -+void cpuinfo_riscv_linux_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ free(cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ free(cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ free(cpuinfo_linux_cpu_to_processor_map); -+ cpuinfo_linux_cpu_to_processor_map = NULL; -+ -+ free(cpuinfo_linux_cpu_to_core_map); -+ cpuinfo_linux_cpu_to_core_map = NULL; -+ -+ free((void*)cpuinfo_linux_cpu_to_uarch_index_map); -+ cpuinfo_linux_cpu_to_uarch_index_map = NULL; -+ -+ cpuinfo_linux_cpu_max = 0; -+} -diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c -index 797fa24b95..ba30a5d9c8 100644 ---- a/src/x86/freebsd/init.c -+++ b/src/x86/freebsd/init.c -@@ -396,3 +396,30 @@ cleanup: - free(l3); - free(l4); - } -+ -+void cpuinfo_x86_freebsd_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ free(cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ free(cpuinfo_cache[lvl]); -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; -+} -diff --git a/src/x86/linux/init.c b/src/x86/linux/init.c -index d2b2d47514..62e1187e06 100644 ---- a/src/x86/linux/init.c -+++ b/src/x86/linux/init.c -@@ -676,3 +676,38 @@ cleanup: - free(linux_cpu_to_processor_map); - free(linux_cpu_to_core_map); - } -+ -+void cpuinfo_x86_linux_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ free(cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ free(cpuinfo_cache[lvl]); -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ free(cpuinfo_linux_cpu_to_processor_map); -+ cpuinfo_linux_cpu_to_processor_map = NULL; -+ -+ free(cpuinfo_linux_cpu_to_core_map); -+ cpuinfo_linux_cpu_to_core_map = NULL; -+ -+ cpuinfo_linux_cpu_max = 0; -+ -+ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; -+} -diff --git a/src/x86/mach/init.c b/src/x86/mach/init.c -index b8ea6047ac..9009e830ab 100644 ---- a/src/x86/mach/init.c -+++ b/src/x86/mach/init.c -@@ -378,3 +378,30 @@ cleanup: - free(l3); - free(l4); - } -+ -+void cpuinfo_x86_mach_deinit(void) { -+ free(cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ free(cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ free(cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ free(cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ free(cpuinfo_cache[lvl]); -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; -+} -diff --git a/src/x86/windows/init.c b/src/x86/windows/init.c -index 7380bb46d5..f1401beb60 100644 ---- a/src/x86/windows/init.c -+++ b/src/x86/windows/init.c -@@ -665,5 +665,39 @@ cleanup: - if (l4 != NULL) { - HeapFree(heap, 0, l4); - } -+ if (processor_infos != NULL) { -+ HeapFree(heap, 0, processor_infos); -+ } -+ return TRUE; -+} -+ -+BOOL CALLBACK cpuinfo_x86_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { -+ HANDLE heap = GetProcessHeap(); -+ -+ HeapFree(heap, 0, cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ HeapFree(heap, 0, cpuinfo_cache[lvl]); -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ cpuinfo_global_uarch = (struct cpuinfo_uarch_info){0}; -+ - return TRUE; - } diff --git a/cmake/vcpkg-ports/cpuinfo/portfile.cmake b/cmake/vcpkg-ports/cpuinfo/portfile.cmake index 1062ccba4b8b3..aa2205b699e07 100644 --- a/cmake/vcpkg-ports/cpuinfo/portfile.cmake +++ b/cmake/vcpkg-ports/cpuinfo/portfile.cmake @@ -12,7 +12,8 @@ vcpkg_from_github( PATCHES patch_cpuinfo_h_for_arm64ec.patch patch_vcpkg_arm64ec_support.patch # https://github.com/pytorch/cpuinfo/pull/324 - enable_deinit_refcounting.patch # https://github.com/pytorch/cpuinfo/pull/400 + # https://github.com/pytorch/cpuinfo/pull/400 + "${CMAKE_CURRENT_LIST_DIR}/../../patches/cpuinfo/enable_deinit_refcounting.patch" ) vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS diff --git a/cmake/vcpkg-ports/xnnpack/portfile.cmake b/cmake/vcpkg-ports/xnnpack/portfile.cmake index 60b3566629e10..6ccfd2909454f 100644 --- a/cmake/vcpkg-ports/xnnpack/portfile.cmake +++ b/cmake/vcpkg-ports/xnnpack/portfile.cmake @@ -11,6 +11,7 @@ vcpkg_from_github( PATCHES fix-build.patch disable_gcc_warning.patch + "${CMAKE_CURRENT_LIST_DIR}/../../patches/xnnpack/release_cpuinfo_after_hardware_config.patch" ) vcpkg_find_acquire_program(PYTHON3) diff --git a/onnxruntime/test/common/cpuinfo_refcount_test.cc b/onnxruntime/test/common/cpuinfo_refcount_test.cc new file mode 100644 index 0000000000000..dbbc433bf7cea --- /dev/null +++ b/onnxruntime/test/common/cpuinfo_refcount_test.cc @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include +#include +#include +#include + +namespace { + +bool HasValidCpuinfoState() { + return cpuinfo_get_processors_count() != 0 && + cpuinfo_get_processors() != nullptr && + cpuinfo_get_processor(0) != nullptr; +} + +bool TestSequentialConsumers() { + if (!cpuinfo_initialize()) { + std::cerr << "The first cpuinfo_initialize call failed" << std::endl; + return false; + } + + if (!cpuinfo_initialize()) { + std::cerr << "The second cpuinfo_initialize call failed" << std::endl; + cpuinfo_deinitialize(); + return false; + } + + const uint32_t processor_count = cpuinfo_get_processors_count(); + const cpuinfo_processor* processors = cpuinfo_get_processors(); + if (processor_count == 0 || processors == nullptr) { + std::cerr << "cpuinfo did not expose valid processor data" << std::endl; + cpuinfo_deinitialize(); + cpuinfo_deinitialize(); + return false; + } + + cpuinfo_deinitialize(); + + const bool first_consumer_release_preserved_state = + cpuinfo_get_processors_count() == processor_count && + cpuinfo_get_processors() == processors; + + cpuinfo_deinitialize(); + + if (!first_consumer_release_preserved_state) { + std::cerr << "cpuinfo released shared state while another consumer was still active" << std::endl; + return false; + } + + if (!cpuinfo_initialize()) { + std::cerr << "cpuinfo failed to reinitialize after the final consumer released it" << std::endl; + return false; + } + cpuinfo_deinitialize(); + + return true; +} + +bool TestConcurrentConsumers() { + constexpr size_t kThreadCount = 8; + constexpr size_t kIterations = 100; + std::atomic initialized_count{0}; + std::atomic validated_count{0}; + std::atomic deinitialized_count{0}; + std::atomic failed{false}; + + const auto consumer = [&]() { + for (size_t iteration = 0; iteration < kIterations; ++iteration) { + const bool initialized = cpuinfo_initialize(); + if (!initialized) { + failed = true; + } + + const size_t expected_count = (iteration + 1) * kThreadCount; + ++initialized_count; + while (initialized_count < expected_count) { + std::this_thread::yield(); + } + + if (initialized && !HasValidCpuinfoState()) { + failed = true; + } + + ++validated_count; + while (validated_count < expected_count) { + std::this_thread::yield(); + } + + if (initialized) { + cpuinfo_deinitialize(); + } + + ++deinitialized_count; + while (deinitialized_count < expected_count) { + std::this_thread::yield(); + } + } + }; + + std::vector consumers; + consumers.reserve(kThreadCount); + for (size_t thread = 0; thread < kThreadCount; ++thread) { + consumers.emplace_back(consumer); + } + for (std::thread& consumer_thread : consumers) { + consumer_thread.join(); + } + + if (failed) { + std::cerr << "cpuinfo failed during concurrent initialization and deinitialization" << std::endl; + return false; + } + + if (!cpuinfo_initialize() || !HasValidCpuinfoState()) { + std::cerr << "cpuinfo failed to reinitialize after concurrent use" << std::endl; + return false; + } + cpuinfo_deinitialize(); + + return true; +} + +} // namespace + +int main() { + if (!TestSequentialConsumers() || !TestConcurrentConsumers()) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc index 33d682ef63028..f819785f70a5a 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc @@ -72,6 +72,8 @@ bool LoadQueryHardwareAndUnload() { const OrtApi* ort_api = ort_api_base->GetApi(ORT_API_VERSION); OrtEnv* env = nullptr; + OrtSessionOptions* session_options = nullptr; + OrtSession* session = nullptr; bool success = ort_api != nullptr; if (success) { @@ -85,6 +87,28 @@ bool LoadQueryHardwareAndUnload() { success = false; } } +#if defined(ORT_CPUINFO_DLOPEN_TEST_USE_XNNPACK) + if (success) { + success = CheckStatus(*ort_api, ort_api->CreateSessionOptions(&session_options)); + } + if (success) { + success = CheckStatus( + *ort_api, + ort_api->SessionOptionsAppendExecutionProvider(session_options, "XNNPACK", nullptr, nullptr, 0)); + } + if (success) { + success = CheckStatus( + *ort_api, + ort_api->CreateSession(env, L"testdata\\matmul_1.onnx", session_options, &session)); + } +#endif + + if (session != nullptr) { + ort_api->ReleaseSession(session); + } + if (session_options != nullptr) { + ort_api->ReleaseSessionOptions(session_options); + } if (env != nullptr) { ort_api->ReleaseEnv(env); } diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc deleted file mode 100644 index 616afa18bbc1b..0000000000000 --- a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include - -#include -#include - -int main() { - if (!cpuinfo_initialize()) { - std::cerr << "The first cpuinfo_initialize call failed" << std::endl; - return EXIT_FAILURE; - } - - if (!cpuinfo_initialize()) { - std::cerr << "The second cpuinfo_initialize call failed" << std::endl; - cpuinfo_deinitialize(); - return EXIT_FAILURE; - } - - const uint32_t processor_count = cpuinfo_get_processors_count(); - const cpuinfo_processor* processors = cpuinfo_get_processors(); - if (processor_count == 0 || processors == nullptr) { - std::cerr << "cpuinfo did not expose valid processor data" << std::endl; - cpuinfo_deinitialize(); - cpuinfo_deinitialize(); - return EXIT_FAILURE; - } - - cpuinfo_deinitialize(); - - const bool first_consumer_release_preserved_state = - cpuinfo_get_processors_count() == processor_count && - cpuinfo_get_processors() == processors; - - cpuinfo_deinitialize(); - - if (!first_consumer_release_preserved_state) { - std::cerr << "cpuinfo released shared state while another consumer was still active" << std::endl; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} From 43981fa2445bfa2adeeffc7c7852bb1a20c3a1f0 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Fri, 28 Aug 2026 12:26:49 -0700 Subject: [PATCH 07/16] Rerun CI after cpuinfo artifact upload From 2d231ffe6c0dd771dfbae13aaf2651e42d1f0f2c Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Fri, 28 Aug 2026 13:06:35 -0700 Subject: [PATCH 08/16] Fix cpuinfo CI build failures --- .../external/onnxruntime_external_deps.cmake | 6 +- cmake/onnxruntime_unittests.cmake | 2 +- cmake/patches/.gitattributes | 3 + .../cpuinfo/enable_deinit_refcounting.patch | 288 +++++++++--------- .../cpuinfo_refcount_test.cc | 0 5 files changed, 151 insertions(+), 148 deletions(-) rename onnxruntime/test/{common => shared_lib}/cpuinfo_refcount_test.cc (100%) diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index c98c62213133c..5bbe349309f9c 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -375,7 +375,7 @@ if (CPUINFO_SUPPORTED) # https://github.com/pytorch/cpuinfo/pull/324 ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch FIND_PACKAGE_ARGS NAMES cpuinfo ) @@ -390,7 +390,7 @@ if (CPUINFO_SUPPORTED) # https://github.com/microsoft/onnxruntime/issues/10038 ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch FIND_PACKAGE_ARGS NAMES cpuinfo ) @@ -402,7 +402,7 @@ if (CPUINFO_SUPPORTED) EXCLUDE_FROM_ALL PATCH_COMMAND # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch FIND_PACKAGE_ARGS NAMES cpuinfo ) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 6b32ab88e163c..a86f3042dc07b 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -1989,7 +1989,7 @@ endif() if (CPUINFO_SUPPORTED AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") onnxruntime_add_executable( onnxruntime_cpuinfo_refcount_test - ${TEST_SRC_DIR}/common/cpuinfo_refcount_test.cc) + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_refcount_test.cc) target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) add_test( NAME onnxruntime_cpuinfo_refcount_test diff --git a/cmake/patches/.gitattributes b/cmake/patches/.gitattributes index 9812ceb1ffd9b..24d5f860ef2a8 100644 --- a/cmake/patches/.gitattributes +++ b/cmake/patches/.gitattributes @@ -1 +1,4 @@ *.patch text eol=lf + +# cpuinfo contains both LF and CRLF source files, so this patch must preserve both. +cpuinfo/enable_deinit_refcounting.patch -text diff --git a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch index 0cc10d075fc7f..960cfc6df58fa 100644 --- a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch +++ b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch @@ -88,163 +88,163 @@ index 815ecb770a5bc5c48c7dff049f50b111b427954a..f0d66223f0d189776659248f525200eb --- a/src/arm/windows/init-by-logical-sys-info.c +++ b/src/arm/windows/init-by-logical-sys-info.c @@ -14,6 +14,8 @@ - - #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) - -+static uint32_t cache_counters[MAX_NR_OF_CACHES]; -+ - /* Call chain: - * cpu_info_init_by_logical_sys_info - * read_packages_for_processors + + #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) + ++static uint32_t cache_counters[MAX_NR_OF_CACHES]; ++ + /* Call chain: + * cpu_info_init_by_logical_sys_info + * read_packages_for_processors @@ -126,6 +128,8 @@ bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, co - uint32_t nr_of_uarchs = 0; - bool result = false; - -+ memset(cache_counters, 0, sizeof(cache_counters)); -+ - HANDLE heap = GetProcessHeap(); - - /* 1. Count available logical processor groups and processors */ + uint32_t nr_of_uarchs = 0; + bool result = false; + ++ memset(cache_counters, 0, sizeof(cache_counters)); ++ + HANDLE heap = GetProcessHeap(); + + /* 1. Count available logical processor groups and processors */ @@ -372,6 +376,40 @@ clean_up: - return result; - } - -+BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { -+ HANDLE heap = GetProcessHeap(); -+ -+ HeapFree(heap, 0, cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ /* Caches are allocated as a single contiguous block starting at l1i */ -+ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ return TRUE; -+} -+ - static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { - uint32_t nr_of_processors = 0; - + return result; + } + ++BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ /* Caches are allocated as a single contiguous block starting at l1i */ ++ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ return TRUE; ++} ++ + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { + uint32_t nr_of_processors = 0; + @@ -606,11 +644,6 @@ static bool parse_relation_cache_info( - uint32_t* numbers_of_caches, - const uint32_t* global_proc_index_per_group, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { -- static uint32_t l1i_counter = 0; -- static uint32_t l1d_counter = 0; -- static uint32_t l2_counter = 0; -- static uint32_t l3_counter = 0; -- - /* Count cache types for allocation at first. */ - if (caches == NULL) { - switch (info->Cache.Level) { + uint32_t* numbers_of_caches, + const uint32_t* global_proc_index_per_group, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { +- static uint32_t l1i_counter = 0; +- static uint32_t l1d_counter = 0; +- static uint32_t l2_counter = 0; +- static uint32_t l3_counter = 0; +- + /* Count cache types for allocation at first. */ + if (caches == NULL) { + switch (info->Cache.Level) { @@ -665,12 +698,12 @@ static bool parse_relation_cache_info( - case 1: - switch (info->Cache.Type) { - case CacheInstruction: -- current_cache = l1i_base + l1i_counter; -- l1i_counter++; -+ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; -+ cache_counters[cpuinfo_cache_level_1i]++; - break; - case CacheData: -- current_cache = l1d_base + l1d_counter; -- l1d_counter++; -+ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; -+ cache_counters[cpuinfo_cache_level_1d]++; - break; - case CacheUnified: - break; + case 1: + switch (info->Cache.Type) { + case CacheInstruction: +- current_cache = l1i_base + l1i_counter; +- l1i_counter++; ++ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; ++ cache_counters[cpuinfo_cache_level_1i]++; + break; + case CacheData: +- current_cache = l1d_base + l1d_counter; +- l1d_counter++; ++ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; ++ cache_counters[cpuinfo_cache_level_1d]++; + break; + case CacheUnified: + break; @@ -681,12 +714,12 @@ static bool parse_relation_cache_info( - } - break; - case 2: -- current_cache = l2_base + l2_counter; -- l2_counter++; -+ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; -+ cache_counters[cpuinfo_cache_level_2]++; - break; - case 3: -- current_cache = l3_base + l3_counter; -- l3_counter++; -+ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; -+ cache_counters[cpuinfo_cache_level_3]++; - break; - } - current_cache->size = info->Cache.CacheSize; + } + break; + case 2: +- current_cache = l2_base + l2_counter; +- l2_counter++; ++ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; ++ cache_counters[cpuinfo_cache_level_2]++; + break; + case 3: +- current_cache = l3_base + l3_counter; +- l3_counter++; ++ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; ++ cache_counters[cpuinfo_cache_level_3]++; + break; + } + current_cache->size = info->Cache.CacheSize; diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c index 528ae3279f5e979409ac07de6cee833691e034cd..fcf219df153419249211be4fdf014f50fc5198da 100644 --- a/src/arm/windows/init.c +++ b/src/arm/windows/init.c @@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV - set_cpuinfo_isa_fields(); - - chip_info = get_system_info_from_registry(); -+ const bool chip_info_from_registry = chip_info != NULL; - if (chip_info == NULL) { - chip_info = &woa_chip_unknown; - } - - cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); - -+ if (chip_info_from_registry) { -+ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); -+ HeapFree(GetProcessHeap(), 0, chip_info); -+ } -+ - return true; - } - + set_cpuinfo_isa_fields(); + + chip_info = get_system_info_from_registry(); ++ const bool chip_info_from_registry = chip_info != NULL; + if (chip_info == NULL) { + chip_info = &woa_chip_unknown; + } + + cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); + ++ if (chip_info_from_registry) { ++ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); ++ HeapFree(GetProcessHeap(), 0, chip_info); ++ } ++ + return true; + } + @@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); - if (midr_qword == 0) { - cpuinfo_log_error("Registry read error for MIDR value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // MIDR is only 32 bits, so we need to cast it to uint32_t + uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); + if (midr_qword == 0) { + cpuinfo_log_error("Registry read error for MIDR value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // MIDR is only 32 bits, so we need to cast it to uint32_t @@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); - if (frequency_mhz == 0) { - cpuinfo_log_error("Registry read error for frequency value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // Convert MHz to Hz + uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); + if (frequency_mhz == 0) { + cpuinfo_log_error("Registry read error for frequency value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // Convert MHz to Hz @@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); - if (chip_info == NULL) { - cpuinfo_log_error("Heap allocation error for chip_info"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - - // set chip_info fields - chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); - - cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); + chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); + if (chip_info == NULL) { + cpuinfo_log_error("Heap allocation error for chip_info"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + + // set chip_info fields + chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); + + cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h index d84b26a89cfc38e726216688e490f50799f31455..9c1588872da413535f2fb68fb96d6106b420ac49 100644 --- a/src/cpuinfo/internal-api.h diff --git a/onnxruntime/test/common/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc similarity index 100% rename from onnxruntime/test/common/cpuinfo_refcount_test.cc rename to onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc From 26f04d80d956a71a33b30626191a1370b728c1fe Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Fri, 28 Aug 2026 15:11:54 -0700 Subject: [PATCH 09/16] Isolate cpuinfo DLL leak detection --- cmake/onnxruntime_unittests.cmake | 5 - .../test/shared_lib/cpuinfo_dlopen_test.cc | 102 ++++++++---------- 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index a86f3042dc07b..924b038ed38f3 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -2012,11 +2012,6 @@ endif() onnxruntime_shared_lib_cpuinfo_dlopen_test ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test.cc) add_dependencies(onnxruntime_shared_lib_cpuinfo_dlopen_test ${all_dependencies} onnxruntime) - if(onnxruntime_USE_XNNPACK) - target_compile_definitions( - onnxruntime_shared_lib_cpuinfo_dlopen_test - PRIVATE ORT_CPUINFO_DLOPEN_TEST_USE_XNNPACK) - endif() add_test( NAME onnxruntime_shared_lib_cpuinfo_dlopen_test COMMAND onnxruntime_shared_lib_cpuinfo_dlopen_test diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc index f819785f70a5a..1efb51dae997f 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc @@ -3,8 +3,8 @@ #include -#include #include +#include #include #include @@ -18,6 +18,11 @@ struct ProcessHeapSnapshot { size_t busy_bytes = 0; }; +struct ProcessHeapGrowth { + int64_t busy_block_count = 0; + int64_t busy_bytes = 0; +}; + bool CaptureProcessHeapSnapshot(ProcessHeapSnapshot& snapshot) { HANDLE process_heap = GetProcessHeap(); if (!HeapLock(process_heap)) { @@ -47,7 +52,7 @@ bool CheckStatus(const OrtApi& ort_api, OrtStatus* status) { return false; } -bool LoadQueryHardwareAndUnload() { +bool LoadAndUnload(bool query_hardware) { HMODULE ort_library = LoadLibraryW(L"onnxruntime.dll"); if (ort_library == nullptr) { std::cerr << "LoadLibraryW failed with error " << GetLastError() << std::endl; @@ -72,14 +77,12 @@ bool LoadQueryHardwareAndUnload() { const OrtApi* ort_api = ort_api_base->GetApi(ORT_API_VERSION); OrtEnv* env = nullptr; - OrtSessionOptions* session_options = nullptr; - OrtSession* session = nullptr; bool success = ort_api != nullptr; if (success) { success = CheckStatus(*ort_api, ort_api->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "CpuinfoDlopenTest", &env)); } - if (success) { + if (success && query_hardware) { size_t num_devices = 0; success = CheckStatus(*ort_api, ort_api->GetNumHardwareDevices(env, &num_devices)); if (success && num_devices == 0) { @@ -87,28 +90,6 @@ bool LoadQueryHardwareAndUnload() { success = false; } } -#if defined(ORT_CPUINFO_DLOPEN_TEST_USE_XNNPACK) - if (success) { - success = CheckStatus(*ort_api, ort_api->CreateSessionOptions(&session_options)); - } - if (success) { - success = CheckStatus( - *ort_api, - ort_api->SessionOptionsAppendExecutionProvider(session_options, "XNNPACK", nullptr, nullptr, 0)); - } - if (success) { - success = CheckStatus( - *ort_api, - ort_api->CreateSession(env, L"testdata\\matmul_1.onnx", session_options, &session)); - } -#endif - - if (session != nullptr) { - ort_api->ReleaseSession(session); - } - if (session_options != nullptr) { - ort_api->ReleaseSessionOptions(session_options); - } if (env != nullptr) { ort_api->ReleaseEnv(env); } @@ -126,6 +107,13 @@ bool LoadQueryHardwareAndUnload() { return success; } +ProcessHeapGrowth operator-(const ProcessHeapSnapshot& after, const ProcessHeapSnapshot& before) { + return { + static_cast(after.busy_block_count) - static_cast(before.busy_block_count), + static_cast(after.busy_bytes) - static_cast(before.busy_bytes), + }; +} + } // namespace int wmain() { @@ -133,45 +121,49 @@ int wmain() { constexpr size_t kMeasuredCycles = 3; for (size_t cycle = 0; cycle < kWarmupCycles; ++cycle) { - if (!LoadQueryHardwareAndUnload()) { + if (!LoadAndUnload(false) || !LoadAndUnload(true)) { return EXIT_FAILURE; } } - std::array snapshots; - if (!CaptureProcessHeapSnapshot(snapshots[0])) { - std::cerr << "Failed to capture the initial process heap snapshot" << std::endl; - return EXIT_FAILURE; - } + ProcessHeapGrowth baseline_growth; + ProcessHeapGrowth cpuinfo_growth; + // CreateEnv must not initialize CPUIDInfo. GetNumHardwareDevices does so through Windows device discovery. for (size_t cycle = 0; cycle < kMeasuredCycles; ++cycle) { - if (!LoadQueryHardwareAndUnload()) { + ProcessHeapSnapshot before_baseline; + ProcessHeapSnapshot after_baseline; + ProcessHeapSnapshot after_cpuinfo; + if (!CaptureProcessHeapSnapshot(before_baseline)) { + std::cerr << "Failed to capture process heap before baseline cycle " << cycle << std::endl; return EXIT_FAILURE; } - if (!CaptureProcessHeapSnapshot(snapshots[cycle + 1])) { - std::cerr << "Failed to capture process heap snapshot " << cycle + 1 << std::endl; + if (!LoadAndUnload(false) || !CaptureProcessHeapSnapshot(after_baseline)) { + std::cerr << "Failed to measure baseline DLL cycle " << cycle << std::endl; + return EXIT_FAILURE; + } + if (!LoadAndUnload(true) || !CaptureProcessHeapSnapshot(after_cpuinfo)) { + std::cerr << "Failed to measure cpuinfo DLL cycle " << cycle << std::endl; return EXIT_FAILURE; } - } - - bool block_count_grew_each_cycle = true; - bool allocated_bytes_grew_each_cycle = true; - // Ignore one-time loader caching and detect the repeated growth caused by unreleased cpuinfo globals. - for (size_t cycle = 0; cycle < kMeasuredCycles; ++cycle) { - block_count_grew_each_cycle = - block_count_grew_each_cycle && - snapshots[cycle + 1].busy_block_count > snapshots[cycle].busy_block_count; - allocated_bytes_grew_each_cycle = - allocated_bytes_grew_each_cycle && - snapshots[cycle + 1].busy_bytes > snapshots[cycle].busy_bytes; - } - if (block_count_grew_each_cycle && allocated_bytes_grew_each_cycle) { - std::cerr << "Process heap grew after every ONNX Runtime DLL load/unload cycle:" - << " blocks " << snapshots[0].busy_block_count << " -> " - << snapshots[kMeasuredCycles].busy_block_count - << ", bytes " << snapshots[0].busy_bytes << " -> " - << snapshots[kMeasuredCycles].busy_bytes << std::endl; + const ProcessHeapGrowth baseline_cycle = after_baseline - before_baseline; + const ProcessHeapGrowth cpuinfo_cycle = after_cpuinfo - after_baseline; + baseline_growth.busy_block_count += baseline_cycle.busy_block_count; + baseline_growth.busy_bytes += baseline_cycle.busy_bytes; + cpuinfo_growth.busy_block_count += cpuinfo_cycle.busy_block_count; + cpuinfo_growth.busy_bytes += cpuinfo_cycle.busy_bytes; + } + + constexpr int64_t kBlockTolerance = 2; + constexpr int64_t kByteTolerance = 1024; + if (cpuinfo_growth.busy_block_count > baseline_growth.busy_block_count + kBlockTolerance && + cpuinfo_growth.busy_bytes > baseline_growth.busy_bytes + kByteTolerance) { + std::cerr << "Hardware discovery retained additional process-heap allocations across DLL unload cycles:" + << " baseline blocks/bytes " << baseline_growth.busy_block_count << "/" + << baseline_growth.busy_bytes + << ", cpuinfo blocks/bytes " << cpuinfo_growth.busy_block_count << "/" + << cpuinfo_growth.busy_bytes << std::endl; return EXIT_FAILURE; } From 2e451746bc9de6c5dcc83b1f43e7173bc4b19f30 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 13:36:06 -0700 Subject: [PATCH 10/16] Address cpuinfo lifecycle review feedback --- .../external/onnxruntime_external_deps.cmake | 88 +++++++++---------- cmake/external/xnnpack.cmake | 1 - cmake/onnxruntime_unittests.cmake | 44 +++++++--- .../test/shared_lib/cpuinfo_refcount_test.cc | 54 ++++++++++++ 4 files changed, 132 insertions(+), 55 deletions(-) diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 5bbe349309f9c..9ce7a3424c4b1 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -363,52 +363,52 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") - if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") - message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && - # https://github.com/pytorch/cpuinfo/pull/324 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/microsoft/onnxruntime/issues/10038 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + if(onnxruntime_USE_VCPKG AND NOT APPLE) + find_package(cpuinfo CONFIG REQUIRED) else() - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") + message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && + # https://github.com/pytorch/cpuinfo/pull/324 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/microsoft/onnxruntime/issues/10038 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + else() + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + endif() + onnxruntime_fetchcontent_makeavailable(pytorch_cpuinfo) endif() - set(ONNXRUNTIME_CPUINFO_PROJ pytorch_cpuinfo) - onnxruntime_fetchcontent_makeavailable(${ONNXRUNTIME_CPUINFO_PROJ}) if(TARGET cpuinfo::cpuinfo AND NOT TARGET cpuinfo) message(STATUS "Aliasing cpuinfo::cpuinfo to cpuinfo") add_library(cpuinfo ALIAS cpuinfo::cpuinfo) diff --git a/cmake/external/xnnpack.cmake b/cmake/external/xnnpack.cmake index 223f99001b916..59cc26316a770 100644 --- a/cmake/external/xnnpack.cmake +++ b/cmake/external/xnnpack.cmake @@ -87,7 +87,6 @@ onnxruntime_fetchcontent_declare(googlexnnpack URL ${DEP_URL_googlexnnpack} URL_ PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch && ${Patch_EXECUTABLE} --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/release_cpuinfo_after_hardware_config.patch EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS NAMES xnnpack ) onnxruntime_fetchcontent_makeavailable(googlexnnpack) set(XNNPACK_DIR ${googlexnnpack_SOURCE_DIR}) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 924b038ed38f3..1dface4bccb9c 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -1987,16 +1987,40 @@ endif() endif() if (CPUINFO_SUPPORTED AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - onnxruntime_add_executable( - onnxruntime_cpuinfo_refcount_test - ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_refcount_test.cc) - target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) - add_test( - NAME onnxruntime_cpuinfo_refcount_test - COMMAND onnxruntime_cpuinfo_refcount_test) - set_target_properties( - onnxruntime_cpuinfo_refcount_test - PROPERTIES FOLDER "ONNXRuntimeTest") + set(onnxruntime_cpuinfo_test_library cpuinfo) + get_target_property(onnxruntime_cpuinfo_aliased_target cpuinfo ALIASED_TARGET) + if(onnxruntime_cpuinfo_aliased_target) + set(onnxruntime_cpuinfo_test_library ${onnxruntime_cpuinfo_aliased_target}) + endif() + get_target_property(onnxruntime_cpuinfo_library_type ${onnxruntime_cpuinfo_test_library} TYPE) + if(onnxruntime_cpuinfo_library_type STREQUAL "STATIC_LIBRARY") + onnxruntime_add_executable( + onnxruntime_cpuinfo_refcount_test + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_refcount_test.cc) + target_compile_definitions( + onnxruntime_cpuinfo_refcount_test + PRIVATE ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) + if(onnxruntime_USE_XNNPACK) + target_compile_definitions( + onnxruntime_cpuinfo_refcount_test + PRIVATE ORT_CPUINFO_TEST_USE_XNNPACK) + if(onnxruntime_USE_VCPKG) + target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_HDR}) + else() + target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_INCLUDE_DIR}) + endif() + target_link_libraries( + onnxruntime_cpuinfo_refcount_test + PRIVATE ${onnxruntime_EXTERNAL_LIBRARIES_XNNPACK}) + endif() + target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) + add_test( + NAME onnxruntime_cpuinfo_refcount_test + COMMAND onnxruntime_cpuinfo_refcount_test) + set_target_properties( + onnxruntime_cpuinfo_refcount_test + PROPERTIES FOLDER "ONNXRuntimeTest") + endif() endif() # shared lib diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc index dbbc433bf7cea..3802d44841a72 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc @@ -9,6 +9,14 @@ #include #include +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) +#include +#endif + +#if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) +extern "C" bool cpuinfo_is_initialized; +#endif + namespace { bool HasValidCpuinfoState() { @@ -17,6 +25,14 @@ bool HasValidCpuinfoState() { cpuinfo_get_processor(0) != nullptr; } +bool IsCpuinfoDeinitialized() { +#if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) + return !cpuinfo_is_initialized; +#else + return true; +#endif +} + bool TestSequentialConsumers() { if (!cpuinfo_initialize()) { std::cerr << "The first cpuinfo_initialize call failed" << std::endl; @@ -46,6 +62,11 @@ bool TestSequentialConsumers() { cpuinfo_deinitialize(); + if (!IsCpuinfoDeinitialized()) { + std::cerr << "cpuinfo remained initialized after the final consumer released it" << std::endl; + return false; + } + if (!first_consumer_release_preserved_state) { std::cerr << "cpuinfo released shared state while another consumer was still active" << std::endl; return false; @@ -57,6 +78,11 @@ bool TestSequentialConsumers() { } cpuinfo_deinitialize(); + if (!IsCpuinfoDeinitialized()) { + std::cerr << "cpuinfo remained initialized after the reinitialized consumer released it" << std::endl; + return false; + } + return true; } @@ -121,9 +147,31 @@ bool TestConcurrentConsumers() { } cpuinfo_deinitialize(); + if (!IsCpuinfoDeinitialized()) { + std::cerr << "cpuinfo remained initialized after concurrent use" << std::endl; + return false; + } + return true; } +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) +bool TestXnnpackReleasesCpuinfo() { + if (xnn_initialize(nullptr) != xnn_status_success) { + std::cerr << "XNNPACK initialization failed" << std::endl; + return false; + } + + if (!IsCpuinfoDeinitialized()) { + std::cerr << "XNNPACK retained a cpuinfo reference after hardware discovery" << std::endl; + return false; + } + + xnn_deinitialize(); + return true; +} +#endif + } // namespace int main() { @@ -131,5 +179,11 @@ int main() { return EXIT_FAILURE; } +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) + if (!TestXnnpackReleasesCpuinfo()) { + return EXIT_FAILURE; + } +#endif + return EXIT_SUCCESS; } From 8cea7ac1193e0ba641feacd637ebb0cf72d26666 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 16:23:56 -0700 Subject: [PATCH 11/16] Make cpuinfo unload test deterministic --- cmake/onnxruntime_unittests.cmake | 24 ++- .../test/shared_lib/cpuinfo_dlopen_test.cc | 169 +++--------------- .../shared_lib/cpuinfo_dlopen_test_library.cc | 11 ++ .../cpuinfo_dlopen_test_library.def | 3 + .../test/shared_lib/cpuinfo_refcount_test.cc | 38 +++- 5 files changed, 94 insertions(+), 151 deletions(-) create mode 100644 onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc create mode 100644 onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 1dface4bccb9c..f222bf63dd246 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -2031,16 +2031,34 @@ endif() add_test(NAME onnxruntime_shared_lib_dlopen_test COMMAND onnxruntime_shared_lib_dlopen_test WORKING_DIRECTORY $) set_target_properties(onnxruntime_shared_lib_dlopen_test PROPERTIES FOLDER "ONNXRuntimeTest") - if(CPUINFO_SUPPORTED AND NOT onnxruntime_MINIMAL_BUILD) + if(onnxruntime_cpuinfo_library_type STREQUAL "STATIC_LIBRARY") + onnxruntime_add_shared_library( + onnxruntime_cpuinfo_dlopen_test_library + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test_library.cc + ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test_library.def) + target_include_directories( + onnxruntime_cpuinfo_dlopen_test_library + PRIVATE ${ONNXRUNTIME_ROOT}) + target_link_libraries( + onnxruntime_cpuinfo_dlopen_test_library + PRIVATE onnxruntime_common cpuinfo) + onnxruntime_add_executable( onnxruntime_shared_lib_cpuinfo_dlopen_test ${ONNXRUNTIME_SHARED_LIB_TEST_SRC_DIR}/cpuinfo_dlopen_test.cc) - add_dependencies(onnxruntime_shared_lib_cpuinfo_dlopen_test ${all_dependencies} onnxruntime) + add_dependencies( + onnxruntime_shared_lib_cpuinfo_dlopen_test + onnxruntime_cpuinfo_dlopen_test_library) + target_compile_definitions( + onnxruntime_shared_lib_cpuinfo_dlopen_test + PRIVATE + ORT_CPUINFO_DLOPEN_TEST_LIBRARY=L"$") add_test( NAME onnxruntime_shared_lib_cpuinfo_dlopen_test COMMAND onnxruntime_shared_lib_cpuinfo_dlopen_test - WORKING_DIRECTORY $) + WORKING_DIRECTORY $) set_target_properties( + onnxruntime_cpuinfo_dlopen_test_library onnxruntime_shared_lib_cpuinfo_dlopen_test PROPERTIES FOLDER "ONNXRuntimeTest") endif() diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc index 1efb51dae997f..e2f97769f0b32 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test.cc @@ -3,167 +3,44 @@ #include -#include -#include #include #include -#include "onnxruntime_c_api.h" +#ifndef ORT_CPUINFO_DLOPEN_TEST_LIBRARY +#error ORT_CPUINFO_DLOPEN_TEST_LIBRARY must name the test DLL. +#endif -namespace { - -// cpuinfo's Windows backend allocates its global topology data directly from the process heap. -struct ProcessHeapSnapshot { - size_t busy_block_count = 0; - size_t busy_bytes = 0; -}; - -struct ProcessHeapGrowth { - int64_t busy_block_count = 0; - int64_t busy_bytes = 0; -}; - -bool CaptureProcessHeapSnapshot(ProcessHeapSnapshot& snapshot) { - HANDLE process_heap = GetProcessHeap(); - if (!HeapLock(process_heap)) { - return false; - } - - PROCESS_HEAP_ENTRY entry{}; - while (HeapWalk(process_heap, &entry)) { - if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { - ++snapshot.busy_block_count; - snapshot.busy_bytes += entry.cbData; - } - } - - const DWORD error = GetLastError(); - const bool unlocked = HeapUnlock(process_heap) != FALSE; - return error == ERROR_NO_MORE_ITEMS && unlocked; -} - -bool CheckStatus(const OrtApi& ort_api, OrtStatus* status) { - if (status == nullptr) { - return true; - } - - std::cerr << "ONNX Runtime error: " << ort_api.GetErrorMessage(status) << std::endl; - ort_api.ReleaseStatus(status); - return false; -} - -bool LoadAndUnload(bool query_hardware) { - HMODULE ort_library = LoadLibraryW(L"onnxruntime.dll"); - if (ort_library == nullptr) { +int wmain() { + HMODULE library = LoadLibraryW(ORT_CPUINFO_DLOPEN_TEST_LIBRARY); + if (library == nullptr) { std::cerr << "LoadLibraryW failed with error " << GetLastError() << std::endl; - return false; + return EXIT_FAILURE; } - using OrtGetApiBaseFunction = const OrtApiBase*(ORT_API_CALL*)(); - const auto ort_get_api_base = - reinterpret_cast(GetProcAddress(ort_library, "OrtGetApiBase")); - if (ort_get_api_base == nullptr) { + using GetCpuinfoAllocation = const void* (*)(); + const auto get_cpuinfo_allocation = reinterpret_cast( + GetProcAddress(library, "OrtGetCpuinfoAllocationForTesting")); + if (get_cpuinfo_allocation == nullptr) { std::cerr << "GetProcAddress failed with error " << GetLastError() << std::endl; - FreeLibrary(ort_library); - return false; - } - - const OrtApiBase* ort_api_base = ort_get_api_base(); - if (ort_api_base == nullptr) { - std::cerr << "OrtGetApiBase returned null" << std::endl; - FreeLibrary(ort_library); - return false; + FreeLibrary(library); + return EXIT_FAILURE; } - const OrtApi* ort_api = ort_api_base->GetApi(ORT_API_VERSION); - OrtEnv* env = nullptr; - bool success = ort_api != nullptr; - - if (success) { - success = CheckStatus(*ort_api, ort_api->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "CpuinfoDlopenTest", &env)); - } - if (success && query_hardware) { - size_t num_devices = 0; - success = CheckStatus(*ort_api, ort_api->GetNumHardwareDevices(env, &num_devices)); - if (success && num_devices == 0) { - std::cerr << "ONNX Runtime reported no hardware devices" << std::endl; - success = false; - } - } - if (env != nullptr) { - ort_api->ReleaseEnv(env); + const void* allocation = get_cpuinfo_allocation(); + HANDLE process_heap = GetProcessHeap(); + if (allocation == nullptr || !HeapValidate(process_heap, 0, allocation)) { + std::cerr << "cpuinfo did not return a valid process-heap allocation" << std::endl; + FreeLibrary(library); + return EXIT_FAILURE; } - if (!FreeLibrary(ort_library)) { + if (!FreeLibrary(library)) { std::cerr << "FreeLibrary failed with error " << GetLastError() << std::endl; - return false; - } - - if (GetModuleHandleW(L"onnxruntime.dll") != nullptr) { - std::cerr << "onnxruntime.dll remained loaded after FreeLibrary" << std::endl; - return false; - } - - return success; -} - -ProcessHeapGrowth operator-(const ProcessHeapSnapshot& after, const ProcessHeapSnapshot& before) { - return { - static_cast(after.busy_block_count) - static_cast(before.busy_block_count), - static_cast(after.busy_bytes) - static_cast(before.busy_bytes), - }; -} - -} // namespace - -int wmain() { - constexpr size_t kWarmupCycles = 2; - constexpr size_t kMeasuredCycles = 3; - - for (size_t cycle = 0; cycle < kWarmupCycles; ++cycle) { - if (!LoadAndUnload(false) || !LoadAndUnload(true)) { - return EXIT_FAILURE; - } - } - - ProcessHeapGrowth baseline_growth; - ProcessHeapGrowth cpuinfo_growth; - - // CreateEnv must not initialize CPUIDInfo. GetNumHardwareDevices does so through Windows device discovery. - for (size_t cycle = 0; cycle < kMeasuredCycles; ++cycle) { - ProcessHeapSnapshot before_baseline; - ProcessHeapSnapshot after_baseline; - ProcessHeapSnapshot after_cpuinfo; - if (!CaptureProcessHeapSnapshot(before_baseline)) { - std::cerr << "Failed to capture process heap before baseline cycle " << cycle << std::endl; - return EXIT_FAILURE; - } - if (!LoadAndUnload(false) || !CaptureProcessHeapSnapshot(after_baseline)) { - std::cerr << "Failed to measure baseline DLL cycle " << cycle << std::endl; - return EXIT_FAILURE; - } - if (!LoadAndUnload(true) || !CaptureProcessHeapSnapshot(after_cpuinfo)) { - std::cerr << "Failed to measure cpuinfo DLL cycle " << cycle << std::endl; - return EXIT_FAILURE; - } - - const ProcessHeapGrowth baseline_cycle = after_baseline - before_baseline; - const ProcessHeapGrowth cpuinfo_cycle = after_cpuinfo - after_baseline; - baseline_growth.busy_block_count += baseline_cycle.busy_block_count; - baseline_growth.busy_bytes += baseline_cycle.busy_bytes; - cpuinfo_growth.busy_block_count += cpuinfo_cycle.busy_block_count; - cpuinfo_growth.busy_bytes += cpuinfo_cycle.busy_bytes; + return EXIT_FAILURE; } - constexpr int64_t kBlockTolerance = 2; - constexpr int64_t kByteTolerance = 1024; - if (cpuinfo_growth.busy_block_count > baseline_growth.busy_block_count + kBlockTolerance && - cpuinfo_growth.busy_bytes > baseline_growth.busy_bytes + kByteTolerance) { - std::cerr << "Hardware discovery retained additional process-heap allocations across DLL unload cycles:" - << " baseline blocks/bytes " << baseline_growth.busy_block_count << "/" - << baseline_growth.busy_bytes - << ", cpuinfo blocks/bytes " << cpuinfo_growth.busy_block_count << "/" - << cpuinfo_growth.busy_bytes << std::endl; + if (HeapValidate(process_heap, 0, allocation)) { + std::cerr << "cpuinfo allocation remained valid after the CPUIDInfo test DLL was unloaded" << std::endl; return EXIT_FAILURE; } diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc new file mode 100644 index 0000000000000..edad81c76388d --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.cc @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include "core/common/cpuid_info.h" + +extern "C" const void* OrtGetCpuinfoAllocationForTesting() { + static_cast(onnxruntime::CPUIDInfo::GetCPUIDInfo()); + return cpuinfo_get_processors(); +} diff --git a/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def new file mode 100644 index 0000000000000..ea4221dc1e5d3 --- /dev/null +++ b/onnxruntime/test/shared_lib/cpuinfo_dlopen_test_library.def @@ -0,0 +1,3 @@ +LIBRARY onnxruntime_cpuinfo_dlopen_test_library +EXPORTS + OrtGetCpuinfoAllocationForTesting diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc index 3802d44841a72..7c91ce9f88afd 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc @@ -14,7 +14,22 @@ #endif #if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) -extern "C" bool cpuinfo_is_initialized; +constexpr int kCpuinfoCacheLevelCount = 5; + +extern "C" { +extern bool cpuinfo_is_initialized; +extern struct cpuinfo_processor* cpuinfo_processors; +extern struct cpuinfo_core* cpuinfo_cores; +extern struct cpuinfo_cluster* cpuinfo_clusters; +extern struct cpuinfo_package* cpuinfo_packages; +extern struct cpuinfo_cache* cpuinfo_cache[kCpuinfoCacheLevelCount]; +extern uint32_t cpuinfo_processors_count; +extern uint32_t cpuinfo_cores_count; +extern uint32_t cpuinfo_clusters_count; +extern uint32_t cpuinfo_packages_count; +extern uint32_t cpuinfo_cache_count[kCpuinfoCacheLevelCount]; +extern uint32_t cpuinfo_max_cache_size; +} #endif namespace { @@ -27,7 +42,26 @@ bool HasValidCpuinfoState() { bool IsCpuinfoDeinitialized() { #if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) - return !cpuinfo_is_initialized; + if (cpuinfo_is_initialized || + cpuinfo_processors != nullptr || + cpuinfo_cores != nullptr || + cpuinfo_clusters != nullptr || + cpuinfo_packages != nullptr || + cpuinfo_processors_count != 0 || + cpuinfo_cores_count != 0 || + cpuinfo_clusters_count != 0 || + cpuinfo_packages_count != 0 || + cpuinfo_max_cache_size != 0) { + return false; + } + + for (int level = 0; level < kCpuinfoCacheLevelCount; ++level) { + if (cpuinfo_cache[level] != nullptr || cpuinfo_cache_count[level] != 0) { + return false; + } + } + + return true; #else return true; #endif From 149bbfa9a5a51e1cfff89793079588a99b6dae15 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 16:37:35 -0700 Subject: [PATCH 12/16] Limit cpuinfo lifecycle changes to ORT --- cmake/external/xnnpack.cmake | 4 +- cmake/onnxruntime_unittests.cmake | 13 ------ ...elease_cpuinfo_after_hardware_config.patch | 46 ------------------- cmake/vcpkg-ports/xnnpack/portfile.cmake | 1 - .../test/shared_lib/cpuinfo_refcount_test.cc | 27 ----------- 5 files changed, 2 insertions(+), 89 deletions(-) delete mode 100644 cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch diff --git a/cmake/external/xnnpack.cmake b/cmake/external/xnnpack.cmake index 59cc26316a770..571283c33c713 100644 --- a/cmake/external/xnnpack.cmake +++ b/cmake/external/xnnpack.cmake @@ -84,9 +84,9 @@ endif() onnxruntime_fetchcontent_declare(googlexnnpack URL ${DEP_URL_googlexnnpack} URL_HASH SHA1=${DEP_SHA1_googlexnnpack} - PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch && - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/release_cpuinfo_after_hardware_config.patch + PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS NAMES xnnpack ) onnxruntime_fetchcontent_makeavailable(googlexnnpack) set(XNNPACK_DIR ${googlexnnpack_SOURCE_DIR}) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index f222bf63dd246..3c30d19c8b7e2 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -2000,19 +2000,6 @@ endif() target_compile_definitions( onnxruntime_cpuinfo_refcount_test PRIVATE ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) - if(onnxruntime_USE_XNNPACK) - target_compile_definitions( - onnxruntime_cpuinfo_refcount_test - PRIVATE ORT_CPUINFO_TEST_USE_XNNPACK) - if(onnxruntime_USE_VCPKG) - target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_HDR}) - else() - target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_INCLUDE_DIR}) - endif() - target_link_libraries( - onnxruntime_cpuinfo_refcount_test - PRIVATE ${onnxruntime_EXTERNAL_LIBRARIES_XNNPACK}) - endif() target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) add_test( NAME onnxruntime_cpuinfo_refcount_test diff --git a/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch deleted file mode 100644 index 8aea6bfc4d156..0000000000000 --- a/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/src/configs/hardware-config.c b/src/configs/hardware-config.c -index b4e942aa6955360e3ea85619d549410e65ab7a9c..6cf6e9a213b6f72c94940ff815e11f70a658c419 100644 ---- a/src/configs/hardware-config.c -+++ b/src/configs/hardware-config.c -@@ -343,6 +343,7 @@ static void init_hardware_config(void) { - xnn_log_info("cpu_get_uarch(%i): 0x%x", i, cpuinfo_get_uarch(i)->uarch); - } - #endif // XNN_MAX_UARCH_TYPES > 1 -+ cpuinfo_deinitialize(); - #else - xnn_log_warning("Unable to determine L1/L2 data cache properties."); - #endif // XNN_ENABLE_CPUINFO -@@ -358,21 +359,33 @@ const struct xnn_hardware_config* xnn_init_hardware_config() { - #if XNN_ARCH_ARM - if (!cpuinfo_has_arm_v6()) { - xnn_log_debug("unsupported hardware: ARMv6 not detected"); -+ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO -+ cpuinfo_deinitialize(); -+ #endif - return NULL; - } - - if (!cpuinfo_has_arm_vfpv2() && !cpuinfo_has_arm_vfpv3()) { - xnn_log_debug("unsupported hardware: VFP FPU not detected"); -+ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO -+ cpuinfo_deinitialize(); -+ #endif - return NULL; - } - #endif // XNN_ARCH_ARM - #if XNN_ARCH_X86 - if (!cpuinfo_has_x86_sse2()) { - xnn_log_debug("unsupported hardware: SSE2 not detected"); -+ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO -+ cpuinfo_deinitialize(); -+ #endif - return NULL; - } - #endif // XNN_ARCH_X86 - - XNN_INIT_ONCE(hardware); -+ #if !XNN_PLATFORM_WEB && !XNN_ARCH_RISCV && !XNN_ARCH_PPC64 && XNN_ENABLE_CPUINFO -+ cpuinfo_deinitialize(); -+ #endif - return &hardware_config; - } diff --git a/cmake/vcpkg-ports/xnnpack/portfile.cmake b/cmake/vcpkg-ports/xnnpack/portfile.cmake index 6ccfd2909454f..60b3566629e10 100644 --- a/cmake/vcpkg-ports/xnnpack/portfile.cmake +++ b/cmake/vcpkg-ports/xnnpack/portfile.cmake @@ -11,7 +11,6 @@ vcpkg_from_github( PATCHES fix-build.patch disable_gcc_warning.patch - "${CMAKE_CURRENT_LIST_DIR}/../../patches/xnnpack/release_cpuinfo_after_hardware_config.patch" ) vcpkg_find_acquire_program(PYTHON3) diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc index 7c91ce9f88afd..d4481f2e260e3 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc @@ -9,10 +9,6 @@ #include #include -#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) -#include -#endif - #if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) constexpr int kCpuinfoCacheLevelCount = 5; @@ -189,23 +185,6 @@ bool TestConcurrentConsumers() { return true; } -#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) -bool TestXnnpackReleasesCpuinfo() { - if (xnn_initialize(nullptr) != xnn_status_success) { - std::cerr << "XNNPACK initialization failed" << std::endl; - return false; - } - - if (!IsCpuinfoDeinitialized()) { - std::cerr << "XNNPACK retained a cpuinfo reference after hardware discovery" << std::endl; - return false; - } - - xnn_deinitialize(); - return true; -} -#endif - } // namespace int main() { @@ -213,11 +192,5 @@ int main() { return EXIT_FAILURE; } -#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) - if (!TestXnnpackReleasesCpuinfo()) { - return EXIT_FAILURE; - } -#endif - return EXIT_SUCCESS; } From 10cb2b6045c0d3906ed1ead48d96a22d416a9190 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 18:16:30 -0700 Subject: [PATCH 13/16] Simplify cpuinfo patch application --- .../external/onnxruntime_external_deps.cmake | 88 +++--- cmake/patches/.gitattributes | 3 - .../cpuinfo/enable_deinit_refcounting.patch | 288 +++++++++--------- 3 files changed, 188 insertions(+), 191 deletions(-) diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index 9ce7a3424c4b1..c98c62213133c 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -363,52 +363,52 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") - if(onnxruntime_USE_VCPKG AND NOT APPLE) - find_package(cpuinfo CONFIG REQUIRED) + if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") + message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && + # https://github.com/pytorch/cpuinfo/pull/324 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + FIND_PACKAGE_ARGS NAMES cpuinfo + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/microsoft/onnxruntime/issues/10038 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + FIND_PACKAGE_ARGS NAMES cpuinfo + ) else() - if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") - message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && - # https://github.com/pytorch/cpuinfo/pull/324 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - ) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/microsoft/onnxruntime/issues/10038 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - ) - else() - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - ) - endif() - onnxruntime_fetchcontent_makeavailable(pytorch_cpuinfo) + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + FIND_PACKAGE_ARGS NAMES cpuinfo + ) endif() + set(ONNXRUNTIME_CPUINFO_PROJ pytorch_cpuinfo) + onnxruntime_fetchcontent_makeavailable(${ONNXRUNTIME_CPUINFO_PROJ}) if(TARGET cpuinfo::cpuinfo AND NOT TARGET cpuinfo) message(STATUS "Aliasing cpuinfo::cpuinfo to cpuinfo") add_library(cpuinfo ALIAS cpuinfo::cpuinfo) diff --git a/cmake/patches/.gitattributes b/cmake/patches/.gitattributes index 24d5f860ef2a8..9812ceb1ffd9b 100644 --- a/cmake/patches/.gitattributes +++ b/cmake/patches/.gitattributes @@ -1,4 +1 @@ *.patch text eol=lf - -# cpuinfo contains both LF and CRLF source files, so this patch must preserve both. -cpuinfo/enable_deinit_refcounting.patch -text diff --git a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch index 960cfc6df58fa..0cc10d075fc7f 100644 --- a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch +++ b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch @@ -88,163 +88,163 @@ index 815ecb770a5bc5c48c7dff049f50b111b427954a..f0d66223f0d189776659248f525200eb --- a/src/arm/windows/init-by-logical-sys-info.c +++ b/src/arm/windows/init-by-logical-sys-info.c @@ -14,6 +14,8 @@ - - #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) - -+static uint32_t cache_counters[MAX_NR_OF_CACHES]; -+ - /* Call chain: - * cpu_info_init_by_logical_sys_info - * read_packages_for_processors + + #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) + ++static uint32_t cache_counters[MAX_NR_OF_CACHES]; ++ + /* Call chain: + * cpu_info_init_by_logical_sys_info + * read_packages_for_processors @@ -126,6 +128,8 @@ bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, co - uint32_t nr_of_uarchs = 0; - bool result = false; - -+ memset(cache_counters, 0, sizeof(cache_counters)); -+ - HANDLE heap = GetProcessHeap(); - - /* 1. Count available logical processor groups and processors */ + uint32_t nr_of_uarchs = 0; + bool result = false; + ++ memset(cache_counters, 0, sizeof(cache_counters)); ++ + HANDLE heap = GetProcessHeap(); + + /* 1. Count available logical processor groups and processors */ @@ -372,6 +376,40 @@ clean_up: - return result; - } - -+BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { -+ HANDLE heap = GetProcessHeap(); -+ -+ HeapFree(heap, 0, cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ /* Caches are allocated as a single contiguous block starting at l1i */ -+ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ return TRUE; -+} -+ - static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { - uint32_t nr_of_processors = 0; - + return result; + } + ++BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ /* Caches are allocated as a single contiguous block starting at l1i */ ++ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ return TRUE; ++} ++ + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { + uint32_t nr_of_processors = 0; + @@ -606,11 +644,6 @@ static bool parse_relation_cache_info( - uint32_t* numbers_of_caches, - const uint32_t* global_proc_index_per_group, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { -- static uint32_t l1i_counter = 0; -- static uint32_t l1d_counter = 0; -- static uint32_t l2_counter = 0; -- static uint32_t l3_counter = 0; -- - /* Count cache types for allocation at first. */ - if (caches == NULL) { - switch (info->Cache.Level) { + uint32_t* numbers_of_caches, + const uint32_t* global_proc_index_per_group, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { +- static uint32_t l1i_counter = 0; +- static uint32_t l1d_counter = 0; +- static uint32_t l2_counter = 0; +- static uint32_t l3_counter = 0; +- + /* Count cache types for allocation at first. */ + if (caches == NULL) { + switch (info->Cache.Level) { @@ -665,12 +698,12 @@ static bool parse_relation_cache_info( - case 1: - switch (info->Cache.Type) { - case CacheInstruction: -- current_cache = l1i_base + l1i_counter; -- l1i_counter++; -+ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; -+ cache_counters[cpuinfo_cache_level_1i]++; - break; - case CacheData: -- current_cache = l1d_base + l1d_counter; -- l1d_counter++; -+ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; -+ cache_counters[cpuinfo_cache_level_1d]++; - break; - case CacheUnified: - break; + case 1: + switch (info->Cache.Type) { + case CacheInstruction: +- current_cache = l1i_base + l1i_counter; +- l1i_counter++; ++ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; ++ cache_counters[cpuinfo_cache_level_1i]++; + break; + case CacheData: +- current_cache = l1d_base + l1d_counter; +- l1d_counter++; ++ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; ++ cache_counters[cpuinfo_cache_level_1d]++; + break; + case CacheUnified: + break; @@ -681,12 +714,12 @@ static bool parse_relation_cache_info( - } - break; - case 2: -- current_cache = l2_base + l2_counter; -- l2_counter++; -+ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; -+ cache_counters[cpuinfo_cache_level_2]++; - break; - case 3: -- current_cache = l3_base + l3_counter; -- l3_counter++; -+ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; -+ cache_counters[cpuinfo_cache_level_3]++; - break; - } - current_cache->size = info->Cache.CacheSize; + } + break; + case 2: +- current_cache = l2_base + l2_counter; +- l2_counter++; ++ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; ++ cache_counters[cpuinfo_cache_level_2]++; + break; + case 3: +- current_cache = l3_base + l3_counter; +- l3_counter++; ++ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; ++ cache_counters[cpuinfo_cache_level_3]++; + break; + } + current_cache->size = info->Cache.CacheSize; diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c index 528ae3279f5e979409ac07de6cee833691e034cd..fcf219df153419249211be4fdf014f50fc5198da 100644 --- a/src/arm/windows/init.c +++ b/src/arm/windows/init.c @@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV - set_cpuinfo_isa_fields(); - - chip_info = get_system_info_from_registry(); -+ const bool chip_info_from_registry = chip_info != NULL; - if (chip_info == NULL) { - chip_info = &woa_chip_unknown; - } - - cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); - -+ if (chip_info_from_registry) { -+ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); -+ HeapFree(GetProcessHeap(), 0, chip_info); -+ } -+ - return true; - } - + set_cpuinfo_isa_fields(); + + chip_info = get_system_info_from_registry(); ++ const bool chip_info_from_registry = chip_info != NULL; + if (chip_info == NULL) { + chip_info = &woa_chip_unknown; + } + + cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); + ++ if (chip_info_from_registry) { ++ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); ++ HeapFree(GetProcessHeap(), 0, chip_info); ++ } ++ + return true; + } + @@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); - if (midr_qword == 0) { - cpuinfo_log_error("Registry read error for MIDR value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // MIDR is only 32 bits, so we need to cast it to uint32_t + uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); + if (midr_qword == 0) { + cpuinfo_log_error("Registry read error for MIDR value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // MIDR is only 32 bits, so we need to cast it to uint32_t @@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); - if (frequency_mhz == 0) { - cpuinfo_log_error("Registry read error for frequency value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // Convert MHz to Hz + uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); + if (frequency_mhz == 0) { + cpuinfo_log_error("Registry read error for frequency value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // Convert MHz to Hz @@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); - if (chip_info == NULL) { - cpuinfo_log_error("Heap allocation error for chip_info"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - - // set chip_info fields - chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); - - cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); + chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); + if (chip_info == NULL) { + cpuinfo_log_error("Heap allocation error for chip_info"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + + // set chip_info fields + chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); + + cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h index d84b26a89cfc38e726216688e490f50799f31455..9c1588872da413535f2fb68fb96d6106b420ac49 100644 --- a/src/cpuinfo/internal-api.h From a5bbd9b13f578dfe3b54bf32ee09ba208ffee060 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 18:37:42 -0700 Subject: [PATCH 14/16] Preserve cpuinfo patch line endings --- cmake/patches/.gitattributes | 3 + .../cpuinfo/enable_deinit_refcounting.patch | 288 +++++++++--------- 2 files changed, 147 insertions(+), 144 deletions(-) diff --git a/cmake/patches/.gitattributes b/cmake/patches/.gitattributes index 9812ceb1ffd9b..24d5f860ef2a8 100644 --- a/cmake/patches/.gitattributes +++ b/cmake/patches/.gitattributes @@ -1 +1,4 @@ *.patch text eol=lf + +# cpuinfo contains both LF and CRLF source files, so this patch must preserve both. +cpuinfo/enable_deinit_refcounting.patch -text diff --git a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch index 0cc10d075fc7f..960cfc6df58fa 100644 --- a/cmake/patches/cpuinfo/enable_deinit_refcounting.patch +++ b/cmake/patches/cpuinfo/enable_deinit_refcounting.patch @@ -88,163 +88,163 @@ index 815ecb770a5bc5c48c7dff049f50b111b427954a..f0d66223f0d189776659248f525200eb --- a/src/arm/windows/init-by-logical-sys-info.c +++ b/src/arm/windows/init-by-logical-sys-info.c @@ -14,6 +14,8 @@ - - #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) - -+static uint32_t cache_counters[MAX_NR_OF_CACHES]; -+ - /* Call chain: - * cpu_info_init_by_logical_sys_info - * read_packages_for_processors + + #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) + ++static uint32_t cache_counters[MAX_NR_OF_CACHES]; ++ + /* Call chain: + * cpu_info_init_by_logical_sys_info + * read_packages_for_processors @@ -126,6 +128,8 @@ bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, co - uint32_t nr_of_uarchs = 0; - bool result = false; - -+ memset(cache_counters, 0, sizeof(cache_counters)); -+ - HANDLE heap = GetProcessHeap(); - - /* 1. Count available logical processor groups and processors */ + uint32_t nr_of_uarchs = 0; + bool result = false; + ++ memset(cache_counters, 0, sizeof(cache_counters)); ++ + HANDLE heap = GetProcessHeap(); + + /* 1. Count available logical processor groups and processors */ @@ -372,6 +376,40 @@ clean_up: - return result; - } - -+BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { -+ HANDLE heap = GetProcessHeap(); -+ -+ HeapFree(heap, 0, cpuinfo_processors); -+ cpuinfo_processors = NULL; -+ cpuinfo_processors_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_packages); -+ cpuinfo_packages = NULL; -+ cpuinfo_packages_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_clusters); -+ cpuinfo_clusters = NULL; -+ cpuinfo_clusters_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_cores); -+ cpuinfo_cores = NULL; -+ cpuinfo_cores_count = 0; -+ -+ HeapFree(heap, 0, cpuinfo_uarchs); -+ cpuinfo_uarchs = NULL; -+ cpuinfo_uarchs_count = 0; -+ -+ /* Caches are allocated as a single contiguous block starting at l1i */ -+ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); -+ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { -+ cpuinfo_cache[lvl] = NULL; -+ cpuinfo_cache_count[lvl] = 0; -+ } -+ cpuinfo_max_cache_size = 0; -+ -+ return TRUE; -+} -+ - static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { - uint32_t nr_of_processors = 0; - + return result; + } + ++BOOL CALLBACK cpuinfo_arm_windows_deinit(PINIT_ONCE init_once, PVOID parameter, PVOID* context) { ++ HANDLE heap = GetProcessHeap(); ++ ++ HeapFree(heap, 0, cpuinfo_processors); ++ cpuinfo_processors = NULL; ++ cpuinfo_processors_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_packages); ++ cpuinfo_packages = NULL; ++ cpuinfo_packages_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_clusters); ++ cpuinfo_clusters = NULL; ++ cpuinfo_clusters_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_cores); ++ cpuinfo_cores = NULL; ++ cpuinfo_cores_count = 0; ++ ++ HeapFree(heap, 0, cpuinfo_uarchs); ++ cpuinfo_uarchs = NULL; ++ cpuinfo_uarchs_count = 0; ++ ++ /* Caches are allocated as a single contiguous block starting at l1i */ ++ HeapFree(heap, 0, cpuinfo_cache[cpuinfo_cache_level_1i]); ++ for (int lvl = 0; lvl < cpuinfo_cache_level_max; ++lvl) { ++ cpuinfo_cache[lvl] = NULL; ++ cpuinfo_cache_count[lvl] = 0; ++ } ++ cpuinfo_max_cache_size = 0; ++ ++ return TRUE; ++} ++ + static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) { + uint32_t nr_of_processors = 0; + @@ -606,11 +644,6 @@ static bool parse_relation_cache_info( - uint32_t* numbers_of_caches, - const uint32_t* global_proc_index_per_group, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { -- static uint32_t l1i_counter = 0; -- static uint32_t l1d_counter = 0; -- static uint32_t l2_counter = 0; -- static uint32_t l3_counter = 0; -- - /* Count cache types for allocation at first. */ - if (caches == NULL) { - switch (info->Cache.Level) { + uint32_t* numbers_of_caches, + const uint32_t* global_proc_index_per_group, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) { +- static uint32_t l1i_counter = 0; +- static uint32_t l1d_counter = 0; +- static uint32_t l2_counter = 0; +- static uint32_t l3_counter = 0; +- + /* Count cache types for allocation at first. */ + if (caches == NULL) { + switch (info->Cache.Level) { @@ -665,12 +698,12 @@ static bool parse_relation_cache_info( - case 1: - switch (info->Cache.Type) { - case CacheInstruction: -- current_cache = l1i_base + l1i_counter; -- l1i_counter++; -+ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; -+ cache_counters[cpuinfo_cache_level_1i]++; - break; - case CacheData: -- current_cache = l1d_base + l1d_counter; -- l1d_counter++; -+ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; -+ cache_counters[cpuinfo_cache_level_1d]++; - break; - case CacheUnified: - break; + case 1: + switch (info->Cache.Type) { + case CacheInstruction: +- current_cache = l1i_base + l1i_counter; +- l1i_counter++; ++ current_cache = l1i_base + cache_counters[cpuinfo_cache_level_1i]; ++ cache_counters[cpuinfo_cache_level_1i]++; + break; + case CacheData: +- current_cache = l1d_base + l1d_counter; +- l1d_counter++; ++ current_cache = l1d_base + cache_counters[cpuinfo_cache_level_1d]; ++ cache_counters[cpuinfo_cache_level_1d]++; + break; + case CacheUnified: + break; @@ -681,12 +714,12 @@ static bool parse_relation_cache_info( - } - break; - case 2: -- current_cache = l2_base + l2_counter; -- l2_counter++; -+ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; -+ cache_counters[cpuinfo_cache_level_2]++; - break; - case 3: -- current_cache = l3_base + l3_counter; -- l3_counter++; -+ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; -+ cache_counters[cpuinfo_cache_level_3]++; - break; - } - current_cache->size = info->Cache.CacheSize; + } + break; + case 2: +- current_cache = l2_base + l2_counter; +- l2_counter++; ++ current_cache = l2_base + cache_counters[cpuinfo_cache_level_2]; ++ cache_counters[cpuinfo_cache_level_2]++; + break; + case 3: +- current_cache = l3_base + l3_counter; +- l3_counter++; ++ current_cache = l3_base + cache_counters[cpuinfo_cache_level_3]; ++ cache_counters[cpuinfo_cache_level_3]++; + break; + } + current_cache->size = info->Cache.CacheSize; diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c index 528ae3279f5e979409ac07de6cee833691e034cd..fcf219df153419249211be4fdf014f50fc5198da 100644 --- a/src/arm/windows/init.c +++ b/src/arm/windows/init.c @@ -25,12 +25,18 @@ BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PV - set_cpuinfo_isa_fields(); - - chip_info = get_system_info_from_registry(); -+ const bool chip_info_from_registry = chip_info != NULL; - if (chip_info == NULL) { - chip_info = &woa_chip_unknown; - } - - cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); - -+ if (chip_info_from_registry) { -+ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); -+ HeapFree(GetProcessHeap(), 0, chip_info); -+ } -+ - return true; - } - + set_cpuinfo_isa_fields(); + + chip_info = get_system_info_from_registry(); ++ const bool chip_info_from_registry = chip_info != NULL; + if (chip_info == NULL) { + chip_info = &woa_chip_unknown; + } + + cpuinfo_is_initialized = cpu_info_init_by_logical_sys_info(chip_info, chip_info->uarchs[0].vendor); + ++ if (chip_info_from_registry) { ++ HeapFree(GetProcessHeap(), 0, chip_info->chip_name_string); ++ HeapFree(GetProcessHeap(), 0, chip_info); ++ } ++ + return true; + } + @@ -177,6 +183,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); - if (midr_qword == 0) { - cpuinfo_log_error("Registry read error for MIDR value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // MIDR is only 32 bits, so we need to cast it to uint32_t + uint64_t midr_qword = (uint32_t)read_registry_qword(cpu0_subkey, chip_midr_value); + if (midr_qword == 0) { + cpuinfo_log_error("Registry read error for MIDR value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // MIDR is only 32 bits, so we need to cast it to uint32_t @@ -187,6 +194,7 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); - if (frequency_mhz == 0) { - cpuinfo_log_error("Registry read error for frequency value"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - // Convert MHz to Hz + uint64_t frequency_mhz = read_registry_dword(cpu0_subkey, chip_mhz_value); + if (frequency_mhz == 0) { + cpuinfo_log_error("Registry read error for frequency value"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + // Convert MHz to Hz @@ -196,11 +204,13 @@ static struct woa_chip_info* get_system_info_from_registry(void) { - chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); - if (chip_info == NULL) { - cpuinfo_log_error("Heap allocation error for chip_info"); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - return NULL; - } - - // set chip_info fields - chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); -+ HeapFree(GetProcessHeap(), 0, text_buffer); - chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); - - cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); + chip_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct woa_chip_info)); + if (chip_info == NULL) { + cpuinfo_log_error("Heap allocation error for chip_info"); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + return NULL; + } + + // set chip_info fields + chip_info->chip_name_string = wcsndup(text_buffer, CPUINFO_PACKAGE_NAME_MAX - 1); ++ HeapFree(GetProcessHeap(), 0, text_buffer); + chip_info->uarchs[0] = get_core_info_from_midr(midr_value, frequency_hz); + + cpuinfo_log_debug("detected chip model name: %ls", chip_info->chip_name_string); diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h index d84b26a89cfc38e726216688e490f50799f31455..9c1588872da413535f2fb68fb96d6106b420ac49 100644 --- a/src/cpuinfo/internal-api.h From ad1a412ca78b9ab15cff3e398cd75d39c015f856 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 20:01:17 -0700 Subject: [PATCH 15/16] Revert "Simplify cpuinfo patch application" This reverts commit 10cb2b6045c0d3906ed1ead48d96a22d416a9190. --- .../external/onnxruntime_external_deps.cmake | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/cmake/external/onnxruntime_external_deps.cmake b/cmake/external/onnxruntime_external_deps.cmake index c98c62213133c..9ce7a3424c4b1 100644 --- a/cmake/external/onnxruntime_external_deps.cmake +++ b/cmake/external/onnxruntime_external_deps.cmake @@ -363,52 +363,52 @@ if (CPUINFO_SUPPORTED) set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE INTERNAL "") set(CPUINFO_BUILD_BENCHMARKS OFF CACHE INTERNAL "") - if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") - message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && - # https://github.com/pytorch/cpuinfo/pull/324 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/microsoft/onnxruntime/issues/10038 - ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + if(onnxruntime_USE_VCPKG AND NOT APPLE) + find_package(cpuinfo CONFIG REQUIRED) else() - onnxruntime_fetchcontent_declare( - pytorch_cpuinfo - URL ${DEP_URL_pytorch_cpuinfo} - URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} - EXCLUDE_FROM_ALL - PATCH_COMMAND - # https://github.com/pytorch/cpuinfo/pull/400 - ${Patch_EXECUTABLE} --ignore-whitespace -p1 < - ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch - FIND_PACKAGE_ARGS NAMES cpuinfo - ) + if (onnxruntime_target_platform STREQUAL "ARM64EC" OR onnxruntime_target_platform STREQUAL "ARM64") + message(STATUS "Applying patches for Windows ARM64/ARM64EC in cpuinfo") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_cpuinfo_h_for_arm64ec.patch && + # https://github.com/pytorch/cpuinfo/pull/324 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/patch_vcpkg_arm64ec_support.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(STATUS "Applying sysfs fallback patch for cpuinfo on Linux") + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/microsoft/onnxruntime/issues/10038 + ${Patch_EXECUTABLE} -p1 < ${PROJECT_SOURCE_DIR}/patches/cpuinfo/fix_missing_sysfs_fallback.patch && + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + else() + onnxruntime_fetchcontent_declare( + pytorch_cpuinfo + URL ${DEP_URL_pytorch_cpuinfo} + URL_HASH SHA1=${DEP_SHA1_pytorch_cpuinfo} + EXCLUDE_FROM_ALL + PATCH_COMMAND + # https://github.com/pytorch/cpuinfo/pull/400 + ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < + ${PROJECT_SOURCE_DIR}/patches/cpuinfo/enable_deinit_refcounting.patch + ) + endif() + onnxruntime_fetchcontent_makeavailable(pytorch_cpuinfo) endif() - set(ONNXRUNTIME_CPUINFO_PROJ pytorch_cpuinfo) - onnxruntime_fetchcontent_makeavailable(${ONNXRUNTIME_CPUINFO_PROJ}) if(TARGET cpuinfo::cpuinfo AND NOT TARGET cpuinfo) message(STATUS "Aliasing cpuinfo::cpuinfo to cpuinfo") add_library(cpuinfo ALIAS cpuinfo::cpuinfo) From b6116a77389df702dfde21622388d455e1aa2b92 Mon Sep 17 00:00:00 2001 From: Vineeth Chelur Date: Mon, 31 Aug 2026 20:01:17 -0700 Subject: [PATCH 16/16] Revert "Limit cpuinfo lifecycle changes to ORT" This reverts commit 149bbfa9a5a51e1cfff89793079588a99b6dae15. --- cmake/external/xnnpack.cmake | 4 +- cmake/onnxruntime_unittests.cmake | 13 ++++++ ...elease_cpuinfo_after_hardware_config.patch | 46 +++++++++++++++++++ cmake/vcpkg-ports/xnnpack/portfile.cmake | 1 + .../test/shared_lib/cpuinfo_refcount_test.cc | 27 +++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch diff --git a/cmake/external/xnnpack.cmake b/cmake/external/xnnpack.cmake index 571283c33c713..59cc26316a770 100644 --- a/cmake/external/xnnpack.cmake +++ b/cmake/external/xnnpack.cmake @@ -84,9 +84,9 @@ endif() onnxruntime_fetchcontent_declare(googlexnnpack URL ${DEP_URL_googlexnnpack} URL_HASH SHA1=${DEP_SHA1_googlexnnpack} - PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch + PATCH_COMMAND ${Patch_EXECUTABLE} --binary --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/AddEmscriptenAndIosSupport.patch && + ${Patch_EXECUTABLE} --ignore-whitespace -p1 < ${PROJECT_SOURCE_DIR}/patches/xnnpack/release_cpuinfo_after_hardware_config.patch EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS NAMES xnnpack ) onnxruntime_fetchcontent_makeavailable(googlexnnpack) set(XNNPACK_DIR ${googlexnnpack_SOURCE_DIR}) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 3c30d19c8b7e2..f222bf63dd246 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -2000,6 +2000,19 @@ endif() target_compile_definitions( onnxruntime_cpuinfo_refcount_test PRIVATE ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) + if(onnxruntime_USE_XNNPACK) + target_compile_definitions( + onnxruntime_cpuinfo_refcount_test + PRIVATE ORT_CPUINFO_TEST_USE_XNNPACK) + if(onnxruntime_USE_VCPKG) + target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_HDR}) + else() + target_include_directories(onnxruntime_cpuinfo_refcount_test PRIVATE ${XNNPACK_INCLUDE_DIR}) + endif() + target_link_libraries( + onnxruntime_cpuinfo_refcount_test + PRIVATE ${onnxruntime_EXTERNAL_LIBRARIES_XNNPACK}) + endif() target_link_libraries(onnxruntime_cpuinfo_refcount_test PRIVATE cpuinfo Threads::Threads) add_test( NAME onnxruntime_cpuinfo_refcount_test diff --git a/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch new file mode 100644 index 0000000000000..8aea6bfc4d156 --- /dev/null +++ b/cmake/patches/xnnpack/release_cpuinfo_after_hardware_config.patch @@ -0,0 +1,46 @@ +diff --git a/src/configs/hardware-config.c b/src/configs/hardware-config.c +index b4e942aa6955360e3ea85619d549410e65ab7a9c..6cf6e9a213b6f72c94940ff815e11f70a658c419 100644 +--- a/src/configs/hardware-config.c ++++ b/src/configs/hardware-config.c +@@ -343,6 +343,7 @@ static void init_hardware_config(void) { + xnn_log_info("cpu_get_uarch(%i): 0x%x", i, cpuinfo_get_uarch(i)->uarch); + } + #endif // XNN_MAX_UARCH_TYPES > 1 ++ cpuinfo_deinitialize(); + #else + xnn_log_warning("Unable to determine L1/L2 data cache properties."); + #endif // XNN_ENABLE_CPUINFO +@@ -358,21 +359,33 @@ const struct xnn_hardware_config* xnn_init_hardware_config() { + #if XNN_ARCH_ARM + if (!cpuinfo_has_arm_v6()) { + xnn_log_debug("unsupported hardware: ARMv6 not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + + if (!cpuinfo_has_arm_vfpv2() && !cpuinfo_has_arm_vfpv3()) { + xnn_log_debug("unsupported hardware: VFP FPU not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + #endif // XNN_ARCH_ARM + #if XNN_ARCH_X86 + if (!cpuinfo_has_x86_sse2()) { + xnn_log_debug("unsupported hardware: SSE2 not detected"); ++ #if !XNN_PLATFORM_WEB && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return NULL; + } + #endif // XNN_ARCH_X86 + + XNN_INIT_ONCE(hardware); ++ #if !XNN_PLATFORM_WEB && !XNN_ARCH_RISCV && !XNN_ARCH_PPC64 && XNN_ENABLE_CPUINFO ++ cpuinfo_deinitialize(); ++ #endif + return &hardware_config; + } diff --git a/cmake/vcpkg-ports/xnnpack/portfile.cmake b/cmake/vcpkg-ports/xnnpack/portfile.cmake index 60b3566629e10..6ccfd2909454f 100644 --- a/cmake/vcpkg-ports/xnnpack/portfile.cmake +++ b/cmake/vcpkg-ports/xnnpack/portfile.cmake @@ -11,6 +11,7 @@ vcpkg_from_github( PATCHES fix-build.patch disable_gcc_warning.patch + "${CMAKE_CURRENT_LIST_DIR}/../../patches/xnnpack/release_cpuinfo_after_hardware_config.patch" ) vcpkg_find_acquire_program(PYTHON3) diff --git a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc index d4481f2e260e3..7c91ce9f88afd 100644 --- a/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc +++ b/onnxruntime/test/shared_lib/cpuinfo_refcount_test.cc @@ -9,6 +9,10 @@ #include #include +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) +#include +#endif + #if defined(ORT_CPUINFO_TEST_HAS_INTERNAL_STATE) constexpr int kCpuinfoCacheLevelCount = 5; @@ -185,6 +189,23 @@ bool TestConcurrentConsumers() { return true; } +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) +bool TestXnnpackReleasesCpuinfo() { + if (xnn_initialize(nullptr) != xnn_status_success) { + std::cerr << "XNNPACK initialization failed" << std::endl; + return false; + } + + if (!IsCpuinfoDeinitialized()) { + std::cerr << "XNNPACK retained a cpuinfo reference after hardware discovery" << std::endl; + return false; + } + + xnn_deinitialize(); + return true; +} +#endif + } // namespace int main() { @@ -192,5 +213,11 @@ int main() { return EXIT_FAILURE; } +#if defined(ORT_CPUINFO_TEST_USE_XNNPACK) + if (!TestXnnpackReleasesCpuinfo()) { + return EXIT_FAILURE; + } +#endif + return EXIT_SUCCESS; }