From 69ebb404e70e784008cf4a789f3fe2da8d573ac3 Mon Sep 17 00:00:00 2001 From: Clement Skau Date: Thu, 6 May 2021 17:59:33 +0000 Subject: [PATCH] [vm/debugger] Fix notify-debugger-.. for yielding. For yielding functions (async, sync*, async*) we need to look for annotations on the outer function instead of the synthetic inner function associated with the handler frame. Fixes https://github.com/dart-lang/sdk/issues/45673 TEST=runtime/observatory{,_2}/tests/service{,_2}/notify_debugger_on_exception_yielding_test.dart Bug: https://github.com/dart-lang/sdk/issues/45673 Change-Id: I8b1718b3614852f6f8db98811177b21fe587fea1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/198408 Reviewed-by: Vyacheslav Egorov Commit-Queue: Clement Skau --- ...y_debugger_on_exception_yielding_test.dart | 64 +++++++++++++++++++ ...y_debugger_on_exception_yielding_test.dart | 64 +++++++++++++++++++ runtime/vm/debugger.cc | 18 +++++- 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart create mode 100644 runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart diff --git a/runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart b/runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart new file mode 100644 index 00000000000..1cab6a7d938 --- /dev/null +++ b/runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2021, 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/dart-lang/sdk/issues/45673 + +import 'test_helper.dart'; +import 'service_test_common.dart'; + +const int LINE_A = 19; +const int LINE_B = 29; +const int LINE_C = 39; + +@pragma('vm:notify-debugger-on-exception') +Future throwFromAsync() async { + try { + throw 'Throw from throwFromAsync'; // LINE_A + } catch (e) { + // Ignore. Internals will notify debugger. + } + return Future.value(); +} + +@pragma('vm:notify-debugger-on-exception') +Stream throwFromAsyncStar() async* { + try { + throw 'Throw from throwFromAsyncStar'; // LINE_B + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 13; +} + +@pragma('vm:notify-debugger-on-exception') +Iterable throwFromSyncStar() sync* { + try { + throw 'Throw from throwFromSyncStar'; // LINE_C + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 7; +} + +testMain() async { + throwFromAsync(); + await for (var e in throwFromAsyncStar()) {/*ignore*/} + for (var e in throwFromSyncStar()) {/*ignore*/} +} + +final tests = [ + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_A), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_B), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_C), +]; + +main([args = const []]) => runIsolateTests(args, tests, + testeeConcurrent: testMain, pause_on_unhandled_exceptions: true); diff --git a/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart b/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart new file mode 100644 index 00000000000..1cab6a7d938 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2021, 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/dart-lang/sdk/issues/45673 + +import 'test_helper.dart'; +import 'service_test_common.dart'; + +const int LINE_A = 19; +const int LINE_B = 29; +const int LINE_C = 39; + +@pragma('vm:notify-debugger-on-exception') +Future throwFromAsync() async { + try { + throw 'Throw from throwFromAsync'; // LINE_A + } catch (e) { + // Ignore. Internals will notify debugger. + } + return Future.value(); +} + +@pragma('vm:notify-debugger-on-exception') +Stream throwFromAsyncStar() async* { + try { + throw 'Throw from throwFromAsyncStar'; // LINE_B + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 13; +} + +@pragma('vm:notify-debugger-on-exception') +Iterable throwFromSyncStar() sync* { + try { + throw 'Throw from throwFromSyncStar'; // LINE_C + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 7; +} + +testMain() async { + throwFromAsync(); + await for (var e in throwFromAsyncStar()) {/*ignore*/} + for (var e in throwFromSyncStar()) {/*ignore*/} +} + +final tests = [ + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_A), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_B), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_C), +]; + +main([args = const []]) => runIsolateTests(args, tests, + testeeConcurrent: testMain, pause_on_unhandled_exceptions: true); diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index dc9d048b6f8..0f18a4756f4 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -2248,11 +2248,27 @@ bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace, // it will be caught once we unwind the stack. return true; } + + auto& handler_function = Function::Handle(handler_frame->function().ptr()); + // If the handler function is an synthetic inner function, we need to look for + // the annotations on the outer function. + if (handler_function.IsAsyncClosure()) { + // async :async_op + handler_function = handler_function.parent_function(); + } else if (handler_frame->function().IsAsyncGenClosure()) { + // async* :async_op + handler_function = handler_function.parent_function(); + } else if (handler_frame->function().IsSyncGenClosure()) { + // sync* :sync_op + :sync_op_gen + handler_function = handler_function.parent_function(); + handler_function = handler_function.parent_function(); + } + // 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(), + handler_function, Symbols::vm_notify_debugger_on_exception())) { return true; }