update crash handler to log when a native plugin has crashed

This commit is contained in:
Zack Radisic
2024-11-27 17:48:07 -08:00
parent 7b9716043b
commit def9582019
7 changed files with 58 additions and 10 deletions

View File

@@ -290,7 +290,7 @@ impl<'a> OnBeforeParse<'a> {
}
/// Get the output loader for the current file.
pub fn output_loader(&self) -> BunLogLevel {
pub fn output_loader(&self) -> BunLoader {
unsafe { std::mem::transmute(self.result_raw.loader as u32) }
}

View File

@@ -430,7 +430,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
// TODO: think about the finalizer here
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), globalObject->m_pendingNapiModuleDlopenHandle, nullptr, nullptr);
auto *meta = new Bun::NapiModuleMeta(globalObject->m_pendingNapiModuleDlopenHandle);
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, nullptr);
bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
ASSERT(success);

View File

@@ -252,15 +252,18 @@ void BundlerPlugin::NativePluginList::append(JSC::VM& vm, JSC::RegExp* filter, S
}
if (index == std::numeric_limits<unsigned>::max()) {
this->fileCallbacks.append(std::make_pair(callback, external));
this->fileCallbacks.append(NativePluginCallback{callback, external});
} else {
if (this->namespaceCallbacks.size() <= index) {
this->namespaceCallbacks.grow(index + 1);
}
this->namespaceCallbacks[index].append(std::make_pair(callback, external));
this->namespaceCallbacks[index].append(NativePluginCallback{callback, external});
}
}
extern "C" void CrashHandler__setInsideNativePlugin(bool value);
int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, void* onBeforeParseArgs, void* onBeforeParseResult)
{
unsigned index = 0;
@@ -284,11 +287,16 @@ int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void
{
std::lock_guard<std::mutex> lock(*group->at(i).second);
if (group->at(i).first.match(path) > -1) {
Bun::NapiExternal* external = callbacks[i].second;
Bun::NapiExternal* external = callbacks[i].external;
if (external) {
((OnBeforeParseArguments*) (onBeforeParseArgs))->external = external->value();
}
callbacks[i].first(onBeforeParseArgs, onBeforeParseResult);
JSBundlerPluginNativeOnBeforeParseCallback callback = callbacks[i].callback;
CrashHandler__setInsideNativePlugin(true);
callback(onBeforeParseArgs, onBeforeParseResult);
CrashHandler__setInsideNativePlugin(false);
count++;
}
}
@@ -335,12 +343,14 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onBeforeParse, (JSC::JSGlobalOb
}
WTF::String on_before_parse_symbol = on_before_parse_symbol_js.toWTFString(globalObject);
// The dlopen *void handle is attached to the node_addon as a NapiExternal
Bun::NapiExternal* napi_external = jsDynamicCast<Bun::NapiExternal*>(node_addon.getObject()->get(globalObject, WebCore::builtinNames(vm).napiDlopenHandlePrivateName()));
if (UNLIKELY(!napi_external)) {
Bun::throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_TYPE, "Expected node_addon (2nd argument) to have a napiDlopenHandle property"_s);
return {};
}
void* dlopen_handle = napi_external->value();
Bun::NapiModuleMeta* meta = (Bun::NapiModuleMeta*)napi_external->value();
void* dlopen_handle = meta->dlopenHandle;
#if OS(WINDOWS)
BunString onbefore_parse_symbol_str = Bun::toString(on_before_parse_symbol);

View File

@@ -52,9 +52,14 @@ public:
/// Therefore, we need a mutex to synchronize access.
typedef std::pair<Yarr::RegularExpression, std::shared_ptr<std::mutex>> NativeFilterRegexp;
struct NativePluginCallback {
JSBundlerPluginNativeOnBeforeParseCallback callback;
Bun::NapiExternal* external;
};
class NativePluginList {
public:
using PerNamespaceCallbackList = Vector<std::pair<JSBundlerPluginNativeOnBeforeParseCallback, Bun::NapiExternal*>>;
using PerNamespaceCallbackList = Vector<NativePluginCallback>;
Vector<NativeFilterRegexp> fileNamespace = {};
Vector<String> namespaces = {};

View File

@@ -1005,8 +1005,10 @@ extern "C" void napi_module_register(napi_module* mod)
return;
}
auto *meta = new Bun::NapiModuleMeta(globalObject->m_pendingNapiModuleDlopenHandle);
// TODO: think about the finalizer here
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), globalObject->m_pendingNapiModuleDlopenHandle, nullptr, nullptr);
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, nullptr);
bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
ASSERT(success);

View File

@@ -12,6 +12,11 @@ namespace Bun {
using namespace JSC;
using namespace WebCore;
typedef struct {
/// The result of call to dlopen to load the module
void* dlopenHandle;
} NapiModuleMeta;
class NapiExternal : public JSC::JSDestructibleObject {
using Base = JSC::JSDestructibleObject;

View File

@@ -50,6 +50,12 @@ var panic_mutex = std.Thread.Mutex{};
/// This is used to catch and handle panics triggered by the panic handler.
threadlocal var panic_stage: usize = 0;
threadlocal var inside_native_plugin: bool = false;
export fn CrashHandler__setInsideNativePlugin(value: bool) callconv(.C) void {
inside_native_plugin = value;
}
/// This can be set by various parts of the codebase to indicate a broader
/// action being taken. It is printed when a crash happens, which can help
/// narrow down what the bug is. Example: "Crashed while parsing /path/to/file.js"
@@ -226,6 +232,15 @@ pub fn crashHandler(
writer.writeAll("=" ** 60 ++ "\n") catch std.posix.abort();
printMetadata(writer) catch std.posix.abort();
writer.print(
\\
\\Bun has encountered a crash while running the a native plugin.
\\
\\This indicates either a bug in the native plugin or in Bun.
\\
\\
, .{}) catch std.posix.abort();
} else {
if (Output.enable_ansi_colors) {
writer.writeAll(Output.prettyFmt("<red>", true)) catch std.posix.abort();
@@ -308,7 +323,16 @@ pub fn crashHandler(
} else {
writer.writeAll(Output.prettyFmt(": ", true)) catch std.posix.abort();
}
if (reason == .out_of_memory) {
if (inside_native_plugin) {
writer.print(
\\Bun has encountered a crash while running a native plugin.
\\
\\To send a redacted crash report to Bun's team,
\\please file a GitHub issue using the link below:
\\
\\
, .{}) catch std.posix.abort();
} else if (reason == .out_of_memory) {
writer.writeAll(
\\Bun has ran out of memory.
\\
@@ -737,6 +761,7 @@ fn handleSegfaultPosix(sig: i32, info: *const std.posix.siginfo_t, _: ?*const an
std.posix.SIG.ILL => .{ .illegal_instruction = addr },
std.posix.SIG.BUS => .{ .bus_error = addr },
std.posix.SIG.FPE => .{ .floating_point_error = addr },
std.posix.SIG.KILL => .{ .segmentation_fault = addr },
// we do not register this handler for other signals
else => unreachable,