3333#include < fbjni/ByteBuffer.h>
3434#include < fbjni/fbjni.h>
3535
36+ using namespace executorch ::extension;
37+ using namespace torch ::executor;
38+
3639#ifdef __ANDROID__
3740#include < android/log.h>
41+ #include < mutex>
42+ #include < sstream>
43+
44+ // Number of entries to store in the in-memory log buffer.
45+ const size_t log_buffer_length = 16 ;
46+
47+ struct log_entry {
48+ et_timestamp_t timestamp;
49+ et_pal_log_level_t level;
50+ std::string filename;
51+ std::string function;
52+ size_t line;
53+ std::string message;
54+
55+ log_entry (
56+ et_timestamp_t timestamp,
57+ et_pal_log_level_t level,
58+ const char * filename,
59+ const char * function,
60+ size_t line,
61+ const char * message,
62+ size_t length)
63+ : timestamp(timestamp),
64+ level (level),
65+ filename(filename),
66+ function(function),
67+ line(line),
68+ message(message, length) {}
69+ };
70+
71+ namespace {
72+ std::vector<log_entry> log_buffer_;
73+ std::mutex log_buffer_mutex_;
74+ } // namespace
3875
3976// For Android, write to logcat
4077void et_pal_emit_log_message (
@@ -45,6 +82,15 @@ void et_pal_emit_log_message(
4582 size_t line,
4683 const char * message,
4784 size_t length) {
85+ std::lock_guard<std::mutex> guard (log_buffer_mutex_);
86+
87+ while (log_buffer_.size () >= log_buffer_length) {
88+ log_buffer_.erase (log_buffer_.begin ());
89+ }
90+
91+ log_buffer_.emplace_back (
92+ timestamp, level, filename, function, line, message, length);
93+
4894 int android_log_level = ANDROID_LOG_UNKNOWN;
4995 if (level == ' D' ) {
5096 android_log_level = ANDROID_LOG_DEBUG;
@@ -60,9 +106,6 @@ void et_pal_emit_log_message(
60106}
61107#endif
62108
63- using namespace executorch ::extension;
64- using namespace torch ::executor;
65-
66109namespace executorch ::extension {
67110class TensorHybrid : public facebook ::jni::HybridClass<TensorHybrid> {
68111 public:
@@ -391,12 +434,44 @@ class ExecuTorchJni : public facebook::jni::HybridClass<ExecuTorchJni> {
391434 return jresult;
392435 }
393436
437+ facebook::jni::local_ref<facebook::jni::JArrayClass<jstring>>
438+ readLogBuffer () {
439+ #ifdef __ANDROID__
440+ std::lock_guard<std::mutex> guard (log_buffer_mutex_);
441+
442+ const auto size = log_buffer_.size ();
443+ facebook::jni::local_ref<facebook::jni::JArrayClass<jstring>> ret =
444+ facebook::jni::JArrayClass<jstring>::newArray (size);
445+
446+ for (auto i = 0u ; i < size; i++) {
447+ const auto & entry = log_buffer_[i];
448+ // Format the log entry as "[TIMESTAMP FUNCTION FILE:LINE] LEVEL MESSAGE".
449+ std::stringstream ss;
450+ ss << " [" << entry.timestamp << " " << entry.function << " "
451+ << entry.filename << " :" << entry.line << " ] "
452+ << static_cast <char >(entry.level ) << " " << entry.message ;
453+
454+ facebook::jni::local_ref<facebook::jni::JString> jstr_message =
455+ facebook::jni::make_jstring (ss.str ().c_str ());
456+ (*ret)[i] = jstr_message;
457+ }
458+
459+ return ret;
460+ #else
461+ return facebook::jni::JArrayClass<String>::newArray (0 );
462+ #endif
463+ }
464+
394465 static void registerNatives () {
395466 registerHybrid ({
396467 makeNativeMethod (" initHybrid" , ExecuTorchJni::initHybrid),
397468 makeNativeMethod (" forward" , ExecuTorchJni::forward),
398469 makeNativeMethod (" execute" , ExecuTorchJni::execute),
399470 makeNativeMethod (" loadMethod" , ExecuTorchJni::load_method),
471+
472+ #ifdef __ANDROID__
473+ makeNativeMethod (" readLogBuffer" , ExecuTorchJni::readLogBuffer),
474+ #endif
400475 });
401476 }
402477};
0 commit comments