// Copyright (c) 2018, 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 'dart:math' show max; import 'package:stack_trace/stack_trace.dart'; import 'package:dart2js_tools/src/trace.dart'; import 'package:dart2js_tools/src/name_decoder.dart'; import 'package:dart2js_tools/src/util.dart'; import 'package:dart2js_tools/src/trace_decoder.dart'; /// Script that deobuscates a stack-trace given in a text file. /// /// To run this script you need 3 or more files: /// /// * A stacktrace file /// * The deployed .js file /// * The corresponding .map file /// /// There might be more than one .js/.map file if your app is divided in /// deferred chunks. /// /// The stack trace file contains a copy/paste of a JavaScript stack trace, of /// this form: /// /// at aB.a20 (main.dart.js:71969:32) /// at aNk.goV (main.dart.js:72040:52) /// at aNk.gfK (main.dart.js:72038:27) /// at FE.gtn (main.dart.js:72640:24) /// at aBZ.ghN (main.dart.js:72642:24) /// at inheritance (main.dart.js:105334:0) /// at FE (main.dart.js:5037:18) /// /// If you download the stacktrace from a production service, you can keep the /// full URL (including http://....) and this script will simply try to match /// the name of the file at the end with a file in the current working /// directory. /// /// The .js file must contain a `//# sourceMappingURL=` line at the end, which /// tells this script how to determine the name of the source-map file. main(List args) { if (args.length != 1) { print('usage: deobfuscate.dart '); exit(1); } var sb = new StringBuffer(); try { String obfuscatedTrace = new File(args[0]).readAsStringSync(); String error = extractErrorMessage(obfuscatedTrace); var provider = new CachingFileProvider(logger: Logger()); StackDeobfuscationResult result = deobfuscateStack(obfuscatedTrace, provider); Frame firstFrame = result.original.frames.first; String translatedError = translate(error, provider.mappingFor(firstFrame.uri)); if (translatedError == null) translatedError = ''; printPadded(translatedError, error, sb); int longest = result.deobfuscated.frames.fold(0, (m, f) => max(f.member.length, m)); for (var originalFrame in result.original.frames) { var deobfuscatedFrames = result.frameMap[originalFrame]; if (deobfuscatedFrames == null) { printPadded('no mapping', '${originalFrame.location}', sb); } else { for (var frame in deobfuscatedFrames) { printPadded('${frame.member.padRight(longest)} ${frame.location}', '${originalFrame.location}', sb); } } } } finally { print('$sb'); } } final green = stdout.hasTerminal ? '\x1b[32m' : ''; final none = stdout.hasTerminal ? '\x1b[0m' : ''; printPadded(String mapping, String original, sb) { var len = mapping.length; var s = mapping.indexOf('\n'); if (s >= 0) len -= s + 1; var pad = ' ' * (50 - len); sb.writeln('$green$mapping$none$pad ... $original'); }