diff --git a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java index f7e2e37dcec..0e13284e0ac 100644 --- a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java +++ b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java @@ -227,6 +227,28 @@ public String[] readLogBuffer() { @DoNotStrip public native boolean etdump(); + /** + * Dump the ExecuTorch ETDump file to {@code outputPath}. + * + * @param outputPath absolute path to write the etdump file to. + * @return true if the etdump was successfully written, false otherwise. + */ + @Experimental + public boolean etdump(String outputPath) { + mLock.lock(); + try { + if (!mHybridData.isValid()) { + throw new IllegalStateException("Module has been destroyed"); + } + return etdumpToNative(outputPath); + } finally { + mLock.unlock(); + } + } + + @DoNotStrip + private native boolean etdumpToNative(String outputPath); + /** * Explicitly destroys the native Module object. Calling this method is not required, as the * native object will be destroyed when this object is garbage-collected. However, the timing of diff --git a/extension/android/jni/jni_layer.cpp b/extension/android/jni/jni_layer.cpp index beff72119b8..c0c8cf8e7f0 100644 --- a/extension/android/jni/jni_layer.cpp +++ b/extension/android/jni/jni_layer.cpp @@ -466,34 +466,15 @@ class ExecuTorchJni : public facebook::jni::HybridClass { } jboolean etdump() { -#ifdef EXECUTORCH_ANDROID_PROFILING - executorch::etdump::ETDumpGen* etdumpgen = - (executorch::etdump::ETDumpGen*)module_->event_tracer(); - auto etdump_data = etdumpgen->get_etdump_data(); + return etdump_to_path("/data/local/tmp/result.etdump"); + } - if (etdump_data.buf != nullptr && etdump_data.size > 0) { - int etdump_file = - open("/data/local/tmp/result.etdump", O_WRONLY | O_CREAT, 0644); - if (etdump_file == -1) { - ET_LOG(Error, "Cannot create result.etdump error: %d", errno); - return false; - } - ssize_t bytes_written = - write(etdump_file, (uint8_t*)etdump_data.buf, etdump_data.size); - if (bytes_written == -1) { - ET_LOG(Error, "Cannot write result.etdump error: %d", errno); - return false; - } else { - ET_LOG(Info, "ETDump written %d bytes to file.", bytes_written); - } - close(etdump_file); - free(etdump_data.buf); - return true; - } else { - ET_LOG(Error, "No ETDump data available!"); + jboolean etdumpTo(facebook::jni::alias_ref outputPath) { + if (!outputPath) { + ET_LOG(Error, "etdumpTo called with null outputPath"); + return false; } -#endif - return false; + return etdump_to_path(outputPath->toStdString().c_str()); } facebook::jni::local_ref> getMethods() { @@ -565,10 +546,51 @@ class ExecuTorchJni : public facebook::jni::HybridClass { makeNativeMethod( "readLogBufferStaticNative", ExecuTorchJni::readLogBufferStatic), makeNativeMethod("etdump", ExecuTorchJni::etdump), + makeNativeMethod("etdumpToNative", ExecuTorchJni::etdumpTo), makeNativeMethod("getMethods", ExecuTorchJni::getMethods), makeNativeMethod("getUsedBackends", ExecuTorchJni::getUsedBackends), }); } + + private: + jboolean etdump_to_path(const char* path) { +#ifdef EXECUTORCH_ANDROID_PROFILING + auto* tracer = module_->event_tracer(); + if (!tracer) { + ET_LOG(Error, "ETDump not available: no event tracer attached"); + return false; + } + auto* etdumpgen = static_cast(tracer); + auto etdump_data = etdumpgen->get_etdump_data(); + + if (etdump_data.buf != nullptr && etdump_data.size > 0) { + int etdump_file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (etdump_file == -1) { + ET_LOG(Error, "Cannot create %s error: %d", path, errno); + free(etdump_data.buf); + return false; + } + ssize_t bytes_written = + write(etdump_file, (uint8_t*)etdump_data.buf, etdump_data.size); + if (bytes_written == -1) { + ET_LOG(Error, "Cannot write %s error: %d", path, errno); + close(etdump_file); + free(etdump_data.buf); + return false; + } else { + ET_LOG(Info, "ETDump written %zd bytes to %s.", bytes_written, path); + } + close(etdump_file); + free(etdump_data.buf); + return true; + } else { + ET_LOG(Error, "No ETDump data available!"); + } +#else + (void)path; +#endif + return false; + } }; } // namespace executorch::extension diff --git a/scripts/build_android_library.sh b/scripts/build_android_library.sh index 0fb9e909884..5363c64b87c 100755 --- a/scripts/build_android_library.sh +++ b/scripts/build_android_library.sh @@ -37,9 +37,11 @@ build_android_native_library() { cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \ -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \ + -DPYTHON_EXECUTABLE="${PYTHON_EXECUTABLE}" \ --preset "android-${ANDROID_ABI}" \ -DANDROID_PLATFORM=android-26 \ -DEXECUTORCH_ENABLE_EVENT_TRACER="${EXECUTORCH_ANDROID_PROFILING:-OFF}" \ + -DEXECUTORCH_ANDROID_PROFILING="${EXECUTORCH_ANDROID_PROFILING:-OFF}" \ -DEXECUTORCH_BUILD_EXTENSION_LLM="${EXECUTORCH_BUILD_EXTENSION_LLM:-ON}" \ -DEXECUTORCH_BUILD_EXTENSION_LLM_RUNNER="${EXECUTORCH_BUILD_EXTENSION_LLM:-ON}" \ -DEXECUTORCH_BUILD_EXTENSION_ASR_RUNNER="${EXECUTORCH_BUILD_EXTENSION_LLM:-ON}" \ @@ -51,6 +53,7 @@ build_android_native_library() { -DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \ -DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \ -DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \ + -DFLATCC_ALLOW_WERROR=OFF \ -DSUPPORT_REGEX_LOOKAHEAD=ON \ -DCMAKE_BUILD_TYPE="${EXECUTORCH_CMAKE_BUILD_TYPE}" \ -B"${CMAKE_OUT}" diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index bafcd74ec77..b81ad94de89 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -100,6 +100,7 @@ ExternalProject_Add( -DFLATCC_TEST=OFF -DFLATCC_REFLECTION=OFF -DFLATCC_DEBUG_CLANG_SANITIZE=OFF + -DFLATCC_ALLOW_WERROR=OFF -DFLATCC_INSTALL=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_INSTALL_PREFIX:PATH= diff --git a/tools/cmake/common/preset.cmake b/tools/cmake/common/preset.cmake index 4ac45e28562..7d016db2347 100644 --- a/tools/cmake/common/preset.cmake +++ b/tools/cmake/common/preset.cmake @@ -82,12 +82,12 @@ macro(define_overridable_option NAME DESCRIPTION VALUE_TYPE DEFAULT_VALUE) if(DEFINED ${NAME} AND NOT DEFINED CACHE{${NAME}}) set(${NAME} ${${NAME}} - CACHE ${VALUE_TYPE} ${DESCRIPTION} FORCE + CACHE ${VALUE_TYPE} "${DESCRIPTION}" FORCE ) else() set(${NAME} ${DEFAULT_VALUE} - CACHE ${VALUE_TYPE} ${DESCRIPTION} + CACHE ${VALUE_TYPE} "${DESCRIPTION}" ) endif()