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

Stack traces from Flutter web app don't deobfuscate function names #1430

Closed
blaugold opened this issue May 9, 2023 · 13 comments · Fixed by getsentry/symbolicator#1195
Closed

Comments

@blaugold
Copy link
Contributor

blaugold commented May 9, 2023

Continuing from here: #897 (comment)

Platform

Flutter Web

Obfuscation

Disabled

Debug Info

Disabled

Doctor

Flutter 3.7.12 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 4d9e56e694 (3 weeks ago) • 2023-04-17 21:47:46 -0400
Engine • revision 1a65d409c7
Tools • Dart 2.19.6 • DevTools 2.20.1

Version

7.5.1

Steps to Reproduce

  1. flutter create sentry_flutter_web_stacktraces_repro

  2. cd sentry_flutter_web_stacktraces_repro

  3. flutter pub add sentry_flutter

  4. flutter pub add dev:sentry_dart_plugin

  5. Configure sentry_dart_plugin:

    sentry:
      upload_debug_symbols: false
      upload_source_maps: true
      org: ...
      project: ...
      auth_token: ...
      commits: false
    
  6. Replace lib/main.dart with the following:

    void main() {
      SentryFlutter.init(
        (options) {
          options.dsn = '...';
        },
        appRunner: () => runApp(const MainApp()),
      );
    }
    
    class MainApp extends StatelessWidget {
      const MainApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Center(
              child: ElevatedButton(
                onPressed: () {
                  throw Exception('Test exception');
                },
                child: const Text('Throw exception'),
              ),
            ),
          ),
        );
      }
    }
  7. Configure DSN in main.dart

  8. flutter build web --source-maps

  9. dart run sentry_dart_plugin

  10. cd build/web

  11. Start a web server to serve the files, e.g. caddy file-server

  12. Open the web app in a browser, wherever it is served, e.g. http://localhost.

  13. Click on the "Throw exception" button

Expected Result

The stack trace for error events sent to sentry should contain deobfuscated funtion names.

Actual Result

While the correct line and column numbers have been recovered, function names are not deobufscated:

minified:Gi: Exception: Test exception
  at A.c(org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart:1128:37)
  at A.Tt.prototype.$0(../../../lib/main.dart:24:15)
  at A.v6.prototype.Em(../../../../../../fvm/versions/stable/packages/flutter/lib/src/widgets/framework.dart:921:26)
  at A.akv(org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart:2105:9)
  at A.cN.prototype.EC(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart:253:16)
  at A.cN.prototype.fG(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart:239:6)
  at A.fA.prototype.Eo(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart:627:11)
  at A.xl.prototype.zd(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart:306:5)
  at A.xl.prototype.El(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart:239:7)
  at A.nF.prototype.i4(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart:615:9)

Link to an example event: https://gabriel-59.sentry.io/issues/4170223115/events/234cd5801ab546e3a8812316462ebda4/?project=4505149812834304

Are you willing to submit a PR?

None

@marandaneto
Copy link
Contributor

Thanks @blaugold , will test it out.

@marandaneto
Copy link
Contributor

marandaneto commented May 10, 2023

Image:

Screenshot 2023-05-10 at 10 40 13

JSON event:

event.txt

Uploaded bundle:

Screenshot 2023-05-10 at 10 58 35

Bundles are actually here: https://sentry-sdks.sentry.io/settings/projects/sentry-flutter/source-maps/artifact-bundles/13d8dc56-b866-5275-8cb8-618012f1b6d5/

Either the generated source maps changed something or there's a regression in our system, I will check this internally.

@marandaneto
Copy link
Contributor

The processing team is looking at this.

@marandaneto
Copy link
Contributor

@loewenheim should we close this as done? re getsentry/symbolic#786

@loewenheim
Copy link

Actually I meant to post the corrected stacktrace here for feedback, thanks for reminding me :)

With the linked symbolic change, we now get this stacktrace for the provided example program:

Frame #0                                                                                                                                                          
     Function:  wrapException 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 
  Line/Column:  1128:37 
                 
Frame #1                                                                                                                                                          
       Module:  lib/main 
     Function:  MainApp.build.<anonymous function> 
         File:  ../../../lib/main.dart 
  Line/Column:  23:15 
                 
