Skip to content

Commit bb9d1bf

Browse files
authored
Merge pull request #4 from getsentry/timfish/fix/dont-mix-isolates
fix: Don't send JavaScript objects between isolates
2 parents 00114e3 + e05d387 commit bb9d1bf

File tree

3 files changed

+105
-106
lines changed

3 files changed

+105
-106
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules/
22
build/
33
lib/
44
/*.tgz
5+
test/yarn.lock

module.cc

Lines changed: 104 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -21,78 +21,63 @@ static std::mutex threads_mutex;
2121
// Map to hold all registered threads and their information
2222
static std::unordered_map<v8::Isolate *, ThreadInfo> threads = {};
2323

24+
// Structure to hold stack frame information
25+
struct JsStackFrame {
26+
std::string function_name;
27+
std::string filename;
28+
int lineno;
29+
int colno;
30+
};
31+
32+
// Type alias for a vector of JsStackFrame
33+
using JsStackTrace = std::vector<JsStackFrame>;
34+
2435
// Function to be called when an isolate's execution is interrupted
2536
static void ExecutionInterrupted(Isolate *isolate, void *data) {
26-
auto promise = static_cast<std::promise<Local<Array>> *>(data);
37+
auto promise = static_cast<std::promise<JsStackTrace> *>(data);
2738
auto stack = StackTrace::CurrentStackTrace(isolate, kMaxStackFrames,
2839
StackTrace::kDetailed);
2940

30-
if (stack.IsEmpty()) {
31-
promise->set_value(Array::New(isolate, 0));
32-
return;
33-
}
34-
35-
auto frames = Array::New(isolate, stack->GetFrameCount());
36-
37-
for (int i = 0; i < stack->GetFrameCount(); i++) {
38-
auto frame = stack->GetFrame(isolate, i);
39-
auto fn_name = frame->GetFunctionName();
40-
41-
if (frame->IsEval()) {
42-
fn_name =
43-
String::NewFromUtf8(isolate, "[eval]", NewStringType::kInternalized)
44-
.ToLocalChecked();
45-
} else if (fn_name.IsEmpty() || fn_name->Length() == 0) {
46-
fn_name = String::NewFromUtf8(isolate, "?", NewStringType::kInternalized)
47-
.ToLocalChecked();
48-
} else if (frame->IsConstructor()) {
49-
fn_name = String::NewFromUtf8(isolate, "[constructor]",
50-
NewStringType::kInternalized)
51-
.ToLocalChecked();
41+
JsStackTrace frames;
42+
if (!stack.IsEmpty()) {
43+
for (int i = 0; i < stack->GetFrameCount(); i++) {
44+
auto frame = stack->GetFrame(isolate, i);
45+
auto fn_name = frame->GetFunctionName();
46+
47+
std::string function_name;
48+
if (frame->IsEval()) {
49+
function_name = "[eval]";
50+
} else if (fn_name.IsEmpty() || fn_name->Length() == 0) {
51+
function_name = "?";
52+
} else if (frame->IsConstructor()) {
53+
function_name = "[constructor]";
54+
} else {
55+
v8::String::Utf8Value utf8_fn(isolate, fn_name);
56+
function_name = *utf8_fn ? *utf8_fn : "?";
57+
}
58+
59+
std::string filename;
60+
auto script_name = frame->GetScriptName();
61+
if (!script_name.IsEmpty()) {
62+
v8::String::Utf8Value utf8_filename(isolate, script_name);
63+
filename = *utf8_filename ? *utf8_filename : "<unknown>";
64+
} else {
65+
filename = "<unknown>";
66+
}
67+
68+
int lineno = frame->GetLineNumber();
69+
int colno = frame->GetColumn();
70+
71+
frames.push_back(JsStackFrame{function_name, filename, lineno, colno});
5272
}
53-
54-
auto frame_obj = Object::New(isolate);
55-
frame_obj
56-
->Set(isolate->GetCurrentContext(),
57-
String::NewFromUtf8(isolate, "function",
58-
NewStringType::kInternalized)
59-
.ToLocalChecked(),
60-
fn_name)
61-
.Check();
62-
63-
frame_obj
64-
->Set(isolate->GetCurrentContext(),
65-
String::NewFromUtf8(isolate, "filename",
66-
NewStringType::kInternalized)
67-
.ToLocalChecked(),
68-
frame->GetScriptName())
69-
.Check();
70-
71-
frame_obj
72-
->Set(
73-
isolate->GetCurrentContext(),
74-
String::NewFromUtf8(isolate, "lineno", NewStringType::kInternalized)
75-
.ToLocalChecked(),
76-
Integer::New(isolate, frame->GetLineNumber()))
77-
.Check();
78-
79-
frame_obj
80-
->Set(
81-
isolate->GetCurrentContext(),
82-
String::NewFromUtf8(isolate, "colno", NewStringType::kInternalized)
83-
.ToLocalChecked(),
84-
Integer::New(isolate, frame->GetColumn()))
85-
.Check();
86-
87-
frames->Set(isolate->GetCurrentContext(), i, frame_obj).Check();
8873
}
8974

9075
promise->set_value(frames);
9176
}
9277

9378
// Function to capture the stack trace of a single isolate
94-
Local<Array> CaptureStackTrace(Isolate *isolate) {
95-
std::promise<Local<Array>> promise;
79+
JsStackTrace CaptureStackTrace(Isolate *isolate) {
80+
std::promise<JsStackTrace> promise;
9681
auto future = promise.get_future();
9782

9883
// The v8 isolate must be interrupted to capture the stack trace
@@ -104,37 +89,77 @@ Local<Array> CaptureStackTrace(Isolate *isolate) {
10489
// Function to capture stack traces from all registered threads
10590
void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
10691
auto capture_from_isolate = args.GetIsolate();
92+
auto current_context = capture_from_isolate->GetCurrentContext();
10793

108-
using ThreadResult = std::tuple<std::string, Local<Array>>;
94+
using ThreadResult = std::tuple<std::string, JsStackTrace>;
10995
std::vector<std::future<ThreadResult>> futures;
11096

111-
// We collect the futures into a vec so they can be processed in parallel
112-
std::lock_guard<std::mutex> lock(threads_mutex);
113-
for (auto [thread_isolate, thread_info] : threads) {
114-
if (thread_isolate == capture_from_isolate)
115-
continue;
116-
117-
auto thread_name = thread_info.thread_name;
118-
119-
futures.emplace_back(std::async(
120-
std::launch::async,
121-
[thread_name](Isolate *isolate) -> ThreadResult {
122-
return std::make_tuple(thread_name, CaptureStackTrace(isolate));
123-
},
124-
thread_isolate));
97+
{
98+
std::lock_guard<std::mutex> lock(threads_mutex);
99+
for (auto [thread_isolate, thread_info] : threads) {
100+
if (thread_isolate == capture_from_isolate)
101+
continue;
102+
auto thread_name = thread_info.thread_name;
103+
104+
futures.emplace_back(std::async(
105+
std::launch::async,
106+
[thread_name](Isolate *isolate) -> ThreadResult {
107+
return std::make_tuple(thread_name, CaptureStackTrace(isolate));
108+
},
109+
thread_isolate));
110+
}
125111
}
126112

127-
// We wait for all futures to complete and collect their results into a
128-
// JavaScript object
129113
Local<Object> result = Object::New(capture_from_isolate);
114+
130115
for (auto &future : futures) {
131116
auto [thread_name, frames] = future.get();
132-
133117
auto key = String::NewFromUtf8(capture_from_isolate, thread_name.c_str(),
134118
NewStringType::kNormal)
135119
.ToLocalChecked();
136120

137-
result->Set(capture_from_isolate->GetCurrentContext(), key, frames).Check();
121+
Local<Array> jsFrames = Array::New(capture_from_isolate, frames.size());
122+
for (size_t i = 0; i < frames.size(); ++i) {
123+
const auto &f = frames[i];
124+
Local<Object> frameObj = Object::New(capture_from_isolate);
125+
frameObj
126+
->Set(current_context,
127+
String::NewFromUtf8(capture_from_isolate, "function",
128+
NewStringType::kInternalized)
129+
.ToLocalChecked(),
130+
String::NewFromUtf8(capture_from_isolate,
131+
f.function_name.c_str(),
132+
NewStringType::kNormal)
133+
.ToLocalChecked())
134+
.Check();
135+
frameObj
136+
->Set(current_context,
137+
String::NewFromUtf8(capture_from_isolate, "filename",
138+
NewStringType::kInternalized)
139+
.ToLocalChecked(),
140+
String::NewFromUtf8(capture_from_isolate, f.filename.c_str(),
141+
NewStringType::kNormal)
142+
.ToLocalChecked())
143+
.Check();
144+
frameObj
145+
->Set(current_context,
146+
String::NewFromUtf8(capture_from_isolate, "lineno",
147+
NewStringType::kInternalized)
148+
.ToLocalChecked(),
149+
Integer::New(capture_from_isolate, f.lineno))
150+
.Check();
151+
frameObj
152+
->Set(current_context,
153+
String::NewFromUtf8(capture_from_isolate, "colno",
154+
NewStringType::kInternalized)
155+
.ToLocalChecked(),
156+
Integer::New(capture_from_isolate, f.colno))
157+
.Check();
158+
jsFrames->Set(current_context, static_cast<uint32_t>(i), frameObj)
159+
.Check();
160+
}
161+
162+
result->Set(current_context, key, jsFrames).Check();
138163
}
139164

140165
args.GetReturnValue().Set(result);

test/yarn.lock

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)