diff --git a/extension/android/CMakeLists.txt b/extension/android/CMakeLists.txt index c96cfeb5d70..70f21f2751c 100644 --- a/extension/android/CMakeLists.txt +++ b/extension/android/CMakeLists.txt @@ -64,7 +64,7 @@ set(executorch_DIR ${CMAKE_CURRENT_BINARY_DIR}/../../lib/cmake/ExecuTorch) find_package(executorch CONFIG REQUIRED) target_link_options_shared_lib(executorch) -add_library(executorch_jni SHARED jni/jni_layer.cpp) +add_library(executorch_jni SHARED jni/jni_layer.cpp jni/log.cpp) set(link_libraries) list( @@ -146,7 +146,7 @@ if(EXECUTORCH_JNI_CUSTOM_LIBRARY) endif() if(EXECUTORCH_BUILD_LLAMA_JNI) - target_sources(executorch_jni PRIVATE jni/jni_layer_llama.cpp) + target_sources(executorch_jni PRIVATE jni/jni_layer_llama.cpp jni/log.cpp) list(APPEND link_libraries llama_runner llava_runner) target_compile_definitions(executorch_jni PUBLIC EXECUTORCH_BUILD_LLAMA_JNI=1) add_subdirectory( diff --git a/extension/android/jni/BUCK b/extension/android/jni/BUCK index 6f269739c06..e1bf26fef26 100644 --- a/extension/android/jni/BUCK +++ b/extension/android/jni/BUCK @@ -1,5 +1,6 @@ load("@fbsource//tools/build_defs/android:fb_android_cxx_library.bzl", "fb_android_cxx_library") load("@fbsource//xplat/executorch/backends/xnnpack/third-party:third_party_libs.bzl", "third_party_dep") +load("@fbsource//xplat/executorch/build:runtime_wrapper.bzl", "runtime") load("@fbsource//xplat/executorch/codegen:codegen.bzl", "executorch_generated_lib") oncall("executorch") @@ -25,7 +26,7 @@ executorch_generated_lib( fb_android_cxx_library( name = "executorch_jni", - srcs = ["jni_layer.cpp"], + srcs = ["jni_layer.cpp", "log.cpp"], headers = ["jni_layer_constants.h"], allow_jni_merging = False, compiler_flags = [ @@ -36,6 +37,7 @@ fb_android_cxx_library( soname = "libexecutorch.$(ext)", visibility = ["PUBLIC"], deps = [ + ":log_provider_static", "//fbandroid/libraries/fbjni:fbjni", "//fbandroid/native/fb:fb", "//third-party/glog:glog", @@ -49,7 +51,7 @@ fb_android_cxx_library( fb_android_cxx_library( name = "executorch_jni_full", - srcs = ["jni_layer.cpp"], + srcs = ["jni_layer.cpp", "log.cpp"], headers = ["jni_layer_constants.h"], allow_jni_merging = False, compiler_flags = [ @@ -60,6 +62,7 @@ fb_android_cxx_library( soname = "libexecutorch.$(ext)", visibility = ["PUBLIC"], deps = [ + ":log_provider_static", ":generated_op_lib_optimized_static", "//fbandroid/libraries/fbjni:fbjni", "//fbandroid/native/fb:fb", @@ -88,6 +91,7 @@ fb_android_cxx_library( soname = "libexecutorch.$(ext)", visibility = ["PUBLIC"], deps = [ + ":log_provider_static", "//fbandroid/libraries/fbjni:fbjni", "//fbandroid/native/fb:fb", "//third-party/glog:glog", @@ -101,3 +105,18 @@ fb_android_cxx_library( "//xplat/executorch/extension/threadpool:threadpool_static", ], ) + +runtime.cxx_library( + name = "log_provider", + srcs = ["log.cpp"], + exported_headers = ["log.h"], + compiler_flags = [ + "-frtti", + "-fexceptions", + "-Wno-unused-variable", + ], + deps = [ + "//executorch/runtime/core:core", + ], + visibility = ["@EXECUTORCH_CLIENTS"], +) diff --git a/extension/android/jni/jni_layer.cpp b/extension/android/jni/jni_layer.cpp index 479da288063..ddba8462b90 100644 --- a/extension/android/jni/jni_layer.cpp +++ b/extension/android/jni/jni_layer.cpp @@ -17,6 +17,7 @@ #include "jni_layer_constants.h" +#include #include #include #include @@ -36,76 +37,6 @@ using namespace executorch::extension; using namespace torch::executor; -#ifdef __ANDROID__ -#include -#include -#include - -// Number of entries to store in the in-memory log buffer. -const size_t log_buffer_length = 16; - -struct log_entry { - et_timestamp_t timestamp; - et_pal_log_level_t level; - std::string filename; - std::string function; - size_t line; - std::string message; - - log_entry( - et_timestamp_t timestamp, - et_pal_log_level_t level, - const char* filename, - const char* function, - size_t line, - const char* message, - size_t length) - : timestamp(timestamp), - level(level), - filename(filename), - function(function), - line(line), - message(message, length) {} -}; - -namespace { -std::vector log_buffer_; -std::mutex log_buffer_mutex_; -} // namespace - -// For Android, write to logcat -void et_pal_emit_log_message( - et_timestamp_t timestamp, - et_pal_log_level_t level, - const char* filename, - const char* function, - size_t line, - const char* message, - size_t length) { - std::lock_guard guard(log_buffer_mutex_); - - while (log_buffer_.size() >= log_buffer_length) { - log_buffer_.erase(log_buffer_.begin()); - } - - log_buffer_.emplace_back( - timestamp, level, filename, function, line, message, length); - - int android_log_level = ANDROID_LOG_UNKNOWN; - if (level == 'D') { - android_log_level = ANDROID_LOG_DEBUG; - } else if (level == 'I') { - android_log_level = ANDROID_LOG_INFO; - } else if (level == 'E') { - android_log_level = ANDROID_LOG_ERROR; - } else if (level == 'F') { - android_log_level = ANDROID_LOG_FATAL; - } - - __android_log_print(android_log_level, "ExecuTorch", "%s", message); -} -#endif - namespace executorch::extension { class TensorHybrid : public facebook::jni::HybridClass { public: @@ -437,24 +368,26 @@ class ExecuTorchJni : public facebook::jni::HybridClass { facebook::jni::local_ref> readLogBuffer() { #ifdef __ANDROID__ - std::lock_guard guard(log_buffer_mutex_); - - const auto size = log_buffer_.size(); - facebook::jni::local_ref> ret = - facebook::jni::JArrayClass::newArray(size); - - for (auto i = 0u; i < size; i++) { - const auto& entry = log_buffer_[i]; - // Format the log entry as "[TIMESTAMP FUNCTION FILE:LINE] LEVEL MESSAGE". - std::stringstream ss; - ss << "[" << entry.timestamp << " " << entry.function << " " - << entry.filename << ":" << entry.line << "] " - << static_cast(entry.level) << " " << entry.message; - - facebook::jni::local_ref jstr_message = - facebook::jni::make_jstring(ss.str().c_str()); - (*ret)[i] = jstr_message; - } + + facebook::jni::local_ref> ret; + + access_log_buffer([&](std::vector& buffer) { + const auto size = buffer.size(); + ret = facebook::jni::JArrayClass::newArray(size); + for (auto i = 0u; i < size; i++) { + const auto& entry = buffer[i]; + // Format the log entry as "[TIMESTAMP FUNCTION FILE:LINE] LEVEL + // MESSAGE". + std::stringstream ss; + ss << "[" << entry.timestamp << " " << entry.function << " " + << entry.filename << ":" << entry.line << "] " + << static_cast(entry.level) << " " << entry.message; + + facebook::jni::local_ref jstr_message = + facebook::jni::make_jstring(ss.str().c_str()); + (*ret)[i] = jstr_message; + } + }); return ret; #else @@ -468,10 +401,7 @@ class ExecuTorchJni : public facebook::jni::HybridClass { makeNativeMethod("forward", ExecuTorchJni::forward), makeNativeMethod("execute", ExecuTorchJni::execute), makeNativeMethod("loadMethod", ExecuTorchJni::load_method), - -#ifdef __ANDROID__ makeNativeMethod("readLogBuffer", ExecuTorchJni::readLogBuffer), -#endif }); } }; diff --git a/extension/android/jni/log.cpp b/extension/android/jni/log.cpp new file mode 100644 index 00000000000..663198e1271 --- /dev/null +++ b/extension/android/jni/log.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "log.h" + +#ifdef __ANDROID__ + +#include +#include +#include +#include + +using executorch::extension::log_entry; + +// Number of entries to store in the in-memory log buffer. +const size_t log_buffer_length = 16; + +namespace { +std::vector log_buffer_; +std::mutex log_buffer_mutex_; +} // namespace + +// For Android, write to logcat +void et_pal_emit_log_message( + et_timestamp_t timestamp, + et_pal_log_level_t level, + const char* filename, + const char* function, + size_t line, + const char* message, + size_t length) { + std::lock_guard guard(log_buffer_mutex_); + + while (log_buffer_.size() >= log_buffer_length) { + log_buffer_.erase(log_buffer_.begin()); + } + + log_buffer_.emplace_back( + timestamp, level, filename, function, line, message, length); + + int android_log_level = ANDROID_LOG_UNKNOWN; + if (level == 'D') { + android_log_level = ANDROID_LOG_DEBUG; + } else if (level == 'I') { + android_log_level = ANDROID_LOG_INFO; + } else if (level == 'E') { + android_log_level = ANDROID_LOG_ERROR; + } else if (level == 'F') { + android_log_level = ANDROID_LOG_FATAL; + } + + __android_log_print(android_log_level, "ExecuTorch", "%s", message); +} + +namespace executorch::extension { + +void access_log_buffer(std::function&)> accessor) { + std::lock_guard guard(log_buffer_mutex_); + accessor(log_buffer_); +} + +} // namespace executorch::extension + +#endif diff --git a/extension/android/jni/log.h b/extension/android/jni/log.h new file mode 100644 index 00000000000..4389b1d61aa --- /dev/null +++ b/extension/android/jni/log.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +#include +#include +#include + +namespace executorch::extension { +struct log_entry { + et_timestamp_t timestamp; + et_pal_log_level_t level; + std::string filename; + std::string function; + size_t line; + std::string message; + + log_entry( + et_timestamp_t timestamp, + et_pal_log_level_t level, + const char* filename, + const char* function, + size_t line, + const char* message, + size_t length) + : timestamp(timestamp), + level(level), + filename(filename), + function(function), + line(line), + message(message, length) {} +}; + +void access_log_buffer(std::function&)> accessor); +} // namespace executorch::extension