Skip to content

Commit cd2fd0a

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

File tree

3 files changed

+98
-3
lines changed

3 files changed

+98
-3
lines changed

extension/android/jni/jni_layer.cpp

Lines changed: 83 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 <stringstream>
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+
}
3875

3976
// For Android, write to logcat
4077
void et_pal_emit_log_message(
@@ -45,6 +82,22 @@ void et_pal_emit_log_message(
4582
size_t line,
4683
const char* message,
4784
size_t length) {
85+
86+
std::lock_guard<std::mutex> guard(log_buffer_mutex_);
87+
88+
while (log_buffer_.size() >= log_buffer_length) {
89+
log_buffer_.erase(log_buffer_.front());
90+
}
91+
92+
log_buffer_.emplace_back(
93+
timestamp,
94+
level,
95+
filename,
96+
function,
97+
line,
98+
message,
99+
length);
100+
48101
int android_log_level = ANDROID_LOG_UNKNOWN;
49102
if (level == 'D') {
50103
android_log_level = ANDROID_LOG_DEBUG;
@@ -60,9 +113,6 @@ void et_pal_emit_log_message(
60113
}
61114
#endif
62115

63-
using namespace executorch::extension;
64-
using namespace torch::executor;
65-
66116
namespace executorch::extension {
67117
class TensorHybrid : public facebook::jni::HybridClass<TensorHybrid> {
68118
public:
@@ -391,12 +441,42 @@ class ExecuTorchJni : public facebook::jni::HybridClass<ExecuTorchJni> {
391441
return jresult;
392442
}
393443

444+
facebook::jni::local_ref<JArrayClass<JString>> readLogBuffer() {
445+
#ifdef __ANDROID__
446+
std::lock_guard<std::mutex> guard(log_buffer_mutex_);
447+
448+
const auto size = log_buffer_.size();
449+
local_ref<JArrayClass<JString>> ret = JArrayClass<JString>::newArray(size);
450+
451+
for (auto i = 0u; i < size; i++) {
452+
const auto& entry = log_buffer_[i];
453+
// Format the log entry as "[TIMESTAMP FUNCTION FILE:LINE] LEVEL MESSAGE".
454+
std::stringstream ss;
455+
ss
456+
<< "[" << entry.timestamp << " " << entry.function
457+
<< " " << entry.filename << ":" << entry.line << "] "
458+
<< entry.level << " " << entry.message;
459+
460+
auto jstr_message = make_jstring(ss.str().c_str());
461+
ret->setElement(i, *jstr_message);
462+
}
463+
464+
return ret;
465+
#else
466+
return JArrayClass<String>::newArray(0);
467+
#endif
468+
}
469+
394470
static void registerNatives() {
395471
registerHybrid({
396472
makeNativeMethod("initHybrid", ExecuTorchJni::initHybrid),
397473
makeNativeMethod("forward", ExecuTorchJni::forward),
398474
makeNativeMethod("execute", ExecuTorchJni::execute),
399475
makeNativeMethod("loadMethod", ExecuTorchJni::load_method),
476+
477+
#ifdef __ANDROID__
478+
makeNativeMethod("readLogBuffer", ExecuTorchJni::readLogBuffer),
479+
#endif
400480
});
401481
}
402482
};

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ 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+
@DoNotStrip
106+
public String[] readLogBuffer() {
107+
return mNativePeer.readLogBuffer();
108+
}
109+
102110
/**
103111
* Explicitly destroys the native torch::jit::Module. Calling this method is not required, as the
104112
* 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)