blob: 58bc15075edb8e0c70259764ae22bfec98b404b4 [file] [log] [blame]
/*
* 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 <stdio.h>
#include <wtf/ASCIICType.h>
#include <wtf/text/WTFString.h>
namespace JSC {
const char* VMInspector::getTypeName(JSValue value)
{
if (value.isInt32())
return "<Int32>";
if (value.isBoolean())
return "<Boolean>";
if (value.isNull())
return "<Empty>";
if (value.isUndefined())
return "<Undefined>";
if (value.isCell())
return "<Cell>";
if (value.isEmpty())
return "<Empty>";
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<void*>(frame[JSStack::CodeBlock].payload()),
getTypeName(frame[JSStack::CodeBlock].jsValue()),
CAST<void*>(frame[JSStack::ReturnPC].payload()),
getTypeName(frame[JSStack::ReturnPC].jsValue()),
CAST<void*>(frame[JSStack::ScopeChain].payload()),
getTypeName(frame[JSStack::ScopeChain].jsValue()),
CAST<void*>(frame[JSStack::Callee].payload()),
getTypeName(frame[JSStack::Callee].jsValue()),
CAST<void*>(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<x>) 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 <m> where <m> 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 %<x><m>:
if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) {
ABORT_IF_FORMAT_TOO_LONG(curr);
*curr++ = c;
c = *p++;
}
}
// Checks for %<x> where <x> 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 "<your 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<const char*>(chars));
} else {
const UChar* chars = str->characters16();
printArg("%S", reinterpret_cast<const wchar_t*>(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<size_t>(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)