X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/a253471d7f8e4d91bf6ebabab00155c3b387d3d0..93a3786624b2768d89bfa27e46598dc64e2fb70a:/interpreter/VMInspector.cpp diff --git a/interpreter/VMInspector.cpp b/interpreter/VMInspector.cpp new file mode 100644 index 0000000..58bc150 --- /dev/null +++ b/interpreter/VMInspector.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "VMInspector.h" + +#if ENABLE(VMINSPECTOR) + +#include +#include +#include + +namespace JSC { + +const char* VMInspector::getTypeName(JSValue value) +{ + if (value.isInt32()) + return ""; + if (value.isBoolean()) + return ""; + if (value.isNull()) + return ""; + if (value.isUndefined()) + return ""; + if (value.isCell()) + return ""; + if (value.isEmpty()) + return ""; + return ""; +} + +void VMInspector::dumpFrame0(CallFrame* frame) +{ + dumpFrame(frame, 0, 0, 0, 0); +} + +void VMInspector::dumpFrame(CallFrame* frame, const char* prefix, + const char* funcName, const char* file, int line) +{ + int frameCount = VMInspector::countFrames(frame); + if (frameCount < 0) + return; + + Instruction* vPC = 0; + if (frame->codeBlock()) + vPC = frame->currentVPC(); + + #define CAST reinterpret_cast + + if (prefix) + printf("%s ", prefix); + + printf("frame [%d] %p { cb %p:%s, retPC %p:%s, scope %p:%s, callee %p:%s, callerFrame %p:%s, argc %d, vPC %p }", + frameCount, frame, + CAST(frame[JSStack::CodeBlock].payload()), + getTypeName(frame[JSStack::CodeBlock].jsValue()), + CAST(frame[JSStack::ReturnPC].payload()), + getTypeName(frame[JSStack::ReturnPC].jsValue()), + CAST(frame[JSStack::ScopeChain].payload()), + getTypeName(frame[JSStack::ScopeChain].jsValue()), + CAST(frame[JSStack::Callee].payload()), + getTypeName(frame[JSStack::Callee].jsValue()), + CAST(frame[JSStack::CallerFrame].callFrame()), + getTypeName(frame[JSStack::CallerFrame].jsValue()), + frame[JSStack::ArgumentCount].payload(), + vPC); + + if (funcName || file || (line >= 0)) { + printf(" @"); + if (funcName) + printf(" %s", funcName); + if (file) + printf(" %s", file); + if (line >= 0) + printf(":%d", line); + } + printf("\n"); +} + +int VMInspector::countFrames(CallFrame* frame) +{ + int count = -1; + while (frame && !frame->hasHostCallFrameFlag()) { + count++; + frame = frame->callerFrame(); + } + return count; +} + + +//============================================================================ +// class FormatPrinter +// - implements functionality to support fprintf. +// +// The FormatPrinter classes do the real formatting and printing. +// By default, the superclass FormatPrinter will print to stdout (printf). +// Each of the subclass will implement the other ...printf() options. +// The subclasses are: +// +// FileFormatPrinter - fprintf +// StringFormatPrinter - sprintf +// StringNFormatPrinter - snprintf + +class FormatPrinter { +public: + virtual ~FormatPrinter() { } + + void print(const char* format, va_list args); + +protected: + // Low level printers: + bool printArg(const char* format, ...); + virtual bool printArg(const char* format, va_list args); + + // JS type specific printers: + void printWTFString(va_list args, bool verbose); +}; + + +// The public print() function is the real workhorse behind the printf +// family of functions. print() deciphers the % formatting, translate them +// to primitive formats, and dispatches to underlying printArg() functions +// to do the printing. +// +// The non-public internal printArg() function is virtual and is responsible +// for handling the variations between printf, fprintf, sprintf, and snprintf. + +void FormatPrinter::print(const char* format, va_list args) +{ + const char* p = format; + const char* errorStr; + + // buffer is only used for 2 purposes: + // 1. To temporarily hold a copy of normal chars (not needing formatting) + // to be passed to printArg() and printed. + // + // The incoming format string may contain a string of normal chars much + // longer than 128, but we handle this by breaking them out to 128 chars + // fragments and printing each fragment before re-using the buffer to + // load up the next fragment. + // + // 2. To hold a single "%..." format to be passed to printArg() to process + // a single va_arg. + + char buffer[129]; // 128 chars + null terminator. + char* end = &buffer[sizeof(buffer) - 1]; + const char* startOfFormatSpecifier = 0; + + while (true) { + char c = *p++; + char* curr = buffer; + + // Print leading normal chars: + while (c != '\0' && c != '%') { + *curr++ = c; + if (curr == end) { + // Out of buffer space. Flush the fragment, and start over. + *curr = '\0'; + bool success = printArg("%s", buffer); + if (!success) { + errorStr = buffer; + goto handleError; + } + curr = buffer; + } + c = *p++; + } + // If we have stuff in the buffer, flush the fragment: + if (curr != buffer) { + ASSERT(curr < end + 1); + *curr = '\0'; + bool success = printArg("%s", buffer); + if (!success) { + errorStr = buffer; + goto handleError; + } + } + + // End if there are not more chars to print: + if (c == '\0') + break; + + // If we get here, we've must have seen a '%': + startOfFormatSpecifier = p - 1; + ASSERT(*startOfFormatSpecifier == '%'); + c = *p++; + + // Check for "%%" case: + if (c == '%') { + bool success = printArg("%c", '%'); + if (!success) { + errorStr = p - 2; + goto handleError; + } + continue; + } + + // Check for JS (%J) formatting extensions: + if (c == 'J') { + bool verbose = false; + + c = *p++; + if (UNLIKELY(c == '\0')) { + errorStr = p - 2; // Rewind to % in "%J\0" + goto handleError; + } + + if (c == '+') { + verbose = true; + c= *p++; + if (UNLIKELY(c == '\0')) { + errorStr = p - 3; // Rewind to % in "%J+\0" + goto handleError; + } + } + + switch (c) { + // %Js - WTF::String* + case 's': { + printWTFString(args, verbose); + continue; + } + } // END switch. + + // Check for non-JS extensions: + } else if (c == 'b') { + int value = va_arg(args, int); + printArg("%s", value ? "TRUE" : "FALSE"); + continue; + } + + // If we didn't handle the format in one of the above cases, + // rewind p and let the standard formatting check handle it + // if possible: + p = startOfFormatSpecifier; + ASSERT(*p == '%'); + + // Check for standard formatting: + // A format specifier always starts with a % and ends with some + // alphabet. We'll do the simple thing and scan until the next + // alphabet, or the end of string. + + // In the following, we're going to use buffer as storage for a copy + // of a single format specifier. Hence, conceptually, we can think of + // 'buffer' as synonymous with 'argFormat' here: + +#define ABORT_IF_FORMAT_TOO_LONG(curr) \ + do { \ + if (UNLIKELY(curr >= end)) \ + goto formatTooLong; \ + } while (false) + + curr = buffer; + *curr++ = *p++; // Output the first % in the format specifier. + c = *p++; // Grab the next char in the format specifier. + + // Checks for leading modifiers e.g. "%-d": + // 0, -, ' ', +, '\'' + if (c == '0' || c == '-' || c == ' ' || c == '+' || c == '\'' || c == '#') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + // Checks for decimal digit field width modifiers e.g. "%2f": + while (c >= '0' && c <= '9') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + // Checks for '.' e.g. "%2.f": + if (c == '.') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + + // Checks for decimal digit precision modifiers e.g. "%.2f": + while (c >= '0' && c <= '9') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + } + + // Checks for the modifier where can be: + // l, h, j, t, z + // e.g. "%ld" + if (c == 'l' || c == 'h' || c == 'j' || c == 't' || c == 'z' || c == 'L') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + char prevChar = c; + c = *p++; + + // Checks for the modifier ll or hh in %: + if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + } + + // Checks for % where can be: + // d, i, n, o, u, x, X + // But hey, we're just going to do the simple thing and allow any + // alphabet. The user is expected to pass correct format specifiers. + // We won't do any format checking here. We'll just pass it on, and the + // underlying ...printf() implementation may do the needed checking + // at its discretion. + while (c != '\0' && !isASCIIAlpha(c)) { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + if (c == '\0') { + // Uh oh. Bad format. We should have gotten an alphabet instead. + // Print the supposed format as a string instead: + errorStr = buffer; + goto handleError; + } + + // Otherwise, we have the alpha that terminates the format. + // Terminate the buffer (i.e. argFormat) string: + ASSERT(isASCIIAlpha(c)); + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr = '\0'; + + bool success = printArg(buffer, args); + if (!success) { + errorStr = buffer; + goto handleError; + } + } +#undef ABORT_IF_FORMAT_TOO_LONG + + return; + +formatTooLong: + // Print the error string: + ASSERT(!!startOfFormatSpecifier); + p = startOfFormatSpecifier; + ASSERT(p >= format); + printArg("ERROR @ Format too long at \"%s\"\n", p); + return; + +handleError: + // We've got an error. Can't do any more work. Print an error message if + // possible and then just return. + + // The errorStr may be pointing into the middle of buffer, or the original + // format string. Move the string to buffer for consistency, and also so + // that we can strip it of newlines below. + if (errorStr != buffer) { + size_t length = strlen(errorStr); + if (length > sizeof(buffer) - 1) + length = sizeof(buffer) - 1; + memmove(buffer, errorStr, length); + buffer[length] = '\0'; // Terminate the moved error string. + } + // Strip the newlines: + char* cp = buffer; + while (*cp) { + if (*cp == '\n' || *cp == '\r') + *cp = ' '; + cp++; + } + // Print the error string: + printArg("ERROR @ \"%s\"\n", buffer); +} + + +bool FormatPrinter::printArg(const char* format, ...) +{ + va_list args; + va_start(args, format); + bool success = printArg(format, args); + va_end(args); + return success; +} + +bool FormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vprintf(format, args); + return (count >= 0); // Fail if less than 0 chars printed. +} + + +// %Js - WTF::String* +// verbose mode prints: WTF::String "" +void FormatPrinter::printWTFString(va_list args, bool verbose) +{ + const String* str = va_arg(args, const String*); + + // Print verbose header if appropriate: + if (verbose) + printArg("WTF::String \""); + + // Print the string itself: + if (!str->isEmpty()) { + if (str->is8Bit()) { + const LChar* chars = str->characters8(); + printArg("%s", reinterpret_cast(chars)); + } else { + const UChar* chars = str->characters16(); + printArg("%S", reinterpret_cast(chars)); + } + } + + // Print verbose footer if appropriate: + if (verbose) + printArg("\""); +} + + +//============================================================================ +// class FileFormatPrinter +// - implements functionality to support fprintf. + +class FileFormatPrinter: public FormatPrinter { +public: + FileFormatPrinter(FILE*); +private: + virtual bool printArg(const char* format, va_list args); + + FILE* m_file; +}; + +FileFormatPrinter::FileFormatPrinter(FILE* file) + : m_file(file) +{ +} + +bool FileFormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vfprintf(m_file, format, args); + return (count >= 0); // Fail if less than 0 chars printed. +} + + +//============================================================================ +// class StringFormatPrinter +// - implements functionality to support sprintf. + +class StringFormatPrinter: public FormatPrinter { +public: + StringFormatPrinter(char* buffer); +private: + virtual bool printArg(const char* format, va_list args); + + char* m_buffer; +}; + +StringFormatPrinter::StringFormatPrinter(char* buffer) + : m_buffer(buffer) +{ +} + +bool StringFormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vsprintf(m_buffer, format, args); + m_buffer += count; + return (count >= 0); // Fail if less than 0 chars printed. +} + + +//============================================================================ +// class StringNFormatPrinter +// - implements functionality to support snprintf. + +class StringNFormatPrinter: public FormatPrinter { +public: + StringNFormatPrinter(char* buffer, size_t); +private: + virtual bool printArg(const char* format, va_list args); + + char* m_buffer; + size_t m_size; +}; + + +StringNFormatPrinter::StringNFormatPrinter(char* buffer, size_t size) + : m_buffer(buffer) + , m_size(size) +{ +} + +bool StringNFormatPrinter::printArg(const char* format, va_list args) +{ + if (m_size > 0) { + int count = ::vsnprintf(m_buffer, m_size, format, args); + + // According to vsnprintf specs, ... + bool success = (count >= 0); + if (static_cast(count) >= m_size) { + // If count > size, then we didn't have enough buffer space. + count = m_size; + } + + // Adjust the buffer to what's left if appropriate: + if (success) { + m_buffer += count; + m_size -= count; + } + return success; + } + // No more room to print. Declare it a fail: + return false; +} + + +//============================================================================ +// VMInspector printf family of methods: + +void VMInspector::fprintf(FILE* file, const char* format, ...) +{ + va_list args; + va_start(args, format); + FileFormatPrinter(file).print(format, args); + va_end(args); +} + +void VMInspector::printf(const char* format, ...) +{ + va_list args; + va_start(args, format); + FormatPrinter().print(format, args); + va_end(args); +} + +void VMInspector::sprintf(char* buffer, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringFormatPrinter(buffer).print(format, args); + va_end(args); +} + +void VMInspector::snprintf(char* buffer, size_t size, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringNFormatPrinter(buffer, size).print(format, args); + va_end(args); +} + +} // namespace JSC + +#endif // ENABLE(VMINSPECTOR)