#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#include "trace_file.hpp"
-#include "trace_snappyfile.hpp"
+#include "trace_dump.hpp"
#include "trace_parser.hpp"
#define TRACE_VERBOSE 0
-namespace Trace {
+namespace trace {
Parser::Parser() {
file = NULL;
next_call_no = 0;
version = 0;
+ api = API_UNKNOWN;
+
+ glGetErrorSig = NULL;
}
bool Parser::open(const char *filename) {
assert(!file);
- if (File::isZLibCompressed(filename)) {
- file = new ZLibFile;
- } else {
- file = new SnappyFile;
- }
-
- if (!file->open(filename, File::Read)) {
+ file = File::createForRead(filename);
+ if (!file) {
return false;
}
std::cerr << "error: unsupported trace format version " << version << "\n";
return false;
}
+ api = API_UNKNOWN;
return true;
}
for (EnumMap::iterator it = enums.begin(); it != enums.end(); ++it) {
EnumSigState *sig = *it;
if (sig) {
- delete [] sig->name;
+ for (unsigned value = 0; value < sig->num_values; ++value) {
+ delete [] sig->values[value].name;
+ }
+ delete [] sig->values;
delete sig;
}
}
Call *Parser::parse_call(Mode mode) {
do {
+ Call *call;
int c = read_byte();
- switch(c) {
- case Trace::EVENT_ENTER:
+ switch (c) {
+ case trace::EVENT_ENTER:
+#if TRACE_VERBOSE
+ std::cerr << "\tENTER\n";
+#endif
parse_enter(mode);
break;
- case Trace::EVENT_LEAVE:
- return parse_leave(mode);
+ case trace::EVENT_LEAVE:
+#if TRACE_VERBOSE
+ std::cerr << "\tLEAVE\n";
+#endif
+ call = parse_leave(mode);
+ if (call) {
+ adjust_call_flags(call);
+ return call;
+ }
+ break;
default:
std::cerr << "error: unknown event " << c << "\n";
exit(1);
case -1:
if (!calls.empty()) {
- Call *call = calls.front();
- std::cerr << call->no << ": warning: incomplete call " << call->name() << "\n";
+ call = calls.front();
+ call->flags |= CALL_FLAG_INCOMPLETE;
calls.pop_front();
+ adjust_call_flags(call);
return call;
}
return NULL;
}
-FunctionSig *Parser::parse_function_sig(void) {
+Parser::FunctionSigFlags *
+Parser::parse_function_sig(void) {
size_t id = read_uint();
FunctionSigState *sig = lookup(functions, id);
arg_names[i] = read_string();
}
sig->arg_names = arg_names;
+ sig->flags = lookupCallFlags(sig->name);
sig->offset = file->currentOffset();
functions[id] = sig;
+
+ /**
+ * Try to autodetect the API.
+ *
+ * XXX: Ideally we would allow to mix multiple APIs in a single trace,
+ * but as it stands today, retrace is done separately for each API.
+ */
+ if (api == API_UNKNOWN) {
+ const char *n = sig->name;
+ if ((n[0] == 'g' && n[1] == 'l' && n[2] == 'X') || // glX*
+ (n[0] == 'w' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') || // wgl[A-Z]*
+ (n[0] == 'C' && n[1] == 'G' && n[2] == 'L')) { // CGL*
+ api = trace::API_GL;
+ } else if (n[0] == 'e' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') { // egl[A-Z]*
+ api = trace::API_EGL;
+ } else if ((n[0] == 'D' &&
+ ((n[1] == 'i' && n[2] == 'r' && n[3] == 'e' && n[4] == 'c' && n[5] == 't') || // Direct*
+ (n[1] == '3' && n[2] == 'D'))) || // D3D*
+ (n[0] == 'C' && n[1] == 'r' && n[2] == 'e' && n[3] == 'a' && n[4] == 't' && n[5] == 'e')) { // Create*
+ api = trace::API_DX;
+ } else {
+ /* TODO */
+ }
+ }
+
+ /**
+ * Note down the signature of special functions for future reference.
+ *
+ * NOTE: If the number of comparisons increases we should move this to a
+ * separate function and use bisection.
+ */
+ if (sig->num_args == 0 &&
+ strcmp(sig->name, "glGetError") == 0) {
+ glGetErrorSig = sig;
+ }
+
} else if (file->currentOffset() < sig->offset) {
/* skip over the signature */
skip_string(); /* name */
- int num_args = read_uint();
+ unsigned num_args = read_uint();
for (unsigned i = 0; i < num_args; ++i) {
skip_string(); /*arg_name*/
}
}
-EnumSig *Parser::parse_enum_sig() {
+/*
+ * Old enum signatures would cover a single name/value only:
+ *
+ * enum_sig = id name value
+ * | id
+ */
+EnumSig *Parser::parse_old_enum_sig() {
size_t id = read_uint();
EnumSigState *sig = lookup(enums, id);
/* parse the signature */
sig = new EnumSigState;
sig->id = id;
- sig->name = read_string();
- Value *value = parse_value();
- sig->value = value->toSInt();
- delete value;
+ sig->num_values = 1;
+ EnumValue *values = new EnumValue[sig->num_values];
+ values->name = read_string();
+ values->value = read_sint();
+ sig->values = values;
sig->offset = file->currentOffset();
enums[id] = sig;
} else if (file->currentOffset() < sig->offset) {
}
+EnumSig *Parser::parse_enum_sig() {
+ size_t id = read_uint();
+
+ EnumSigState *sig = lookup(enums, id);
+
+ if (!sig) {
+ /* parse the signature */
+ sig = new EnumSigState;
+ sig->id = id;
+ sig->num_values = read_uint();
+ EnumValue *values = new EnumValue[sig->num_values];
+ for (EnumValue *it = values; it != values + sig->num_values; ++it) {
+ it->name = read_string();
+ it->value = read_sint();
+ }
+ sig->values = values;
+ sig->offset = file->currentOffset();
+ enums[id] = sig;
+ } else if (file->currentOffset() < sig->offset) {
+ /* skip over the signature */
+ int num_values = read_uint();
+ for (int i = 0; i < num_values; ++i) {
+ skip_string(); /*name */
+ skip_sint(); /* value */
+ }
+ }
+
+ assert(sig);
+ return sig;
+}
+
+
BitmaskSig *Parser::parse_bitmask_sig() {
size_t id = read_uint();
void Parser::parse_enter(Mode mode) {
- FunctionSig *sig = parse_function_sig();
+ unsigned thread_id;
+
+ if (version >= 4) {
+ thread_id = read_uint();
+ } else {
+ thread_id = 0;
+ }
+
+ FunctionSigFlags *sig = parse_function_sig();
- Call *call = new Call(sig);
+ Call *call = new Call(sig, sig->flags, thread_id);
call->no = next_call_no++;
}
}
if (!call) {
+ /* This might happen on random access, when an asynchronous call is stranded
+ * between two frames. We won't return this call, but we still need to skip
+ * over its data.
+ */
+ const FunctionSig sig = {0, NULL, 0, NULL};
+ call = new Call(&sig, 0, 0);
+ parse_call_details(call, SCAN);
+ delete call;
return NULL;
}
bool Parser::parse_call_details(Call *call, Mode mode) {
do {
int c = read_byte();
- switch(c) {
- case Trace::CALL_END:
+ switch (c) {
+ case trace::CALL_END:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_END\n";
+#endif
return true;
- case Trace::CALL_ARG:
+ case trace::CALL_ARG:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_ARG\n";
+#endif
parse_arg(call, mode);
break;
- case Trace::CALL_RET:
+ case trace::CALL_RET:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_RET\n";
+#endif
call->ret = parse_value(mode);
break;
+ case trace::CALL_BACKTRACE:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_BACKTRACE\n";
+#endif
+ parse_call_backtrace(call, mode);
+ break;
default:
std::cerr << "error: ("<<call->name()<< ") unknown call detail "
<< c << "\n";
} while(true);
}
+bool Parser::parse_call_backtrace(Call *call, Mode mode) {
+ Backtrace* backtrace = new Backtrace();
+ StackFrame* frame = NULL;
+ do {
+ int c = read_byte();
+ switch (c) {
+ case trace::CALL_BACKTRACE_FRAME:
+ if (frame != NULL) {
+ backtrace->addFrame(frame);
+ }
+ frame = new StackFrame();
+ break;
+ case trace::CALL_BACKTRACE_END:
+ if (frame != NULL) {
+ backtrace->addFrame(frame);
+ }
+ call->backtrace = backtrace;
+ return true;
+ case trace::CALL_BACKTRACE_MODULE:
+ frame->module = static_cast<String*>(parse_value(mode));
+ break;
+ case trace::CALL_BACKTRACE_FUNCTION:
+ frame->function = static_cast<String*>(parse_value(mode));
+ break;
+ case trace::CALL_BACKTRACE_FILENAME:
+ frame->filename = static_cast<String*>(parse_value(mode));
+ break;
+ case trace::CALL_BACKTRACE_LINENUMBER:
+ frame->linenumber = static_cast<String*>(parse_value(mode));
+ break;
+ case trace::CALL_BACKTRACE_OFFSET:
+ frame->offset = static_cast<String*>(parse_value(mode));
+ break;
+ default:
+ std::cerr << "error: ("<< call->name() << ") unknown call backtrace detail "
+ << c << "\n";
+ exit(1);
+ case -1:
+ return false;
+ }
+ } while(true);
+}
+
+/**
+ * Make adjustments to this particular call flags.
+ *
+ * NOTE: This is called per-call so no string comparisons should be done here.
+ * All name comparisons should be done when the signature is parsed instead.
+ */
+void Parser::adjust_call_flags(Call *call) {
+ // Mark glGetError() = GL_NO_ERROR as verbose
+ if (call->sig == glGetErrorSig &&
+ call->ret &&
+ call->ret->toSInt() == 0) {
+ call->flags |= CALL_FLAG_VERBOSE;
+ }
+}
void Parser::parse_arg(Call *call, Mode mode) {
unsigned index = read_uint();
if (index >= call->args.size()) {
call->args.resize(index + 1);
}
- call->args[index] = value;
+ call->args[index].value = value;
}
}
int c;
Value *value;
c = read_byte();
- switch(c) {
- case Trace::TYPE_NULL:
+ switch (c) {
+ case trace::TYPE_NULL:
value = new Null;
break;
- case Trace::TYPE_FALSE:
+ case trace::TYPE_FALSE:
value = new Bool(false);
break;
- case Trace::TYPE_TRUE:
+ case trace::TYPE_TRUE:
value = new Bool(true);
break;
- case Trace::TYPE_SINT:
+ case trace::TYPE_SINT:
value = parse_sint();
break;
- case Trace::TYPE_UINT:
+ case trace::TYPE_UINT:
value = parse_uint();
break;
- case Trace::TYPE_FLOAT:
+ case trace::TYPE_FLOAT:
value = parse_float();
break;
- case Trace::TYPE_DOUBLE:
+ case trace::TYPE_DOUBLE:
value = parse_double();
break;
- case Trace::TYPE_STRING:
+ case trace::TYPE_STRING:
value = parse_string();
break;
- case Trace::TYPE_ENUM:
+ case trace::TYPE_ENUM:
value = parse_enum();
break;
- case Trace::TYPE_BITMASK:
+ case trace::TYPE_BITMASK:
value = parse_bitmask();
break;
- case Trace::TYPE_ARRAY:
+ case trace::TYPE_ARRAY:
value = parse_array();
break;
- case Trace::TYPE_STRUCT:
+ case trace::TYPE_STRUCT:
value = parse_struct();
break;
- case Trace::TYPE_BLOB:
+ case trace::TYPE_BLOB:
value = parse_blob();
break;
- case Trace::TYPE_OPAQUE:
+ case trace::TYPE_OPAQUE:
value = parse_opaque();
break;
+ case trace::TYPE_REPR:
+ value = parse_repr();
+ break;
default:
std::cerr << "error: unknown type " << c << "\n";
exit(1);
}
#if TRACE_VERBOSE
if (value) {
- std::cerr << "\tVALUE " << value << "\n";
+ std::cerr << "\tVALUE ";
+ trace::dump(value, std::cerr);
+ std::cerr << "\n";
}
#endif
return value;
void Parser::scan_value(void) {
int c = read_byte();
- switch(c) {
- case Trace::TYPE_NULL:
- case Trace::TYPE_FALSE:
- case Trace::TYPE_TRUE:
+ switch (c) {
+ case trace::TYPE_NULL:
+ case trace::TYPE_FALSE:
+ case trace::TYPE_TRUE:
break;
- case Trace::TYPE_SINT:
+ case trace::TYPE_SINT:
scan_sint();
break;
- case Trace::TYPE_UINT:
+ case trace::TYPE_UINT:
scan_uint();
break;
- case Trace::TYPE_FLOAT:
+ case trace::TYPE_FLOAT:
scan_float();
break;
- case Trace::TYPE_DOUBLE:
+ case trace::TYPE_DOUBLE:
scan_double();
break;
- case Trace::TYPE_STRING:
+ case trace::TYPE_STRING:
scan_string();
break;
- case Trace::TYPE_ENUM:
+ case trace::TYPE_ENUM:
scan_enum();
break;
- case Trace::TYPE_BITMASK:
+ case trace::TYPE_BITMASK:
scan_bitmask();
break;
- case Trace::TYPE_ARRAY:
+ case trace::TYPE_ARRAY:
scan_array();
break;
- case Trace::TYPE_STRUCT:
+ case trace::TYPE_STRUCT:
scan_struct();
break;
- case Trace::TYPE_BLOB:
+ case trace::TYPE_BLOB:
scan_blob();
break;
- case Trace::TYPE_OPAQUE:
+ case trace::TYPE_OPAQUE:
scan_opaque();
break;
+ case trace::TYPE_REPR:
+ scan_repr();
+ break;
default:
std::cerr << "error: unknown type " << c << "\n";
exit(1);
Value *Parser::parse_double() {
double value;
file->read(&value, sizeof value);
- return new Float(value);
+ return new Double(value);
}
Value *Parser::parse_enum() {
- EnumSig *sig = parse_enum_sig();
- return new Enum(sig);
+ EnumSig *sig;
+ signed long long value;
+ if (version >= 3) {
+ sig = parse_enum_sig();
+ value = read_sint();
+ } else {
+ sig = parse_old_enum_sig();
+ assert(sig->num_values == 1);
+ value = sig->values->value;
+ }
+ return new Enum(sig, value);
}
void Parser::scan_enum() {
- parse_enum_sig();
+ if (version >= 3) {
+ parse_enum_sig();
+ skip_sint();
+ } else {
+ parse_old_enum_sig();
+ }
}
size_t size = read_uint();
Blob *blob = new Blob(size);
if (size) {
- file->read(blob->buf, (unsigned)size);
+ file->read(blob->buf, size);
}
return blob;
}
}
+Value *Parser::parse_repr() {
+ Value *humanValue = parse_value();
+ Value *machineValue = parse_value();
+ return new Repr(humanValue, machineValue);
+}
+
+
+void Parser::scan_repr() {
+ scan_value();
+ scan_value();
+}
+
+
const char * Parser::read_string(void) {
size_t len = read_uint();
char * value = new char[len + 1];
if (len) {
- file->read(value, (unsigned)len);
+ file->read(value, len);
}
value[len] = 0;
#if TRACE_VERBOSE
}
+/*
+ * For the time being, a signed int is encoded as any other value, but we here parse
+ * it without the extra baggage of the Value class.
+ */
+signed long long
+Parser::read_sint(void) {
+ int c;
+ c = read_byte();
+ switch (c) {
+ case trace::TYPE_SINT:
+ return -(signed long long)read_uint();
+ case trace::TYPE_UINT:
+ return read_uint();
+ default:
+ std::cerr << "error: unexpected type " << c << "\n";
+ exit(1);
+ case -1:
+ return 0;
+ }
+}
+
+void
+Parser::skip_sint(void) {
+ skip_byte();
+ skip_uint();
+}
+
unsigned long long Parser::read_uint(void) {
unsigned long long value = 0;
int c;
}
-} /* namespace Trace */
+} /* namespace trace */