OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino 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.md file. | |
4 | |
5 part of fletch.debug_state; | |
6 | |
7 class BackTraceFrame { | |
8 final FletchFunction function; | |
9 final int bytecodePointer; | |
10 final IncrementalCompiler compiler; | |
11 final DebugState debugState; | |
12 | |
13 BackTraceFrame(this.function, this.bytecodePointer, this.compiler, | |
14 this.debugState); | |
15 | |
16 bool get inPlatformLibrary => function.element.library.isPlatformLibrary; | |
17 | |
18 bool get isInternal => function.isInternal || inPlatformLibrary; | |
19 | |
20 String invokeString(Bytecode bytecode) { | |
21 if (bytecode is InvokeMethod) { | |
22 String name = | |
23 compiler.lookupFunctionNameBySelector(bytecode.uint32Argument0); | |
24 return ' ($name)'; | |
25 } | |
26 return ''; | |
27 } | |
28 | |
29 bool get isVisible => debugState.showInternalFrames || !isInternal; | |
30 | |
31 DebugInfo get debugInfo => debugState.getDebugInfo(function); | |
32 | |
33 String list(SessionState state, {int contextLines: 5}) { | |
34 return debugInfo.sourceListStringFor( | |
35 bytecodePointer - 1, state, contextLines: contextLines); | |
36 } | |
37 | |
38 String disasm() { | |
39 StringBuffer buffer = new StringBuffer(); | |
40 var bytecodes = function.bytecodes; | |
41 var offset = 0; | |
42 for (var bytecode in bytecodes) offset += bytecode.size; | |
43 int offsetLength = '$offset'.length; | |
44 offset = 0; | |
45 for (var i = 0; i < bytecodes.length; i++) { | |
46 var source = debugInfo.astStringFor(offset); | |
47 var current = bytecodes[i]; | |
48 var byteNumberString = '$offset:'.padLeft(offsetLength); | |
49 var invokeInfo = invokeString(current); | |
50 var bytecodeString = '$byteNumberString $current$invokeInfo'; | |
51 var sourceString = '// $source'; | |
52 var printString = bytecodeString.padRight(30) + sourceString; | |
53 offset += current.size; | |
54 var marker = (offset == bytecodePointer) ? '* ' : ' '; | |
55 buffer.writeln("$marker$printString"); | |
56 } | |
57 return buffer.toString(); | |
58 } | |
59 | |
60 String shortString([int namePadding = 0]) { | |
61 String name = compiler.lookupFunctionName(function); | |
62 String astString = debugInfo.astStringFor(bytecodePointer - 1); | |
63 astString = (astString != null) ? '@$astString' : ''; | |
64 | |
65 String paddedName = name.padRight(namePadding); | |
66 String spaces = ''; | |
67 if (astString.isNotEmpty) { | |
68 int missingSpaces = 4 - (paddedName.length % 4); | |
69 spaces = ' ' * missingSpaces; | |
70 } | |
71 | |
72 return '$paddedName$spaces$astString'; | |
73 } | |
74 | |
75 SourceLocation sourceLocation() { | |
76 return debugInfo.sourceLocationFor(bytecodePointer - 1); | |
77 } | |
78 | |
79 ScopeInfo scopeInfo() { | |
80 return debugInfo.scopeInfoFor(bytecodePointer - 1); | |
81 } | |
82 | |
83 bool isSameSourceLocation(int offset, | |
84 SourceLocation current) { | |
85 SourceLocation location = debugInfo.sourceLocationFor(offset); | |
86 // Treat locations for which we have no source information as the same | |
87 // as the previous location. | |
88 if (location == null || location.node == null) return true; | |
89 return location.isSameSourceLevelLocationAs(current); | |
90 } | |
91 | |
92 int stepBytecodePointer(SourceLocation current) { | |
93 var bytecodes = function.bytecodes; | |
94 // Zip forward to the current bytecode. The bytecode pointer in the stack | |
95 // frame is the return address which is one bytecode after the current one. | |
96 var offset = 0; | |
97 var i = 0; | |
98 for (; i < bytecodes.length; i++) { | |
99 var currentSize = bytecodes[i].size; | |
100 if (offset + currentSize == bytecodePointer) break; | |
101 offset += currentSize; | |
102 } | |
103 // Move forward while we know step should not stop. | |
104 while (!bytecodes[i].isBranching && | |
105 isSameSourceLocation(offset, current)) { | |
106 offset += bytecodes[i++].size; | |
107 } | |
108 return offset <= bytecodePointer ? -1 : offset; | |
109 } | |
110 | |
111 int get functionId => function.functionId; | |
112 } | |
113 | |
114 class BackTrace { | |
115 final List<BackTraceFrame> frames; | |
116 final DebugState debugState; | |
117 | |
118 List<int> visibleFrameMapping; | |
119 int framesToGo; | |
120 int maxNameLength = 0; | |
121 | |
122 BackTrace(int framesToGo, this.debugState) | |
123 : this.framesToGo = framesToGo, | |
124 frames = new List(framesToGo); | |
125 | |
126 int get length => frames.length; | |
127 | |
128 int get visibleFrames { | |
129 ensureVisibleFrameMap(); | |
130 return visibleFrameMapping.length; | |
131 } | |
132 | |
133 void addFrame(IncrementalCompiler compiler, BackTraceFrame frame) { | |
134 frames[--framesToGo] = frame; | |
135 String name = compiler.lookupFunctionName(frame.function); | |
136 var nameLength = name == null ? 0 : name.length; | |
137 if (nameLength > maxNameLength) maxNameLength = nameLength; | |
138 } | |
139 | |
140 String format([int frame]) { | |
141 int currentFrame = frame != null ? frame : debugState.currentFrame; | |
142 StringBuffer buffer = new StringBuffer(); | |
143 assert(framesToGo == 0); | |
144 int frameNumber = 0; | |
145 int frameNumberLength = '$frameNumber'.length; | |
146 for (var i = 0; i < frames.length; i++) { | |
147 if (!frames[i].isVisible) continue; | |
148 var marker = currentFrame == frameNumber ? '* ' : ' '; | |
149 var line = frames[i].shortString(maxNameLength); | |
150 String frameNumberString = '${frameNumber++}: '.padLeft(frameNumberLength)
; | |
151 buffer.writeln('$marker$frameNumberString$line'); | |
152 } | |
153 return buffer.toString(); | |
154 } | |
155 | |
156 void ensureVisibleFrameMap() { | |
157 if (visibleFrameMapping == null) { | |
158 visibleFrameMapping = []; | |
159 for (int i = 0; i < frames.length; i++) { | |
160 if (frames[i].isVisible) visibleFrameMapping.add(i); | |
161 } | |
162 } | |
163 } | |
164 | |
165 // Map user visible frame numbers to actual frame numbers. | |
166 int actualFrameNumber(int visibleFrameNumber) { | |
167 ensureVisibleFrameMap(); | |
168 return (visibleFrameNumber < visibleFrameMapping.length) | |
169 ? visibleFrameMapping[visibleFrameNumber] | |
170 : -1; | |
171 } | |
172 | |
173 BackTraceFrame visibleFrame(int frame) { | |
174 int frameNumber = actualFrameNumber(frame); | |
175 if (frameNumber == -1) return null; | |
176 return frames[frameNumber]; | |
177 } | |
178 | |
179 void visibilityChanged() { | |
180 visibleFrameMapping = null; | |
181 } | |
182 | |
183 String list(SessionState state, [int frame]) { | |
184 if (frame == null) frame = debugState.currentFrame; | |
185 BackTraceFrame visibleStackFrame = visibleFrame(frame); | |
186 if (visibleStackFrame == null) return null; | |
187 return visibleStackFrame.list(state); | |
188 } | |
189 | |
190 String disasm([int frame]) { | |
191 if (frame == null) frame = debugState.currentFrame; | |
192 BackTraceFrame visibleStackFrame = visibleFrame(frame); | |
193 if (visibleStackFrame == null) return null; | |
194 return visibleStackFrame.disasm(); | |
195 } | |
196 | |
197 SourceLocation sourceLocation() { | |
198 return frames[0].sourceLocation(); | |
199 } | |
200 | |
201 ScopeInfo scopeInfo(int frame) { | |
202 BackTraceFrame visibleStackFrame = visibleFrame(frame); | |
203 if (visibleStackFrame == null) return null; | |
204 return visibleStackFrame.scopeInfo(); | |
205 } | |
206 | |
207 ScopeInfo get scopeInfoForCurrentFrame => scopeInfo(debugState.currentFrame); | |
208 | |
209 int stepBytecodePointer(SourceLocation location) { | |
210 return frames[0].stepBytecodePointer(location); | |
211 } | |
212 } | |
OLD | NEW |