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 |