#include "config.h"
#include "VMInspector.h"
#if ENABLE(VMINSPECTOR)
#include "JSCInlines.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->isVMEntrySentinel()) {
count++;
frame = frame->callerFrame();
}
return count;
}
class FormatPrinter {
public:
virtual ~FormatPrinter() { }
void print(const char* format, va_list args);
protected:
bool printArg(const char* format, ...);
virtual bool printArg(const char* format, va_list args);
void printWTFString(va_list args, bool verbose);
};
void FormatPrinter::print(const char* format, va_list args)
{
const char* p = format;
const char* errorStr;
char buffer[129]; char* end = &buffer[sizeof(buffer) - 1];
const char* startOfFormatSpecifier = 0;
while (true) {
char c = *p++;
char* curr = buffer;
while (c != '\0' && c != '%') {
*curr++ = c;
if (curr == end) {
*curr = '\0';
bool success = printArg("%s", buffer);
if (!success) {
errorStr = buffer;
goto handleError;
}
curr = buffer;
}
c = *p++;
}
if (curr != buffer) {
ASSERT(curr < end + 1);
*curr = '\0';
bool success = printArg("%s", buffer);
if (!success) {
errorStr = buffer;
goto handleError;
}
}
if (c == '\0')
break;
startOfFormatSpecifier = p - 1;
ASSERT(*startOfFormatSpecifier == '%');
c = *p++;
if (c == '%') {
bool success = printArg("%c", '%');
if (!success) {
errorStr = p - 2;
goto handleError;
}
continue;
}
if (c == 'J') {
bool verbose = false;
c = *p++;
if (UNLIKELY(c == '\0')) {
errorStr = p - 2; goto handleError;
}
if (c == '+') {
verbose = true;
c= *p++;
if (UNLIKELY(c == '\0')) {
errorStr = p - 3; goto handleError;
}
}
switch (c) {
case 's': {
printWTFString(args, verbose);
continue;
}
}
} else if (c == 'b') {
int value = va_arg(args, int);
printArg("%s", value ? "TRUE" : "FALSE");
continue;
}
p = startOfFormatSpecifier;
ASSERT(*p == '%');
#define ABORT_IF_FORMAT_TOO_LONG(curr) \
do { \
if (UNLIKELY(curr >= end)) \
goto formatTooLong; \
} while (false)
curr = buffer;
*curr++ = *p++; c = *p++;
if (c == '0' || c == '-' || c == ' ' || c == '+' || c == '\'' || c == '#') {
ABORT_IF_FORMAT_TOO_LONG(curr);
*curr++ = c;
c = *p++;
}
while (c >= '0' && c <= '9') {
ABORT_IF_FORMAT_TOO_LONG(curr);
*curr++ = c;
c = *p++;
}
if (c == '.') {
ABORT_IF_FORMAT_TOO_LONG(curr);
*curr++ = c;
c = *p++;
while (c >= '0' && c <= '9') {
ABORT_IF_FORMAT_TOO_LONG(curr);
*curr++ = c;
c = *p++;
}
}
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++;
if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) {
ABORT_IF_FORMAT_TOO_LONG(curr);
*curr++ = c;
c = *p++;
}
}
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') {
errorStr = buffer;
goto handleError;
}
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:
ASSERT(!!startOfFormatSpecifier);
p = startOfFormatSpecifier;
ASSERT(p >= format);
printArg("ERROR @ Format too long at \"%s\"\n", p);
return;
handleError:
if (errorStr != buffer) {
size_t length = strlen(errorStr);
if (length > sizeof(buffer) - 1)
length = sizeof(buffer) - 1;
memmove(buffer, errorStr, length);
buffer[length] = '\0'; }
char* cp = buffer;
while (*cp) {
if (*cp == '\n' || *cp == '\r')
*cp = ' ';
cp++;
}
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); }
void FormatPrinter::printWTFString(va_list args, bool verbose)
{
const String* str = va_arg(args, const String*);
if (verbose)
printArg("WTF::String \"");
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));
}
}
if (verbose)
printArg("\"");
}
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); }
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); }
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);
bool success = (count >= 0);
if (static_cast<size_t>(count) >= m_size) {
count = m_size;
}
if (success) {
m_buffer += count;
m_size -= count;
}
return success;
}
return false;
}
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);
}
}
#endif // ENABLE(VMINSPECTOR)