Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setExceptionPauseMode causes a breakpoint to be triggered with async exceptions that are caught #106

Open
jiahaog opened this issue Feb 19, 2021 · 0 comments

Comments

@jiahaog
Copy link

jiahaog commented Feb 19, 2021

The VM Service Protocol provides the setExceptionPauseMode API. This API is used by debugging tools like the VSCode Dart Plugin or the IntelliJ Dart plugin when debugging, where the following RPC is sent over from the plugin:

==> {"id":"8","jsonrpc":"2.0","method":"setExceptionPauseMode","params":{"isolateId":"isolates/2741664618071259","mode":"Unhandled"}}

When this happens, uncaught exceptions at runtime will surface as breakpoints:

<== {"jsonrpc":"2.0","method":"streamNotify","params":{"streamId":"Debug","event":{"type":"Event","kind":"PauseException","isolate": <truncated>

This has the same effect as the set break-on-exception Unhandled command in the debugger, and the --pause-isolates-on-unhandled-exceptions flag to dart.

Problem

With the following code, exceptions that are caught will surface in the debugger. Consider this:

import 'package:test/test.dart';

void main() {
  test('foo', () async {
    try {
      // Inlining [throwFooException] here also makes the problem go away.
      await throwFooException();
    } on FooException {
      print('caught fooexception');
    }
  });
}

class FooException implements Exception {}

Future<void> throwFooException() async {
  // Commenting this out makes the problem go away.
  // Wait for the next event.
  await Future(() {});
  throw FooException();
}

Running it through the package:test executable, or with dart directly will cause a breakpoint to be added where that exception is thrown, with an awkward backtrace coming from StackZoneSpecification.

With the Test Executable:

  1. pub run test --pause-after-load
  2. Go to the observatory debugger, and enter set break-on-exception Unhandled followed by continue
  3. Observe that a breakpoint has been added where we throw FooException()

Running it with pub run test --pause-after-load --no-chain-stack-traces and repeating the process does not cause the same issue though. The issue is with Chain.capture used to wrap user defined tests here in package:test_api.

With dart

  1. dart run --pause-isolates-on-start --pause-isolates-on-unhandled-exceptions --enable-vm-service bin/dart_debugger_uncaught_exception_repro.dart
  2. Go to the observatory debugger, and enter continue
  3. Observe that a breakpoint has been added where we throw FooException()

Context

This particular pattern is used by, but is not limited to the package:integration_test binding under the hood. A MissingPluginException may be thrown and caught, but it will surface during IDE debugging which is confusing to users.

(cc @DanTup)

Smaller Repro

import 'package:stack_trace/stack_trace.dart';

class FooException implements Exception {}

Future<void> throwFooException() async {
  // Commenting it out makes the problem go away.
  // Wait for the next event.
  await Future(() {});
  throw FooException();
}

void main() {
  Chain.capture(() async {
    try {
      // Inlining [throwFooException] here also makes the problem go away.
      await throwFooException();
    } on FooException {
      print('caught fooexception');
    }
  });
}

Where the same command of dart run --pause-isolates-on-start --pause-isolates-on-unhandled-exceptions --enable-vm-service bin/dart_debugger_uncaught_exception_repro.dart is able to reproduce it.

What seems to help is when this particular line is commented out. I couldn't quite follow what is going on with the recursion though, so I'm not sure what is the exact cause of this.

Versions

Dart SDK version: 2.13.0-38.0.dev (dev) (Mon Feb 15 10:21:50 2021 -0800) on "macos_x64"

stack_trace: 1.10.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant