Skip to content

Commit 0bb24d6

Browse files
committed
Add in-memory log buffer in Android JNI
1 parent 97a4600 commit 0bb24d6

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

extension/android/jni/jni_layer.cpp

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,45 @@
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
4077
void 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-
66109
namespace executorch::extension {
67110
class 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
};

extension/android/src/main/java/org/pytorch/executorch/Module.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ public int loadMethod(String methodName) {
9999
return mNativePeer.loadMethod(methodName);
100100
}
101101

102+
/**
103+
* Retrieve the in-memory log buffer, containing the most recent ExecuTorch log entries.
104+
*/
105+
public String[] readLogBuffer() {
106+
return mNativePeer.readLogBuffer();
107+
}
108+
102109
/**
103110
* Explicitly destroys the native torch::jit::Module. Calling this method is not required, as the
104111
* native object will be destroyed when this object is garbage-collected. However, the timing of

extension/android/src/main/java/org/pytorch/executorch/NativePeer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,11 @@ public void resetNative() {
5454
*/
5555
@DoNotStrip
5656
public native int loadMethod(String methodName);
57+
58+
/**
59+
* Retrieve the in-memory log buffer, containing the most recent ExecuTorch
60+
* log entries.
61+
*/
62+
@DoNotStrip
63+
public native String[] readLogBuffer();
5764
}

0 commit comments

Comments
 (0)