]>
Commit | Line | Data |
---|---|---|
81345200 A |
1 | /* |
2 | * Copyright (C) 2013 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #include "config.h" | |
27 | #include "StackVisitor.h" | |
28 | ||
29 | #include "Arguments.h" | |
30 | #include "CallFrameInlines.h" | |
31 | #include "Executable.h" | |
32 | #include "Interpreter.h" | |
33 | #include "JSCInlines.h" | |
34 | #include <wtf/DataLog.h> | |
35 | ||
36 | namespace JSC { | |
37 | ||
38 | StackVisitor::StackVisitor(CallFrame* startFrame) | |
39 | { | |
40 | m_frame.m_index = 0; | |
41 | readFrame(startFrame); | |
42 | } | |
43 | ||
44 | void StackVisitor::gotoNextFrame() | |
45 | { | |
46 | #if ENABLE(DFG_JIT) | |
47 | if (m_frame.isInlinedFrame()) { | |
48 | InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); | |
49 | CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller; | |
50 | readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); | |
51 | ||
52 | } else | |
53 | #endif // ENABLE(DFG_JIT) | |
54 | readFrame(m_frame.callerFrame()); | |
55 | } | |
56 | ||
57 | void StackVisitor::readFrame(CallFrame* callFrame) | |
58 | { | |
59 | ASSERT(!callFrame->isVMEntrySentinel()); | |
60 | if (!callFrame) { | |
61 | m_frame.setToEnd(); | |
62 | return; | |
63 | } | |
64 | ||
65 | #if !ENABLE(DFG_JIT) | |
66 | readNonInlinedFrame(callFrame); | |
67 | ||
68 | #else // !ENABLE(DFG_JIT) | |
69 | // If the frame doesn't have a code block, then it's not a DFG frame. | |
70 | // Hence, we're not at an inlined frame. | |
71 | CodeBlock* codeBlock = callFrame->codeBlock(); | |
72 | if (!codeBlock) { | |
73 | readNonInlinedFrame(callFrame); | |
74 | return; | |
75 | } | |
76 | ||
77 | // If the code block does not have any code origins, then there's no | |
78 | // inlining. Hence, we're not at an inlined frame. | |
79 | if (!codeBlock->hasCodeOrigins()) { | |
80 | readNonInlinedFrame(callFrame); | |
81 | return; | |
82 | } | |
83 | ||
84 | unsigned index = callFrame->locationAsCodeOriginIndex(); | |
85 | ASSERT(codeBlock->canGetCodeOrigin(index)); | |
86 | if (!codeBlock->canGetCodeOrigin(index)) { | |
87 | // See assertion above. In release builds, we try to protect ourselves | |
88 | // from crashing even though stack walking will be goofed up. | |
89 | m_frame.setToEnd(); | |
90 | return; | |
91 | } | |
92 | ||
93 | CodeOrigin codeOrigin = codeBlock->codeOrigin(index); | |
94 | if (!codeOrigin.inlineCallFrame) { | |
95 | readNonInlinedFrame(callFrame, &codeOrigin); | |
96 | return; | |
97 | } | |
98 | ||
99 | readInlinedFrame(callFrame, &codeOrigin); | |
100 | #endif // !ENABLE(DFG_JIT) | |
101 | } | |
102 | ||
103 | void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) | |
104 | { | |
105 | m_frame.m_callFrame = callFrame; | |
106 | m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); | |
107 | m_frame.m_callerFrame = callFrame->callerFrameSkippingVMEntrySentinel(); | |
108 | m_frame.m_callee = callFrame->callee(); | |
109 | m_frame.m_scope = callFrame->scope(); | |
110 | m_frame.m_codeBlock = callFrame->codeBlock(); | |
111 | m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 | |
112 | : codeOrigin ? codeOrigin->bytecodeIndex | |
113 | : callFrame->locationAsBytecodeOffset(); | |
114 | #if ENABLE(DFG_JIT) | |
115 | m_frame.m_inlineCallFrame = 0; | |
116 | #endif | |
117 | } | |
118 | ||
119 | #if ENABLE(DFG_JIT) | |
120 | static int inlinedFrameOffset(CodeOrigin* codeOrigin) | |
121 | { | |
122 | InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; | |
123 | int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; | |
124 | return frameOffset; | |
125 | } | |
126 | ||
127 | void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) | |
128 | { | |
129 | ASSERT(codeOrigin); | |
130 | ASSERT(!callFrame->isVMEntrySentinel()); | |
131 | ||
132 | int frameOffset = inlinedFrameOffset(codeOrigin); | |
133 | bool isInlined = !!frameOffset; | |
134 | if (isInlined) { | |
135 | InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; | |
136 | ||
137 | m_frame.m_callFrame = callFrame; | |
138 | m_frame.m_inlineCallFrame = inlineCallFrame; | |
139 | m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); | |
140 | m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock(); | |
141 | m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; | |
142 | ||
143 | JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); | |
144 | m_frame.m_scope = callee->scope(); | |
145 | m_frame.m_callee = callee; | |
146 | ASSERT(m_frame.scope()); | |
147 | ASSERT(m_frame.callee()); | |
148 | ||
149 | // The callerFrame just needs to be non-null to indicate that we | |
150 | // haven't reached the last frame yet. Setting it to the root | |
151 | // frame (i.e. the callFrame that this inlined frame is called from) | |
152 | // would work just fine. | |
153 | m_frame.m_callerFrame = callFrame; | |
154 | return; | |
155 | } | |
156 | ||
157 | readNonInlinedFrame(callFrame, codeOrigin); | |
158 | } | |
159 | #endif // ENABLE(DFG_JIT) | |
160 | ||
161 | StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const | |
162 | { | |
163 | if (!isJSFrame()) | |
164 | return CodeType::Native; | |
165 | ||
166 | switch (codeBlock()->codeType()) { | |
167 | case EvalCode: | |
168 | return CodeType::Eval; | |
169 | case FunctionCode: | |
170 | return CodeType::Function; | |
171 | case GlobalCode: | |
172 | return CodeType::Global; | |
173 | } | |
174 | RELEASE_ASSERT_NOT_REACHED(); | |
175 | return CodeType::Global; | |
176 | } | |
177 | ||
178 | String StackVisitor::Frame::functionName() | |
179 | { | |
180 | String traceLine; | |
181 | JSObject* callee = this->callee(); | |
182 | ||
183 | switch (codeType()) { | |
184 | case CodeType::Eval: | |
185 | traceLine = "eval code"; | |
186 | break; | |
187 | case CodeType::Native: | |
188 | if (callee) | |
189 | traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); | |
190 | break; | |
191 | case CodeType::Function: | |
192 | traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); | |
193 | break; | |
194 | case CodeType::Global: | |
195 | traceLine = "global code"; | |
196 | break; | |
197 | } | |
198 | return traceLine.isNull() ? emptyString() : traceLine; | |
199 | } | |
200 | ||
201 | String StackVisitor::Frame::sourceURL() | |
202 | { | |
203 | String traceLine; | |
204 | ||
205 | switch (codeType()) { | |
206 | case CodeType::Eval: | |
207 | case CodeType::Function: | |
208 | case CodeType::Global: { | |
209 | String sourceURL = codeBlock()->ownerExecutable()->sourceURL(); | |
210 | if (!sourceURL.isEmpty()) | |
211 | traceLine = sourceURL.impl(); | |
212 | break; | |
213 | } | |
214 | case CodeType::Native: | |
215 | traceLine = "[native code]"; | |
216 | break; | |
217 | } | |
218 | return traceLine.isNull() ? emptyString() : traceLine; | |
219 | } | |
220 | ||
221 | String StackVisitor::Frame::toString() | |
222 | { | |
223 | StringBuilder traceBuild; | |
224 | String functionName = this->functionName(); | |
225 | String sourceURL = this->sourceURL(); | |
226 | traceBuild.append(functionName); | |
227 | if (!sourceURL.isEmpty()) { | |
228 | if (!functionName.isEmpty()) | |
229 | traceBuild.append('@'); | |
230 | traceBuild.append(sourceURL); | |
231 | if (isJSFrame()) { | |
232 | unsigned line = 0; | |
233 | unsigned column = 0; | |
234 | computeLineAndColumn(line, column); | |
235 | traceBuild.append(':'); | |
236 | traceBuild.appendNumber(line); | |
237 | traceBuild.append(':'); | |
238 | traceBuild.appendNumber(column); | |
239 | } | |
240 | } | |
241 | return traceBuild.toString().impl(); | |
242 | } | |
243 | ||
244 | Arguments* StackVisitor::Frame::createArguments() | |
245 | { | |
246 | ASSERT(m_callFrame); | |
247 | CallFrame* physicalFrame = m_callFrame; | |
248 | VM& vm = physicalFrame->vm(); | |
249 | Arguments* arguments; | |
250 | #if ENABLE(DFG_JIT) | |
251 | if (isInlinedFrame()) { | |
252 | ASSERT(m_inlineCallFrame); | |
253 | arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame); | |
254 | arguments->tearOff(physicalFrame, m_inlineCallFrame); | |
255 | } else | |
256 | #endif | |
257 | { | |
258 | arguments = Arguments::create(vm, physicalFrame); | |
259 | arguments->tearOff(physicalFrame); | |
260 | } | |
261 | return arguments; | |
262 | } | |
263 | ||
264 | Arguments* StackVisitor::Frame::existingArguments() | |
265 | { | |
266 | if (codeBlock()->codeType() != FunctionCode) | |
267 | return 0; | |
268 | if (!codeBlock()->usesArguments()) | |
269 | return 0; | |
270 | ||
271 | VirtualRegister reg; | |
272 | ||
273 | #if ENABLE(DFG_JIT) | |
274 | if (isInlinedFrame()) | |
275 | reg = inlineCallFrame()->argumentsRegister; | |
276 | else | |
277 | #endif // ENABLE(DFG_JIT) | |
278 | reg = codeBlock()->argumentsRegister(); | |
279 | ||
280 | JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue(); | |
281 | if (!result || !result.isCell()) // Protect against Undefined in case we throw in op_enter. | |
282 | return 0; | |
283 | return jsCast<Arguments*>(result); | |
284 | } | |
285 | ||
286 | void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) | |
287 | { | |
288 | CodeBlock* codeBlock = this->codeBlock(); | |
289 | if (!codeBlock) { | |
290 | line = 0; | |
291 | column = 0; | |
292 | return; | |
293 | } | |
294 | ||
295 | int divot = 0; | |
296 | int unusedStartOffset = 0; | |
297 | int unusedEndOffset = 0; | |
298 | unsigned divotLine = 0; | |
299 | unsigned divotColumn = 0; | |
300 | retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); | |
301 | ||
302 | line = divotLine + codeBlock->ownerExecutable()->lineNo(); | |
303 | column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); | |
304 | } | |
305 | ||
306 | void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) | |
307 | { | |
308 | CodeBlock* codeBlock = this->codeBlock(); | |
309 | codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column); | |
310 | divot += codeBlock->sourceOffset(); | |
311 | } | |
312 | ||
313 | void StackVisitor::Frame::setToEnd() | |
314 | { | |
315 | m_callFrame = 0; | |
316 | #if ENABLE(DFG_JIT) | |
317 | m_inlineCallFrame = 0; | |
318 | #endif | |
319 | } | |
320 | ||
321 | #ifndef NDEBUG | |
322 | ||
323 | static const char* jitTypeName(JITCode::JITType jitType) | |
324 | { | |
325 | switch (jitType) { | |
326 | case JITCode::None: return "None"; | |
327 | case JITCode::HostCallThunk: return "HostCallThunk"; | |
328 | case JITCode::InterpreterThunk: return "InterpreterThunk"; | |
329 | case JITCode::BaselineJIT: return "BaselineJIT"; | |
330 | case JITCode::DFGJIT: return "DFGJIT"; | |
331 | case JITCode::FTLJIT: return "FTLJIT"; | |
332 | } | |
333 | return "<unknown>"; | |
334 | } | |
335 | ||
336 | static void printIndents(int levels) | |
337 | { | |
338 | while (levels--) | |
339 | dataLogFString(" "); | |
340 | } | |
341 | ||
342 | static void printif(int indentLevels, const char* format, ...) | |
343 | { | |
344 | va_list argList; | |
345 | va_start(argList, format); | |
346 | ||
347 | if (indentLevels) | |
348 | printIndents(indentLevels); | |
349 | ||
350 | #if COMPILER(CLANG) || COMPILER(GCC) | |
351 | #pragma GCC diagnostic push | |
352 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
353 | #pragma GCC diagnostic ignored "-Wmissing-format-attribute" | |
354 | #endif | |
355 | ||
356 | WTF::dataLogFV(format, argList); | |
357 | ||
358 | #if COMPILER(CLANG) || COMPILER(GCC) | |
359 | #pragma GCC diagnostic pop | |
360 | #endif | |
361 | ||
362 | va_end(argList); | |
363 | } | |
364 | ||
365 | void StackVisitor::Frame::print(int indentLevel) | |
366 | { | |
367 | int i = indentLevel; | |
368 | ||
369 | if (!this->callFrame()) { | |
370 | printif(i, "frame 0x0\n"); | |
371 | return; | |
372 | } | |
373 | ||
374 | CodeBlock* codeBlock = this->codeBlock(); | |
375 | printif(i, "frame %p {\n", this->callFrame()); | |
376 | ||
377 | CallFrame* callFrame = m_callFrame; | |
378 | CallFrame* callerFrame = this->callerFrame(); | |
379 | void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; | |
380 | ||
381 | printif(i, " name '%s'\n", functionName().utf8().data()); | |
382 | printif(i, " sourceURL '%s'\n", sourceURL().utf8().data()); | |
383 | printif(i, " isVMEntrySentinel %d\n", callerFrame->isVMEntrySentinel()); | |
384 | ||
385 | #if ENABLE(DFG_JIT) | |
386 | printif(i, " isInlinedFrame %d\n", isInlinedFrame()); | |
387 | if (isInlinedFrame()) | |
388 | printif(i, " InlineCallFrame %p\n", m_inlineCallFrame); | |
389 | #endif | |
390 | ||
391 | printif(i, " callee %p\n", callee()); | |
392 | printif(i, " returnPC %p\n", returnPC); | |
393 | printif(i, " callerFrame %p\n", callerFrame); | |
394 | unsigned locationRawBits = callFrame->locationAsRawBits(); | |
395 | printif(i, " rawLocationBits %u 0x%x\n", locationRawBits, locationRawBits); | |
396 | printif(i, " codeBlock %p\n", codeBlock); | |
397 | if (codeBlock) { | |
398 | JITCode::JITType jitType = codeBlock->jitType(); | |
399 | if (callFrame->hasLocationAsBytecodeOffset()) { | |
400 | unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); | |
401 | printif(i, " bytecodeOffset %u %p / %zu\n", bytecodeOffset, reinterpret_cast<void*>(bytecodeOffset), codeBlock->instructions().size()); | |
402 | #if ENABLE(DFG_JIT) | |
403 | } else { | |
404 | unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); | |
405 | printif(i, " codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size()); | |
406 | #endif | |
407 | } | |
408 | unsigned line = 0; | |
409 | unsigned column = 0; | |
410 | computeLineAndColumn(line, column); | |
411 | printif(i, " line %d\n", line); | |
412 | printif(i, " column %d\n", column); | |
413 | printif(i, " jitType %d <%s> isOptimizingJIT %d\n", jitType, jitTypeName(jitType), JITCode::isOptimizingJIT(jitType)); | |
414 | #if ENABLE(DFG_JIT) | |
415 | printif(i, " hasCodeOrigins %d\n", codeBlock->hasCodeOrigins()); | |
416 | if (codeBlock->hasCodeOrigins()) { | |
417 | JITCode* jitCode = codeBlock->jitCode().get(); | |
418 | printif(i, " jitCode %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); | |
419 | } | |
420 | #endif | |
421 | } | |
422 | printif(i, "}\n"); | |
423 | } | |
424 | ||
425 | #endif // NDEBUG | |
426 | ||
427 | } // namespace JSC | |
428 | ||
429 | #ifndef NDEBUG | |
430 | using JSC::StackVisitor; | |
431 | ||
432 | // For debugging use | |
433 | JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*); | |
434 | JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame); | |
435 | ||
436 | class DebugPrintFrameFunctor { | |
437 | public: | |
438 | enum Action { | |
439 | PrintOne, | |
440 | PrintAll | |
441 | }; | |
442 | ||
443 | DebugPrintFrameFunctor(Action action) | |
444 | : m_action(action) | |
445 | { | |
446 | } | |
447 | ||
448 | StackVisitor::Status operator()(StackVisitor& visitor) | |
449 | { | |
450 | visitor->print(2); | |
451 | return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done; | |
452 | } | |
453 | ||
454 | private: | |
455 | Action m_action; | |
456 | }; | |
457 | ||
458 | void debugPrintCallFrame(JSC::CallFrame* callFrame) | |
459 | { | |
460 | if (!callFrame) | |
461 | return; | |
462 | DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne); | |
463 | callFrame->iterate(functor); | |
464 | } | |
465 | ||
466 | void debugPrintStack(JSC::CallFrame* topCallFrame) | |
467 | { | |
468 | if (!topCallFrame) | |
469 | return; | |
470 | DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll); | |
471 | topCallFrame->iterate(functor); | |
472 | } | |
473 | ||
474 | #endif // !NDEBUG |