[vm] Avoid allocations when reporting unhandled exceptions.

Bug: https://github.com/dart-lang/sdk/issues/43642
Bug: b/169880355
Change-Id: I260b9d47f2b65d3cb4a48b966557d139978947e2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/165740
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Ryan Macnak
2020-10-05 20:42:02 +00:00
committed by commit-bot@chromium.org
parent f69dc37d5c
commit e078d4f00a
7 changed files with 121 additions and 36 deletions
+41 -20
View File
@@ -133,6 +133,12 @@ static std::unique_ptr<Message> SerializeMessage(Dart_Port dest_port,
}
}
static std::unique_ptr<Message> SerializeMessage(Dart_Port dest_port,
Dart_CObject* obj) {
ApiMessageWriter writer;
return writer.WriteCMessage(obj, dest_port, Message::kNormalPriority);
}
static InstancePtr DeserializeMessage(Thread* thread, Message* message) {
if (message == NULL) {
return Instance::null();
@@ -1447,34 +1453,38 @@ MessageHandler::MessageStatus IsolateMessageHandler::ProcessUnhandledException(
NoReloadScope no_reload_scope(T->isolate(), T);
// Generate the error and stacktrace strings for the error message.
String& exc_str = String::Handle(T->zone());
String& stacktrace_str = String::Handle(T->zone());
const char* exception_cstr = nullptr;
const char* stacktrace_cstr = nullptr;
if (result.IsUnhandledException()) {
Zone* zone = T->zone();
const UnhandledException& uhe = UnhandledException::Cast(result);
const Instance& exception = Instance::Handle(zone, uhe.exception());
Object& tmp = Object::Handle(zone);
tmp = DartLibraryCalls::ToString(exception);
if (!tmp.IsString()) {
tmp = String::New(exception.ToCString());
if (exception.raw() == I->object_store()->out_of_memory()) {
exception_cstr = "Out of Memory"; // Cf. OutOfMemoryError.toString().
} else if (exception.raw() == I->object_store()->stack_overflow()) {
exception_cstr = "Stack Overflow"; // Cf. StackOverflowError.toString().
} else {
const Object& exception_str =
Object::Handle(zone, DartLibraryCalls::ToString(exception));
if (!exception_str.IsString()) {
exception_cstr = exception.ToCString();
} else {
exception_cstr = exception_str.ToCString();
}
}
exc_str ^= tmp.raw();
const Instance& stacktrace = Instance::Handle(zone, uhe.stacktrace());
tmp = DartLibraryCalls::ToString(stacktrace);
if (!tmp.IsString()) {
tmp = String::New(stacktrace.ToCString());
}
stacktrace_str ^= tmp.raw();
stacktrace_cstr = stacktrace.ToCString();
} else {
exc_str = String::New(result.ToErrorCString());
exception_cstr = result.ToErrorCString();
}
if (result.IsUnwindError()) {
// When unwinding we don't notify error listeners and we ignore
// whether errors are fatal for the current isolate.
return StoreError(T, result);
} else {
bool has_listener = I->NotifyErrorListeners(exc_str, stacktrace_str);
bool has_listener =
I->NotifyErrorListeners(exception_cstr, stacktrace_cstr);
if (I->ErrorsFatal()) {
if (has_listener) {
T->ClearStickyError();
@@ -2276,21 +2286,32 @@ void Isolate::RemoveErrorListener(const SendPort& listener) {
}
}
bool Isolate::NotifyErrorListeners(const String& msg,
const String& stacktrace) {
bool Isolate::NotifyErrorListeners(const char* message,
const char* stacktrace) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
current_zone(), isolate_object_store()->error_listeners());
if (listeners.IsNull()) return false;
const Array& arr = Array::Handle(current_zone(), Array::New(2));
arr.SetAt(0, msg);
arr.SetAt(1, stacktrace);
Dart_CObject arr;
Dart_CObject* arr_values[2];
arr.type = Dart_CObject_kArray;
arr.value.as_array.length = 2;
arr.value.as_array.values = arr_values;
Dart_CObject msg;
msg.type = Dart_CObject_kString;
msg.value.as_string = const_cast<char*>(message);
arr_values[0] = &msg;
Dart_CObject stack;
stack.type = Dart_CObject_kString;
stack.value.as_string = const_cast<char*>(stacktrace);
arr_values[1] = &stack;
SendPort& listener = SendPort::Handle(current_zone());
for (intptr_t i = 0; i < listeners.Length(); i++) {
listener ^= listeners.At(i);
if (!listener.IsNull()) {
Dart_Port port_id = listener.Id();
PortMap::PostMessage(SerializeMessage(port_id, arr));
PortMap::PostMessage(SerializeMessage(port_id, &arr));
}
}
return listeners.Length() > 0;
+1 -1
View File
@@ -1004,7 +1004,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
void AddErrorListener(const SendPort& listener);
void RemoveErrorListener(const SendPort& listener);
bool NotifyErrorListeners(const String& msg, const String& stacktrace);
bool NotifyErrorListeners(const char* msg, const char* stacktrace);
bool ErrorsFatal() const { return ErrorsFatalBit::decode(isolate_flags_); }
void SetErrorsFatal(bool val) {
+5 -14
View File
@@ -55,17 +55,6 @@ void IsolateObjectStore::PrintToJSONObject(JSONObject* jsobj) {
}
#endif // !PRODUCT
static UnhandledExceptionPtr CreatePreallocatedUnandledException(
Zone* zone,
const Object& out_of_memory) {
// Allocate pre-allocated unhandled exception object initialized with the
// pre-allocated OutOfMemoryError.
const UnhandledException& unhandled_exception =
UnhandledException::Handle(UnhandledException::New(
Instance::Cast(out_of_memory), StackTrace::Handle(zone)));
return unhandled_exception.raw();
}
static StackTracePtr CreatePreallocatedStackTrace(Zone* zone) {
const Array& code_array = Array::Handle(
zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
@@ -93,10 +82,12 @@ ErrorPtr IsolateObjectStore::PreallocateObjects() {
// pre-allocated OutOfMemoryError.
const Object& out_of_memory =
Object::Handle(zone, object_store_->out_of_memory());
const StackTrace& preallocated_stack_trace =
StackTrace::Handle(zone, CreatePreallocatedStackTrace(zone));
set_preallocated_stack_trace(preallocated_stack_trace);
set_preallocated_unhandled_exception(UnhandledException::Handle(
CreatePreallocatedUnandledException(zone, out_of_memory)));
set_preallocated_stack_trace(
StackTrace::Handle(CreatePreallocatedStackTrace(zone)));
zone, UnhandledException::New(Instance::Cast(out_of_memory),
preallocated_stack_trace)));
return Error::null();
}
@@ -0,0 +1,36 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
import "package:expect/expect.dart";
main(args) async {
if (args.contains("--child")) {
var leak = [];
while (true) {
leak = [leak];
}
} else {
var exec = Platform.resolvedExecutable;
var args = <String>[];
args.addAll(Platform.executableArguments);
args.add("--old_gen_heap_size=20");
args.add(Platform.script.toFilePath());
args.add("--child");
// Should report an unhandled out of memory exception without crashing.
print("+ $exec " + args.join(" "));
var result = await Process.run(exec, args);
print("exit: ${result.exitCode}");
print("stdout:");
print(result.stdout);
print("stderr:");
print(result.stderr);
Expect.equals(255, result.exitCode, "Unhandled exception, not crash");
Expect.isTrue(result.stderr.contains("Out of Memory"));
}
}
+1 -1
View File
@@ -4,9 +4,9 @@
# Sections in this file should contain "$compiler == dartk" or
# "$compiler == dartkp".
fragmentation_test: Pass, Slow # GC heavy
fragmentation_typed_data_test: Pass, Slow # GC heavy
io/process_sync_test: Pass, Slow # Spawns synchronously subprocesses in sequence.
out_of_memory_unhandled_exception_test: Pass, Slow
[ $compiler == dartkb ]
no_lazy_dispatchers_test: SkipByDesign # KBC interpreter doesn't support --no_lazy_dispatchers
@@ -0,0 +1,36 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
import "package:expect/expect.dart";
main(args) async {
if (args.contains("--child")) {
var leak = [];
while (true) {
leak = [leak];
}
} else {
var exec = Platform.resolvedExecutable;
var args = <String>[];
args.addAll(Platform.executableArguments);
args.add("--old_gen_heap_size=20");
args.add(Platform.script.toFilePath());
args.add("--child");
// Should report an unhandled out of memory exception without crashing.
print("+ $exec " + args.join(" "));
var result = await Process.run(exec, args);
print("exit: ${result.exitCode}");
print("stdout:");
print(result.stdout);
print("stderr:");
print(result.stderr);
Expect.equals(255, result.exitCode, "Unhandled exception, not crash");
Expect.isTrue(result.stderr.contains("Out of Memory"));
}
}
@@ -7,6 +7,7 @@
fragmentation_test: Pass, Slow # GC heavy
fragmentation_typed_data_test: Pass, Slow # GC heavy
io/process_sync_test: Pass, Slow # Spawns synchronously subprocesses in sequence.
out_of_memory_unhandled_exception_test: Pass, Slow
[ $compiler == dartkb ]
no_lazy_dispatchers_test: SkipByDesign # KBC interpreter doesn't support --no_lazy_dispatchers