Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 library kernel.treeshaker.stacktracer; | |
| 5 | |
| 6 import 'package:kernel/kernel.dart'; | |
| 7 import 'package:kernel/transformations/treeshaker.dart'; | |
| 8 import 'dart:io'; | |
| 9 import 'package:ansicolor/ansicolor.dart'; | |
|
asgerf
2016/11/23 13:31:56
This depends on the ansicolor package which is not
| |
| 10 | |
| 11 String usage = ''' | |
| 12 USAGE: treeshaker_stacktracer FILE.dill STACKTRACE.txt | |
| 13 | |
| 14 Given a program and a stack trace from it, highlight the stack trace entries | |
| 15 that the tree shaker marked as unreachable. These are indicative of bugs | |
| 16 in the tree shaker since their presence in the stack trace proves them reachable . | |
| 17 | |
| 18 Example usage: | |
| 19 ${comment("# Not shown: insert a 'throw' in testcase.dart")} | |
| 20 dart testcase.dart > error.txt ${comment("# Get the stack trace")} | |
| 21 dartk testcase.dart -l -otestcase.dill ${comment("# Compile binary")} | |
| 22 dart test/treeshaker_stacktracer.dart testcase.dill error.txt ${comment("# Ana lyze")} | |
| 23 '''; | |
| 24 | |
| 25 String legend = ''' | |
| 26 Legend: | |
| 27 ${reachable('reachable member')} or ${reachable('instantiated class')} | |
| 28 ${unreachable('unreachable member')} or ${unreachable('uninstantiated class' )} | |
| 29 ${unknown('name unrecognized')} | |
| 30 '''; | |
| 31 | |
| 32 // TODO: Also support the stack trace format from stack_trace. | |
| 33 RegExp stackTraceEntry = new RegExp(r'(#[0-9]+\s+)([a-zA-Z0-9_$.&<> ]+) \((.*)\) '); | |
| 34 | |
| 35 AnsiPen reachable = new AnsiPen()..green(); | |
| 36 AnsiPen unreachable = new AnsiPen()..red(); | |
| 37 AnsiPen unknown = new AnsiPen()..gray(level: 0.5); | |
| 38 AnsiPen filecolor = new AnsiPen()..gray(level: 0.5); | |
| 39 AnsiPen comment = new AnsiPen()..gray(level: 0.5); | |
| 40 | |
| 41 String stackTraceName(Member member) { | |
| 42 String name = member.name?.name; | |
| 43 String className = member.enclosingClass?.name; | |
| 44 if (member is Constructor && name != null) { | |
| 45 return '$className.$className'; | |
| 46 } else if (className != null) { | |
| 47 return '$className.$name'; | |
| 48 } else { | |
| 49 return name; | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 int findReachablePrefix(String name, Set<String> strings) { | |
| 54 for (int index = name.length; | |
| 55 index > 0; | |
| 56 index = name.lastIndexOf('.', index - 1)) { | |
| 57 if (strings.contains(name.substring(0, index))) { | |
| 58 return index; | |
| 59 } | |
| 60 } | |
| 61 return 0; | |
| 62 } | |
| 63 | |
| 64 String shortenFilename(String filename) { | |
| 65 if (filename.startsWith('file:')) { | |
| 66 int libIndex = filename.lastIndexOf('lib/'); | |
| 67 if (libIndex != -1) { | |
| 68 return filename.substring(libIndex + 'lib/'.length); | |
| 69 } | |
| 70 } | |
| 71 return filename; | |
| 72 } | |
| 73 | |
| 74 main(List<String> args) { | |
| 75 if (args.length != 2) { | |
| 76 print(usage); | |
| 77 exit(1); | |
| 78 } | |
| 79 List<String> stackTrace = new File(args[1]).readAsLinesSync(); | |
| 80 Program program = loadProgramFromBinary(args[0]); | |
| 81 TreeShaker shaker = new TreeShaker(program); | |
| 82 Set<String> reachablePatterns = new Set<String>(); | |
| 83 Set<String> allMembers = new Set<String>(); | |
| 84 Set<String> instantiatedClasses = new Set<String>(); | |
| 85 void visitMember(Member member) { | |
| 86 allMembers.add(stackTraceName(member)); | |
| 87 if (shaker.isMemberUsed(member)) { | |
| 88 reachablePatterns.add(stackTraceName(member)); | |
| 89 } | |
| 90 } | |
| 91 for (var library in program.libraries) { | |
| 92 for (var classNode in library.classes) { | |
| 93 if (shaker.isInstantiated(classNode)) { | |
| 94 instantiatedClasses.add(classNode.name); | |
| 95 } | |
| 96 classNode.members.forEach(visitMember); | |
| 97 } | |
| 98 library.members.forEach(visitMember); | |
| 99 } | |
| 100 for (String line in stackTrace) { | |
| 101 var match = stackTraceEntry.matchAsPrefix(line); | |
| 102 if (match == null) continue; | |
| 103 String entry = match.group(2); | |
| 104 int reachableIndex = findReachablePrefix(entry, reachablePatterns); | |
| 105 int knownIndex = findReachablePrefix(entry, allMembers); | |
| 106 int classIndex = findReachablePrefix(entry, instantiatedClasses); | |
| 107 if (reachableIndex == 0) { | |
| 108 reachableIndex = classIndex; | |
| 109 if (reachableIndex > knownIndex) { | |
| 110 knownIndex = reachableIndex; | |
| 111 } | |
| 112 } | |
| 113 String numberPart = match.group(1); | |
| 114 String reachablePart = reachable(entry.substring(0, reachableIndex)); | |
| 115 String knownPart = unreachable(entry.substring(reachableIndex, knownIndex)); | |
| 116 String unknownPart = unknown(entry.substring(knownIndex)); | |
| 117 String filePart = filecolor(shortenFilename(match.group(3))); | |
| 118 String string = '$numberPart$reachablePart$knownPart$unknownPart'; | |
| 119 print(string.padRight(110) + filePart); | |
| 120 } | |
| 121 print(legend); | |
| 122 } | |
| OLD | NEW |