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 36namespace JSC { 37 38StackVisitor::StackVisitor(CallFrame* startFrame) 39{ 40 m_frame.m_index = 0; 41 readFrame(startFrame); 42} 43 44void 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 57void 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 103void 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) 120static int inlinedFrameOffset(CodeOrigin* codeOrigin) 121{ 122 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; 123 int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; 124 return frameOffset; 125} 126 127void 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 161StackVisitor::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 178String 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 201String 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 221String 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 244Arguments* 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 264Arguments* 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 286void 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 306void 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 313void 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 323static 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 336static void printIndents(int levels) 337{ 338 while (levels--) 339 dataLogFString(" "); 340} 341 342static 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 365void 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 430using JSC::StackVisitor; 431 432// For debugging use 433JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*); 434JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame); 435 436class DebugPrintFrameFunctor { 437public: 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 454private: 455 Action m_action; 456}; 457 458void debugPrintCallFrame(JSC::CallFrame* callFrame) 459{ 460 if (!callFrame) 461 return; 462 DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne); 463 callFrame->iterate(functor); 464} 465 466void debugPrintStack(JSC::CallFrame* topCallFrame) 467{ 468 if (!topCallFrame) 469 return; 470 DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll); 471 topCallFrame->iterate(functor); 472} 473 474#endif // !NDEBUG 475