PassRefPtr<ScriptCallStack> createScriptCallStackFromException(JSC::ExecState* exec, JSC::JSValue& exception, size_t maxStackSize) { Vector<ScriptCallFrame> frames; RefCountedArray<StackFrame> stackTrace = exec->vm().exceptionStack(); for (size_t i = 0; i < stackTrace.size() && i < maxStackSize; i++) { if (!stackTrace[i].callee && frames.size()) break; String functionName = stackTrace[i].friendlyFunctionName(exec); unsigned line; unsigned column; stackTrace[i].computeLineAndColumn(line, column); frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL, line, column)); } // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions // Fallback to getting at least the line and sourceURL from the exception if it has values and the exceptionStack doesn't. if (frames.size() > 0) { const ScriptCallFrame& firstCallFrame = frames.first(); JSObject* exceptionObject = exception.toObject(exec); if (exception.isObject() && firstCallFrame.sourceURL().isEmpty()) { JSValue lineValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "line")); int lineNumber = lineValue && lineValue.isNumber() ? int(lineValue.toNumber(exec)) : 0; JSValue sourceURLValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "sourceURL")); String exceptionSourceURL = sourceURLValue && sourceURLValue.isString() ? sourceURLValue.toString(exec)->value(exec) : ASCIILiteral("undefined"); frames[0] = ScriptCallFrame(firstCallFrame.functionName(), exceptionSourceURL, lineNumber, 0); } } return ScriptCallStack::create(frames); }
void JSGlobalObjectInspectorController::appendAPIBacktrace(ScriptCallStack* callStack) { #if OS(DARWIN) || (OS(LINUX) && !PLATFORM(GTK)) static const int framesToShow = 31; static const int framesToSkip = 3; // WTFGetBacktrace, appendAPIBacktrace, reportAPIException. void* samples[framesToShow + framesToSkip]; int frames = framesToShow + framesToSkip; WTFGetBacktrace(samples, &frames); void** stack = samples + framesToSkip; int size = frames - framesToSkip; for (int i = 0; i < size; ++i) { const char* mangledName = nullptr; char* cxaDemangled = nullptr; Dl_info info; if (dladdr(stack[i], &info) && info.dli_sname) mangledName = info.dli_sname; if (mangledName) cxaDemangled = abi::__cxa_demangle(mangledName, nullptr, nullptr, nullptr); if (mangledName || cxaDemangled) callStack->append(ScriptCallFrame(cxaDemangled ? cxaDemangled : mangledName, ASCIILiteral("[native code]"), 0, 0)); else callStack->append(ScriptCallFrame(ASCIILiteral("?"), ASCIILiteral("[native code]"), 0, 0)); free(cxaDemangled); } #else UNUSED_PARAM(callStack); #endif }
PassRefPtr<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize) { Vector<ScriptCallFrame> frames; CallFrame* callFrame = exec; while (true) { ASSERT(callFrame); int signedLineNumber; intptr_t sourceID; String urlString; JSValue function; exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function); String functionName; if (function) functionName = jsCast<JSFunction*>(function)->name(exec); else { // Caller is unknown, but if frames is empty we should still add the frame, because // something called us, and gave us arguments. if (!frames.isEmpty()) break; } unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0; frames.append(ScriptCallFrame(functionName, urlString, lineNumber)); if (!function || frames.size() == maxStackSize) break; callFrame = callFrame->callerFrame(); } return ScriptCallStack::create(frames); }
PassRefPtr<ScriptCallStack> createScriptCallStack(size_t maxStackSize, bool emptyIsAllowed) { Vector<ScriptCallFrame> frames; if (JSC::ExecState* exec = JSMainThreadExecState::currentState()) { Vector<StackFrame> stackTrace; Interpreter::getStackTrace(&exec->vm(), stackTrace, maxStackSize); for (size_t i = 0; i < stackTrace.size(); i++) frames.append(ScriptCallFrame(stackTrace[i].friendlyFunctionName(exec), stackTrace[i].friendlySourceURL(), stackTrace[i].line(), stackTrace[i].column())); } if (frames.isEmpty() && !emptyIsAllowed) { // No frames found. It may happen in the case where // a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". frames.append(ScriptCallFrame("undefined", "undefined", 0, 0)); } return ScriptCallStack::create(frames); }
PassRefPtr<ScriptCallStack> createScriptCallStack(size_t maxStackSize, bool emptyIsAllowed) { Vector<ScriptCallFrame> frames; if (JSC::ExecState* exec = JSMainThreadExecState::currentState()) { CallFrame* frame = exec->vm().topCallFrame; for (StackIterator iter = frame->begin(); iter != frame->end() && maxStackSize--; ++iter) { unsigned line; unsigned column; iter->computeLineAndColumn(line, column); frames.append(ScriptCallFrame(iter->functionName(), iter->sourceURL(), line, column)); } } if (frames.isEmpty() && !emptyIsAllowed) { // No frames found. It may happen in the case where // a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". frames.append(ScriptCallFrame("undefined", "undefined", 0, 0)); } return ScriptCallStack::create(frames); }
PassRefPtr<ScriptCallStack> createScriptCallStack(size_t maxStackSize, bool emptyIsAllowed) { Vector<ScriptCallFrame> frames; if (JSC::ExecState* exec = JSMainThreadExecState::currentState()) { Vector<StackFrame> stackTrace; Interpreter::getStackTrace(&exec->globalData(), stackTrace); for (Vector<StackFrame>::const_iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) { frames.append(ScriptCallFrame(iter->friendlyFunctionName(exec), iter->friendlySourceURL(), iter->friendlyLineNumber())); if (frames.size() >= maxStackSize) break; } } if (frames.isEmpty() && !emptyIsAllowed) { // No frames found. It may happen in the case where // a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". frames.append(ScriptCallFrame("undefined", "undefined", 0)); } return ScriptCallStack::create(frames); }
Ref<ScriptCallStack> createScriptCallStackFromException(JSC::ExecState* exec, JSC::Exception* exception, size_t maxStackSize) { Vector<ScriptCallFrame> frames; auto& stackTrace = exception->stack(); VM& vm = exec->vm(); for (size_t i = 0; i < stackTrace.size() && i < maxStackSize; i++) { unsigned line; unsigned column; stackTrace[i].computeLineAndColumn(line, column); String functionName = stackTrace[i].functionName(vm); frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL(), static_cast<SourceID>(stackTrace[i].sourceID()), line, column)); } // Fallback to getting at least the line and sourceURL from the exception object if it has values and the exceptionStack doesn't. if (exception->value().isObject()) { JSObject* exceptionObject = exception->value().toObject(exec); ASSERT(exceptionObject); int lineNumber; int columnNumber; String exceptionSourceURL; if (!frames.size()) { if (extractSourceInformationFromException(exec, exceptionObject, &lineNumber, &columnNumber, &exceptionSourceURL)) frames.append(ScriptCallFrame(String(), exceptionSourceURL, noSourceID, lineNumber, columnNumber)); } else { // FIXME: The typical stack trace will have a native frame at the top, and consumers of // this code already know this (see JSDOMExceptionHandling.cpp's reportException, for // example - it uses firstNonNativeCallFrame). This looks like it splats something else // over it. That something else is probably already at stackTrace[1]. // https://bugs.webkit.org/show_bug.cgi?id=176663 if (!stackTrace[0].hasLineAndColumnInfo() || stackTrace[0].sourceURL().isEmpty()) { const ScriptCallFrame& firstCallFrame = frames.first(); if (extractSourceInformationFromException(exec, exceptionObject, &lineNumber, &columnNumber, &exceptionSourceURL)) frames[0] = ScriptCallFrame(firstCallFrame.functionName(), exceptionSourceURL, stackTrace[0].sourceID(), lineNumber, columnNumber); } } } return ScriptCallStack::create(frames); }
ScriptCallStack::ScriptCallStack(ExecState* exec, unsigned skipArgumentCount) : m_initialized(false) , m_exec(exec) , m_caller(0) { int signedLineNumber; intptr_t sourceID; UString urlString; JSValue function; exec->interpreter()->retrieveLastCaller(exec, signedLineNumber, sourceID, urlString, function); unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0; if (function) { m_caller = asFunction(function); m_frames.append(ScriptCallFrame(m_caller->name(m_exec), urlString, lineNumber, m_exec, skipArgumentCount)); } else { // Caller is unknown, but we should still add the frame, because // something called us, and gave us arguments. m_frames.append(ScriptCallFrame(UString(), urlString, lineNumber, m_exec, skipArgumentCount)); } }
void ScriptCallStack::initialize() { if (!m_caller || m_initialized) return; JSValue func = m_exec->interpreter()->retrieveCaller(m_exec, m_caller); while (!func.isNull()) { InternalFunction* internalFunction = asInternalFunction(func); ArgList emptyArgList; m_frames.append(ScriptCallFrame(internalFunction->name(m_exec), UString(), 0, emptyArgList, 0)); func = m_exec->interpreter()->retrieveCaller(m_exec, internalFunction); } m_initialized = true; }
void JSGlobalObjectInspectorController::appendAPIBacktrace(ScriptCallStack& callStack) { #if OS(DARWIN) || (OS(LINUX) && !PLATFORM(GTK)) static const int framesToShow = 31; static const int framesToSkip = 3; // WTFGetBacktrace, appendAPIBacktrace, reportAPIException. void* samples[framesToShow + framesToSkip]; int frames = framesToShow + framesToSkip; WTFGetBacktrace(samples, &frames); void** stack = samples + framesToSkip; int size = frames - framesToSkip; for (int i = 0; i < size; ++i) { auto demangled = StackTrace::demangle(stack[i]); if (demangled) callStack.append(ScriptCallFrame(demangled->demangledName() ? demangled->demangledName() : demangled->mangledName(), ASCIILiteral("[native code]"), noSourceID, 0, 0)); else callStack.append(ScriptCallFrame(ASCIILiteral("?"), ASCIILiteral("[native code]"), noSourceID, 0, 0)); } #else UNUSED_PARAM(callStack); #endif }
static ScriptCallFrame toScriptCallFrame(v8::Handle<v8::StackFrame> frame) { String sourceName; v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL()); if (!sourceNameValue.IsEmpty()) sourceName = toWebCoreString(sourceNameValue); String functionName; v8::Local<v8::String> functionNameValue(frame->GetFunctionName()); if (!functionNameValue.IsEmpty()) functionName = toWebCoreString(functionNameValue); int sourceLineNumber = frame->GetLineNumber(); int sourceColumn = frame->GetColumn(); return ScriptCallFrame(functionName, sourceName, sourceLineNumber, sourceColumn); }
static void toScriptCallFramesVector(v8::Handle<v8::StackTrace> stackTrace, Vector<ScriptCallFrame>& scriptCallFrames, size_t maxStackSize, bool emptyStackIsAllowed, v8::Isolate* isolate) { ASSERT(isolate->InContext()); int frameCount = stackTrace->GetFrameCount(); if (frameCount > static_cast<int>(maxStackSize)) frameCount = maxStackSize; for (int i = 0; i < frameCount; i++) { v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i); scriptCallFrames.append(toScriptCallFrame(stackFrame)); } if (!frameCount && !emptyStackIsAllowed) { // Successfully grabbed stack trace, but there are no frames. It may happen in case // when a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". scriptCallFrames.append(ScriptCallFrame()); } }
PassRefPtr<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize) { Vector<ScriptCallFrame> frames; Vector<StackFrame> stackTrace; Interpreter::getStackTrace(&exec->vm(), stackTrace, maxStackSize + 1); for (size_t i = stackTrace.size() == 1 ? 0 : 1; i < stackTrace.size(); i++) { // This early exit is necessary to maintain our old behaviour // but the stack trace we produce now is complete and handles all // ways in which code may be running if (!stackTrace[i].callee && frames.size()) break; String functionName = stackTrace[i].friendlyFunctionName(exec); frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL, stackTrace[i].line(), stackTrace[i].column())); } return ScriptCallStack::create(frames); }
PassRefPtr<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize, bool emptyIsAllowed) { Vector<ScriptCallFrame> frames; if (exec) { CallFrame* frame = exec->vm().topCallFrame; CreateScriptCallStackFunctor functor(false, frames, maxStackSize); frame->iterate(functor); } if (frames.isEmpty() && !emptyIsAllowed) { // No frames found. It may happen in the case where // a bound function is called from native code for example. // Fallback to setting lineNumber to 0, and source and function name to "undefined". frames.append(ScriptCallFrame(ASCIILiteral("undefined"), ASCIILiteral("undefined"), 0, 0)); } return ScriptCallStack::create(frames); }
StackVisitor::Status operator()(StackVisitor& visitor) { if (m_needToSkipAFrame) { m_needToSkipAFrame = false; return StackVisitor::Continue; } if (m_remainingCapacityForFrameCapture) { unsigned line; unsigned column; visitor->computeLineAndColumn(line, column); m_frames.append(ScriptCallFrame(visitor->functionName(), visitor->sourceURL(), line, column)); m_remainingCapacityForFrameCapture--; return StackVisitor::Continue; } return StackVisitor::Done; }
PassRefPtr<ScriptCallStack> createScriptCallStack(JSC::ExecState* exec, size_t maxStackSize) { Vector<ScriptCallFrame> frames; ASSERT(exec); CallFrame* frame = exec->vm().topCallFrame; StackIterator iter = frame->begin(); if (iter.numberOfFrames() > 1) ++iter; for (; iter != frame->end() && maxStackSize--; ++iter) { // This early exit is necessary to maintain our old behaviour // but the stack trace we produce now is complete and handles all // ways in which code may be running if (!iter->callee() && frames.size()) break; unsigned line; unsigned column; iter->computeLineAndColumn(line, column); frames.append(ScriptCallFrame(iter->functionName(), iter->sourceURL(), line, column)); } return ScriptCallStack::create(frames); }
static ScriptCallFrame toScriptCallFrame(v8::Handle<v8::StackFrame> frame) { StringBuilder stringBuilder; stringBuilder.appendNumber(frame->GetScriptId()); String scriptId = stringBuilder.toString(); String sourceName; v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL()); if (!sourceNameValue.IsEmpty()) { int length = sourceNameValue->Length(); sourceName = StringTraits<String>::fromV8String<V8StringOneByteTrait>(sourceNameValue, length); } String functionName; v8::Local<v8::String> functionNameValue(frame->GetFunctionName()); if (!functionNameValue.IsEmpty()) functionName = toCoreString(functionNameValue); int sourceLineNumber = frame->GetLineNumber(); int sourceColumn = frame->GetColumn(); return ScriptCallFrame(functionName, scriptId, sourceName, sourceLineNumber, sourceColumn); }
void ScriptCallStack::initialize() { if (!m_caller || m_initialized) return; int signedLineNumber; intptr_t sourceID; UString urlString; JSValue function; // callFrame must exist if m_caller is not null. CallFrame* callFrame = m_exec->callerFrame(); while (true) { ASSERT(callFrame); m_exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function); if (!function) break; JSFunction* jsFunction = asFunction(function); unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0; m_frames.append(ScriptCallFrame(jsFunction->name(m_exec), urlString, lineNumber, m_exec, 0)); callFrame = callFrame->callerFrame(); } m_initialized = true; }