Frame #2                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/widgets/framework 
     Function:  _InkResponseState.handleTap 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/widgets/framework.dart 
  Line/Column:  921:26 
                 
Frame #3                                                                                                                                                          
     Function:  A.aku 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 
  Line/Column:  2105:9 
                 
Frame #4                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/recognizer 
     Function:  GestureRecognizer.invokeCallback 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/recognizer.dart 
  Line/Column:  253:16 
                 
Frame #5                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/recognizer 
     Function:  A.cN.prototype.fG 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/recognizer.dart 
  Line/Column:  239:6 
                 
Frame #6                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/tap 
     Function:  TapGestureRecognizer.handleTapUp 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/tap.dart 
  Line/Column:  627:11 
                 
Frame #7                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/tap 
     Function:  BaseTapGestureRecognizer._checkUp 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/tap.dart 
  Line/Column:  306:5 
                 
Frame #8                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/tap 
     Function:  BaseTapGestureRecognizer.handlePrimaryPointer 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/tap.dart 
  Line/Column:  239:7 
                 
Frame #9                                                                                                                                                          
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/recognizer 
     Function:  PrimaryPointerGestureRecognizer.handleEvent 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/recognizer.dart 
  Line/Column:  615:9 
                 
Frame #10                                                                                                                                                         
     Function:  A.aku 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 
  Line/Column:  2115:9 
                 
Frame #11                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router 
     Function:  PointerRouter._dispatch 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router.dart 
  Line/Column:  98:7 
                 
Frame #12                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router 
     Function:  PointerRouter._dispatchEventToRoutes.<anonymous function> 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router.dart 
  Line/Column:  143:9 
                 
Frame #13                                                                                                                                                         
     Function:  JsLinkedHashMap.keys.dn.prototype.P 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/linked_hash_map.dart 
  Line/Column:  190:7 
                 
Frame #14                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router 
     Function:  PointerRouter._dispatchEventToRoutes 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router.dart 
  Line/Column:  141:5 
                 
Frame #15                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router 
     Function:  PointerRouter.route 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/pointer_router.dart 
  Line/Column:  127:7 
                 
Frame #16                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding 
     Function:  GestureBinding.handleEvent 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding.dart 
  Line/Column:  460:5 
                 
Frame #17                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding 
     Function:  GestureBinding.dispatchEvent 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding.dart 
  Line/Column:  440:15 
                 
Frame #18                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/rendering/binding 
     Function:  _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding&ServicesBinding&PaintingBinding&SemanticsBinding&RendererBinding.dispatchEvent 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/rendering/binding.dart 
  Line/Column:  336:11 
                 
Frame #19                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding 
     Function:  GestureBinding._handlePointerEventImmediately 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding.dart 
  Line/Column:  395:7 
                 
Frame #20                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding 
     Function:  GestureBinding.handlePointerEvent 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding.dart 
  Line/Column:  357:5 
                 
Frame #21                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding 
     Function:  GestureBinding._flushPointerEventQueue 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding.dart 
  Line/Column:  314:7 
                 
Frame #22                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding 
     Function:  GestureBinding._handlePointerDataPacket 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter/lib/src/gestures/binding.dart 
  Line/Column:  295:7 
                 
Frame #23                                                                                                                                                         
     Function:  A.aku 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 
  Line/Column:  2115:9 
                 
Frame #24                                                                                                                                                         
     Function:  A.a6K 
         File:  org-dartlang-sdk:///lib/async/zone.dart 
  Line/Column:  1414:12 
                 
Frame #25                                                                                                                                                         
     Function:  _rootRunUnary 
         File:  org-dartlang-sdk:///lib/async/zone.dart 
  Line/Column:  1404:3 
                 
Frame #26                                                                                                                                                         
     Function:  _CustomZone.runUnary 
         File:  org-dartlang-sdk:///lib/async/zone.dart 
  Line/Column:  1306:34 
                 
Frame #27                                                                                                                                                         
     Function:  _CustomZone.runUnaryGuarded 
         File:  org-dartlang-sdk:///lib/async/zone.dart 
  Line/Column:  1216:7 
                 
