Refactor logging and remove dependency on clog
diff --git a/BUILD.bazel b/BUILD.bazel
index 90bef8d..0c92064 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -19,8 +19,9 @@
# Source code common to all platforms.
COMMON_SRCS = [
"src/api.c",
- "src/init.c",
"src/cache.c",
+ "src/init.c",
+ "src/log.c",
]
# Architecture-specific sources and headers.
@@ -139,6 +140,7 @@
}) + [
"-Iexternal/cpuinfo/include",
"-Iexternal/cpuinfo/src",
+ "-DCPUINFO_LOG_LEVEL=2",
],
includes = [
"include",
@@ -167,9 +169,6 @@
"src/arm/api.h",
"src/arm/midr.h",
],
- deps = [
- "@org_pytorch_cpuinfo//deps/clog",
- ],
)
cc_library(
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22a79d6..b2f9434 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,11 @@
SET_PROPERTY(CACHE CPUINFO_RUNTIME_TYPE PROPERTY STRINGS default static shared)
SET(CPUINFO_LOG_LEVEL "default" CACHE STRING "Minimum logging level (info with lower severity will be ignored)")
SET_PROPERTY(CACHE CPUINFO_LOG_LEVEL PROPERTY STRINGS default debug info warning error fatal none)
+IF(ANDROID)
+ OPTION(CPUINFO_LOG_TO_STDIO "Log errors, warnings, and information to stdout/stderr" OFF)
+ELSE()
+ OPTION(CPUINFO_LOG_TO_STDIO "Log errors, warnings, and information to stdout/stderr" ON)
+ENDIF()
OPTION(CPUINFO_BUILD_TOOLS "Build command-line tools" ON)
OPTION(CPUINFO_BUILD_UNIT_TESTS "Build cpuinfo unit tests" ON)
OPTION(CPUINFO_BUILD_MOCK_TESTS "Build cpuinfo mock tests" ON)
@@ -129,10 +134,7 @@
ENDIF()
# ---[ cpuinfo library
-SET(CPUINFO_SRCS
- src/init.c
- src/api.c
- src/cache.c)
+SET(CPUINFO_SRCS src/api.c src/cache.c src/init.c src/log.c)
IF(CPUINFO_SUPPORTED_PLATFORM)
IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten" AND (CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$" OR IOS_ARCH MATCHES "^(i386|x86_64)$"))
@@ -233,29 +235,34 @@
TARGET_LINK_LIBRARIES(cpuinfo PUBLIC Kernel32)
endif()
ENDIF()
+IF(ANDROID AND NOT CPUINFO_LOG_TO_STDIO)
+ TARGET_LINK_LIBRARIES(cpuinfo PRIVATE "log")
+ENDIF()
SET_TARGET_PROPERTIES(cpuinfo PROPERTIES PUBLIC_HEADER include/cpuinfo.h)
TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PRIVATE src)
TARGET_INCLUDE_DIRECTORIES(cpuinfo_internals BEFORE PUBLIC include src)
+TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_TO_STDIO=$<BOOL:${CPUINFO_LOG_TO_STDIO}>")
IF(CPUINFO_LOG_LEVEL STREQUAL "default")
# default logging level: error (subject to change)
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=2)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=2")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "debug")
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=5)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=5")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "info")
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=4)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=4")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "warning")
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=3)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=3")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "error")
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=2)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=2")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "fatal")
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=1)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=1")
ELSEIF(CPUINFO_LOG_LEVEL STREQUAL "none")
- TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE CPUINFO_LOG_LEVEL=0)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE "CPUINFO_LOG_LEVEL=0")
ELSE()
MESSAGE(FATAL_ERROR "Unsupported logging level ${CPUINFO_LOG_LEVEL}")
ENDIF()
-TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE CPUINFO_LOG_LEVEL=0)
+TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE "CPUINFO_LOG_LEVEL=0")
+TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE "CPUINFO_LOG_TO_STDIO=1")
IF(CPUINFO_SUPPORTED_PLATFORM)
TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=1)
@@ -269,21 +276,6 @@
TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=0)
ENDIF()
-# ---[ cpuinfo dependencies: clog
-IF(NOT DEFINED CLOG_SOURCE_DIR)
- SET(CLOG_SOURCE_DIR "${PROJECT_SOURCE_DIR}/deps/clog")
-ENDIF()
-IF(NOT TARGET clog)
- SET(CLOG_BUILD_TESTS OFF CACHE BOOL "")
- SET(CLOG_RUNTIME_TYPE "${CPUINFO_RUNTIME_TYPE}" CACHE STRING "")
- ADD_SUBDIRECTORY(
- "${CLOG_SOURCE_DIR}")
- # We build static version of clog but a dynamic library may indirectly depend on it
- SET_PROPERTY(TARGET clog PROPERTY POSITION_INDEPENDENT_CODE ON)
-ENDIF()
-TARGET_LINK_LIBRARIES(cpuinfo PRIVATE clog)
-TARGET_LINK_LIBRARIES(cpuinfo_internals PRIVATE clog)
-
ADD_LIBRARY(${PROJECT_NAME}::cpuinfo ALIAS cpuinfo)
# support find_package(cpuinfo CONFIG)
@@ -302,7 +294,7 @@
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
INSTALL(EXPORT cpuinfo-targets
- NAMESPACE ${PROJECT_NAME}:: # IMPORTED cpuinfo::cpuinfo, cpuinfo::clog
+ NAMESPACE ${PROJECT_NAME}:: # IMPORTED cpuinfo::cpuinfo
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME})
# ---[ cpuinfo micro-benchmarks
@@ -354,13 +346,13 @@
SET_TARGET_PROPERTIES(cpuinfo_mock PROPERTIES PUBLIC_HEADER include/cpuinfo.h)
TARGET_INCLUDE_DIRECTORIES(cpuinfo_mock BEFORE PUBLIC include)
TARGET_INCLUDE_DIRECTORIES(cpuinfo_mock BEFORE PRIVATE src)
- TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PUBLIC CPUINFO_MOCK=1)
- TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE CLOG_LOG_TO_STDIO=1)
+ TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PUBLIC "CPUINFO_MOCK=1")
+ TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE "CPUINFO_LOG_LEVEL=5")
+ TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE "CPUINFO_LOG_TO_STDIO=1")
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
TARGET_LINK_LIBRARIES(cpuinfo_mock PUBLIC ${CMAKE_THREAD_LIBS_INIT})
TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE _GNU_SOURCE=1)
ENDIF()
- TARGET_LINK_LIBRARIES(cpuinfo_mock PRIVATE clog)
IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv5te|armv7-a)$")
ADD_EXECUTABLE(atm7029b-tablet-test test/mock/atm7029b-tablet.cc)
diff --git a/src/cpuinfo/log.h b/src/cpuinfo/log.h
index dac8cdb..5bd4305 100644
--- a/src/cpuinfo/log.h
+++ b/src/cpuinfo/log.h
@@ -1,17 +1,103 @@
#pragma once
#include <inttypes.h>
-
-#include <clog.h>
-
-#define CPUINFO_LOG_DEBUG_PARSERS 0
+#include <stdarg.h>
+#include <stdlib.h>
#ifndef CPUINFO_LOG_LEVEL
- #define CPUINFO_LOG_LEVEL CLOG_ERROR
+ #error "Undefined CPUINFO_LOG_LEVEL"
#endif
-CLOG_DEFINE_LOG_DEBUG(cpuinfo_log_debug, "cpuinfo", CPUINFO_LOG_LEVEL);
-CLOG_DEFINE_LOG_INFO(cpuinfo_log_info, "cpuinfo", CPUINFO_LOG_LEVEL);
-CLOG_DEFINE_LOG_WARNING(cpuinfo_log_warning, "cpuinfo", CPUINFO_LOG_LEVEL);
-CLOG_DEFINE_LOG_ERROR(cpuinfo_log_error, "cpuinfo", CPUINFO_LOG_LEVEL);
-CLOG_DEFINE_LOG_FATAL(cpuinfo_log_fatal, "cpuinfo", CPUINFO_LOG_LEVEL);
+#define CPUINFO_LOG_NONE 0
+#define CPUINFO_LOG_FATAL 1
+#define CPUINFO_LOG_ERROR 2
+#define CPUINFO_LOG_WARNING 3
+#define CPUINFO_LOG_INFO 4
+#define CPUINFO_LOG_DEBUG 5
+
+#ifndef CPUINFO_LOG_DEBUG_PARSERS
+ #define CPUINFO_LOG_DEBUG_PARSERS 0
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG
+ void cpuinfo_vlog_debug(const char* format, va_list args);
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO
+ void cpuinfo_vlog_info(const char* format, va_list args);
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING
+ void cpuinfo_vlog_warning(const char* format, va_list args);
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR
+ void cpuinfo_vlog_error(const char* format, va_list args);
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL
+ void cpuinfo_vlog_fatal(const char* format, va_list args);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#ifndef CPUINFO_LOG_ARGUMENTS_FORMAT
+ #ifdef __GNUC__
+ #define CPUINFO_LOG_ARGUMENTS_FORMAT __attribute__((__format__(__printf__, 1, 2)))
+ #else
+ #define CPUINFO_LOG_ARGUMENTS_FORMAT
+ #endif
+#endif
+
+CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_debug(const char* format, ...) {
+ #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG
+ va_list args;
+ va_start(args, format);
+ cpuinfo_vlog_debug(format, args);
+ va_end(args);
+ #endif
+}
+
+CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_info(const char* format, ...) {
+ #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO
+ va_list args;
+ va_start(args, format);
+ cpuinfo_vlog_info(format, args);
+ va_end(args);
+ #endif
+}
+
+CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_warning(const char* format, ...) {
+ #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING
+ va_list args;
+ va_start(args, format);
+ cpuinfo_vlog_warning(format, args);
+ va_end(args);
+ #endif
+}
+
+CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_error(const char* format, ...) {
+ #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR
+ va_list args;
+ va_start(args, format);
+ cpuinfo_vlog_error(format, args);
+ va_end(args);
+ #endif
+}
+
+CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_fatal(const char* format, ...) {
+ #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL
+ va_list args;
+ va_start(args, format);
+ cpuinfo_vlog_fatal(format, args);
+ va_end(args);
+ #endif
+ abort();
+}
\ No newline at end of file
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..bec604e
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,192 @@
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef _WIN32
+ #include <windows.h>
+#else
+ #include <unistd.h>
+#endif
+#if defined(__ANDROID__)
+ #include <android/log.h>
+#endif
+#if defined(__hexagon__)
+ #include <qurt_printf.h>
+#endif
+
+#ifndef CPUINFO_LOG_TO_STDIO
+ #if defined(__ANDROID__)
+ #define CPUINFO_LOG_TO_STDIO 0
+ #else
+ #define CPUINFO_LOG_TO_STDIO 1
+ #endif
+#endif
+
+#include <cpuinfo/log.h>
+
+
+/* Messages up to this size are formatted entirely on-stack, and don't allocate heap memory */
+#define CPUINFO_LOG_STACK_BUFFER_SIZE 1024
+
+#ifdef _WIN32
+ #define CPUINFO_LOG_NEWLINE_LENGTH 2
+
+ #define CPUINFO_LOG_STDERR STD_ERROR_HANDLE
+ #define CPUINFO_LOG_STDOUT STD_OUTPUT_HANDLE
+#elif defined(__hexagon__)
+ #define CPUINFO_LOG_NEWLINE_LENGTH 1
+
+ #define CPUINFO_LOG_STDERR 0
+ #define CPUINFO_LOG_STDOUT 0
+#else
+ #define CPUINFO_LOG_NEWLINE_LENGTH 1
+
+ #define CPUINFO_LOG_STDERR STDERR_FILENO
+ #define CPUINFO_LOG_STDOUT STDOUT_FILENO
+#endif
+
+#if CPUINFO_LOG_TO_STDIO
+static void cpuinfo_vlog(int output_handle, const char* prefix, size_t prefix_length, const char* format, va_list args) {
+ char stack_buffer[CPUINFO_LOG_STACK_BUFFER_SIZE];
+ char* heap_buffer = NULL;
+ char* out_buffer = &stack_buffer[0];
+
+ /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
+ va_list args_copy;
+ va_copy(args_copy, args);
+
+ memcpy(stack_buffer, prefix, prefix_length * sizeof(char));
+ assert((prefix_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char) <= CPUINFO_LOG_STACK_BUFFER_SIZE);
+
+ const int format_chars = vsnprintf(
+ &stack_buffer[prefix_length],
+ CPUINFO_LOG_STACK_BUFFER_SIZE - (prefix_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char),
+ format,
+ args);
+ if (format_chars < 0) {
+ /* Format error in the message: silently ignore this particular message. */
+ goto cleanup;
+ }
+ const size_t format_length = (size_t) format_chars;
+ if ((prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char) > CPUINFO_LOG_STACK_BUFFER_SIZE) {
+ /* Allocate a buffer on heap, and vsnprintf to this buffer */
+ const size_t heap_buffer_size = (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char);
+ #if _WIN32
+ heap_buffer = HeapAlloc(GetProcessHeap(), 0, heap_buffer_size);
+ #else
+ heap_buffer = malloc(heap_buffer_size);
+ #endif
+ if (heap_buffer == NULL) {
+ goto cleanup;
+ }
+
+ /* Copy pre-formatted prefix into the on-heap buffer */
+ memcpy(heap_buffer, prefix, prefix_length * sizeof(char));
+ vsnprintf(&heap_buffer[prefix_length], (format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char), format, args_copy);
+ out_buffer = heap_buffer;
+ }
+ #ifdef _WIN32
+ out_buffer[prefix_length + format_length] = '\r';
+ out_buffer[prefix_length + format_length + 1] = '\n';
+
+ DWORD bytes_written;
+ WriteFile(
+ GetStdHandle((DWORD) output_handle),
+ out_buffer, (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char),
+ &bytes_written, NULL);
+ #elif defined(__hexagon__)
+ qurt_printf("%s", out_buffer);
+ #else
+ out_buffer[prefix_length + format_length] = '\n';
+
+ ssize_t bytes_written = write(output_handle, out_buffer, (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char));
+ (void) bytes_written;
+ #endif
+
+cleanup:
+ #ifdef _WIN32
+ HeapFree(GetProcessHeap(), 0, heap_buffer);
+ #else
+ free(heap_buffer);
+ #endif
+ va_end(args_copy);
+}
+#elif defined(__ANDROID__) && CPUINFO_LOG_LEVEL > CPUINFO_LOG_NONE
+ static const char cpuinfo_module[] = "XNNPACK";
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG
+ void cpuinfo_vlog_debug(const char* format, va_list args) {
+ #if CPUINFO_LOG_TO_STDIO
+ static const char debug_prefix[17] = {
+ 'D', 'e', 'b', 'u', 'g', ' ', '(', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ')', ':', ' '
+ };
+ cpuinfo_vlog(CPUINFO_LOG_STDOUT, debug_prefix, 17, format, args);
+ #elif defined(__ANDROID__)
+ __android_log_vprint(ANDROID_LOG_DEBUG, cpuinfo_module, format, args);
+ #else
+ #error "Platform-specific implementation required"
+ #endif
+ }
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO
+ void cpuinfo_vlog_info(const char* format, va_list args) {
+ #if CPUINFO_LOG_TO_STDIO
+ static const char info_prefix[16] = {
+ 'N', 'o', 't', 'e', ' ', '(', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ')', ':', ' '
+ };
+ cpuinfo_vlog(CPUINFO_LOG_STDOUT, info_prefix, 16, format, args);
+ #elif defined(__ANDROID__)
+ __android_log_vprint(ANDROID_LOG_INFO, cpuinfo_module, format, args);
+ #else
+ #error "Platform-specific implementation required"
+ #endif
+ }
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING
+ void cpuinfo_vlog_warning(const char* format, va_list args) {
+ #if CPUINFO_LOG_TO_STDIO
+ static const char warning_prefix[20] = {
+ 'W', 'a', 'r', 'n', 'i', 'n', 'g', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' '
+ };
+ cpuinfo_vlog(CPUINFO_LOG_STDERR, warning_prefix, 20, format, args);
+ #elif defined(__ANDROID__)
+ __android_log_vprint(ANDROID_LOG_WARN, cpuinfo_module, format, args);
+ #else
+ #error "Platform-specific implementation required"
+ #endif
+ }
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR
+ void cpuinfo_vlog_error(const char* format, va_list args) {
+ #if CPUINFO_LOG_TO_STDIO
+ static const char error_prefix[18] = {
+ 'E', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' '
+ };
+ cpuinfo_vlog(CPUINFO_LOG_STDERR, error_prefix, 18, format, args);
+ #elif defined(__ANDROID__)
+ __android_log_vprint(ANDROID_LOG_ERROR, cpuinfo_module, format, args);
+ #else
+ #error "Platform-specific implementation required"
+ #endif
+ }
+#endif
+
+#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL
+ void cpuinfo_vlog_fatal(const char* format, va_list args) {
+ #if CPUINFO_LOG_TO_STDIO
+ static const char fatal_prefix[24] = {
+ 'F', 'a', 't', 'a', 'l', ' ', 'e', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' '
+ };
+ cpuinfo_vlog(CPUINFO_LOG_STDERR, fatal_prefix, 24, format, args);
+ #elif defined(__ANDROID__)
+ __android_log_vprint(ANDROID_LOG_FATAL, cpuinfo_module, format, args);
+ #else
+ #error "Platform-specific implementation required"
+ #endif
+ }
+#endif