blob: d6ba8370903c17c6525b605be0528158b435e9f9 [file] [log] [blame]
// 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<String> args) {
if (args.length != 1) {
print('usage: deobfuscate.dart <stack-trace-file>');
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 = '<no error message found>';
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');
}