Frame #28                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/platform_dispatcher 
     Function:  invoke1 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 
  Line/Column:  1185:9 
                 
Frame #29                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/platform_dispatcher 
     Function:  PointerBinding._onPointerData 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/platform_dispatcher.dart 
  Line/Column:  243:5 
                 
Frame #30                                                                                                                                                         
     Function:  A.aku 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 
  Line/Column:  2115:9 
                 
Frame #31                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding 
     Function:  _PointerAdapter.setup.<anonymous function> 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 
  Line/Column:  689:20 
                 
Frame #32                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding 
     Function:  _PointerAdapter._addPointerEventListener.<anonymous function> 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 
  Line/Column:  620:7 
                 
Frame #33                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding 
     Function:  _BaseAdapter.addEventListener.loggedHandler 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 
  Line/Column:  303:9 
                 
Frame #34                                                                                                                                                         
       Module:  usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding 
     Function:  _Listener.register.<anonymous function>.a3C.prototype.$1 
         File:  /usr/local/Caskroom/flutter/3.7.12/flutter/bin/cache/flutter_web_sdk/lib/_engine/engine/pointer_binding.dart 
  Line/Column:  188:73 
                 
Frame #35                                                                                                                                                         
     Function:  Primitives.applyFunction 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 
  Line/Column:  851:11 
                 
Frame #36                                                                                                                                                         
     Function:  Function.apply 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/core_patch.dart 
  Line/Column:  84:23 
                 
Frame #37                                                                                                                                                         
     Function:  _callDartFunctionFast 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_patch.dart 
  Line/Column:  545:3 
                 
Frame #38                                                                                                                                                         
     Function:  _convertDartFunctionFast 
         File:  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_patch.dart 
  Line/Column:  523:1 
                 
Errors during symbolication                                                                                                                                                       
        Path:  http://localhost/main.dart.js 
       Error:  Missing source contents for source file org-dartlang-sdk:///lib/_internal/js_runtime/lib/core_patch.dart and sourcemap file http://localhost/main.dart.js.map 
                
        Path:  http://localhost/main.dart.js 
       Error:  Missing source contents for source file org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart and sourcemap file http://localhost/main.dart.js.map 
                
        Path:  http://localhost/main.dart.js 
       Error:  Missing source contents for source file org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_patch.dart and sourcemap file http://localhost/main.dart.js.map 
                
        Path:  http://localhost/main.dart.js 
       Error:  Missing source contents for source file org-dartlang-sdk:///lib/_internal/js_runtime/lib/linked_hash_map.dart and sourcemap file http://localhost/main.dart.js.map 
                
        Path:  http://localhost/main.dart.js 
       Error:  Missing source contents for source file org-dartlang-sdk:///lib/async/zone.dart and sourcemap file http://localhost/main.dart.js.map 

As you can see, most function names are now resolved, but we don't have source contents for all files.

@marandaneto
Copy link
Contributor

@loewenheim is this deployed and live already? If so, @blaugold mind testing once more? Thanks!

@marandaneto marandaneto moved this from Needs Investigation to In Progress in Mobile & Cross Platform SDK May 16, 2023
@marandaneto marandaneto moved this from In Progress to Needs More Information in Mobile & Cross Platform SDK May 16, 2023
@loewenheim
Copy link

It's not live yet, I'll get it ready ASAP

@loewenheim
Copy link

Closed this prematurely. The fix is now live.

@blaugold
Copy link
Contributor Author

Thanks for the swift fix! For synchronous code, I now get the correct function names. 🎉 Unfortunately, it does not solve the issue for async code. 😅

If you change the function that throws the exception to the code below, the throwing frame is reported like this: at $async$$0(../../../lib/main.dart:25:15).

onPressed: () async {
  await Future(() {});
  throw Exception('Test exception');
},

Here is an example of how the source_map_stack_trace package can be used to unminify even async code:

import 'dart:convert';
import 'dart:io';

import 'package:source_map_stack_trace/source_map_stack_trace.dart';
import 'package:source_maps/source_maps.dart';

