[VM] Adds @pragma('vm:notify-debugger-on-exception')

TEST=runtime/tests/vm/dart{,_2}/notify_debugger_on_exception_test.dart

Bug: https://github.com/flutter/flutter/issues/17007
Change-Id: I988d2385c3d0fc42c4eb769312278261720bb68d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176663
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Clement Skau 2020-12-22 14:42:01 +00:00 committed by commit-bot@chromium.org
parent 7e3b19e111
commit 9b45fcbd1f
7 changed files with 94 additions and 5 deletions

View file

@ -7,8 +7,12 @@ These pragmas are part of the VM's API and are safe for use in external code.
| Pragma | Meaning |
| --- | --- |
| `vm:entry-point` | [Defining entry-points into Dart code for an embedder or native methods](compiler/aot/entry_point_pragma.md) |
| `vm:never-inline` | [Never inline a function or method](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-never-be-inlined) |
| `vm:prefer-inline` | [Inline a function or method when possible](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-be-inlined) |
| `vm:never-inline` | [Never inline a function or method](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-never-be-inlined) |
| `vm:prefer-inline` | [Inline a function or method when possible](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-be-inlined) |
| `vm:notify-debugger-on-exception` | Marks a function that catches exceptions,
making the VM treat any caught exception as if they were uncaught.
This can be used to notify an attached debugger during debugging, without
pausing the app during regular execution. |
## Pragmas for internal use

View file

@ -0,0 +1,37 @@
// 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.
//
// VMOptions=--verbose_debug
// See: https://github.com/flutter/flutter/issues/17007
import '../../../observatory/tests/service/service_test_common.dart';
import '../../../observatory/tests/service/test_helper.dart';
const int LINE_A = 24;
@pragma('vm:notify-debugger-on-exception')
void catchNotifyDebugger(Function() code) {
try {
code();
} catch (e) {
// Ignore. Internals will notify debugger.
}
}
syncThrow() {
throw 'Hello from syncThrow!'; // Line A.
}
testMain() {
catchNotifyDebugger(syncThrow);
}
final tests = <IsolateTest>[
hasStoppedWithUnhandledException,
stoppedAtLine(LINE_A),
];
main([args = const <String>[]]) => runIsolateTests(args, tests,
testeeConcurrent: testMain, pause_on_unhandled_exceptions: true);

View file

@ -0,0 +1,37 @@
// 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.
//
// VMOptions=--verbose_debug
// See: https://github.com/flutter/flutter/issues/17007
import '../../../observatory/tests/service/service_test_common.dart';
import '../../../observatory/tests/service/test_helper.dart';
const int LINE_A = 24;
@pragma('vm:notify-debugger-on-exception')
void catchNotifyDebugger(Function() code) {
try {
code();
} catch (e) {
// Ignore. Internals will notify debugger.
}
}
syncThrow() {
throw 'Hello from syncThrow!'; // Line A.
}
testMain() {
catchNotifyDebugger(syncThrow);
}
final tests = <IsolateTest>[
hasStoppedWithUnhandledException,
stoppedAtLine(LINE_A),
];
main([args = const <String>[]]) => runIsolateTests(args, tests,
testeeConcurrent: testMain, pause_on_unhandled_exceptions: true);

View file

@ -2173,7 +2173,7 @@ bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace,
return false;
}
ActivationFrame* handler_frame = stack_trace->GetHandlerFrame(exception);
if (handler_frame == NULL) {
if (handler_frame == nullptr) {
// Did not find an exception handler that catches this exception.
// Note that this check is not precise, since we can't check
// uninstantiated types, i.e. types containing type parameters.
@ -2181,6 +2181,14 @@ bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace,
// it will be caught once we unwind the stack.
return true;
}
// If handler_frame's function is annotated with
// @pragma('vm:notify-debugger-on-exception'), we specifically want to notify
// the debugger of this otherwise ignored exception.
if (Library::FindPragma(Thread::Current(), /*only_core=*/false,
handler_frame->function(),
Symbols::vm_notify_debugger_on_exception())) {
return true;
}
return false;
}

View file

@ -3638,7 +3638,9 @@ bool Library::FindPragma(Thread* T,
pragma_name.raw()) {
continue;
}
*options = Instance::Cast(pragma).GetField(pragma_options_field);
if (options != nullptr) {
*options = Instance::Cast(pragma).GetField(pragma_options_field);
}
return true;
}

View file

@ -4773,7 +4773,7 @@ class Library : public Object {
bool only_core,
const Object& object,
const String& pragma_name,
Object* options);
Object* options = nullptr);
ClassPtr toplevel_class() const { return raw_ptr()->toplevel_class(); }
void set_toplevel_class(const Class& value) const;

View file

@ -494,6 +494,7 @@ class ObjectPointerVisitor;
V(vm_inferred_type_metadata, "vm.inferred-type.metadata") \
V(vm_never_inline, "vm:never-inline") \
V(vm_non_nullable_result_type, "vm:non-nullable-result-type") \
V(vm_notify_debugger_on_exception, "vm:notify-debugger-on-exception") \
V(vm_recognized, "vm:recognized") \
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \
V(vm_procedure_attributes_metadata, "vm.procedure-attributes.metadata") \