Skip to content

Commit b04666d

Browse files
committed
fix: close breadcrumb races and restore enclosing module after nested loads
Address review findings: - Stop rendering once a crash is recorded: a thread that kept running could otherwise flip the double buffer twice and rewrite the buffer the handler is copying out. - Make g_storeFd atomic with release/acquire ordering; handlers are installed at JNI_OnLoad, before OpenStore publishes the fd. - Replace SetCurrentModule with a scoped ModuleScope that restores the enclosing module on every return and throw path, so a crash after a nested require no longer blames the inner module.
1 parent c3fbe51 commit b04666d

3 files changed

Lines changed: 38 additions & 7 deletions

File tree

test-app/runtime/src/main/cpp/CrashBreadcrumbs.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ char g_rendered[2][kBufferMax];
3939
size_t g_renderedLength[2];
4040
std::atomic<int> g_active{-1};
4141

42-
int g_storeFd = -1;
42+
std::atomic<int> g_storeFd{-1};
4343
std::atomic_flag g_recorded = ATOMIC_FLAG_INIT;
4444
struct sigaction g_previous[NSIG];
4545

@@ -84,6 +84,11 @@ void Append(char* out, size_t& length, const char* format, ...) {
8484
}
8585

8686
void RenderLocked() {
87+
// Once a crash is recorded the handler may be reading either buffer; a
88+
// second flip after that point would rewrite the one it is copying out.
89+
if (g_recorded.test(std::memory_order_acquire)) {
90+
return;
91+
}
8792
int next = g_active.load(std::memory_order_relaxed) == 0 ? 1 : 0;
8893
char* out = g_rendered[next];
8994
size_t length = 0;
@@ -138,7 +143,7 @@ void AppendRawInt(char* out, size_t capacity, size_t& length, int value) {
138143
void Handler(int signalNumber, siginfo_t* info, void* context) {
139144
// Only the first thread to fault records; the rest are already doomed.
140145
if (!g_recorded.test_and_set()) {
141-
int fd = g_storeFd;
146+
int fd = g_storeFd.load(std::memory_order_acquire);
142147
if (fd >= 0) {
143148
char header[kHeaderMax];
144149
size_t length = 0;
@@ -219,7 +224,7 @@ void CrashBreadcrumbs::OpenStore(const std::string& filesRoot) {
219224
ftruncate(fd, 0);
220225
}
221226

222-
g_storeFd = fd;
227+
g_storeFd.store(fd, std::memory_order_release);
223228
});
224229
}
225230

@@ -273,14 +278,26 @@ void CrashBreadcrumbs::SetWorkerScript(int runtimeId, const char* script) {
273278
RenderLocked();
274279
}
275280

276-
void CrashBreadcrumbs::SetCurrentModule(const char* modulePath) {
281+
CrashBreadcrumbs::ModuleScope::ModuleScope(const char* modulePath) {
277282
Slot* slot = t_slot;
278283
if (slot == nullptr) {
279284
return;
280285
}
281286
std::lock_guard<std::mutex> lock(g_mutex);
287+
previous_ = slot->module;
288+
restore_ = true;
282289
CopyField(slot->module, modulePath);
283290
RenderLocked();
284291
}
285292

293+
CrashBreadcrumbs::ModuleScope::~ModuleScope() {
294+
Slot* slot = t_slot;
295+
if (!restore_ || slot == nullptr) {
296+
return;
297+
}
298+
std::lock_guard<std::mutex> lock(g_mutex);
299+
CopyField(slot->module, previous_.c_str());
300+
RenderLocked();
301+
}
302+
286303
} // namespace tns

test-app/runtime/src/main/cpp/CrashBreadcrumbs.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,22 @@ class CrashBreadcrumbs {
3636
/* Marks a registered runtime as a worker started from `script`. */
3737
static void SetWorkerScript(int runtimeId, const char* script);
3838

39-
/* Records the module the calling runtime is about to execute. */
40-
static void SetCurrentModule(const char* modulePath);
39+
/*
40+
* Records the module the calling runtime is executing for the lifetime of
41+
* the scope. Module loads nest (`require` inside a module body), so the
42+
* enclosing module is restored on destruction, on throw paths included.
43+
*/
44+
class ModuleScope {
45+
public:
46+
explicit ModuleScope(const char* modulePath);
47+
~ModuleScope();
48+
ModuleScope(const ModuleScope&) = delete;
49+
ModuleScope& operator=(const ModuleScope&) = delete;
50+
51+
private:
52+
std::string previous_;
53+
bool restore_ = false;
54+
};
4155
};
4256

4357
} // namespace tns

test-app/runtime/src/main/cpp/ModuleInternal.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ Local<Object> ModuleInternal::LoadImpl(Isolate* isolate, const string& moduleNam
364364
Local<Object> ModuleInternal::LoadModule(Isolate* isolate, const string& modulePath, const string& moduleCacheKey) {
365365
string frameName("LoadModule " + modulePath);
366366
tns::instrumentation::Frame frame(frameName);
367-
CrashBreadcrumbs::SetCurrentModule(modulePath.c_str());
367+
CrashBreadcrumbs::ModuleScope moduleBreadcrumb(modulePath.c_str());
368368
Local<Object> result;
369369

370370
auto context = isolate->GetCurrentContext();

0 commit comments

Comments
 (0)