const stackTraceString = r'''
minified:GR: Exception: Test exception
    at Object.b (main.dart.js:4293:3)
    at <fn> (main.dart.js:56371:16)
    at a45.a (main.dart.js:5603:62)
    at a45.$2 (main.dart.js:31015:14)
    at a2X.$1 (main.dart.js:31009:21)
    at Object.a3Z (main.dart.js:5858:19)
    at Ed.<fn> (main.dart.js:57768:68)
    at Gh.jD (main.dart.js:31819:12)
    at a0i.$0 (main.dart.js:31350:11)
    at Object.p3 (main.dart.js:5729:40)
''';

void main() {
  final mapping = SingleMapping.fromJson(jsonDecode(File('build/web/main.dart.js.map').readAsStringSync()));
  final stackTrace = StackTrace.fromString(stackTraceString);
  final mappedStackTrace = mapStackTrace(mapping, stackTrace, minified: true);
  print(mappedStackTrace.toString());
}

And this is its output:

org-dartlang-sdk:///dart-sdk/lib/_internal/js_runtime/lib/js_helper.dart 1195:37   wrapException
../../../lib/main.dart 25:15                                                       MainApp.build.<anonymous function>
org-dartlang-sdk:///dart-sdk/lib/_internal/js_runtime/lib/async_patch.dart 306:19  _wrapJsFunctionForAsync
org-dartlang-sdk:///dart-sdk/lib/_internal/js_runtime/lib/async_patch.dart 331:23  _wrapJsFunctionForAsync.<anonymous function>
org-dartlang-sdk:///dart-sdk/lib/_internal/js_runtime/lib/async_patch.dart 282:19  _awaitOnObject.<anonymous function>
org-dartlang-sdk:///dart-sdk/lib/async/zone.dart 1407:46                           _rootRunUnary
org-dartlang-sdk:///dart-sdk/lib/async/zone.dart 1405:3                            _rootRunUnary[function-entry$5]
org-dartlang-sdk:///dart-sdk/lib/async/zone.dart 1307:34                           _CustomZone.runUnary
org-dartlang-sdk:///dart-sdk/lib/async/future_impl.dart 112:29                     _FutureListener.handleValue
org-dartlang-sdk:///dart-sdk/lib/async/future_impl.dart 813:13                     _Future._propagateToListeners

@blaugold
Copy link
Contributor Author

Looking at how source_map_stack_trace and source_maps work, there does not seem to be anything special, as far as I can tell with my limited knowledge of source maps.

For reference, here is how source maps are parsed and here how a location is looked up.

@blaugold
Copy link
Contributor Author

Running the source map from the example through symbolic I can see that a useful function name is available in the name field of a source location.

#[test]
fn dart_debug() {
    let minified = std::fs::read_to_string(fixture("sourcemapcache/dart/main.dart.js")).unwrap();
    let map = std::fs::read_to_string(fixture("sourcemapcache/dart/main.dart.js.map")).unwrap();

    let writer = SourceMapCacheWriter::new(&minified, &map).unwrap();

    let mut buf = vec![];
    writer.serialize(&mut buf).unwrap();

    let cache = SourceMapCache::parse(&buf).unwrap();

    let sl = cache.lookup(SourcePosition::new(56371, 16)).unwrap();
    println!("{:?}", sl);
}
   Compiling symbolic-sourcemapcache v12.1.5 (/Users/terwesten/dev/symbolic/symbolic-sourcemapcache)
    Finished test [unoptimized + debuginfo] target(s) in 0.40s
     Running tests/integration.rs (target/debug/deps/integration-9650d2080c26a134)

running 1 test
SourceLocation { file: Some(File { name: Some("../../../lib/main.dart"), source: None, line_offsets: [LineOffset(0)] }), line: 25, column: 13, name: Some("MainApp.build.<anonymous function>"), scope: NamedScope("$async$$0") }
test dart_debug ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 1.91s

Would it be a problem to use the mapped name for function names of Dart stack traces when available instead of scope?

@marandaneto
Copy link
Contributor

@loewenheim can you take a look at this use case as well?

@marandaneto
Copy link
Contributor

Thanks @blaugold for debugging this with us.
I've raised the issue getsentry/symbolic#791
Let's track the linked issue there, closing this one since the original bug is resolved.

@github-project-automation github-project-automation bot moved this from Needs Investigation to Done in Mobile & Cross Platform SDK May 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants