summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJose Fonseca <jfonseca@vmware.com>2016-05-05 15:20:03 +0100
committerJose Fonseca <jfonseca@vmware.com>2016-05-05 15:20:03 +0100
commit5e9a2b11b248aaa2f6f353bbcf9f600d65bcaf51 (patch)
treeb3178d019ae82f3088a75315a6b7534c7a792414 /lib
parentac4cdf379e076c69c7c5ec9d6766362f15968b66 (diff)
trace: Move the rest of common to lib/trace.
Renaming the library to something else is left to another change, as there's already a trace lib in the wrappers dir.
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/trace/CMakeLists.txt37
-rw-r--r--lib/trace/trace_api.hpp56
-rw-r--r--lib/trace/trace_callset.cpp264
-rw-r--r--lib/trace/trace_callset.hpp195
-rw-r--r--lib/trace/trace_dump.cpp327
-rw-r--r--lib/trace/trace_dump.hpp76
-rw-r--r--lib/trace/trace_dump_internal.hpp81
-rw-r--r--lib/trace/trace_fast_callset.cpp210
-rw-r--r--lib/trace/trace_fast_callset.hpp140
-rw-r--r--lib/trace/trace_file.cpp60
-rw-r--r--lib/trace/trace_file.hpp169
-rw-r--r--lib/trace/trace_file_brotli.cpp150
-rw-r--r--lib/trace/trace_file_read.cpp70
-rw-r--r--lib/trace/trace_file_snappy.cpp353
-rw-r--r--lib/trace/trace_file_zlib.cpp144
-rw-r--r--lib/trace/trace_format.hpp83
-rw-r--r--lib/trace/trace_lookup.hpp109
-rw-r--r--lib/trace/trace_model.cpp301
-rw-r--r--lib/trace/trace_model.hpp593
-rw-r--r--lib/trace/trace_option.cpp62
-rw-r--r--lib/trace/trace_option.hpp38
-rw-r--r--lib/trace/trace_ostream.hpp52
-rw-r--r--lib/trace/trace_ostream_snappy.cpp207
-rw-r--r--lib/trace/trace_ostream_zlib.cpp93
-rw-r--r--lib/trace/trace_parser.cpp1039
-rw-r--r--lib/trace/trace_parser.hpp248
-rw-r--r--lib/trace/trace_parser_flags.cpp550
-rw-r--r--lib/trace/trace_parser_flags_test.cpp644
-rw-r--r--lib/trace/trace_parser_loop.cpp111
-rw-r--r--lib/trace/trace_profiler.cpp259
-rw-r--r--lib/trace/trace_profiler.hpp143
-rw-r--r--lib/trace/trace_snappy.hpp34
-rw-r--r--lib/trace/trace_writer.cpp372
-rw-r--r--lib/trace/trace_writer.hpp116
-rw-r--r--lib/trace/trace_writer_local.cpp287
-rw-r--r--lib/trace/trace_writer_local.hpp121
-rw-r--r--lib/trace/trace_writer_model.cpp149
38 files changed, 7944 insertions, 0 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 23be7605..be8a4454 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -2,3 +2,4 @@ add_subdirectory (os)
add_subdirectory (highlight)
add_subdirectory (guids)
add_subdirectory (image)
+add_subdirectory (trace)
diff --git a/lib/trace/CMakeLists.txt b/lib/trace/CMakeLists.txt
new file mode 100644
index 00000000..a3615616
--- /dev/null
+++ b/lib/trace/CMakeLists.txt
@@ -0,0 +1,37 @@
+include_directories (
+ ${CMAKE_SOURCE_DIR}/lib/guids
+ ${CMAKE_SOURCE_DIR}/lib/highlight
+ ${CMAKE_SOURCE_DIR}/thirdparty
+)
+
+add_convenience_library (common
+ trace_callset.cpp
+ trace_dump.cpp
+ trace_fast_callset.cpp
+ trace_file.cpp
+ trace_file_read.cpp
+ trace_file_zlib.cpp
+ trace_file_brotli.cpp
+ trace_file_snappy.cpp
+ trace_model.cpp
+ trace_parser.cpp
+ trace_parser_flags.cpp
+ trace_parser_loop.cpp
+ trace_writer.cpp
+ trace_writer_local.cpp
+ trace_writer_model.cpp
+ trace_profiler.cpp
+ trace_option.cpp
+ trace_ostream_snappy.cpp
+ trace_ostream_zlib.cpp
+)
+
+target_link_libraries (common
+ guids
+ highlight
+ os
+ brotli_dec_bundled
+)
+
+add_gtest (trace_parser_flags_test trace_parser_flags_test.cpp)
+target_link_libraries (trace_parser_flags_test common)
diff --git a/lib/trace/trace_api.hpp b/lib/trace/trace_api.hpp
new file mode 100644
index 00000000..3b119f8e
--- /dev/null
+++ b/lib/trace/trace_api.hpp
@@ -0,0 +1,56 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#pragma once
+
+
+#include <stdlib.h>
+
+#include "os_string.hpp"
+
+
+namespace trace {
+
+
+/**
+ * Enum to distuinguish the API for tools.
+ *
+ * It should never be embedded in the trace file.
+ */
+enum API {
+ API_UNKNOWN = 0,
+ API_GL, // GL + GLX/WGL/CGL
+ API_EGL, // GL/GLES1/GLES2/VG + EGL
+ API_DX, // All DirectX
+ API_D3D7,
+ API_D3D8,
+ API_D3D9,
+ API_DXGI, // D3D10.x, D3D11.x
+ API_D2D1, // Direct2D
+};
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_callset.cpp b/lib/trace/trace_callset.cpp
new file mode 100644
index 00000000..28cbf24c
--- /dev/null
+++ b/lib/trace/trace_callset.cpp
@@ -0,0 +1,264 @@
+/**************************************************************************
+ *
+ * Copyright 2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <trace_callset.hpp>
+
+using namespace trace;
+
+
+// Parser class for call sets
+class CallSetParser
+{
+ CallSet &set;
+
+protected:
+ char lookahead;
+
+ CallSetParser(CallSet &_set) :
+ set(_set),
+ lookahead(0)
+ {}
+
+public:
+ void parse() {
+ skipWhiteSpace();
+ while (lookahead) {
+ assert(!isSpace());
+ parseRange();
+ // skip any comma
+ isOperator(',');
+ }
+ }
+
+private:
+ void parseRange() {
+ CallNo start = std::numeric_limits<CallNo>::min();
+ CallNo stop = std::numeric_limits<CallNo>::max();
+ CallNo step = 1;
+ CallFlags freq = FREQUENCY_ALL;
+ if (isAlpha()) {
+ freq = parseFrequency();
+ } else {
+ if (isOperator('*')) {
+ // no-change
+ } else {
+ start = parseCallNo();
+ if (isOperator('-')) {
+ if (isDigit()) {
+ stop = parseCallNo();
+ } else {
+ // no-change
+ }
+ } else {
+ stop = start;
+ }
+ }
+ if (isOperator('/')) {
+ if (isDigit()) {
+ step = parseCallNo();
+ } else {
+ freq = parseFrequency();
+ }
+ }
+ }
+ set.addRange(CallRange(start, stop, step, freq));
+ }
+
+ // match and consume an operator
+ bool isOperator(char c) {
+ if (lookahead == c) {
+ consume();
+ skipWhiteSpace();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ CallNo parseCallNo() {
+ CallNo number = 0;
+ if (isDigit()) {
+ do {
+ CallNo digit = consume() - '0';
+ number = number * 10 + digit;
+ } while (isDigit());
+ } else {
+ std::cerr << "error: expected digit, found '" << lookahead << "'\n";
+ exit(0);
+ }
+ skipWhiteSpace();
+ return number;
+ }
+
+ CallFlags parseFrequency() {
+ std::string freq;
+ if (isAlpha()) {
+ do {
+ freq.push_back(consume());
+ } while (isAlpha());
+ } else {
+ std::cerr << "error: expected frequency, found '" << lookahead << "'\n";
+ exit(0);
+ }
+ skipWhiteSpace();
+ if (freq == "frame") {
+ return FREQUENCY_FRAME;
+ } else if (freq == "rendertarget" || freq == "fbo") {
+ return FREQUENCY_RENDERTARGET;
+ } else if (freq == "render" || freq == "draw") {
+ return FREQUENCY_RENDER;
+ } else {
+ std::cerr << "error: expected frequency, found '" << freq << "'\n";
+ exit(0);
+ return FREQUENCY_NONE;
+ }
+ }
+
+ // match lookahead with a digit (does not consume)
+ bool isDigit() const {
+ return lookahead >= '0' && lookahead <= '9';
+ }
+
+ bool isAlpha() const {
+ return lookahead >= 'a' && lookahead <= 'z';
+ }
+
+ void skipWhiteSpace() {
+ while (isSpace()) {
+ consume();
+ }
+ }
+
+ bool isSpace() const {
+ return lookahead == ' ' ||
+ lookahead == '\t' ||
+ lookahead == '\r' ||
+ lookahead == '\n';
+ }
+
+ virtual char consume() = 0;
+};
+
+
+class StringCallSetParser : public CallSetParser
+{
+ const char *buf;
+
+public:
+ StringCallSetParser(CallSet &_set, const char *_buf) :
+ CallSetParser(_set),
+ buf(_buf)
+ {
+ lookahead = *buf;
+ }
+
+ char consume() override {
+ char c = lookahead;
+ if (lookahead) {
+ ++buf;
+ lookahead = *buf;
+ }
+ return c;
+ }
+};
+
+
+class FileCallSetParser : public CallSetParser
+{
+ std::ifstream stream;
+
+public:
+ FileCallSetParser(CallSet &_set, const char *filename) :
+ CallSetParser(_set)
+ {
+ stream.open(filename);
+ if (!stream.is_open()) {
+ std::cerr << "error: failed to open \"" << filename << "\"\n";
+ exit(1);
+ }
+
+ stream.get(lookahead);
+ }
+
+ char consume() override {
+ char c = lookahead;
+ if (stream.eof()) {
+ lookahead = 0;
+ } else {
+ stream.get(lookahead);
+ }
+ return c;
+ }
+};
+
+
+void
+CallSet::merge(const char *string)
+{
+ if (firstmerge) {
+ if (!empty()) {
+ *this = CallSet();
+ }
+ firstmerge = false;
+ }
+
+ /*
+ * Parse a comma-separated list of files or ranges
+ */
+ std::stringstream calls_arg(string);
+ std::string token;
+ const char *str;
+
+ while (std::getline(calls_arg, token, ',')) {
+ str = token.c_str();
+ if (str[0] == '@') {
+ FileCallSetParser parser(*this, &str[1]);
+ parser.parse();
+ } else {
+ StringCallSetParser parser(*this, str);
+ parser.parse();
+ }
+ }
+}
+
+
+CallSet::CallSet(CallFlags freq): limits(std::numeric_limits<CallNo>::min(), std::numeric_limits<CallNo>::max()), firstmerge(true) {
+ if (freq != FREQUENCY_NONE) {
+ CallNo start = std::numeric_limits<CallNo>::min();
+ CallNo stop = std::numeric_limits<CallNo>::max();
+ CallNo step = 1;
+ addRange(CallRange(start, stop, step, freq));
+ assert(!empty());
+ }
+}
+
diff --git a/lib/trace/trace_callset.hpp b/lib/trace/trace_callset.hpp
new file mode 100644
index 00000000..4070a4b8
--- /dev/null
+++ b/lib/trace/trace_callset.hpp
@@ -0,0 +1,195 @@
+/**************************************************************************
+ *
+ * Copyright 2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Representation of call sets.
+ *
+ * Grammar:
+ *
+ * set = '@' filename
+ * | range ( ',' ? range ) *
+ *
+ * range = interval ( '/' frequency )
+ *
+ * interval = '*'
+ * | number
+ * | start_number '-' end_number
+ *
+ * frequency = divisor
+ * | "frame"
+ * | "rendertarget" | "fbo"
+ * | "render | "draw"
+ *
+ */
+
+#pragma once
+
+
+#include <limits>
+#include <list>
+
+#include "trace_model.hpp"
+#include "trace_fast_callset.hpp"
+
+namespace trace {
+
+ // Aliases for call flags
+ enum {
+ FREQUENCY_NONE = 0,
+ FREQUENCY_FRAME = CALL_FLAG_END_FRAME,
+ FREQUENCY_RENDERTARGET = CALL_FLAG_END_FRAME | CALL_FLAG_SWAP_RENDERTARGET,
+ FREQUENCY_RENDER = CALL_FLAG_RENDER,
+ FREQUENCY_ALL = 0xffffffff
+ };
+
+ // A linear range of calls
+ class CallRange
+ {
+ public:
+ CallNo start;
+ CallNo stop;
+ CallNo step;
+ CallFlags freq;
+
+ CallRange(CallNo callNo) :
+ start(callNo),
+ stop(callNo),
+ step(1),
+ freq(FREQUENCY_ALL)
+ {}
+
+ CallRange(CallNo _start, CallNo _stop, CallNo _step = 1, CallFlags _freq = FREQUENCY_ALL) :
+ start(_start),
+ stop(_stop),
+ step(_step),
+ freq(_freq)
+ {}
+
+ bool
+ contains(CallNo callNo, CallFlags callFlags) const {
+ return callNo >= start &&
+ callNo <= stop &&
+ ((callNo - start) % step) == 0 &&
+ ((callFlags & freq) ||
+ freq == FREQUENCY_ALL);
+ }
+ };
+
+
+ // A collection of call ranges
+ class CallSet
+ {
+ private:
+ CallRange limits;
+ bool firstmerge;
+
+ public:
+ FastCallSet fast_call_set;
+
+ // TODO: use binary tree to speed up lookups, (less important
+ // now that we are using FastCallSet for ranges without step
+ // or freq).
+ typedef std::list< CallRange > RangeList;
+ RangeList ranges;
+
+ CallSet(): limits(std::numeric_limits<CallNo>::min(), std::numeric_limits<CallNo>::max()), firstmerge(true) {}
+
+ CallSet(CallFlags freq);
+
+ void
+ merge(const char *str);
+
+ // Not empty set
+ inline bool
+ empty() const {
+ return fast_call_set.empty() && ranges.empty();
+ }
+
+ void
+ addRange(const CallRange & range) {
+ if (range.start <= range.stop &&
+ range.freq != FREQUENCY_NONE) {
+
+ if (empty()) {
+ limits.start = range.start;
+ limits.stop = range.stop;
+ } else {
+ if (range.start < limits.start)
+ limits.start = range.start;
+ if (range.stop > limits.stop)
+ limits.stop = range.stop;
+ }
+
+ /* Optimize by using fast_call_set whenever possible */
+ if (range.step == 1 && range.freq == FREQUENCY_ALL) {
+ fast_call_set.add(range.start, range.stop);
+ } else {
+ RangeList::iterator it = ranges.begin();
+ while (it != ranges.end() && it->start < range.start) {
+ ++it;
+ }
+
+ ranges.insert(it, range);
+ }
+ }
+ }
+
+ inline bool
+ contains(CallNo callNo, CallFlags callFlags = FREQUENCY_ALL) const {
+ if (empty()) {
+ return false;
+ }
+ if (fast_call_set.contains(callNo))
+ return true;
+ RangeList::const_iterator it;
+ for (it = ranges.begin(); it != ranges.end() && it->start <= callNo; ++it) {
+ if (it->contains(callNo, callFlags)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline bool
+ contains(const trace::Call &call) {
+ return contains(call.no, call.flags);
+ }
+
+ CallNo getFirst() {
+ return limits.start;
+ }
+
+ CallNo getLast() {
+ return limits.stop;
+ }
+ };
+
+
+ CallSet parse(const char *string);
+
+
+} /* namespace trace */
+
+
diff --git a/lib/trace/trace_dump.cpp b/lib/trace/trace_dump.cpp
new file mode 100644
index 00000000..6b73c930
--- /dev/null
+++ b/lib/trace/trace_dump.cpp
@@ -0,0 +1,327 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_dump_internal.hpp"
+
+#include <limits>
+
+#include <assert.h>
+#include <string.h>
+
+#include "highlight.hpp"
+#include "guids.hpp"
+
+
+namespace trace {
+
+
+Dumper::Dumper(std::ostream &_os, DumpFlags _flags) :
+ os(_os),
+ dumpFlags(_flags),
+ highlighter(highlight::defaultHighlighter(!(dumpFlags & DUMP_FLAG_NO_COLOR))),
+ normal(highlighter.normal()),
+ bold(highlighter.bold()),
+ italic(highlighter.italic()),
+ strike(highlighter.strike()),
+ red(highlighter.color(highlight::RED)),
+ pointer(highlighter.color(highlight::GREEN)),
+ literal(highlighter.color(highlight::BLUE))
+{
+}
+
+Dumper::~Dumper() {
+}
+
+void Dumper::visit(Null *) {
+ os << literal << "NULL" << normal;
+}
+
+void Dumper::visit(Bool *node) {
+ os << literal << (node->value ? "true" : "false") << normal;
+}
+
+void Dumper::visit(SInt *node) {
+ os << literal << node->value << normal;
+}
+
+void Dumper::visit(UInt *node) {
+ os << literal << node->value << normal;
+}
+
+void Dumper::visit(Float *node) {
+ std::streamsize oldPrecision = os.precision(std::numeric_limits<float>::digits10 + 1);
+ os << literal << node->value << normal;
+ os.precision(oldPrecision);
+}
+
+void Dumper::visit(Double *node) {
+ std::streamsize oldPrecision = os.precision(std::numeric_limits<double>::digits10 + 1);
+ os << literal << node->value << normal;
+ os.precision(oldPrecision);
+}
+
+template< typename C >
+void Dumper::visitString(const C *value) {
+ os << literal << "\"";
+ for (const C *it = value; *it; ++it) {
+ unsigned c = (unsigned) *it;
+ if (c == '\"')
+ os << "\\\"";
+ else if (c == '\\')
+ os << "\\\\";
+ else if (c >= 0x20 && c <= 0x7e)
+ os << (char)c;
+ else if (c == '\t') {
+ os << "\t";
+ } else if (c == '\r') {
+ // Ignore carriage-return
+ } else if (c == '\n') {
+ if (dumpFlags & DUMP_FLAG_NO_MULTILINE) {
+ os << "\\n";
+ } else {
+ // Reset formatting so that it looks correct with 'less -R'
+ os << normal << '\n' << literal;
+ }
+ } else {
+ // FIXME: handle wchar_t octals properly
+ unsigned octal0 = c & 0x7;
+ unsigned octal1 = (c >> 3) & 0x7;
+ unsigned octal2 = (c >> 3) & 0x7;
+ os << "\\";
+ if (octal2)
+ os << octal2;
+ if (octal1)
+ os << octal1;
+ os << octal0;
+ }
+ }
+ os << "\"" << normal;
+}
+
+void Dumper::visit(String *node) {
+ visitString(node->value);
+}
+
+void Dumper::visit(WString *node) {
+ os << literal << "L";
+ visitString(node->value);
+}
+
+void Dumper::visit(Enum *node) {
+ const EnumValue *it = node->lookup();
+ if (it) {
+ os << literal << it->name << normal;
+ return;
+ }
+ os << literal << node->value << normal;
+}
+
+void Dumper::visit(Bitmask *bitmask) {
+ unsigned long long value = bitmask->value;
+ const BitmaskSig *sig = bitmask->sig;
+ bool first = true;
+ for (const BitmaskFlag *it = sig->flags; it != sig->flags + sig->num_flags; ++it) {
+ assert(it->value || first);
+ if ((it->value && (value & it->value) == it->value) ||
+ (!it->value && value == 0)) {
+ if (!first) {
+ os << " | ";
+ }
+ os << literal << it->name << normal;
+ value &= ~it->value;
+ first = false;
+ }
+ if (value == 0) {
+ break;
+ }
+ }
+ if (value || first) {
+ if (!first) {
+ os << " | ";
+ }
+ os << literal << "0x" << std::hex << value << std::dec << normal;
+ }
+}
+
+const char *
+Dumper::visitMembers(Struct *s, const char *sep) {
+ for (unsigned i = 0; i < s->members.size(); ++i) {
+ const char *memberName = s->sig->member_names[i];
+ Value *memberValue = s->members[i];
+
+ if (!memberName || !*memberName) {
+ // Anonymous structure
+ Struct *memberStruct = memberValue->toStruct();
+ assert(memberStruct);
+ if (memberStruct) {
+ sep = visitMembers(memberStruct, sep);
+ continue;
+ }
+ }
+
+ os << sep << italic << memberName << normal << " = ",
+ _visit(memberValue);
+ sep = ", ";
+ }
+ return sep;
+}
+
+void Dumper::visit(Struct *s) {
+ // Replace GUIDs with their symbolic name
+ // TODO: Move this to parsing, so it can be shared everywhere
+ if (s->members.size() == 4 &&
+ strcmp(s->sig->name, "GUID") == 0) {
+ GUID guid;
+ guid.Data1 = s->members[0]->toUInt();
+ guid.Data2 = s->members[1]->toUInt();
+ guid.Data3 = s->members[2]->toUInt();
+ Array *data4 = s->members[3]->toArray();
+ assert(data4);
+ assert(data4->values.size() == 8);
+ for (int i = 0; i < sizeof guid.Data4; ++i) {
+ guid.Data4[i] = data4->values[i]->toUInt();
+ }
+ const char *name = getGuidName(guid);
+ os << literal << name << normal;
+ return;
+ }
+
+ os << "{";
+ visitMembers(s);
+ os << "}";
+}
+
+void Dumper::visit(Array *array) {
+ if (array->values.size() == 1) {
+ os << "&";
+ _visit(array->values[0]);
+ }
+ else {
+ const char *sep = "";
+ os << "{";
+ for (auto & value : array->values) {
+ os << sep;
+ _visit(value);
+ sep = ", ";
+ }
+ os << "}";
+ }
+}
+
+void Dumper::visit(Blob *blob) {
+ os << pointer << "blob(" << blob->size << ")" << normal;
+}
+
+void Dumper::visit(Pointer *p) {
+ os << pointer << "0x" << std::hex << p->value << std::dec << normal;
+}
+
+void Dumper::visit(Repr *r) {
+ _visit(r->humanValue);
+}
+
+void Dumper::visit(StackFrame *frame) {
+ frame->dump(os);
+}
+
+void Dumper::visit(Backtrace & backtrace) {
+ for (auto & frame : backtrace) {
+ visit(frame);
+ os << "\n";
+ }
+}
+
+void Dumper::visit(Call *call) {
+ CallFlags callFlags = call->flags;
+
+ if (!(dumpFlags & DUMP_FLAG_NO_CALL_NO)) {
+ os << call->no << " ";
+ }
+ if (dumpFlags & DUMP_FLAG_THREAD_IDS) {
+ os << "@" << std::hex << call->thread_id << std::dec << " ";
+ }
+
+ if (callFlags & CALL_FLAG_NON_REPRODUCIBLE) {
+ os << strike;
+ } else if (callFlags & (CALL_FLAG_FAKE | CALL_FLAG_NO_SIDE_EFFECTS)) {
+ os << normal;
+ } else {
+ os << bold;
+ }
+ os << call->sig->name << normal;
+
+ os << "(";
+ const char *sep = "";
+ for (unsigned i = 0; i < call->args.size(); ++i) {
+ os << sep;
+ if (!(dumpFlags & DUMP_FLAG_NO_ARG_NAMES)) {
+ os << italic << call->sig->arg_names[i] << normal << " = ";
+ }
+ if (call->args[i].value) {
+ _visit(call->args[i].value);
+ } else {
+ os << "?";
+ }
+ sep = ", ";
+ }
+ os << ")";
+
+ if (call->ret) {
+ os << " = ";
+ _visit(call->ret);
+ }
+
+ if (callFlags & CALL_FLAG_INCOMPLETE) {
+ os << " // " << red << "incomplete" << normal;
+ }
+
+ if (!(dumpFlags & DUMP_FLAG_NO_MULTILINE)) {
+ os << "\n";
+
+ if (call->backtrace != NULL) {
+ os << bold << red << "Backtrace:\n" << normal;
+ visit(*call->backtrace);
+ }
+ if (callFlags & CALL_FLAG_END_FRAME) {
+ os << "\n";
+ }
+ }
+}
+
+
+void dump(Value *value, std::ostream &os, DumpFlags flags) {
+ Dumper d(os, flags);
+ value->visit(d);
+}
+
+
+void dump(Call &call, std::ostream &os, DumpFlags flags) {
+ Dumper d(os, flags);
+ d.visit(&call);
+}
+
+
+} /* namespace trace */
diff --git a/lib/trace/trace_dump.hpp b/lib/trace/trace_dump.hpp
new file mode 100644
index 00000000..4c756ca4
--- /dev/null
+++ b/lib/trace/trace_dump.hpp
@@ -0,0 +1,76 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Human-readible dumping.
+ */
+
+#pragma once
+
+
+#include <iostream>
+
+#include "trace_model.hpp"
+
+
+namespace trace {
+
+
+typedef unsigned DumpFlags;
+
+enum {
+ DUMP_FLAG_NO_COLOR = (1 << 0),
+ DUMP_FLAG_NO_ARG_NAMES = (1 << 1),
+ DUMP_FLAG_NO_CALL_NO = (1 << 2),
+ DUMP_FLAG_THREAD_IDS = (1 << 3),
+ DUMP_FLAG_NO_MULTILINE = (1 << 4),
+};
+
+
+void dump(Value *value, std::ostream &os, DumpFlags flags = 0);
+
+
+inline std::ostream &
+operator << (std::ostream &os, Value & value) {
+ dump(& value, os);
+ return os;
+}
+
+
+std::ostream &
+operator << (std::ostream &os, Value *value);
+
+
+void dump(Call &call, std::ostream &os, DumpFlags flags = 0);
+
+
+inline std::ostream & operator <<(std::ostream &os, Call &call) {
+ dump(call, os);
+ return os;
+}
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_dump_internal.hpp b/lib/trace/trace_dump_internal.hpp
new file mode 100644
index 00000000..26ce10a9
--- /dev/null
+++ b/lib/trace/trace_dump_internal.hpp
@@ -0,0 +1,81 @@
+/**************************************************************************
+ *
+ * Copyright 2010-2016 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#pragma once
+
+#include "highlight.hpp"
+#include "trace_dump.hpp"
+
+
+namespace trace {
+
+
+class Dumper : public Visitor
+{
+protected:
+ std::ostream &os;
+ DumpFlags dumpFlags;
+ const highlight::Highlighter & highlighter;
+ const highlight::Attribute & normal;
+ const highlight::Attribute & bold;
+ const highlight::Attribute & italic;
+ const highlight::Attribute & strike;
+ const highlight::Attribute & red;
+ const highlight::Attribute & pointer;
+ const highlight::Attribute & literal;
+
+public:
+ Dumper(std::ostream &_os, DumpFlags _flags);
+ ~Dumper();
+
+ virtual void visit(Null *) override;
+ virtual void visit(Bool *node) override;
+ virtual void visit(SInt *node) override;
+ virtual void visit(UInt *node) override;
+ virtual void visit(Float *node) override;
+ virtual void visit(Double *node) override;
+
+ template< typename C >
+ void visitString(const C *value);
+ virtual void visit(String *node) override;
+ virtual void visit(WString *node) override;
+ virtual void visit(Enum *node) override;
+ virtual void visit(Bitmask *bitmask) override;
+ virtual const char *
+ visitMembers(Struct *s, const char *sep = "");
+ virtual void visit(Struct *s) override;
+ virtual void visit(Array *array) override;
+ virtual void visit(Blob *blob) override;
+ virtual void visit(Pointer *p) override;
+ virtual void visit(Repr *r) override;
+ virtual void visit(StackFrame *frame);
+ virtual void visit(Backtrace & backtrace);
+ virtual void visit(Call *call);
+};
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_fast_callset.cpp b/lib/trace/trace_fast_callset.cpp
new file mode 100644
index 00000000..2ac5343a
--- /dev/null
+++ b/lib/trace/trace_fast_callset.cpp
@@ -0,0 +1,210 @@
+/*********************************************************************
+ *
+ * Copyright 2006 Keith Packard and Carl Worth
+ * Copyright 2012 Intel Corporation
+ * Copyright 2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <vector>
+
+#include "os.hpp"
+#include "trace_fast_callset.hpp"
+
+using namespace trace;
+
+#define MAX_LEVEL 16
+
+FastCallRange::FastCallRange(CallNo first, CallNo last, int level)
+: ref_counter(0)
+{
+ this->first = first;
+ this->last = last;
+ this->level = level;
+
+ next.resize(level, 0);
+}
+
+bool
+FastCallRange::contains(CallNo call_no) const
+{
+ return (first <= call_no && last >= call_no);
+}
+
+bool
+FastCallSet::empty(void) const
+{
+ return max_level == 0;
+}
+
+FastCallSet::FastCallSet(): head(0, 0, MAX_LEVEL)
+{
+ head.first = std::numeric_limits<CallNo>::max();
+ head.last = std::numeric_limits<CallNo>::min();
+
+ max_level = 0;
+}
+
+/*
+ * Generate a random level number, distributed
+ * so that each level is 1/4 as likely as the one before
+ *
+ * Note that level numbers run 1 <= level < MAX_LEVEL
+ */
+static int
+random_level (void)
+{
+ /* tricky bit -- each bit is '1' 75% of the time */
+ long int bits = os::random() | os::random();
+ int level = 1;
+
+ while (level < MAX_LEVEL)
+ {
+ if (bits & 1)
+ break;
+ level++;
+ bits >>= 1;
+ }
+
+ return level;
+}
+
+void
+FastCallSet::add(CallNo first, CallNo last)
+{
+ std::vector<FastCallRangePtr*> update (MAX_LEVEL);
+ FastCallRange *node;
+ FastCallRangePtr new_node;
+ int i, level;
+
+ /* Find node immediately before insertion point.
+ * NOTE: FastCallRangePtr(), e.g., next[i](), returns FastCallRange* */
+ node = &head; // Can't reference &head as a FastCallRangePtr
+ for (i = max_level - 1; i >= 0; i--) {
+ while (node->next[i]() && first > node->next[i]->last) {
+ node = node->next[i]();
+ }
+ update[i] = &node->next[i];
+ }
+
+ /* Can we contain first by expanding tail of current range by 1? */
+ if (node != &head && node->last == first - 1) {
+
+ new_node = FastCallRangePtr(node);
+ new_node->last = last;
+ goto MERGE_NODE_WITH_SUBSEQUENT_COVERED_NODES;
+
+ }
+
+ /* Current range could not contain first, look at next. */
+ node = node->next[0]();
+
+ if (node) {
+ /* Do nothing if new range is already entirely contained. */
+ if (node->first <= first && node->last >= last) {
+ return;
+ }
+
+ /* If last is already included, we can simply expand
+ * node->first to fully include the range. */
+ if (node->first <= last && node->last >= last) {
+ node->first = first;
+ return;
+ }
+
+ /* This is our candidate node if first is contained */
+ if (node->first <= first && node->last >= first) {
+ new_node = FastCallRangePtr(node);
+ new_node->last = last;
+ goto MERGE_NODE_WITH_SUBSEQUENT_COVERED_NODES;
+ }
+ }
+
+ /* Not possible to expand any existing node, so create a new one. */
+
+ level = random_level();
+
+ /* Handle first node of this level. */
+ if (level > max_level) {
+ level = max_level + 1;
+ update[max_level] = &head.next[max_level];
+ max_level = level;
+ }
+
+ new_node = FastCallRangePtr(new FastCallRange(first, last, level));
+
+ /* Perform insertion into all lists. */
+ for (i = 0; i < level; i++) {
+ new_node->next[i] = *update[i];
+ *update[i] = new_node;
+ }
+
+MERGE_NODE_WITH_SUBSEQUENT_COVERED_NODES:
+ FastCallRangePtr next = new_node->next[0];
+ node = new_node();
+ while (next() && next->first <= node->last + 1) {
+ if (next->last > node->last)
+ node->last = next->last;
+
+ for (i = 0; i < node->level && i < next->level; i++) {
+ node->next[i] = next->next[i];
+ }
+
+ for (; i < next->level; i++) {
+ *update[i] = next->next[i];
+ }
+
+ next = node->next[0];
+ }
+}
+
+void
+FastCallSet::add(CallNo call_no)
+{
+ this->add(call_no, call_no);
+}
+
+bool
+FastCallSet::contains(CallNo call_no) const
+{
+ FastCallRange *node;
+ int i;
+
+ node = const_cast<FastCallRange*>(&head);
+ for (i = max_level - 1; i >= 0; i--) {
+ while (node->next[i]() && call_no > node->next[i]->last) {
+ node = node->next[i]();
+ }
+ }
+
+ node = node->next[0]();
+
+ if (node == NULL)
+ return false;
+
+ return node->contains(call_no);
+}
diff --git a/lib/trace/trace_fast_callset.hpp b/lib/trace/trace_fast_callset.hpp
new file mode 100644
index 00000000..55d507df
--- /dev/null
+++ b/lib/trace/trace_fast_callset.hpp
@@ -0,0 +1,140 @@
+/*********************************************************************
+ *
+ * Copyright 2012 Intel Corporation
+ * Copyright 2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *********************************************************************/
+
+#pragma once
+
+#include "trace_model.hpp"
+
+namespace trace {
+
+/* A set of call numbers.
+ *
+ * This was originally designed as a more efficient replacement for
+ * std::set<unsigned> which was used heavily within the trim code's
+ * TraceAnalyzer. This is quite similar to the trace::CallSet with the
+ * following differences:
+ *
+ * Simplifications:
+ *
+ * * There is no support here for the 'step' and 'freq' features
+ * supported by trace::CallSet.
+ *
+ * Sophistications:
+ *
+ * * This callset is implemented with a skip list for
+ * (probabilistically) logarithmic lookup times for
+ * out-of-order lookups.
+ *
+ * * This callset optimizes the addition of new calls which are
+ * within or adjacent to existing ranges, (by either doing
+ * nothing, expanding the limits of an existing range, or also
+ * merging two or more ranges).
+ *
+ * It would not be impossible to extend this code to support the
+ * missing features of trace::CallSet, (though the 'step' and 'freq'
+ * features would prevent some range-extending and merging
+ * optimizations in some cases).
+ */
+
+class FastCallRangePtr;
+
+class FastCallRange {
+public:
+ CallNo first;
+ CallNo last;
+ int level;
+ std::vector<FastCallRangePtr> next;
+
+ // (NOTE: Initalize ref_counter to 0 in all constructors)
+ FastCallRange(CallNo first, CallNo last, int level);
+
+ bool contains(CallNo call_no) const;
+
+private:
+ friend class FastCallRangePtr;
+ size_t ref_counter;
+ // ref_counter must be initialized to 0 by all constructors
+ // ref_counter is the number of FastCallRangePtr objects that point at this
+};
+
+class FastCallRangePtr {
+public:
+ FastCallRange* operator-> () { return this->ptr; }
+ FastCallRange& operator * () { return *this->ptr; }
+ FastCallRange* operator ()() { return this->ptr; } // get pointer
+
+
+ FastCallRangePtr () : ptr(0) {}
+ FastCallRangePtr(FastCallRange* _ptr) : ptr(_ptr)
+ { if (this->ptr) ++this->ptr->ref_counter; }
+
+ ~FastCallRangePtr() { if (this->ptr)
+ if (--this->ptr->ref_counter == 0)
+ delete this->ptr;
+ }
+
+ FastCallRangePtr(FastCallRangePtr const& _ptr) : ptr(_ptr.ptr)
+ { if (this->ptr) ++this->ptr->ref_counter; }
+
+ FastCallRangePtr& operator= (FastCallRangePtr const& new_ptr)
+ { // DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
+ // (This order properly handles self-assignment)
+ // (This order also properly handles recursion, e.g.,
+ // if a FastCallRange contains FastCallRangePtrs)
+ FastCallRange* const old_ptr = this->ptr;
+ this->ptr = new_ptr.ptr;
+ if (this->ptr)
+ ++this->ptr->ref_counter;
+ if (old_ptr) {
+ if (--old_ptr->ref_counter == 0)
+ delete old_ptr;
+ }
+ return *this;
+ }
+private:
+ FastCallRange* ptr;
+};
+
+class FastCallSet {
+public:
+ FastCallRange head;
+ int max_level;
+
+ FastCallSet();
+
+ bool empty(void) const;
+
+ void add(CallNo first, CallNo last);
+
+ void add(CallNo call_no);
+
+ bool contains(CallNo call_no) const;
+};
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_file.cpp b/lib/trace/trace_file.cpp
new file mode 100644
index 00000000..ffe4983d
--- /dev/null
+++ b/lib/trace/trace_file.cpp
@@ -0,0 +1,60 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_file.hpp"
+
+#include <assert.h>
+
+
+using namespace trace;
+
+
+File::File(void)
+{
+}
+
+File::~File()
+{
+ // We can't invoke any overriden virtual method here anymore
+ assert(!m_isOpened);
+}
+
+
+bool File::supportsOffsets(void) const
+{
+ return false;
+}
+
+File::Offset File::currentOffset(void) const
+{
+ return 0;
+}
+
+void File::setCurrentOffset(const File::Offset &offset)
+{
+ assert(0);
+}
+
diff --git a/lib/trace/trace_file.hpp b/lib/trace/trace_file.hpp
new file mode 100644
index 00000000..26b525c3
--- /dev/null
+++ b/lib/trace/trace_file.hpp
@@ -0,0 +1,169 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#pragma once
+
+#include <fstream>
+#include <stdint.h>
+
+
+namespace trace {
+
+class File {
+public:
+ struct Offset {
+ Offset(uint64_t _chunk = 0, uint32_t _offsetInChunk = 0)
+ : chunk(_chunk),
+ offsetInChunk(_offsetInChunk)
+ {}
+ uint64_t chunk;
+ uint32_t offsetInChunk;
+ };
+
+public:
+ static File *createZLib(void);
+ static File *createBrotli(void);
+ static File *createSnappy(void);
+ static File *createForRead(const char *filename);
+public:
+ File(void);
+ virtual ~File();
+
+ bool isOpened(void) const;
+
+ bool open(const char *filename);
+ size_t read(void *buffer, size_t length);
+ void close(void);
+ int getc(void);
+ bool skip(size_t length);
+ int percentRead(void);
+
+ virtual bool supportsOffsets(void) const;
+ virtual File::Offset currentOffset(void) const;
+ virtual void setCurrentOffset(const File::Offset &offset);
+protected:
+ virtual bool rawOpen(const char *filename) = 0;
+ virtual size_t rawRead(void *buffer, size_t length) = 0;
+ virtual int rawGetc(void) = 0;
+ virtual void rawClose(void) = 0;
+ virtual bool rawSkip(size_t length) = 0;
+ virtual int rawPercentRead(void) = 0;
+
+protected:
+ bool m_isOpened = false;
+};
+
+inline bool File::isOpened(void) const
+{
+ return m_isOpened;
+}
+
+inline bool File::open(const char *filename)
+{
+ if (m_isOpened) {
+ close();
+ }
+ m_isOpened = rawOpen(filename);
+
+ return m_isOpened;
+}
+
+inline size_t File::read(void *buffer, size_t length)
+{
+ if (!m_isOpened) {
+ return 0;
+ }
+ return rawRead(buffer, length);
+}
+
+inline int File::percentRead(void)
+{
+ if (!m_isOpened) {
+ return 0;
+ }
+ return rawPercentRead();
+}
+
+inline void File::close(void)
+{
+ if (m_isOpened) {
+ rawClose();
+ m_isOpened = false;
+ }
+}
+
+inline int File::getc(void)
+{
+ if (!m_isOpened) {
+ return -1;
+ }
+ return rawGetc();
+}
+
+inline bool File::skip(size_t length)
+{
+ if (!m_isOpened) {
+ return false;
+ }
+ return rawSkip(length);
+}
+
+
+inline bool
+operator<(const File::Offset &one, const File::Offset &two)
+{
+ return one.chunk < two.chunk ||
+ (one.chunk == two.chunk && one.offsetInChunk < two.offsetInChunk);
+}
+
+inline bool
+operator==(const File::Offset &one, const File::Offset &two)
+{
+ return one.chunk == two.chunk &&
+ one.offsetInChunk == two.offsetInChunk;
+}
+
+inline bool
+operator>=(const File::Offset &one, const File::Offset &two)
+{
+ return one.chunk > two.chunk ||
+ (one.chunk == two.chunk && one.offsetInChunk >= two.offsetInChunk);
+}
+
+inline bool
+operator>(const File::Offset &one, const File::Offset &two)
+{
+ return two < one;
+}
+
+inline bool
+operator<=(const File::Offset &one, const File::Offset &two)
+{
+ return two >= one;
+}
+
+
+} /* namespace trace */
diff --git a/lib/trace/trace_file_brotli.cpp b/lib/trace/trace_file_brotli.cpp
new file mode 100644
index 00000000..e99b010e
--- /dev/null
+++ b/lib/trace/trace_file_brotli.cpp
@@ -0,0 +1,150 @@
+/**************************************************************************
+ *
+ * Copyright 2016 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_file.hpp"
+
+
+#include <assert.h>
+#include <string.h>
+
+#include <iostream>
+
+#include <brotli/dec/decode.h>
+
+#include "os.hpp"
+
+
+using namespace trace;
+
+
+class BrotliFile : public File {
+public:
+ BrotliFile(void);
+ virtual ~BrotliFile();
+
+protected:
+ virtual bool rawOpen(const char *filename) override;
+ virtual size_t rawRead(void *buffer, size_t length) override;
+ virtual int rawGetc(void) override;
+ virtual void rawClose(void) override;
+ virtual bool rawSkip(size_t length) override;
+ virtual int rawPercentRead(void) override;
+private:
+ BrotliState state;
+ std::ifstream m_stream;
+ static const size_t kFileBufferSize = 65536;
+ uint8_t input[kFileBufferSize];
+ const uint8_t* next_in;
+ size_t available_in;
+};
+
+BrotliFile::BrotliFile(void)
+{
+ BrotliStateInit(&state);
+ available_in = 0;
+ next_in = input;
+}
+
+BrotliFile::~BrotliFile()
+{
+ close();
+ BrotliStateCleanup(&state);
+}
+
+bool BrotliFile::rawOpen(const char *filename)
+{
+ std::ios_base::openmode fmode = std::fstream::binary
+ | std::fstream::in;
+
+ m_stream.open(filename, fmode);
+ return m_stream.is_open();
+}
+
+size_t BrotliFile::rawRead(void *buffer, size_t length)
+{
+ uint8_t* output = (uint8_t *)buffer;
+ size_t total_out;
+ size_t available_out = length;
+ uint8_t* next_out = output;
+
+ while (true) {
+ BrotliResult result;
+ result = BrotliDecompressStream(&available_in, &next_in,
+ &available_out, &next_out, &total_out,
+ &state);
+ if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) {
+ if (m_stream.fail()) {
+ break;
+ }
+ m_stream.read((char *)input, kFileBufferSize);
+ available_in = kFileBufferSize;
+ if (m_stream.fail()) {
+ available_in = m_stream.gcount();
+ if (!available_in) {
+ break;
+ }
+ }
+ next_in = input;
+ } else {
+ assert(result == BROTLI_RESULT_NEEDS_MORE_OUTPUT ||
+ result == BROTLI_RESULT_SUCCESS);
+ break;
+ }
+ }
+
+ assert(next_out - output <= length);
+
+ return next_out - output;
+}
+
+int BrotliFile::rawGetc()
+{
+ unsigned char c;
+ if (rawRead(&c, 1) != 1) {
+ return -1;
+ }
+ return c;
+}
+
+void BrotliFile::rawClose()
+{
+ m_stream.close();
+}
+
+bool BrotliFile::rawSkip(size_t)
+{
+ return false;
+}
+
+int BrotliFile::rawPercentRead(void)
+{
+ return 0;
+}
+
+
+File * File::createBrotli(void) {
+ return new BrotliFile;
+}
diff --git a/lib/trace/trace_file_read.cpp b/lib/trace/trace_file_read.cpp
new file mode 100644
index 00000000..fb303aa8
--- /dev/null
+++ b/lib/trace/trace_file_read.cpp
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <fstream>
+
+#include "os.hpp"
+#include "trace_file.hpp"
+#include "trace_snappy.hpp"
+
+
+using namespace trace;
+
+
+File *
+File::createForRead(const char *filename)
+{
+ std::ifstream stream(filename, std::ifstream::binary | std::ifstream::in);
+ if (!stream.is_open()) {
+ os::log("error: failed to open %s\n", filename);
+ return NULL;
+ }
+ unsigned char byte1, byte2;
+ stream >> byte1;
+ stream >> byte2;
+ stream.close();
+
+ File *file;
+ if (byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2) {
+ file = File::createSnappy();
+ } else if (byte1 == 0x1f && byte2 == 0x8b) {
+ file = File::createZLib();
+ } else {
+ // XXX: Brotli has no magic header
+ file = File::createBrotli();
+ }
+ if (!file) {
+ return NULL;
+ }
+
+ if (!file->open(filename)) {
+ os::log("error: could not open %s for reading\n", filename);
+ delete file;
+ return NULL;
+ }
+
+ return file;
+}
diff --git a/lib/trace/trace_file_snappy.cpp b/lib/trace/trace_file_snappy.cpp
new file mode 100644
index 00000000..119e4147
--- /dev/null
+++ b/lib/trace/trace_file_snappy.cpp
@@ -0,0 +1,353 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+/*
+ * Snappy file format.
+ * -------------------
+ *
+ * Snappy at its core is just a compressoin algorithm so we're
+ * creating a new file format which uses snappy compression
+ * to hold the trace data.
+ *
+ * The file is composed of a number of chunks, they are:
+ * chunk {
+ * uint32 - specifying the length of the compressed data
+ * compressed data, in little endian
+ * }
+ * File can contain any number of such chunks.
+ * The default size of an uncompressed chunk is specified in
+ * SNAPPY_CHUNK_SIZE.
+ *
+ * Note:
+ * Currently the default size for a a to-be-compressed data is
+ * 1mb, meaning that the compressed data will be <= 1mb.
+ * The reason it's 1mb is because it seems
+ * to offer a pretty good compression/disk io speed ratio
+ * but that might change.
+ *
+ */
+
+
+#include <snappy.h>
+#include <snappy-sinksource.h>
+
+#include <iostream>
+#include <algorithm>
+
+#include <assert.h>
+#include <string.h>
+
+#include "trace_file.hpp"
+#include "trace_snappy.hpp"
+
+
+#define SNAPPY_CHUNK_SIZE (1 * 1024 * 1024)
+
+
+
+using namespace trace;
+
+
+class SnappyFile : public File {
+public:
+ SnappyFile(void);
+ virtual ~SnappyFile();
+
+ virtual bool supportsOffsets(void) const override;
+ virtual File::Offset currentOffset(void) const override;
+ virtual void setCurrentOffset(const File::Offset &offset) override;
+protected:
+ virtual bool rawOpen(const char *filename) override;
+ virtual size_t rawRead(void *buffer, size_t length) override;
+ virtual int rawGetc(void) override;
+ virtual void rawClose(void) override;
+ virtual bool rawSkip(size_t length) override;
+ virtual int rawPercentRead(void) override;
+
+private:
+ inline size_t usedCacheSize(void) const
+ {
+ assert(m_cachePtr >= m_cache);
+ return m_cachePtr - m_cache;
+ }
+ inline size_t freeCacheSize(void) const
+ {
+ assert(m_cacheSize >= usedCacheSize());
+ if (m_cacheSize > 0) {
+ return m_cacheSize - usedCacheSize();
+ } else {
+ return 0;
+ }
+ }
+ inline bool endOfData(void) const
+ {
+ return m_stream.eof() && freeCacheSize() == 0;
+ }
+ void flushWriteCache(void);
+ void flushReadCache(size_t skipLength = 0);
+ void createCache(size_t size);
+ size_t readCompressedLength();
+private:
+ std::ifstream m_stream;
+ size_t m_cacheMaxSize;
+ size_t m_cacheSize;
+ char *m_cache;
+ char *m_cachePtr;
+
+ char *m_compressedCache;
+
+ uint64_t m_currentChunkOffset;
+ std::streampos m_endPos;
+};
+
+SnappyFile::SnappyFile(void)
+ : File(),
+ m_cacheMaxSize(SNAPPY_CHUNK_SIZE),
+ m_cacheSize(m_cacheMaxSize),
+ m_cache(new char [m_cacheMaxSize]),
+ m_cachePtr(m_cache)
+{
+ size_t maxCompressedLength =
+ snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
+ m_compressedCache = new char[maxCompressedLength];
+}
+
+SnappyFile::~SnappyFile()
+{
+ close();
+ delete [] m_compressedCache;
+ delete [] m_cache;
+}
+
+bool SnappyFile::rawOpen(const char *filename)
+{
+ std::ios_base::openmode fmode = std::fstream::binary
+ | std::fstream::in;
+
+ m_stream.open(filename, fmode);
+
+ //read in the initial buffer if we're reading
+ if (m_stream.is_open()) {
+ m_stream.seekg(0, std::ios::end);
+ m_endPos = m_stream.tellg();
+ m_stream.seekg(0, std::ios::beg);
+
+ // read the snappy file identifier
+ unsigned char byte1, byte2;
+ m_stream >> byte1;
+ m_stream >> byte2;
+ assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);
+
+ flushReadCache();
+ }
+ return m_stream.is_open();
+}
+
+size_t SnappyFile::rawRead(void *buffer, size_t length)
+{
+ if (endOfData()) {
+ return 0;
+ }
+
+ if (freeCacheSize() >= length) {
+ memcpy(buffer, m_cachePtr, length);
+ m_cachePtr += length;
+ } else {
+ size_t sizeToRead = length;
+ size_t offset = 0;
+ while (sizeToRead) {
+ size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
+ offset = length - sizeToRead;
+ memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
+ m_cachePtr += chunkSize;
+ sizeToRead -= chunkSize;
+ if (sizeToRead > 0) {
+ flushReadCache();
+ }
+ if (!m_cacheSize) {
+ return length - sizeToRead;
+ }
+ }
+ }
+
+ return length;
+}
+
+int SnappyFile::rawGetc(void)
+{
+ unsigned char c = 0;
+ if (rawRead(&c, 1) != 1)
+ return -1;
+ return c;
+}
+
+void SnappyFile::rawClose(void)
+{
+ m_stream.close();
+ delete [] m_cache;
+ m_cache = NULL;
+ m_cachePtr = NULL;
+}
+
+void SnappyFile::flushReadCache(size_t skipLength)
+{
+ //assert(m_cachePtr == m_cache + m_cacheSize);
+ m_currentChunkOffset = m_stream.tellg();
+ size_t compressedLength;
+ compressedLength = readCompressedLength();
+ if (!compressedLength) {
+ // Reached end of file
+ createCache(0);
+ return;
+ }
+
+ m_stream.read((char*)m_compressedCache, compressedLength);
+ if (m_stream.fail()) {
+ std::cerr << "warning: unexpected end of file while reading trace\n";
+
+ compressedLength = m_stream.gcount();
+ if (!snappy::GetUncompressedLength(m_compressedCache, compressedLength,
+ &m_cacheSize)) {
+ createCache(0);
+ return;
+ }
+
+ createCache(m_cacheSize);
+ snappy::ByteArraySource source(m_compressedCache, compressedLength);
+
+ snappy::UncheckedByteArraySink sink(m_cache);
+ m_cacheSize = snappy::UncompressAsMuchAsPossible(&source, &sink);
+
+ return;
+ }
+
+ if (!snappy::GetUncompressedLength(m_compressedCache, compressedLength,
+ &m_cacheSize)) {
+ createCache(0);
+ return;
+ }
+
+ createCache(m_cacheSize);
+ if (skipLength < m_cacheSize) {
+ snappy::RawUncompress(m_compressedCache, compressedLength,
+ m_cache);
+ }
+}
+
+void SnappyFile::createCache(size_t size)
+{
+ if (size > m_cacheMaxSize) {
+ do {
+ m_cacheMaxSize <<= 1;
+ } while (size > m_cacheMaxSize);
+
+ delete [] m_cache;
+ m_cache = new char[size];
+ m_cacheMaxSize = size;
+ }
+
+ m_cachePtr = m_cache;
+ m_cacheSize = size;
+}
+
+size_t SnappyFile::readCompressedLength()
+{
+ unsigned char buf[4];
+ size_t length;
+ m_stream.read((char *)buf, sizeof buf);
+ if (m_stream.fail()) {
+ length = 0;
+ } else {
+ length = (size_t)buf[0];
+ length |= ((size_t)buf[1] << 8);
+ length |= ((size_t)buf[2] << 16);
+ length |= ((size_t)buf[3] << 24);
+ }
+ return length;
+}
+
+bool SnappyFile::supportsOffsets(void) const
+{
+ return true;
+}
+
+File::Offset SnappyFile::currentOffset(void) const
+{
+ File::Offset offset;
+ offset.chunk = m_currentChunkOffset;
+ offset.offsetInChunk = m_cachePtr - m_cache;
+ return offset;
+}
+
+void SnappyFile::setCurrentOffset(const File::Offset &offset)
+{
+ // to remove eof bit
+ m_stream.clear();
+ // seek to the start of a chunk
+ m_stream.seekg(offset.chunk, std::ios::beg);
+ // load the chunk
+ flushReadCache();
+ assert(m_cacheSize >= offset.offsetInChunk);
+ // seek within our cache to the correct location within the chunk
+ m_cachePtr = m_cache + offset.offsetInChunk;
+
+}
+
+bool SnappyFile::rawSkip(size_t length)
+{
+ if (endOfData()) {
+ return false;
+ }
+
+ if (freeCacheSize() >= length) {
+ m_cachePtr += length;
+ } else {
+ size_t sizeToRead = length;
+ while (sizeToRead) {
+ size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
+ m_cachePtr += chunkSize;
+ sizeToRead -= chunkSize;
+ if (sizeToRead > 0) {
+ flushReadCache(sizeToRead);
+ }
+ if (!m_cacheSize) {
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+int SnappyFile::rawPercentRead(void)
+{
+ return int(100 * (double(m_stream.tellg()) / double(m_endPos)));
+}
+
+
+File* File::createSnappy(void) {
+ return new SnappyFile;
+}
diff --git a/lib/trace/trace_file_zlib.cpp b/lib/trace/trace_file_zlib.cpp
new file mode 100644
index 00000000..f0dd4560
--- /dev/null
+++ b/lib/trace/trace_file_zlib.cpp
@@ -0,0 +1,144 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_file.hpp"
+
+
+#include <assert.h>
+#include <string.h>
+
+#include <zlib.h>
+
+#include <fcntl.h>
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include "os.hpp"
+
+#include <iostream>
+
+
+using namespace trace;
+
+
+class ZLibFile : public File {
+public:
+ ZLibFile(void);
+ virtual ~ZLibFile();
+
+protected:
+ virtual bool rawOpen(const char *filename) override;
+ virtual size_t rawRead(void *buffer, size_t length) override;
+ virtual int rawGetc(void) override;
+ virtual void rawClose(void) override;
+ virtual bool rawSkip(size_t length) override;
+ virtual int rawPercentRead(void) override;
+private:
+ int fd = 0;
+ gzFile m_gzFile = nullptr;
+ off_t m_endOffset = 0;
+};
+
+ZLibFile::ZLibFile(void)
+{
+}
+
+ZLibFile::~ZLibFile()
+{
+ close();
+}
+
+bool ZLibFile::rawOpen(const char *filename)
+{
+ int flags = O_RDONLY;
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+#ifdef O_LARGEFILE
+ flags |= O_LARGEFILE;
+#endif
+
+#ifdef _WIN32
+ fd = _open(filename, flags, 0666);
+#else
+ fd = ::open(filename, flags, 0666);
+#endif
+ if (fd < 0) {
+ return false;
+ }
+
+ m_gzFile = gzdopen(fd, "rb");
+
+ if (m_gzFile) {
+ //XXX: unfortunately zlib doesn't support
+ // SEEK_END or we could've done:
+ //m_endOffset = gzseek(m_gzFile, 0, SEEK_END);
+ //gzrewind(m_gzFile);
+ off_t loc = lseek(fd, 0, SEEK_CUR);
+ m_endOffset = lseek(fd, 0, SEEK_END);
+ lseek(fd, loc, SEEK_SET);
+ }
+
+ return m_gzFile != NULL;
+}
+
+size_t ZLibFile::rawRead(void *buffer, size_t length)
+{
+ int ret = gzread(m_gzFile, buffer, unsigned(length));
+ return ret < 0 ? 0 : ret;
+}
+
+int ZLibFile::rawGetc()
+{
+ return gzgetc(m_gzFile);
+}
+
+void ZLibFile::rawClose()
+{
+ if (m_gzFile) {
+ gzclose(m_gzFile);
+ m_gzFile = NULL;
+ }
+}
+
+bool ZLibFile::rawSkip(size_t)
+{
+ return false;
+}
+
+int ZLibFile::rawPercentRead(void)
+{
+ return int(100 * (lseek(fd, 0, SEEK_CUR) / m_endOffset));
+}
+
+
+File * File::createZLib(void) {
+ return new ZLibFile;
+}
diff --git a/lib/trace/trace_format.hpp b/lib/trace/trace_format.hpp
new file mode 100644
index 00000000..4c6d821e
--- /dev/null
+++ b/lib/trace/trace_format.hpp
@@ -0,0 +1,83 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Trace binary format definitions.
+ *
+ * See FORMAT.markdown for details.
+ */
+
+#pragma once
+
+namespace trace {
+
+
+#define TRACE_VERSION 5
+
+
+enum Event {
+ EVENT_ENTER = 0,
+ EVENT_LEAVE,
+};
+
+enum CallDetail {
+ CALL_END = 0,
+ CALL_ARG,
+ CALL_RET,
+ CALL_THREAD,
+ CALL_BACKTRACE,
+};
+
+enum Type {
+ TYPE_NULL = 0,
+ TYPE_FALSE,
+ TYPE_TRUE,
+ TYPE_SINT,
+ TYPE_UINT,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_BLOB,
+ TYPE_ENUM,
+ TYPE_BITMASK,
+ TYPE_ARRAY,
+ TYPE_STRUCT,
+ TYPE_OPAQUE,
+ TYPE_REPR,
+ TYPE_WSTRING,
+};
+
+enum BacktraceDetail {
+ BACKTRACE_END = 0,
+ BACKTRACE_MODULE,
+ BACKTRACE_FUNCTION,
+ BACKTRACE_FILENAME,
+ BACKTRACE_LINENUMBER,
+ BACKTRACE_OFFSET,
+};
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_lookup.hpp b/lib/trace/trace_lookup.hpp
new file mode 100644
index 00000000..55655027
--- /dev/null
+++ b/lib/trace/trace_lookup.hpp
@@ -0,0 +1,109 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Helper code for function name indexed lookup tables.
+ */
+
+#pragma once
+
+
+#include <assert.h>
+#include <string.h>
+
+#include <algorithm>
+#include <iostream>
+
+
+namespace trace {
+
+
+/**
+ * Generic type for (name, value) pairs.
+ */
+template< class T >
+struct Entry
+{
+ const char *name;
+ T value;
+};
+
+
+/**
+ * Function object which compare entries by name.
+ */
+template< class T >
+struct EntryCompare {
+ inline bool
+ operator() (const Entry<T> & a, const Entry<T> & b) const {
+ return strcmp(a.name, b.name) < 0;
+ }
+};
+
+
+/**
+ * Lookup the entry with the given name, .
+ *
+ * The entry table must be sorted alphabetically (the same rules used by
+ * strcmp).
+ */
+template< class T, std::size_t n >
+inline const T &
+entryLookup(const char *name, const Entry<T> (& entries)[n], const T & default_)
+{
+ typedef const Entry<T> * ConstIterator;
+
+ ConstIterator first = &entries[0];
+ ConstIterator last = &entries[n];
+
+ assert(first != last);
+
+ Entry<T> reference;
+ reference.name = name;
+
+ EntryCompare<T> compare;
+
+#ifndef NDEBUG
+ for (ConstIterator it = first; it != last; ++it) {
+ ConstIterator next = it + 1;
+ if (next != last && !compare(*it, *next)) {
+ std::cerr << "error: " << it->name << " and " << next->name << " not properly sorted\n";
+ assert(0);
+ }
+ }
+#endif
+
+ first = std::lower_bound(first, last, reference, compare);
+
+ if (first == last || compare(reference, *first)) {
+ return default_;
+ }
+
+ return first->value;
+}
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_model.cpp b/lib/trace/trace_model.cpp
new file mode 100644
index 00000000..fbf3bdaa
--- /dev/null
+++ b/lib/trace/trace_model.cpp
@@ -0,0 +1,301 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <string.h>
+#include <deque>
+
+#include "trace_model.hpp"
+
+
+namespace trace {
+
+
+static Null null;
+
+
+Call::~Call() {
+ for (auto & arg : args) {
+ delete arg.value;
+ }
+
+ if (ret) {
+ delete ret;
+ }
+}
+
+Value &
+Call::argByName(const char *argName) {
+ for (unsigned i = 0; i < sig->num_args; ++i) {
+ if (strcmp(sig->arg_names[i], argName) == 0) {
+ return arg(i);
+ }
+ }
+ return null;
+}
+
+
+String::~String() {
+ delete [] value;
+}
+
+
+WString::~WString() {
+ delete [] value;
+}
+
+
+Struct::~Struct() {
+ for (auto & member : members) {
+ delete member;
+ }
+}
+
+
+Array::~Array() {
+ for (auto & value : values) {
+ delete value;
+ }
+}
+
+
+#define BLOB_MAX_BOUND_SIZE (1*1024*1024*1024)
+
+class BoundBlob {
+public:
+ static size_t totalSize;
+
+private:
+ size_t size;
+ char *buf;
+
+public:
+ inline
+ BoundBlob(size_t _size, char *_buf) :
+ size(_size),
+ buf(_buf)
+ {
+ assert(totalSize + size >= totalSize);
+ totalSize += size;
+ }
+
+ inline
+ ~BoundBlob() {
+ assert(totalSize >= size);
+ totalSize -= size;
+ delete [] buf;
+ }
+
+ // Fake move constructor
+ // std::deque:push_back with move semantics was added only from c++11.
+ BoundBlob(const BoundBlob & other)
+ {
+ size = other.size;
+ buf = other.buf;
+ const_cast<BoundBlob &>(other).size = 0;
+ const_cast<BoundBlob &>(other).buf = 0;
+ }
+
+ // Disallow assignment operator
+ BoundBlob& operator = (const BoundBlob &);
+};
+
+size_t BoundBlob::totalSize = 0;
+
+typedef std::deque<BoundBlob> BoundBlobQueue;
+static BoundBlobQueue boundBlobQueue;
+
+
+Blob::~Blob() {
+ // Blobs are often bound and referred during many calls, so we can't delete
+ // them here in that case.
+ //
+ // Once bound there is no way to know when they were unbound, which
+ // effectively means we have to leak them. But some applications
+ // (particularly OpenGL applications that use vertex arrays in user memory)
+ // we can easily exhaust all memory. So instead we maintain a queue of
+ // bound blobs and keep the total size bounded.
+
+ if (!bound) {
+ delete [] buf;
+ return;
+ }
+
+ while (!boundBlobQueue.empty() &&
+ BoundBlob::totalSize + size > BLOB_MAX_BOUND_SIZE) {
+ boundBlobQueue.pop_front();
+ }
+
+ boundBlobQueue.push_back(BoundBlob(size, buf));
+}
+
+StackFrame::~StackFrame() {
+ if (module != NULL) {
+ delete [] module;
+ }
+ if (function != NULL) {
+ delete [] function;
+ }
+ if (filename != NULL) {
+ delete [] filename;
+ }
+}
+
+
+// bool cast
+bool Null ::toBool(void) const { return false; }
+bool Bool ::toBool(void) const { return value; }
+bool SInt ::toBool(void) const { return value != 0; }
+bool UInt ::toBool(void) const { return value != 0; }
+bool Float ::toBool(void) const { return value != 0; }
+bool Double ::toBool(void) const { return value != 0; }
+bool String ::toBool(void) const { return true; }
+bool WString::toBool(void) const { return true; }
+bool Struct ::toBool(void) const { return true; }
+bool Array ::toBool(void) const { return true; }
+bool Blob ::toBool(void) const { return true; }
+bool Pointer::toBool(void) const { return value != 0; }
+bool Repr ::toBool(void) const { return static_cast<bool>(machineValue); }
+
+
+// signed integer cast
+signed long long Value ::toSInt(void) const { assert(0); return 0; }
+signed long long Null ::toSInt(void) const { return 0; }
+signed long long Bool ::toSInt(void) const { return static_cast<signed long long>(value); }
+signed long long SInt ::toSInt(void) const { return value; }
+signed long long UInt ::toSInt(void) const { assert(static_cast<signed long long>(value) >= 0); return static_cast<signed long long>(value); }
+signed long long Float ::toSInt(void) const { return static_cast<signed long long>(value); }
+signed long long Double ::toSInt(void) const { return static_cast<signed long long>(value); }
+signed long long Repr ::toSInt(void) const { return machineValue->toSInt(); }
+
+
+// unsigned integer cast
+unsigned long long Value ::toUInt(void) const { assert(0); return 0; }
+unsigned long long Null ::toUInt(void) const { return 0; }
+unsigned long long Bool ::toUInt(void) const { return static_cast<unsigned long long>(value); }
+unsigned long long SInt ::toUInt(void) const { assert(value >= 0); return static_cast<signed long long>(value); }
+unsigned long long UInt ::toUInt(void) const { return value; }
+unsigned long long Float ::toUInt(void) const { return static_cast<unsigned long long>(value); }
+unsigned long long Double ::toUInt(void) const { return static_cast<unsigned long long>(value); }
+unsigned long long Repr ::toUInt(void) const { return machineValue->toUInt(); }
+
+
+// floating point cast
+float Value ::toFloat(void) const { assert(0); return 0; }
+float Null ::toFloat(void) const { return 0; }
+float Bool ::toFloat(void) const { return static_cast<float>(value); }
+float SInt ::toFloat(void) const { return static_cast<float>(value); }
+float UInt ::toFloat(void) const { return static_cast<float>(value); }
+float Float ::toFloat(void) const { return value; }
+float Double ::toFloat(void) const { return value; }
+float Repr ::toFloat(void) const { return machineValue->toFloat(); }
+
+
+// floating point cast
+double Value ::toDouble(void) const { assert(0); return 0; }
+double Null ::toDouble(void) const { return 0; }
+double Bool ::toDouble(void) const { return static_cast<double>(value); }
+double SInt ::toDouble(void) const { return static_cast<double>(value); }
+double UInt ::toDouble(void) const { return static_cast<double>(value); }
+double Float ::toDouble(void) const { return value; }
+double Double ::toDouble(void) const { return value; }
+double Repr ::toDouble(void) const { return machineValue->toDouble(); }
+
+
+// pointer cast
+void * Value ::toPointer(void) const { assert(0); return NULL; }
+void * Null ::toPointer(void) const { return NULL; }
+void * Blob ::toPointer(void) const { return buf; }
+void * Pointer::toPointer(void) const { return (void *)value; }
+void * Repr ::toPointer(void) const { return machineValue->toPointer(); }
+
+void * Value ::toPointer(bool bind) { assert(0); return NULL; }
+void * Null ::toPointer(bool bind) { return NULL; }
+void * Blob ::toPointer(bool bind) { if (bind) bound = true; return buf; }
+void * Pointer::toPointer(bool bind) { return (void *)value; }
+void * Repr ::toPointer(bool bind) { return machineValue->toPointer(bind); }
+
+
+// unsigned int pointer cast
+unsigned long long Value ::toUIntPtr(void) const { assert(0); return 0; }
+unsigned long long Null ::toUIntPtr(void) const { return 0; }
+unsigned long long Pointer::toUIntPtr(void) const { return value; }
+unsigned long long Repr ::toUIntPtr(void) const { return machineValue->toUIntPtr(); }
+
+
+// string cast
+const char * Value ::toString(void) const { assert(0); return NULL; }
+const char * Null ::toString(void) const { return NULL; }
+const char * String::toString(void) const { return value; }
+const char * Repr ::toString(void) const { return machineValue->toString(); }
+
+
+// virtual Value::visit()
+void Null ::visit(Visitor &visitor) { visitor.visit(this); }
+void Bool ::visit(Visitor &visitor) { visitor.visit(this); }
+void SInt ::visit(Visitor &visitor) { visitor.visit(this); }
+void UInt ::visit(Visitor &visitor) { visitor.visit(this); }
+void Float ::visit(Visitor &visitor) { visitor.visit(this); }
+void Double ::visit(Visitor &visitor) { visitor.visit(this); }
+void String ::visit(Visitor &visitor) { visitor.visit(this); }
+void WString::visit(Visitor &visitor) { visitor.visit(this); }
+void Enum ::visit(Visitor &visitor) { visitor.visit(this); }
+void Bitmask::visit(Visitor &visitor) { visitor.visit(this); }
+void Struct ::visit(Visitor &visitor) { visitor.visit(this); }
+void Array ::visit(Visitor &visitor) { visitor.visit(this); }
+void Blob ::visit(Visitor &visitor) { visitor.visit(this); }
+void Pointer::visit(Visitor &visitor) { visitor.visit(this); }
+void Repr ::visit(Visitor &visitor) { visitor.visit(this); }
+
+
+void Visitor::visit(Null *) { assert(0); }
+void Visitor::visit(Bool *) { assert(0); }
+void Visitor::visit(SInt *) { assert(0); }
+void Visitor::visit(UInt *) { assert(0); }
+void Visitor::visit(Float *) { assert(0); }
+void Visitor::visit(Double *) { assert(0); }
+void Visitor::visit(String *) { assert(0); }
+void Visitor::visit(WString *) { assert(0); }
+void Visitor::visit(Enum *node) { assert(0); }
+void Visitor::visit(Bitmask *node) { visit(static_cast<UInt *>(node)); }
+void Visitor::visit(Struct *) { assert(0); }
+void Visitor::visit(Array *) { assert(0); }
+void Visitor::visit(Blob *) { assert(0); }
+void Visitor::visit(Pointer *) { assert(0); }
+void Visitor::visit(Repr *node) { node->machineValue->visit(*this); }
+
+
+Value &
+Value::operator[] (size_t index) const {
+ const Array *array = toArray();
+ if (array) {
+ if (index < array->values.size()) {
+ return *array->values[index];
+ }
+ }
+ return null;
+}
+
+} /* namespace trace */
diff --git a/lib/trace/trace_model.hpp b/lib/trace/trace_model.hpp
new file mode 100644
index 00000000..6b224e76
--- /dev/null
+++ b/lib/trace/trace_model.hpp
@@ -0,0 +1,593 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Object hierarchy for describing the traces in memory.
+ */
+
+#pragma once
+
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <map>
+#include <vector>
+#include <ostream>
+
+
+namespace trace {
+
+
+// Should match Call::no
+typedef unsigned CallNo;
+
+
+typedef unsigned Id;
+
+
+struct FunctionSig {
+ Id id;
+ const char *name;
+ unsigned num_args;
+ const char **arg_names;
+};
+
+
+struct StructSig {
+ Id id;
+ const char *name;
+ unsigned num_members;
+ const char **member_names;
+};
+
+
+struct EnumValue {
+ const char *name;
+ signed long long value;
+};
+
+
+struct EnumSig {
+ Id id;
+ unsigned num_values;
+ const EnumValue *values;
+};
+
+
+struct BitmaskFlag {
+ const char *name;
+ unsigned long long value;
+};
+
+
+struct BitmaskSig {
+ Id id;
+ unsigned num_flags;
+ const BitmaskFlag *flags;
+};
+
+
+class Visitor;
+class Null;
+class Struct;
+class Array;
+class Blob;
+
+
+class Value
+{
+public:
+ virtual ~Value() {}
+ virtual void visit(Visitor &visitor) = 0;
+
+ virtual bool toBool(void) const = 0;
+ virtual signed long long toSInt(void) const;
+ virtual unsigned long long toUInt(void) const;
+ virtual float toFloat(void) const;
+ virtual double toDouble(void) const;
+
+ virtual void *toPointer(void) const;
+ virtual void *toPointer(bool bind);
+ virtual unsigned long long toUIntPtr(void) const;
+ virtual const char *toString(void) const;
+
+ virtual const Null *toNull(void) const { return NULL; }
+ virtual Null *toNull(void) { return NULL; }
+
+ virtual const Array *toArray(void) const { return NULL; }
+ virtual Array *toArray(void) { return NULL; }
+
+ virtual const Struct *toStruct(void) const { return NULL; }
+ virtual Struct *toStruct(void) { return NULL; }
+
+ virtual const Blob *toBlob(void) const { return NULL; }
+ virtual Blob *toBlob(void) { return NULL; }
+
+ Value & operator[](size_t index) const;
+};
+
+
+class Null : public Value
+{
+public:
+ bool toBool(void) const override;
+ signed long long toSInt(void) const override;
+ unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+ void *toPointer(void) const override;
+ void *toPointer(bool bind) override;
+ unsigned long long toUIntPtr(void) const override;
+ const char *toString(void) const override;
+ void visit(Visitor &visitor) override;
+
+ const Null *toNull(void) const override { return this; }
+ Null *toNull(void) override { return this; }
+};
+
+
+class Bool : public Value
+{
+public:
+ Bool(bool _value) : value(_value) {}
+
+ bool toBool(void) const override;
+ signed long long toSInt(void) const override;
+ unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+ void visit(Visitor &visitor) override;
+
+ bool value;
+};
+
+
+class SInt : public Value
+{
+public:
+ SInt(signed long long _value) : value(_value) {}
+
+ bool toBool(void) const override;
+ signed long long toSInt(void) const override;
+ unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+ void visit(Visitor &visitor) override;
+
+ signed long long value;
+};
+
+
+class UInt : public Value
+{
+public:
+ UInt(unsigned long long _value) : value(_value) {}
+
+ bool toBool(void) const override;
+ signed long long toSInt(void) const override;
+ unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+ void visit(Visitor &visitor) override;
+
+ unsigned long long value;
+};
+
+
+class Float : public Value
+{
+public:
+ Float(float _value) : value(_value) {}
+
+ bool toBool(void) const override;
+ signed long long toSInt(void) const override;
+ unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+ void visit(Visitor &visitor) override;
+
+ float value;
+};
+
+
+class Double : public Value
+{
+public:
+ Double(double _value) : value(_value) {}
+
+ bool toBool(void) const override;
+ signed long long toSInt(void) const override;
+ unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+ void visit(Visitor &visitor) override;
+
+ double value;
+};
+
+
+class String : public Value
+{
+public:
+ String(const char * _value) : value(_value) {}
+ ~String();
+
+ bool toBool(void) const override;
+ const char *toString(void) const override;
+ void visit(Visitor &visitor) override;
+
+ const char * value;
+};
+
+
+class WString : public Value
+{
+public:
+ WString(const wchar_t * _value) : value(_value) {}
+ ~WString();
+
+ bool toBool(void) const override;
+ void visit(Visitor &visitor) override;
+
+ const wchar_t * value;
+};
+
+
+class Enum : public SInt
+{
+public:
+ Enum(const EnumSig *_sig, signed long long _value) : SInt(_value), sig(_sig) {}
+
+ void visit(Visitor &visitor) override;
+
+ const EnumSig *sig;
+
+ const EnumValue *
+ lookup() {
+ // TODO: use a std::map
+ for (const EnumValue *it = sig->values; it != sig->values + sig->num_values; ++it) {
+ if (it->value == value) {
+ return it;
+ }
+ }
+ return NULL;
+ }
+};
+
+
+class Bitmask : public UInt
+{
+public:
+ Bitmask(const BitmaskSig *_sig, unsigned long long _value) : UInt(_value), sig(_sig) {}
+
+ void visit(Visitor &visitor) override;
+
+ const BitmaskSig *sig;
+};
+
+
+class Struct : public Value
+{
+public:
+ Struct(StructSig *_sig) : sig(_sig), members(_sig->num_members) { }
+ ~Struct();
+
+ bool toBool(void) const override;
+ void visit(Visitor &visitor) override;
+
+ const Struct *toStruct(void) const override { return this; }
+ Struct *toStruct(void) override { return this; }
+
+ const StructSig *sig;
+ std::vector<Value *> members;
+};
+
+
+class Array : public Value
+{
+public:
+ Array(size_t len) : values(len) {}
+ ~Array();
+
+ bool toBool(void) const override;
+ void visit(Visitor &visitor) override;
+
+ const Array *toArray(void) const override { return this; }
+ Array *toArray(void) override { return this; }
+
+ std::vector<Value *> values;
+
+ inline size_t
+ size(void) const {
+ return values.size();
+ }
+};
+
+
+class Blob : public Value
+{
+public:
+ Blob(size_t _size) {
+ size = _size;
+ buf = new char[_size];
+ bound = false;
+ }
+
+ ~Blob();
+
+ bool toBool(void) const override;
+ void *toPointer(void) const override;
+ void *toPointer(bool bind) override;
+
+ void visit(Visitor &visitor) override;
+
+ const Blob *toBlob(void) const override { return this; }
+ Blob *toBlob(void) override { return this; }
+
+ size_t size;
+ char *buf;
+ bool bound;
+};
+
+
+class Pointer : public UInt
+{
+public:
+ Pointer(unsigned long long value) : UInt(value) {}
+
+ bool toBool(void) const override;
+ void *toPointer(void) const override;
+ void *toPointer(bool bind) override;
+ unsigned long long toUIntPtr(void) const override;
+ void visit(Visitor &visitor) override;
+};
+
+
+class Repr : public Value
+{
+public:
+ Repr(Value *human, Value *machine) :
+ humanValue(human),
+ machineValue(machine)
+ {}
+
+ /** Human-readible value */
+ Value *humanValue;
+
+ /** Machine-readible value */
+ Value *machineValue;
+
+ virtual bool toBool(void) const override;
+ virtual signed long long toSInt(void) const override;
+ virtual unsigned long long toUInt(void) const override;
+ virtual float toFloat(void) const override;
+ virtual double toDouble(void) const override;
+
+ virtual void *toPointer(void) const override;
+ virtual void *toPointer(bool bind) override;
+ virtual unsigned long long toUIntPtr(void) const override;
+ virtual const char *toString(void) const override;
+
+ void visit(Visitor &visitor) override;
+};
+
+struct RawStackFrame {
+ Id id;
+ const char * module;
+ const char * function;
+ const char * filename;
+ int linenumber;
+ long long offset;
+ RawStackFrame() :
+ module(0),
+ function(0),
+ filename(0),
+ linenumber(-1),
+ offset(-1)
+ {
+ }
+
+ void dump(std::ostream &os) {
+ os << (this->module ? this->module : "?");
+ if (this->function != NULL) {
+ os << ": " << this->function;
+ }
+ if (this->offset >= 0) {
+ os << "+0x" << std::hex << this->offset << std::dec;
+ }
+ if (this->filename != NULL) {
+ os << ": " << this->filename;
+ if (this->linenumber >= 0) {
+ os << ":" << this->linenumber;
+ }
+ }
+ }
+};
+
+class StackFrame : public RawStackFrame {
+public:
+ ~StackFrame();
+};
+
+typedef std::vector<StackFrame *> Backtrace;
+
+class Visitor
+{
+public:
+ virtual void visit(Null *);
+ virtual void visit(Bool *);
+ virtual void visit(SInt *);
+ virtual void visit(UInt *);
+ virtual void visit(Float *);
+ virtual void visit(Double *);
+ virtual void visit(String *);
+ virtual void visit(WString *);
+ virtual void visit(Enum *);
+ virtual void visit(Bitmask *);
+ virtual void visit(Struct *);
+ virtual void visit(Array *);
+ virtual void visit(Blob *);
+ virtual void visit(Pointer *);
+ virtual void visit(Repr *);
+protected:
+ inline void _visit(Value *value) {
+ if (value) {
+ value->visit(*this);
+ }
+ }
+};
+
+
+typedef unsigned CallFlags;
+
+/**
+ * Call flags.
+ *
+ * TODO: It might be better to to record some of these (but not all) into the
+ * trace file.
+ */
+enum {
+
+ /**
+ * Whether a call was really done by the application or not.
+ *
+ * This flag is set for fake calls -- calls not truly done by the application
+ * but emitted and recorded for completeness, to provide contextual information
+ * necessary for retracing, that would not be available through other ways.
+ *
+ * XXX: This one definetely needs to go into the trace file.
+ */
+ CALL_FLAG_FAKE = (1 << 0),
+
+ /**
+ * Whether this call should be retraced or ignored.
+ *
+ * This flag is set for calls which can't be safely replayed (due to incomplete
+ * information) or that have no sideffects.
+ *
+ * Some incomplete calls are unreproduceable, but not all.
+ */
+ CALL_FLAG_NON_REPRODUCIBLE = (1 << 1),
+
+ /**
+ * Whether this call has no side-effects, therefore don't need to be
+ * retraced.
+ *
+ * This flag is set for calls that merely query information which is not
+ * needed for posterior calls.
+ */
+ CALL_FLAG_NO_SIDE_EFFECTS = (1 << 2),
+
+ /**
+ * Whether this call renders into the bound rendertargets.
+ */
+ CALL_FLAG_RENDER = (1 << 3),
+
+ /**
+ * Whether this call causes render target to be swapped.
+ *
+ * This does not mark frame termination by itself -- that's solely the
+ * responsibility of `endOfFrame` bit.
+ *
+ * This mean that snapshots should be take prior to the call, and not
+ * after.
+ */
+ CALL_FLAG_SWAP_RENDERTARGET = (1 << 4),
+
+ /**
+ * Whether this call terminates a frame.
+ *
+ * XXX: This can't always be determined by the function name, so it should also
+ * go into the trace file eventually.
+ */
+ CALL_FLAG_END_FRAME = (1 << 5),
+
+ /**
+ * Whether this call is incomplete, i.e., it never returned.
+ */
+ CALL_FLAG_INCOMPLETE = (1 << 6),
+
+ /**
+ * Whether this call is verbose (i.e., not usually interesting).
+ */
+ CALL_FLAG_VERBOSE = (1 << 7),
+
+ /**
+ * String markers.
+ */
+ CALL_FLAG_MARKER = (1 << 8),
+ CALL_FLAG_MARKER_PUSH = (1 << 9),
+ CALL_FLAG_MARKER_POP = (1 << 10),
+};
+
+
+struct Arg
+{
+ Value *value;
+};
+
+
+class Call
+{
+public:
+ unsigned thread_id;
+ unsigned no;
+ const FunctionSig *sig;
+ std::vector<Arg> args;
+ Value *ret;
+
+ CallFlags flags;
+ Backtrace* backtrace;
+
+ Call(const FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :
+ thread_id(_thread_id),
+ sig(_sig),
+ args(_sig->num_args),
+ ret(0),
+ flags(_flags),
+ backtrace(0) {
+ }
+
+ ~Call();
+
+ inline const char *
+ name(void) const {
+ return sig->name;
+ }
+
+ inline Value &
+ arg(unsigned index) {
+ assert(index < args.size());
+ return *(args[index].value);
+ }
+
+ Value &
+ argByName(const char *argName);
+};
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_option.cpp b/lib/trace/trace_option.cpp
new file mode 100644
index 00000000..feae1496
--- /dev/null
+++ b/lib/trace/trace_option.cpp
@@ -0,0 +1,62 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "trace_option.hpp"
+
+#include <string.h>
+#include <iostream>
+#include <stdlib.h>
+
+namespace trace {
+
+bool
+boolOption(const char *option, bool default_) {
+ if (!option) {
+ return default_;
+ }
+ if (strcmp(option, "0") == 0 ||
+ strcmp(option, "no") == 0 ||
+ strcmp(option, "false") == 0) {
+ return false;
+ }
+ if (strcmp(option, "0") == 0 ||
+ strcmp(option, "yes") == 0 ||
+ strcmp(option, "true") == 0) {
+ return true;
+ }
+ std::cerr << "error: unexpected bool " << option << "\n";
+ return default_;
+}
+
+int
+intOption(const char *option, int default_) {
+ if (!option) {
+ return default_;
+ }
+ return atoi(option);
+}
+
+} /* namespace trace */
diff --git a/lib/trace/trace_option.hpp b/lib/trace/trace_option.hpp
new file mode 100644
index 00000000..f11d4658
--- /dev/null
+++ b/lib/trace/trace_option.hpp
@@ -0,0 +1,38 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#pragma once
+
+namespace trace {
+
+bool
+boolOption(const char *option, bool default_ = true);
+
+int
+intOption(const char *option, int default_ = 0);
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_ostream.hpp b/lib/trace/trace_ostream.hpp
new file mode 100644
index 00000000..502905b4
--- /dev/null
+++ b/lib/trace/trace_ostream.hpp
@@ -0,0 +1,52 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc.
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#pragma once
+
+#include <stdlib.h>
+
+
+namespace trace {
+
+
+class OutStream {
+public:
+ virtual ~OutStream() {}
+
+ virtual bool write(const void *buffer, size_t length) = 0;
+ virtual void flush(void) = 0;
+};
+
+
+OutStream *
+createSnappyStream(const char *filename);
+
+OutStream *
+createZLibStream(const char *filename);
+
+
+} /* namespace trace */
diff --git a/lib/trace/trace_ostream_snappy.cpp b/lib/trace/trace_ostream_snappy.cpp
new file mode 100644
index 00000000..219e0934
--- /dev/null
+++ b/lib/trace/trace_ostream_snappy.cpp
@@ -0,0 +1,207 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_ostream.hpp"
+
+#include <fstream>
+
+#include <assert.h>
+#include <string.h>
+
+#include <snappy.h>
+
+#include "os.hpp"
+#include "trace_snappy.hpp"
+
+
+#define SNAPPY_CHUNK_SIZE (1 * 1024 * 1024)
+
+
+using namespace trace;
+
+
+class SnappyOutStream : public OutStream {
+public:
+ SnappyOutStream(const char *filename);
+ ~SnappyOutStream();
+
+ SnappyOutStream(void);
+ bool write(const void *buffer, size_t length) override;
+ void flush(void) override;
+ bool isOpen(void) {
+ return m_stream.is_open();
+ }
+
+
+private:
+ void close(void);
+
+ inline size_t usedCacheSize(void) const
+ {
+ assert(m_cachePtr >= m_cache);
+ return m_cachePtr - m_cache;
+ }
+ inline size_t freeCacheSize(void) const
+ {
+ assert(m_cacheSize >= usedCacheSize());
+ if (m_cacheSize > 0) {
+ return m_cacheSize - usedCacheSize();
+ } else {
+ return 0;
+ }
+ }
+ inline bool endOfData(void) const
+ {
+ return m_stream.eof() && freeCacheSize() == 0;
+ }
+ void flushWriteCache(void);
+ void createCache(size_t size);
+ void writeCompressedLength(size_t length);
+private:
+ std::ofstream m_stream;
+ size_t m_cacheMaxSize;
+ size_t m_cacheSize;
+ char *m_cache;
+ char *m_cachePtr;
+
+ char *m_compressedCache;
+};
+
+SnappyOutStream::SnappyOutStream(const char *filename)
+ : m_cacheMaxSize(SNAPPY_CHUNK_SIZE),
+ m_cacheSize(m_cacheMaxSize),
+ m_cache(new char [m_cacheMaxSize]),
+ m_cachePtr(m_cache)
+{
+ size_t maxCompressedLength =
+ snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
+ m_compressedCache = new char[maxCompressedLength];
+
+ std::ios_base::openmode fmode = std::fstream::binary
+ | std::fstream::out
+ | std::fstream::trunc;
+ m_stream.open(filename, fmode);
+ if (m_stream.is_open()) {
+ m_stream << SNAPPY_BYTE1;
+ m_stream << SNAPPY_BYTE2;
+ m_stream.flush();
+ }
+}
+
+SnappyOutStream::~SnappyOutStream()
+{
+ close();
+ delete [] m_compressedCache;
+ delete [] m_cache;
+}
+
+bool SnappyOutStream::write(const void *buffer, size_t length)
+{
+ if (freeCacheSize() > length) {
+ memcpy(m_cachePtr, buffer, length);
+ m_cachePtr += length;
+ } else if (freeCacheSize() == length) {
+ memcpy(m_cachePtr, buffer, length);
+ m_cachePtr += length;
+ flushWriteCache();
+ } else {
+ size_t sizeToWrite = length;
+
+ while (sizeToWrite >= freeCacheSize()) {
+ size_t endSize = freeCacheSize();
+ size_t offset = length - sizeToWrite;
+ memcpy(m_cachePtr, (const char*)buffer + offset, endSize);
+ sizeToWrite -= endSize;
+ m_cachePtr += endSize;
+ flushWriteCache();
+ }
+ if (sizeToWrite) {
+ size_t offset = length - sizeToWrite;
+ memcpy(m_cachePtr, (const char*)buffer + offset, sizeToWrite);
+ m_cachePtr += sizeToWrite;
+ }
+ }
+
+ return true;
+}
+
+void SnappyOutStream::close(void)
+{
+ flushWriteCache();
+ m_stream.close();
+ delete [] m_cache;
+ m_cache = NULL;
+ m_cachePtr = NULL;
+}
+
+void SnappyOutStream::flush(void)
+{
+ flushWriteCache();
+ m_stream.flush();
+}
+
+void SnappyOutStream::flushWriteCache(void)
+{
+ size_t inputLength = usedCacheSize();
+
+ if (inputLength) {
+ size_t compressedLength;
+
+ ::snappy::RawCompress(m_cache, inputLength,
+ m_compressedCache, &compressedLength);
+
+ writeCompressedLength(compressedLength);
+ m_stream.write(m_compressedCache, compressedLength);
+ m_cachePtr = m_cache;
+ }
+ assert(m_cachePtr == m_cache);
+}
+
+void SnappyOutStream::writeCompressedLength(size_t length)
+{
+ unsigned char buf[4];
+ buf[0] = length & 0xff; length >>= 8;
+ buf[1] = length & 0xff; length >>= 8;
+ buf[2] = length & 0xff; length >>= 8;
+ buf[3] = length & 0xff; length >>= 8;
+ assert(length == 0);
+ m_stream.write((const char *)buf, sizeof buf);
+}
+
+
+OutStream *
+trace::createSnappyStream(const char *filename)
+{
+ SnappyOutStream *outStream = new SnappyOutStream(filename);
+ if (!outStream->isOpen()) {
+ os::log("error: could not open %s for writing\n", filename);
+ delete outStream;
+ outStream = nullptr;
+ }
+
+ return outStream;
+}
diff --git a/lib/trace/trace_ostream_zlib.cpp b/lib/trace/trace_ostream_zlib.cpp
new file mode 100644
index 00000000..a5bf04fd
--- /dev/null
+++ b/lib/trace/trace_ostream_zlib.cpp
@@ -0,0 +1,93 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc.
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_ostream.hpp"
+
+
+#include <assert.h>
+#include <string.h>
+
+#include <zlib.h>
+
+#include "os.hpp"
+
+#include <iostream>
+
+
+using namespace trace;
+
+
+class ZLibOutStream : public OutStream {
+public:
+ ZLibOutStream(gzFile file);
+ virtual ~ZLibOutStream();
+
+protected:
+ virtual bool write(const void *buffer, size_t length) override;
+ virtual void flush(void) override;
+private:
+ gzFile m_gzFile;
+};
+
+ZLibOutStream::ZLibOutStream(gzFile file)
+ : m_gzFile(file)
+{
+}
+
+ZLibOutStream::~ZLibOutStream()
+{
+ if (m_gzFile) {
+ gzclose(m_gzFile);
+ m_gzFile = nullptr;
+ }
+}
+
+bool ZLibOutStream::write(const void *buffer, size_t length)
+{
+ return gzwrite(m_gzFile, buffer, unsigned(length)) != -1;
+}
+
+void ZLibOutStream::flush(void)
+{
+ gzflush(m_gzFile, Z_SYNC_FLUSH);
+}
+
+
+OutStream *
+trace::createZLibStream(const char *filename)
+{
+ gzFile file = gzopen(filename, "wb");
+ if (!file) {
+ return nullptr;
+ }
+
+ // Currently we only use gzip for offline compression, so aim for maximum
+ // compression
+ gzsetparams(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY);
+
+ return new ZLibOutStream(file);
+}
diff --git a/lib/trace/trace_parser.cpp b/lib/trace/trace_parser.cpp
new file mode 100644
index 00000000..7816aeb3
--- /dev/null
+++ b/lib/trace/trace_parser.cpp
@@ -0,0 +1,1039 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "trace_file.hpp"
+#include "trace_dump.hpp"
+#include "trace_parser.hpp"
+
+
+#define TRACE_VERBOSE 0
+
+
+namespace trace {
+
+
+Parser::Parser() {
+ file = NULL;
+ next_call_no = 0;
+ version = 0;
+ api = API_UNKNOWN;
+
+ glGetErrorSig = NULL;
+}
+
+
+Parser::~Parser() {
+ close();
+}
+
+
+bool Parser::open(const char *filename) {
+ assert(!file);
+ file = File::createForRead(filename);
+ if (!file) {
+ return false;
+ }
+
+ version = read_uint();
+ if (version > TRACE_VERSION) {
+ std::cerr << "error: unsupported trace format version " << version << "\n";
+ delete file;
+ file = NULL;
+ return false;
+ }
+ api = API_UNKNOWN;
+
+ return true;
+}
+
+template <typename Iter>
+inline void
+deleteAll(Iter begin, Iter end)
+{
+ while (begin != end) {
+ delete *begin;
+ ++begin;
+ }
+}
+
+template <typename Container>
+inline void
+deleteAll(Container &c)
+{
+ deleteAll(c.begin(), c.end());
+ c.clear();
+}
+
+void Parser::close(void) {
+ if (file) {
+ file->close();
+ delete file;
+ file = NULL;
+ }
+
+ deleteAll(calls);
+
+ // Delete all signature data. Signatures are mere structures which don't
+ // own their own memory, so we need to destroy all data we created here.
+
+ for (auto sig : functions) {
+ if (sig) {
+ delete [] sig->name;
+ for (unsigned arg = 0; arg < sig->num_args; ++arg) {
+ delete [] sig->arg_names[arg];
+ }
+ delete [] sig->arg_names;
+ delete sig;
+ }
+ }
+ functions.clear();
+
+ for (auto sig : structs) {
+ if (sig) {
+ delete [] sig->name;
+ for (unsigned member = 0; member < sig->num_members; ++member) {
+ delete [] sig->member_names[member];
+ }
+ delete [] sig->member_names;
+ delete sig;
+ }
+ }
+ structs.clear();
+
+ for (auto sig : enums) {
+ if (sig) {
+ for (unsigned value = 0; value < sig->num_values; ++value) {
+ delete [] sig->values[value].name;
+ }
+ delete [] sig->values;
+ delete sig;
+ }
+ }
+ enums.clear();
+
+ for (auto sig : bitmasks) {
+ if (sig) {
+ for (unsigned flag = 0; flag < sig->num_flags; ++flag) {
+ delete [] sig->flags[flag].name;
+ }
+ delete [] sig->flags;
+ delete sig;
+ }
+ }
+ bitmasks.clear();
+
+ next_call_no = 0;
+}
+
+
+void Parser::getBookmark(ParseBookmark &bookmark) {
+ bookmark.offset = file->currentOffset();
+ bookmark.next_call_no = next_call_no;
+}
+
+
+void Parser::setBookmark(const ParseBookmark &bookmark) {
+ file->setCurrentOffset(bookmark.offset);
+ next_call_no = bookmark.next_call_no;
+
+ // Simply ignore all pending calls
+ deleteAll(calls);
+}
+
+
+Call *Parser::parse_call(Mode mode) {
+ do {
+ Call *call;
+ int c = read_byte();
+ switch (c) {
+ case trace::EVENT_ENTER:
+#if TRACE_VERBOSE
+ std::cerr << "\tENTER\n";
+#endif
+ parse_enter(mode);
+ break;
+ 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 = calls.front();
+ call->flags |= CALL_FLAG_INCOMPLETE;
+ calls.pop_front();
+ adjust_call_flags(call);
+ return call;
+ }
+ return NULL;
+ }
+ } while(true);
+}
+
+
+/**
+ * Helper function to lookup an ID in a vector, resizing the vector if it doesn't fit.
+ */
+template<class T>
+T *lookup(std::vector<T *> &map, size_t index) {
+ if (index >= map.size()) {
+ map.resize(index + 1);
+ return NULL;
+ } else {
+ return map[index];
+ }
+}
+
+
+Parser::FunctionSigFlags *
+Parser::parse_function_sig(void) {
+ size_t id = read_uint();
+
+ FunctionSigState *sig = lookup(functions, id);
+
+ if (!sig) {
+ /* parse the signature */
+ sig = new FunctionSigState;
+ sig->id = id;
+ sig->name = read_string();
+ sig->num_args = read_uint();
+ const char **arg_names = new const char *[sig->num_args];
+ for (unsigned i = 0; i < sig->num_args; ++i) {
+ arg_names[i] = read_string();
+ }
+ sig->arg_names = arg_names;
+ sig->flags = lookupCallFlags(sig->name);
+ sig->fileOffset = 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->fileOffset) {
+ /* skip over the signature */
+ skip_string(); /* name */
+ unsigned num_args = read_uint();
+ for (unsigned i = 0; i < num_args; ++i) {
+ skip_string(); /*arg_name*/
+ }
+ }
+
+ assert(sig);
+ return sig;
+}
+
+
+StructSig *Parser::parse_struct_sig() {
+ size_t id = read_uint();
+
+ StructSigState *sig = lookup(structs, id);
+
+ if (!sig) {
+ /* parse the signature */
+ sig = new StructSigState;
+ sig->id = id;
+ sig->name = read_string();
+ sig->num_members = read_uint();
+ const char **member_names = new const char *[sig->num_members];
+ for (unsigned i = 0; i < sig->num_members; ++i) {
+ member_names[i] = read_string();
+ }
+ sig->member_names = member_names;
+ sig->fileOffset = file->currentOffset();
+ structs[id] = sig;
+ } else if (file->currentOffset() < sig->fileOffset) {
+ /* skip over the signature */
+ skip_string(); /* name */
+ unsigned num_members = read_uint();
+ for (unsigned i = 0; i < num_members; ++i) {
+ skip_string(); /* member_name */
+ }
+ }
+
+ assert(sig);
+ return 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);
+
+ if (!sig) {
+ /* parse the signature */
+ sig = new EnumSigState;
+ sig->id = id;
+ sig->num_values = 1;
+ EnumValue *values = new EnumValue[sig->num_values];
+ values->name = read_string();
+ values->value = read_sint();
+ sig->values = values;
+ sig->fileOffset = file->currentOffset();
+ enums[id] = sig;
+ } else if (file->currentOffset() < sig->fileOffset) {
+ /* skip over the signature */
+ skip_string(); /*name*/
+ scan_value();
+ }
+
+ assert(sig);
+ return sig;
+}
+
+
+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->fileOffset = file->currentOffset();
+ enums[id] = sig;
+ } else if (file->currentOffset() < sig->fileOffset) {
+ /* 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();
+
+ BitmaskSigState *sig = lookup(bitmasks, id);
+
+ if (!sig) {
+ /* parse the signature */
+ sig = new BitmaskSigState;
+ sig->id = id;
+ sig->num_flags = read_uint();
+ BitmaskFlag *flags = new BitmaskFlag[sig->num_flags];
+ for (BitmaskFlag *it = flags; it != flags + sig->num_flags; ++it) {
+ it->name = read_string();
+ it->value = read_uint();
+ if (it->value == 0 && it != flags) {
+ std::cerr << "warning: bitmask " << it->name << " is zero but is not first flag\n";
+ }
+ }
+ sig->flags = flags;
+ sig->fileOffset = file->currentOffset();
+ bitmasks[id] = sig;
+ } else if (file->currentOffset() < sig->fileOffset) {
+ /* skip over the signature */
+ int num_flags = read_uint();
+ for (int i = 0; i < num_flags; ++i) {
+ skip_string(); /*name */
+ skip_uint(); /* value */
+ }
+ }
+
+ assert(sig);
+ return sig;
+}
+
+
+void Parser::parse_enter(Mode mode) {
+ unsigned thread_id;
+
+ if (version >= 4) {
+ thread_id = read_uint();
+ } else {
+ thread_id = 0;
+ }
+
+ FunctionSigFlags *sig = parse_function_sig();
+
+ Call *call = new Call(sig, sig->flags, thread_id);
+
+ call->no = next_call_no++;
+
+ if (parse_call_details(call, mode)) {
+ calls.push_back(call);
+ } else {
+ delete call;
+ }
+}
+
+
+Call *Parser::parse_leave(Mode mode) {
+ unsigned call_no = read_uint();
+ Call *call = NULL;
+ for (CallList::iterator it = calls.begin(); it != calls.end(); ++it) {
+ if ((*it)->no == call_no) {
+ call = *it;
+ calls.erase(it);
+ break;
+ }
+ }
+ 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;
+ }
+
+ if (parse_call_details(call, mode)) {
+ return call;
+ } else {
+ delete call;
+ return NULL;
+ }
+}
+
+
+bool Parser::parse_call_details(Call *call, Mode mode) {
+ do {
+ int c = read_byte();
+ switch (c) {
+ case trace::CALL_END:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_END\n";
+#endif
+ return true;
+ case trace::CALL_ARG:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_ARG\n";
+#endif
+ parse_arg(call, mode);
+ break;
+ 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";
+ exit(1);
+ case -1:
+ return false;
+ }
+ } while(true);
+}
+
+bool Parser::parse_call_backtrace(Call *call, Mode mode) {
+ unsigned num_frames = read_uint();
+ Backtrace* backtrace = new Backtrace(num_frames);
+ for (unsigned i = 0; i < num_frames; ++i) {
+ (*backtrace)[i] = parse_backtrace_frame(mode);
+ }
+ call->backtrace = backtrace;
+ return true;
+}
+
+StackFrame * Parser::parse_backtrace_frame(Mode mode) {
+ size_t id = read_uint();
+
+ StackFrameState *frame = lookup(frames, id);
+
+ if (!frame) {
+ frame = new StackFrameState;
+ int c = read_byte();
+ while (c != trace::BACKTRACE_END &&
+ c != -1) {
+ switch (c) {
+ case trace::BACKTRACE_MODULE:
+ frame->module = read_string();
+ break;
+ case trace::BACKTRACE_FUNCTION:
+ frame->function = read_string();
+ break;
+ case trace::BACKTRACE_FILENAME:
+ frame->filename = read_string();
+ break;
+ case trace::BACKTRACE_LINENUMBER:
+ frame->linenumber = read_uint();
+ break;
+ case trace::BACKTRACE_OFFSET:
+ frame->offset = read_uint();
+ break;
+ default:
+ std::cerr << "error: unknown backtrace detail "
+ << c << "\n";
+ exit(1);
+ }
+ c = read_byte();
+ }
+
+ frame->fileOffset = file->currentOffset();
+ frames[id] = frame;
+ } else if (file->currentOffset() < frame->fileOffset) {
+ int c = read_byte();
+ while (c != trace::BACKTRACE_END &&
+ c != -1) {
+ switch (c) {
+ case trace::BACKTRACE_MODULE:
+ scan_string();
+ break;
+ case trace::BACKTRACE_FUNCTION:
+ scan_string();
+ break;
+ case trace::BACKTRACE_FILENAME:
+ scan_string();
+ break;
+ case trace::BACKTRACE_LINENUMBER:
+ scan_uint();
+ break;
+ case trace::BACKTRACE_OFFSET:
+ scan_uint();
+ break;
+ default:
+ std::cerr << "error: unknown backtrace detail "
+ << c << "\n";
+ exit(1);
+ }
+ c = read_byte();
+ }
+ }
+
+ return frame;
+}
+
+/**
+ * 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();
+ Value *value = parse_value(mode);
+ if (value) {
+ if (index >= call->args.size()) {
+ call->args.resize(index + 1);
+ }
+ call->args[index].value = value;
+ }
+}
+
+
+Value *Parser::parse_value(void) {
+ int c;
+ Value *value;
+ c = read_byte();
+ switch (c) {
+ case trace::TYPE_NULL:
+ value = new Null;
+ break;
+ case trace::TYPE_FALSE:
+ value = new Bool(false);
+ break;
+ case trace::TYPE_TRUE:
+ value = new Bool(true);
+ break;
+ case trace::TYPE_SINT:
+ value = parse_sint();
+ break;
+ case trace::TYPE_UINT:
+ value = parse_uint();
+ break;
+ case trace::TYPE_FLOAT:
+ value = parse_float();
+ break;
+ case trace::TYPE_DOUBLE:
+ value = parse_double();
+ break;
+ case trace::TYPE_STRING:
+ value = parse_string();
+ break;
+ case trace::TYPE_ENUM:
+ value = parse_enum();
+ break;
+ case trace::TYPE_BITMASK:
+ value = parse_bitmask();
+ break;
+ case trace::TYPE_ARRAY:
+ value = parse_array();
+ break;
+ case trace::TYPE_STRUCT:
+ value = parse_struct();
+ break;
+ case trace::TYPE_BLOB:
+ value = parse_blob();
+ break;
+ case trace::TYPE_OPAQUE:
+ value = parse_opaque();
+ break;
+ case trace::TYPE_REPR:
+ value = parse_repr();
+ break;
+ case trace::TYPE_WSTRING:
+ value = parse_wstring();
+ break;
+ default:
+ std::cerr << "error: unknown type " << c << "\n";
+ exit(1);
+ case -1:
+ value = NULL;
+ break;
+ }
+#if TRACE_VERBOSE
+ if (value) {
+ 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:
+ break;
+ case trace::TYPE_SINT:
+ scan_sint();
+ break;
+ case trace::TYPE_UINT:
+ scan_uint();
+ break;
+ case trace::TYPE_FLOAT:
+ scan_float();
+ break;
+ case trace::TYPE_DOUBLE:
+ scan_double();
+ break;
+ case trace::TYPE_STRING:
+ scan_string();
+ break;
+ case trace::TYPE_ENUM:
+ scan_enum();
+ break;
+ case trace::TYPE_BITMASK:
+ scan_bitmask();
+ break;
+ case trace::TYPE_ARRAY:
+ scan_array();
+ break;
+ case trace::TYPE_STRUCT:
+ scan_struct();
+ break;
+ case trace::TYPE_BLOB:
+ scan_blob();
+ break;
+ case trace::TYPE_OPAQUE:
+ scan_opaque();
+ break;
+ case trace::TYPE_REPR:
+ scan_repr();
+ break;
+ case trace::TYPE_WSTRING:
+ scan_wstring();
+ break;
+ default:
+ std::cerr << "error: unknown type " << c << "\n";
+ exit(1);
+ case -1:
+ break;
+ }
+}
+
+
+Value *Parser::parse_sint() {
+ return new SInt(-(signed long long)read_uint());
+}
+
+
+void Parser::scan_sint() {
+ skip_uint();
+}
+
+
+Value *Parser::parse_uint() {
+ return new UInt(read_uint());
+}
+
+
+void Parser::scan_uint() {
+ skip_uint();
+}
+
+
+Value *Parser::parse_float() {
+ float value;
+ file->read(&value, sizeof value);
+ return new Float(value);
+}
+
+
+void Parser::scan_float() {
+ file->skip(sizeof(float));
+}
+
+
+Value *Parser::parse_double() {
+ double value;
+ file->read(&value, sizeof value);
+ return new Double(value);
+}
+
+
+void Parser::scan_double() {
+ file->skip(sizeof(double));
+}
+
+
+Value *Parser::parse_string() {
+ return new String(read_string());
+}
+
+
+void Parser::scan_string() {
+ skip_string();
+}
+
+
+Value *Parser::parse_enum() {
+ 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() {
+ if (version >= 3) {
+ parse_enum_sig();
+ skip_sint();
+ } else {
+ parse_old_enum_sig();
+ }
+}
+
+
+Value *Parser::parse_bitmask() {
+ BitmaskSig *sig = parse_bitmask_sig();
+
+ unsigned long long value = read_uint();
+
+ return new Bitmask(sig, value);
+}
+
+
+void Parser::scan_bitmask() {
+ parse_bitmask_sig();
+ skip_uint(); /* value */
+}
+
+
+Value *Parser::parse_array(void) {
+ size_t len = read_uint();
+ Array *array = new Array(len);
+ for (size_t i = 0; i < len; ++i) {
+ array->values[i] = parse_value();
+ }
+ return array;
+}
+
+
+void Parser::scan_array(void) {
+ size_t len = read_uint();
+ for (size_t i = 0; i < len; ++i) {
+ scan_value();
+ }
+}
+
+
+Value *Parser::parse_blob(void) {
+ size_t size = read_uint();
+ Blob *blob = new Blob(size);
+ if (size) {
+ file->read(blob->buf, size);
+ }
+ return blob;
+}
+
+
+void Parser::scan_blob(void) {
+ size_t size = read_uint();
+ if (size) {
+ file->skip(size);
+ }
+}
+
+
+Value *Parser::parse_struct() {
+ StructSig *sig = parse_struct_sig();
+ Struct *value = new Struct(sig);
+
+ for (size_t i = 0; i < sig->num_members; ++i) {
+ value->members[i] = parse_value();
+ }
+
+ return value;
+}
+
+
+void Parser::scan_struct() {
+ StructSig *sig = parse_struct_sig();
+ for (size_t i = 0; i < sig->num_members; ++i) {
+ scan_value();
+ }
+}
+
+
+Value *Parser::parse_opaque() {
+ unsigned long long addr;
+ addr = read_uint();
+ return new Pointer(addr);
+}
+
+
+void Parser::scan_opaque() {
+ skip_uint();
+}
+
+
+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();
+}
+
+
+Value *Parser::parse_wstring() {
+ size_t len = read_uint();
+ wchar_t * value = new wchar_t[len + 1];
+ for (size_t i = 0; i < len; ++i) {
+ value[i] = read_uint();
+ }
+ value[len] = 0;
+#if TRACE_VERBOSE
+ std::cerr << "\tWSTRING \"" << value << "\"\n";
+#endif
+ return new WString(value);
+}
+
+
+void Parser::scan_wstring() {
+ size_t len = read_uint();
+ for (size_t i = 0; i < len; ++i) {
+ skip_uint();
+ }
+}
+
+
+const char * Parser::read_string(void) {
+ size_t len = read_uint();
+ char * value = new char[len + 1];
+ if (len) {
+ file->read(value, len);
+ }
+ value[len] = 0;
+#if TRACE_VERBOSE
+ std::cerr << "\tSTRING \"" << value << "\"\n";
+#endif
+ return value;
+}
+
+
+void Parser::skip_string(void) {
+ size_t len = read_uint();
+ file->skip(len);
+}
+
+
+/*
+ * 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;
+ unsigned shift = 0;
+ do {
+ c = file->getc();
+ if (c == -1) {
+ break;
+ }
+ value |= (unsigned long long)(c & 0x7f) << shift;
+ shift += 7;
+ } while(c & 0x80);
+#if TRACE_VERBOSE
+ std::cerr << "\tUINT " << value << "\n";
+#endif
+ return value;
+}
+
+
+void Parser::skip_uint(void) {
+ int c;
+ do {
+ c = file->getc();
+ if (c == -1) {
+ break;
+ }
+ } while(c & 0x80);
+}
+
+
+inline int Parser::read_byte(void) {
+ int c = file->getc();
+#if TRACE_VERBOSE
+ if (c < 0)
+ std::cerr << "\tEOF" << "\n";
+ else
+ std::cerr << "\tBYTE 0x" << std::hex << c << std::dec << "\n";
+#endif
+ return c;
+}
+
+
+inline void Parser::skip_byte(void) {
+ file->skip(1);
+}
+
+
+} /* namespace trace */
diff --git a/lib/trace/trace_parser.hpp b/lib/trace/trace_parser.hpp
new file mode 100644
index 00000000..dad9f9cf
--- /dev/null
+++ b/lib/trace/trace_parser.hpp
@@ -0,0 +1,248 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#pragma once
+
+
+#include <iostream>
+#include <list>
+
+#include "trace_file.hpp"
+#include "trace_format.hpp"
+#include "trace_model.hpp"
+#include "trace_api.hpp"
+
+
+namespace trace {
+
+
+struct ParseBookmark
+{
+ File::Offset offset;
+ unsigned next_call_no;
+};
+
+
+// Parser interface
+class AbstractParser
+{
+public:
+ virtual ~AbstractParser() {}
+ virtual Call *parse_call(void) = 0;
+ virtual void getBookmark(ParseBookmark &bookmark) = 0;
+ virtual void setBookmark(const ParseBookmark &bookmark) = 0;
+ virtual bool open(const char *filename) = 0;
+ virtual void close(void) = 0;
+ virtual unsigned long long getVersion(void) const = 0;
+};
+
+
+class Parser: public AbstractParser
+{
+protected:
+ File *file;
+
+ enum Mode {
+ FULL = 0,
+ SCAN,
+ SKIP
+ };
+
+ typedef std::list<Call *> CallList;
+ CallList calls;
+
+ struct FunctionSigFlags : public FunctionSig {
+ CallFlags flags;
+ };
+
+ // Helper template that extends a base signature structure, with additional
+ // parsing information.
+ template< class T >
+ struct SigState : public T {
+ // Offset in the file of where signature was defined. It is used when
+ // reparsing to determine whether the signature definition is to be
+ // expected next or not.
+ File::Offset fileOffset;
+ };
+
+ typedef SigState<FunctionSigFlags> FunctionSigState;
+ typedef SigState<StructSig> StructSigState;
+ typedef SigState<EnumSig> EnumSigState;
+ typedef SigState<BitmaskSig> BitmaskSigState;
+ typedef SigState<StackFrame> StackFrameState;
+
+ typedef std::vector<FunctionSigState *> FunctionMap;
+ typedef std::vector<StructSigState *> StructMap;
+ typedef std::vector<EnumSigState *> EnumMap;
+ typedef std::vector<BitmaskSigState *> BitmaskMap;
+ typedef std::vector<StackFrameState *> StackFrameMap;
+
+ FunctionMap functions;
+ StructMap structs;
+ EnumMap enums;
+ BitmaskMap bitmasks;
+ StackFrameMap frames;
+
+ FunctionSig *glGetErrorSig;
+
+ unsigned next_call_no;
+
+ unsigned long long version;
+public:
+ API api;
+
+ Parser();
+
+ ~Parser();
+
+ bool open(const char *filename) override;
+
+ void close(void) override;
+
+ Call *parse_call(void) override {
+ return parse_call(FULL);
+ }
+
+ bool supportsOffsets() const
+ {
+ return file->supportsOffsets();
+ }
+
+ void getBookmark(ParseBookmark &bookmark) override;
+
+ void setBookmark(const ParseBookmark &bookmark) override;
+
+ unsigned long long getVersion(void) const override {
+ return version;
+ }
+
+ int percentRead()
+ {
+ return file->percentRead();
+ }
+
+ Call *scan_call() {
+ return parse_call(SCAN);
+ }
+
+protected:
+ Call *parse_call(Mode mode);
+
+ FunctionSigFlags *parse_function_sig(void);
+ StructSig *parse_struct_sig();
+ EnumSig *parse_old_enum_sig();
+ EnumSig *parse_enum_sig();
+ BitmaskSig *parse_bitmask_sig();
+
+public:
+ static CallFlags
+ lookupCallFlags(const char *name);
+
+protected:
+ Call *parse_Call(Mode mode);
+
+ void parse_enter(Mode mode);
+
+ Call *parse_leave(Mode mode);
+
+ bool parse_call_details(Call *call, Mode mode);
+
+ bool parse_call_backtrace(Call *call, Mode mode);
+ StackFrame * parse_backtrace_frame(Mode mode);
+
+ void adjust_call_flags(Call *call);
+
+ void parse_arg(Call *call, Mode mode);
+
+ Value *parse_value(void);
+ void scan_value(void);
+ inline Value *parse_value(Mode mode) {
+ if (mode == FULL) {
+ return parse_value();
+ } else {
+ scan_value();
+ return NULL;
+ }
+ }
+
+ Value *parse_sint();
+ void scan_sint();
+
+ Value *parse_uint();
+ void scan_uint();
+
+ Value *parse_float();
+ void scan_float();
+
+ Value *parse_double();
+ void scan_double();
+
+ Value *parse_string();
+ void scan_string();
+
+ Value *parse_enum();
+ void scan_enum();
+
+ Value *parse_bitmask();
+ void scan_bitmask();
+
+ Value *parse_array(void);
+ void scan_array(void);
+
+ Value *parse_blob(void);
+ void scan_blob(void);
+
+ Value *parse_struct();
+ void scan_struct();
+
+ Value *parse_opaque();
+ void scan_opaque();
+
+ Value *parse_repr();
+ void scan_repr();
+
+ Value *parse_wstring();
+ void scan_wstring();
+
+ const char * read_string(void);
+ void skip_string(void);
+
+ signed long long read_sint(void);
+ void skip_sint(void);
+
+ unsigned long long read_uint(void);
+ void skip_uint(void);
+
+ inline int read_byte(void);
+ inline void skip_byte(void);
+};
+
+
+AbstractParser *
+lastFrameLoopParser(AbstractParser *parser, int loopCount);
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_parser_flags.cpp b/lib/trace/trace_parser_flags.cpp
new file mode 100644
index 00000000..69bd8376
--- /dev/null
+++ b/lib/trace/trace_parser_flags.cpp
@@ -0,0 +1,550 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc.
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+/*
+ * Label functions based on their name.
+ */
+
+
+#include "trace_parser.hpp"
+
+#include <assert.h>
+
+#include <regex>
+
+#include "trace_lookup.hpp"
+
+
+using namespace trace;
+
+
+/**
+ * Shortcut for SwapBuffers, which terminate and swap bound render buffer.
+ */
+#define CALL_FLAG_SWAPBUFFERS (CALL_FLAG_END_FRAME | CALL_FLAG_SWAP_RENDERTARGET)
+
+
+
+/**
+ * Default call flags.
+ */
+const CallFlags
+defaultCallFlags = 0;
+
+
+/**
+ * Call flags lookup table.
+ */
+const Entry<CallFlags>
+callFlagTable[] = {
+ { "CGLFlushDrawable", CALL_FLAG_END_FRAME },
+ { "CGLGetCurrentContext", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "D3DPERF_BeginEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "D3DPERF_EndEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP },
+ { "D3DPERF_SetMarker", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER },
+ { "ID3DUserDefinedAnnotation::BeginEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "ID3DUserDefinedAnnotation::EndEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP },
+ { "ID3DUserDefinedAnnotation::SetMarker", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER },
+ { "IDirect3D8::CheckDeviceFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D8::EnumAdapterModes", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D8::GetAdapterModeCount", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D8::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::CheckDeviceFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::EnumAdapterModes", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::GetAdapterModeCount", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::CheckDeviceFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::EnumAdapterModes", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::GetAdapterModeCount", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice2::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice2::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawIndexedPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawIndexedPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawIndexedPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawIndexedPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawIndexedPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawRectPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawTriPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice8::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice8::SetRenderTarget", CALL_FLAG_SWAP_RENDERTARGET },
+ { "IDirect3DDevice9::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawIndexedPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawRectPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawTriPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice9::GetRenderTargetData", CALL_FLAG_END_FRAME },
+ { "IDirect3DDevice9::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice9::SetRenderTarget", CALL_FLAG_SWAP_RENDERTARGET },
+ { "IDirect3DDevice9Ex::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawIndexedPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawRectPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawTriPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice9Ex::GetRenderTargetData", CALL_FLAG_END_FRAME },
+ { "IDirect3DDevice9Ex::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice9Ex::PresentEx", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice9Ex::SetRenderTarget", CALL_FLAG_SWAP_RENDERTARGET },
+ { "IDirect3DSwapChain9::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DSwapChain9Ex::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DViewport2::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DViewport3::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DViewport3::Clear2", CALL_FLAG_RENDER },
+ { "IDirect3DViewport::Clear", CALL_FLAG_RENDER },
+ { "eglGetConfigAttrib", CALL_FLAG_VERBOSE },
+ { "eglGetProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "eglQueryString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "eglSwapBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "eglSwapBuffersWithDamageEXT", CALL_FLAG_SWAPBUFFERS },
+ { "eglSwapBuffersWithDamageKHR", CALL_FLAG_SWAPBUFFERS },
+ { "glAreProgramsResidentNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glAreTexturesResident", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glAreTexturesResidentEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glBufferRegionEnabled", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageControl", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageControlARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageEnableAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageInsert", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDebugMessageInsertAMD", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDebugMessageInsertARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDebugMessageInsertKHR", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glFrameTerminatorGREMEDY", CALL_FLAG_END_FRAME },
+ { "glGetActiveAtomicCounterBufferiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveAttrib", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveAttribARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveSubroutineName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveSubroutineUniformName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveSubroutineUniformiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniform", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformBlockName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformBlockiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformsiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveVaryingNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetArrayObjectfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetArrayObjectivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetAttachedObjectsARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetAttachedShaders", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBooleanIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBooleani_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBooleanv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameteri64v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameterivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameterui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferPointerv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferPointervARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferSubData", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferSubDataARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetClipPlane", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTable", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterfvSGI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterivSGI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableSGI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerInputParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerInputParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerOutputParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerOutputParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerStageParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionFilterEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDetailTexFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDoubleIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDoublei_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDoublev", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetError", CALL_FLAG_NO_SIDE_EFFECTS }, // verbose will be set later for GL_NO_ERROR
+ { "glGetFenceivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFinalCombinerInputParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFinalCombinerInputParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFogFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragDataIndex", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentLightfvSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentLightivSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentMaterialfvSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentMaterialivSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferAttachmentParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferAttachmentParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetGraphicsResetStatusARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHandleARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetImageTransformParameterfvHP", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetImageTransformParameterivHP", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInfoLogARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInstrumentsSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInternalformati64v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInternalformativ", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInvariantBooleanvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInvariantFloatvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInvariantIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLightfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLightiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetListParameterfvSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetListParameterivSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLocalConstantBooleanvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLocalConstantFloatvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLocalConstantIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapAttribParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapAttribParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapControlPointsNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapdv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMaterialfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMaterialiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexEnvfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexEnvivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexGendvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexGenfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexGenivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexLevelParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexLevelParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultisamplefv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultisamplefvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferParameterui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferSubDataEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedFramebufferAttachmentParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedFramebufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterdvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramStringEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedRenderbufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedStringARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedStringivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectBufferfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectBufferivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectParameterfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectParameterivAPPLE", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectParameterivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectPtrLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetOcclusionQueryivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetOcclusionQueryuivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCounterDataAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCounterInfoAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCounterStringAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCountersAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorGroupStringAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorGroupsAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPixelTexGenParameterfvSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPixelTexGenParameterivSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPointerIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPointerv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramBinary", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterIivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterIuivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramInfoLog", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramInterfaceiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterIivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterIuivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramNamedParameterdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramNamedParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramParameterdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramPipelineInfoLog", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramPipelineiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceIndex", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceLocation", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceLocationIndex", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramStageiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramStringARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramStringNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramSubroutineParameteruivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetQueryIndexediv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetQueryiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetQueryivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetRenderbufferParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetRenderbufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameterIiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameterIuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSeparableFilterEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderInfoLog", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderPrecisionFormat", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderSource", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderSourceARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSharpenTexFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glGetStringi", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glGetSynciv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexBumpParameterfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexBumpParameterivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexEnvfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexEnviv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexFilterFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexGendv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexGenfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexGeniv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTrackMatrixivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTransformFeedbackVarying", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTransformFeedbackVaryingEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTransformFeedbackVaryingNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformIndices", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformSubroutineuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformdv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformi64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantArrayObjectfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantArrayObjectivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantBooleanvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantFloatvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayIntegeri_vEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayPointeri_vEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureStreamdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureStreamfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureStreamivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoi64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideouivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnMapdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnMapfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnMapivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformuivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glInsertEventMarkerEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glIsAsyncMarkerSGIX", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsBuffer", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsBufferARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsBufferResidentNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsEnabled", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsEnabledIndexedEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsEnabledi", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFenceAPPLE", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFenceNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFramebuffer", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFramebufferEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsList", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsNameAMD", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsNamedBufferResidentNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsNamedStringARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsObjectBufferATI", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsOcclusionQueryNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgram", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgramARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgramNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgramPipeline", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsQuery", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsQueryARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsRenderbuffer", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsRenderbufferEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsSampler", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsShader", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsSync", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTexture", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTextureEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTransformFeedback", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTransformFeedbackNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVariantEnabledEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVertexArray", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVertexArrayAPPLE", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVertexAttribEnabledAPPLE", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glObjectLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glObjectLabelKHR", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glObjectPtrLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glObjectPtrLabelKHR", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glPopDebugGroup", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP},
+ { "glPopDebugGroupKHR", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP},
+ { "glPopGroupMarkerEXT", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP},
+ { "glPushDebugGroup", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "glPushDebugGroupKHR", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "glPushGroupMarkerEXT", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "glStringMarkerGREMEDY", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER },
+ { "glXGetClientString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetConfig", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentContext", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentDisplay", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentDisplayEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentDrawable", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentReadDrawable", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentReadDrawableSGI", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetFBConfigAttrib", CALL_FLAG_VERBOSE },
+ { "glXGetFBConfigAttribSGIX", CALL_FLAG_VERBOSE },
+ { "glXGetProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetProcAddressARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXIsDirect", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXQueryExtension", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXQueryExtensionsString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXQueryVersion", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXSwapBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "wglDescribePixelFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetCurrentContext", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetCurrentDC", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetDefaultProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetExtensionsStringARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetExtensionsStringEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetPixelFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetPixelFormatAttribivARB", CALL_FLAG_VERBOSE },
+ { "wglGetPixelFormatAttribivEXT", CALL_FLAG_VERBOSE },
+ { "wglGetProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglSwapBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "wglSwapLayerBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "wglSwapMultipleBuffers", CALL_FLAG_SWAPBUFFERS },
+ // NOTE: New entries must be sorted alphabetically
+};
+
+
+
+
+
+/**
+ * Lookup call flags by name.
+ */
+CallFlags
+Parser::lookupCallFlags(const char *name)
+{
+ using namespace std;
+
+ if (name[0] == 'g') {
+ static const regex draw(
+ "^gl([A-Z][a-z]+)*Draw(Range|Mesh)?(Arrays|Elements)([A-Z][a-zA-Z]*)?$"
+ );
+
+ static const regex miscDraw(
+ "^gl("
+ "CallLists?|"
+ "Clear|"
+ "End|"
+ "DrawPixels|"
+ "DrawTransformFeedback([A-Z][a-zA-Z]*)?|"
+ "BlitFramebuffer|"
+ "Rect[dfis]v?|"
+ "EvalMesh[0-9]+"
+ ")[0-9A-Z]*$"
+ );
+ if (regex_match(name, draw) ||
+ regex_match(name, miscDraw)) {
+ return CALL_FLAG_RENDER;
+ }
+
+ static const regex fbo( "^glBindFramebuffer[0-9A-Z]*");
+ if (regex_match(name, fbo)) {
+ return CALL_FLAG_SWAP_RENDERTARGET;
+ }
+
+ static const regex get(
+ "^gl("
+ "GetFloat|"
+ "GetInteger|"
+ "GetVertexAttrib|"
+ "GetTex(ture)?(Level)?Parameter"
+ ")\\w+$"
+ );
+ if (regex_match(name, get)) {
+ return CALL_FLAG_NO_SIDE_EFFECTS;
+ }
+ }
+
+ if (name[0] == 'I') {
+ static const regex present("^IDXGI(Decode)?SwapChain\\w*::Present\\w*$");
+ static const regex draw ("^ID3D1(0Device|1DeviceContext)\\d*::(Draw\\w*|ExecuteCommandList)$");
+ static const regex srt ("^ID3D1(0Device|1DeviceContext)\\d*::OMSetRenderTargets\\w*$");
+ static const regex cmql ("^ID3D1[01]Device\\d*::CheckMultisampleQualityLevels$");
+
+ if (regex_match(name, draw)) return CALL_FLAG_RENDER;
+ if (regex_match(name, srt)) return CALL_FLAG_SWAP_RENDERTARGET;
+ if (regex_match(name, present)) return CALL_FLAG_END_FRAME /* | CALL_FLAG_SWAPBUFFERS */;
+ if (regex_match(name, cmql)) return CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE;
+ }
+
+ return entryLookup(name, callFlagTable, defaultCallFlags);
+}
diff --git a/lib/trace/trace_parser_flags_test.cpp b/lib/trace/trace_parser_flags_test.cpp
new file mode 100644
index 00000000..731dce1f
--- /dev/null
+++ b/lib/trace/trace_parser_flags_test.cpp
@@ -0,0 +1,644 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_parser.hpp"
+
+#include "gtest/gtest.h"
+
+#include "trace_lookup.hpp"
+
+using namespace trace;
+
+
+/**
+ * Shortcut for SwapBuffers, which terminate and swap bound render buffer.
+ */
+#define CALL_FLAG_SWAPBUFFERS (CALL_FLAG_END_FRAME | CALL_FLAG_SWAP_RENDERTARGET)
+
+
+
+/**
+ * Call flags lookup table.
+ */
+static const Entry<CallFlags>
+entries[] = {
+ { "CGLFlushDrawable", CALL_FLAG_END_FRAME },
+ { "CGLGetCurrentContext", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "D3DPERF_BeginEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "D3DPERF_EndEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP },
+ { "D3DPERF_SetMarker", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER },
+ { "ID3D10Device1::CheckMultisampleQualityLevels", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "ID3D10Device1::Draw", CALL_FLAG_RENDER },
+ { "ID3D10Device1::DrawAuto", CALL_FLAG_RENDER },
+ { "ID3D10Device1::DrawIndexed", CALL_FLAG_RENDER },
+ { "ID3D10Device1::DrawIndexedInstanced", CALL_FLAG_RENDER },
+ { "ID3D10Device1::DrawInstanced", CALL_FLAG_RENDER },
+ { "ID3D10Device1::OMSetRenderTargets", CALL_FLAG_SWAP_RENDERTARGET },
+ { "ID3D10Device::CheckMultisampleQualityLevels", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "ID3D10Device::Draw", CALL_FLAG_RENDER },
+ { "ID3D10Device::DrawAuto", CALL_FLAG_RENDER },
+ { "ID3D10Device::DrawIndexed", CALL_FLAG_RENDER },
+ { "ID3D10Device::DrawIndexedInstanced", CALL_FLAG_RENDER },
+ { "ID3D10Device::DrawInstanced", CALL_FLAG_RENDER },
+ { "ID3D10Device::OMSetRenderTargets", CALL_FLAG_SWAP_RENDERTARGET },
+ { "ID3D11Device::CheckMultisampleQualityLevels", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "ID3D11DeviceContext1::Draw", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::DrawAuto", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::DrawIndexed", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::DrawIndexedInstanced", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::DrawIndexedInstancedIndirect", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::DrawInstanced", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::DrawInstancedIndirect", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::ExecuteCommandList", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext1::OMSetRenderTargets", CALL_FLAG_SWAP_RENDERTARGET },
+ { "ID3D11DeviceContext1::OMSetRenderTargetsAndUnorderedAccessViews", CALL_FLAG_SWAP_RENDERTARGET },
+ { "ID3D11DeviceContext::Draw", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::DrawAuto", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::DrawIndexed", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::DrawIndexedInstanced", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::DrawIndexedInstancedIndirect", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::DrawInstanced", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::DrawInstancedIndirect", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::ExecuteCommandList", CALL_FLAG_RENDER },
+ { "ID3D11DeviceContext::OMSetRenderTargets", CALL_FLAG_SWAP_RENDERTARGET },
+ { "ID3D11DeviceContext::OMSetRenderTargetsAndUnorderedAccessViews", CALL_FLAG_SWAP_RENDERTARGET },
+ { "ID3DUserDefinedAnnotation::BeginEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "ID3DUserDefinedAnnotation::EndEvent", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP },
+ { "ID3DUserDefinedAnnotation::SetMarker", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER },
+ { "IDXGIDecodeSwapChain::PresentBuffer", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDXGISwapChain1::Present", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDXGISwapChain1::Present1", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDXGISwapChain2::Present", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDXGISwapChain2::Present1", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDXGISwapChain::Present", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDXGISwapChainDWM::Present", CALL_FLAG_END_FRAME /* CALL_FLAG_SWAPBUFFERS */ },
+ { "IDirect3D8::CheckDeviceFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D8::EnumAdapterModes", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D8::GetAdapterModeCount", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D8::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::CheckDeviceFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::EnumAdapterModes", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::GetAdapterModeCount", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::CheckDeviceFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::EnumAdapterModes", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::GetAdapterModeCount", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3D9Ex::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice2::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice2::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawIndexedPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawIndexedPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice3::DrawPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawIndexedPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawIndexedPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawPrimitiveStrided", CALL_FLAG_RENDER },
+ { "IDirect3DDevice7::DrawPrimitiveVB", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawIndexedPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawRectPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::DrawTriPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice8::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice8::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice8::SetRenderTarget", CALL_FLAG_SWAP_RENDERTARGET },
+ { "IDirect3DDevice9::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawIndexedPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawRectPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::DrawTriPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice9::GetRenderTargetData", CALL_FLAG_END_FRAME },
+ { "IDirect3DDevice9::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice9::SetRenderTarget", CALL_FLAG_SWAP_RENDERTARGET },
+ { "IDirect3DDevice9Ex::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawIndexedPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawIndexedPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawPrimitive", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawPrimitiveUP", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawRectPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::DrawTriPatch", CALL_FLAG_RENDER },
+ { "IDirect3DDevice9Ex::GetDeviceCaps", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "IDirect3DDevice9Ex::GetRenderTargetData", CALL_FLAG_END_FRAME },
+ { "IDirect3DDevice9Ex::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice9Ex::PresentEx", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DDevice9Ex::SetRenderTarget", CALL_FLAG_SWAP_RENDERTARGET },
+ { "IDirect3DSwapChain9::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DSwapChain9Ex::Present", CALL_FLAG_SWAPBUFFERS },
+ { "IDirect3DViewport2::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DViewport3::Clear", CALL_FLAG_RENDER },
+ { "IDirect3DViewport3::Clear2", CALL_FLAG_RENDER },
+ { "IDirect3DViewport::Clear", CALL_FLAG_RENDER },
+ { "eglGetConfigAttrib", CALL_FLAG_VERBOSE },
+ { "eglGetProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "eglQueryString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "eglSwapBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "glAreProgramsResidentNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glAreTexturesResident", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glAreTexturesResidentEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glBindFramebuffer", CALL_FLAG_SWAP_RENDERTARGET },
+ { "glBindFramebufferEXT", CALL_FLAG_SWAP_RENDERTARGET },
+ { "glBindFramebufferOES", CALL_FLAG_SWAP_RENDERTARGET },
+ { "glBlitFramebuffer", CALL_FLAG_RENDER },
+ { "glBlitFramebufferANGLE", CALL_FLAG_RENDER },
+ { "glBlitFramebufferEXT", CALL_FLAG_RENDER },
+ { "glBlitFramebufferNV", CALL_FLAG_RENDER },
+ { "glBufferRegionEnabled", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glCallList", CALL_FLAG_RENDER },
+ { "glCallLists", CALL_FLAG_RENDER },
+ { "glClear", CALL_FLAG_RENDER },
+ { "glDebugMessageControl", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageControlARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageEnableAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glDebugMessageInsert", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDebugMessageInsertAMD", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDebugMessageInsertARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDebugMessageInsertKHR", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glDrawArrays", CALL_FLAG_RENDER },
+ { "glDrawArraysEXT", CALL_FLAG_RENDER },
+ { "glDrawArraysIndirect", CALL_FLAG_RENDER },
+ { "glDrawArraysInstanced", CALL_FLAG_RENDER },
+ { "glDrawArraysInstancedANGLE", CALL_FLAG_RENDER },
+ { "glDrawArraysInstancedARB", CALL_FLAG_RENDER },
+ { "glDrawArraysInstancedBaseInstance", CALL_FLAG_RENDER },
+ { "glDrawArraysInstancedEXT", CALL_FLAG_RENDER },
+ { "glDrawElements", CALL_FLAG_RENDER },
+ { "glDrawElementsBaseVertex", CALL_FLAG_RENDER },
+ { "glDrawElementsIndirect", CALL_FLAG_RENDER },
+ { "glDrawElementsInstanced", CALL_FLAG_RENDER },
+ { "glDrawElementsInstancedANGLE", CALL_FLAG_RENDER },
+ { "glDrawElementsInstancedARB", CALL_FLAG_RENDER },
+ { "glDrawElementsInstancedBaseInstance", CALL_FLAG_RENDER },
+ { "glDrawElementsInstancedBaseVertex", CALL_FLAG_RENDER },
+ { "glDrawElementsInstancedBaseVertexBaseInstance", CALL_FLAG_RENDER },
+ { "glDrawElementsInstancedEXT", CALL_FLAG_RENDER },
+ { "glDrawMeshArraysSUN", CALL_FLAG_RENDER },
+ { "glDrawPixels", CALL_FLAG_RENDER },
+ { "glDrawRangeElementArrayAPPLE", 0 },
+ { "glDrawRangeElementArrayATI", 0 },
+ { "glDrawRangeElements", CALL_FLAG_RENDER },
+ { "glDrawRangeElementsBaseVertex", CALL_FLAG_RENDER },
+ { "glDrawRangeElementsEXT", CALL_FLAG_RENDER },
+ { "glDrawTransformFeedback", CALL_FLAG_RENDER },
+ { "glDrawTransformFeedbackInstanced", CALL_FLAG_RENDER },
+ { "glDrawTransformFeedbackNV", CALL_FLAG_RENDER },
+ { "glDrawTransformFeedbackStream", CALL_FLAG_RENDER },
+ { "glDrawTransformFeedbackStreamInstanced", CALL_FLAG_RENDER },
+ { "glEnd", CALL_FLAG_RENDER },
+ { "glEvalMesh1", CALL_FLAG_RENDER },
+ { "glEvalMesh2", CALL_FLAG_RENDER },
+ { "glFrameTerminatorGREMEDY", CALL_FLAG_END_FRAME },
+ { "glGetActiveAtomicCounterBufferiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveAttrib", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveAttribARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveSubroutineName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveSubroutineUniformName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveSubroutineUniformiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniform", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformBlockName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformBlockiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveUniformsiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetActiveVaryingNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetArrayObjectfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetArrayObjectivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetAttachedObjectsARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetAttachedShaders", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBooleanIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBooleani_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBooleanv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameteri64v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameterivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferParameterui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferPointerv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferPointervARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferSubData", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetBufferSubDataARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetClipPlane", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTable", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterfvSGI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableParameterivSGI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetColorTableSGI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerInputParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerInputParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerOutputParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerOutputParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetCombinerStageParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionFilterEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetConvolutionParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDetailTexFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDoubleIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDoublei_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetDoublev", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetError", CALL_FLAG_NO_SIDE_EFFECTS }, // verbose will be set later for GL_NO_ERROR
+ { "glGetFenceivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFinalCombinerInputParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFinalCombinerInputParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFloatIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFloati_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFloatv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFogFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragDataIndex", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentLightfvSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentLightivSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentMaterialfvSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFragmentMaterialivSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferAttachmentParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferAttachmentParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetFramebufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetGraphicsResetStatusARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHandleARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetHistogramParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetImageTransformParameterfvHP", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetImageTransformParameterivHP", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInfoLogARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInstrumentsSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInteger64i_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInteger64v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetIntegerIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetIntegeri_v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetIntegerui64i_vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetIntegerui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetIntegerv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInternalformati64v", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInternalformativ", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInvariantBooleanvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInvariantFloatvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetInvariantIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLightfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLightiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetListParameterfvSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetListParameterivSGIX", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLocalConstantBooleanvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLocalConstantFloatvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetLocalConstantIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapAttribParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapAttribParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapControlPointsNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapParameterivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapdv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMapiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMaterialfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMaterialiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMinmaxParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexEnvfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexEnvivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexGendvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexGenfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexGenivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexLevelParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexLevelParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultiTexParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultisamplefv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetMultisamplefvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferParameterui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedBufferSubDataEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedFramebufferAttachmentParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedFramebufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterdvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramLocalParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramStringEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedProgramivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedRenderbufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedStringARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetNamedStringivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectBufferfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectBufferivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectParameterfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectParameterivAPPLE", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectParameterivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetObjectPtrLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetOcclusionQueryivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetOcclusionQueryuivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCounterDataAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCounterInfoAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCounterStringAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorCountersAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorGroupStringAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPerfMonitorGroupsAMD", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPixelTexGenParameterfvSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPixelTexGenParameterivSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPointerIndexedvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPointerv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramBinary", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterIivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterIuivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramEnvParameterfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramInfoLog", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramInterfaceiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterIivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterIuivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramLocalParameterfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramNamedParameterdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramNamedParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramParameterdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramParameterfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramPipelineInfoLog", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramPipelineiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceIndex", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceLocation", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceLocationIndex", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceName", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramResourceiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramStageiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramStringARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramStringNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramSubroutineParameteruivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetProgramivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetQueryIndexediv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetQueryiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetQueryivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetRenderbufferParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetRenderbufferParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameterIiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameterIuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSamplerParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSeparableFilterEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderInfoLog", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderPrecisionFormat", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderSource", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderSourceARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetShaderiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetSharpenTexFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glGetStringi", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glGetSynciv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexBumpParameterfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexBumpParameterivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexEnvfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexEnviv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexFilterFuncSGIS", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexGendv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexGenfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexGeniv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexLevelParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexLevelParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameterIiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameterIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameterIuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameterIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameterPointervAPPLE", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameterfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTexParameteriv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTextureLevelParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTextureLevelParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTextureParameterIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTextureParameterIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTextureParameterfvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTextureParameterivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTrackMatrixivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTransformFeedbackVarying", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTransformFeedbackVaryingEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetTransformFeedbackVaryingNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformIndices", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformSubroutineuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformdv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformi64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetUniformuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantArrayObjectfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantArrayObjectivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantBooleanvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantFloatvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVariantPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayIntegeri_vEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayIntegervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayPointeri_vEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexArrayPointervEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribArrayObjectfvATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribArrayObjectivATI", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribIiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribIivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribIuiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribIuivEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribLdv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribLdvEXT", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribLi64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribLui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribPointerv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribPointervARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribPointervNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribdv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribfv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribiv", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVertexAttribivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureStreamdvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureStreamfvNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureStreamivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoCaptureivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoi64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideoui64vNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetVideouivNV", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnMapdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnMapfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnMapivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformdvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformfvARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glGetnUniformuivARB", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glInsertEventMarkerEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_MARKER },
+ { "glIsAsyncMarkerSGIX", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsBuffer", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsBufferARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsBufferResidentNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsEnabled", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsEnabledIndexedEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsEnabledi", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFenceAPPLE", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFenceNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFramebuffer", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsFramebufferEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsList", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsNameAMD", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsNamedBufferResidentNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsNamedStringARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsObjectBufferATI", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsOcclusionQueryNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgram", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgramARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgramNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsProgramPipeline", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsQuery", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsQueryARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsRenderbuffer", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsRenderbufferEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsSampler", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsShader", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsSync", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTexture", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTextureEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTransformFeedback", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsTransformFeedbackNV", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVariantEnabledEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVertexArray", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVertexArrayAPPLE", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glIsVertexAttribEnabledAPPLE", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glMultiDrawArrays", CALL_FLAG_RENDER },
+ { "glMultiDrawArraysEXT", CALL_FLAG_RENDER },
+ { "glMultiDrawArraysIndirect", CALL_FLAG_RENDER },
+ { "glMultiDrawArraysIndirectAMD", CALL_FLAG_RENDER },
+ { "glMultiDrawArraysIndirectBindlessNV", CALL_FLAG_RENDER },
+ { "glMultiDrawArraysIndirectCountARB", CALL_FLAG_RENDER },
+ { "glMultiDrawElementArrayAPPLE", 0 },
+ { "glMultiDrawElements", CALL_FLAG_RENDER },
+ { "glMultiDrawElementsBaseVertex", CALL_FLAG_RENDER },
+ { "glMultiDrawElementsEXT", CALL_FLAG_RENDER },
+ { "glMultiDrawElementsIndirect", CALL_FLAG_RENDER },
+ { "glMultiDrawElementsIndirectAMD", CALL_FLAG_RENDER },
+ { "glMultiDrawElementsIndirectBindlessNV", CALL_FLAG_RENDER },
+ { "glMultiDrawElementsIndirectCountARB", CALL_FLAG_RENDER },
+ { "glMultiDrawRangeElementArrayAPPLE", 0 },
+ { "glMultiModeDrawArraysIBM", CALL_FLAG_RENDER },
+ { "glMultiModeDrawElementsIBM", CALL_FLAG_RENDER },
+ { "glObjectLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glObjectLabelKHR", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glObjectPtrLabel", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glObjectPtrLabelKHR", CALL_FLAG_NO_SIDE_EFFECTS },
+ { "glPopDebugGroup", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP},
+ { "glPopDebugGroupKHR", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP},
+ { "glPopGroupMarkerEXT", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_POP},
+ { "glPushDebugGroup", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "glPushDebugGroupKHR", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "glPushGroupMarkerEXT", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER | CALL_FLAG_MARKER_PUSH },
+ { "glRectd", CALL_FLAG_RENDER },
+ { "glRectdv", CALL_FLAG_RENDER },
+ { "glRectf", CALL_FLAG_RENDER },
+ { "glRectfv", CALL_FLAG_RENDER },
+ { "glRecti", CALL_FLAG_RENDER },
+ { "glRectiv", CALL_FLAG_RENDER },
+ { "glRects", CALL_FLAG_RENDER },
+ { "glRectsv", CALL_FLAG_RENDER },
+ { "glStringMarkerGREMEDY", /* CALL_FLAG_NO_SIDE_EFFECTS | */ CALL_FLAG_MARKER },
+ { "glXGetClientString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetConfig", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentContext", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentDisplay", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentDisplayEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentDrawable", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentReadDrawable", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetCurrentReadDrawableSGI", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetFBConfigAttrib", CALL_FLAG_VERBOSE },
+ { "glXGetFBConfigAttribSGIX", CALL_FLAG_VERBOSE },
+ { "glXGetProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXGetProcAddressARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXIsDirect", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXQueryExtension", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXQueryExtensionsString", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXQueryVersion", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "glXSwapBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "wglDescribePixelFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetCurrentContext", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetCurrentDC", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetDefaultProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetExtensionsStringARB", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetExtensionsStringEXT", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetPixelFormat", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglGetPixelFormatAttribivARB", CALL_FLAG_VERBOSE },
+ { "wglGetPixelFormatAttribivEXT", CALL_FLAG_VERBOSE },
+ { "wglGetProcAddress", CALL_FLAG_NO_SIDE_EFFECTS | CALL_FLAG_VERBOSE },
+ { "wglSwapBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "wglSwapLayerBuffers", CALL_FLAG_SWAPBUFFERS },
+ { "wglSwapMultipleBuffers", CALL_FLAG_SWAPBUFFERS },
+};
+
+
+TEST(common_parser, lookupCallFlags)
+{
+ typedef const Entry<CallFlags> * ConstIterator;
+
+ ConstIterator first = &entries[0];
+ ConstIterator last = &entries[sizeof entries / sizeof entries[0]];
+
+ for (ConstIterator it = first; it != last; ++it) {
+ CallFlags flags = Parser::lookupCallFlags(it->name);
+
+ EXPECT_EQ(it->value, flags) << "flags differ for " << it->name;
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/lib/trace/trace_parser_loop.cpp b/lib/trace/trace_parser_loop.cpp
new file mode 100644
index 00000000..04fbc135
--- /dev/null
+++ b/lib/trace/trace_parser_loop.cpp
@@ -0,0 +1,111 @@
+/**************************************************************************
+ *
+ * Copyright 2014 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_parser.hpp"
+
+
+namespace trace {
+
+
+// Decorator for parser which loops
+class LastFrameLoopParser : public AbstractParser {
+public:
+ LastFrameLoopParser(AbstractParser *p, int c) {
+ parser = p;
+ loopCount = c;
+ }
+
+ ~LastFrameLoopParser() {
+ delete parser;
+ }
+
+ Call *parse_call(void) override;
+
+ // Delegate to Parser
+ void getBookmark(ParseBookmark &bookmark) override { parser->getBookmark(bookmark); }
+ void setBookmark(const ParseBookmark &bookmark) override { parser->setBookmark(bookmark); }
+ bool open(const char *filename) override;
+ void close(void) override { parser->close(); }
+ unsigned long long getVersion(void) const override { return parser->getVersion(); }
+private:
+ int loopCount;
+ AbstractParser *parser;
+ ParseBookmark frameStart;
+ ParseBookmark lastFrameStart;
+};
+
+
+bool
+LastFrameLoopParser::open(const char *filename)
+{
+ bool ret = parser->open(filename);
+ if (ret) {
+ /* If the user wants to loop we need to get a bookmark target. We
+ * usually get this after replaying a call that ends a frame, but
+ * for a trace that has only one frame we need to get it at the
+ * beginning. */
+ parser->getBookmark(frameStart);
+ lastFrameStart = frameStart;
+ }
+ return ret;
+}
+
+Call *
+LastFrameLoopParser::parse_call(void)
+{
+ trace::Call *call;
+
+ call = parser->parse_call();
+
+ /* Restart last frame when looping is requested. */
+ if (call) {
+ lastFrameStart = frameStart;
+ if (call->flags & trace::CALL_FLAG_END_FRAME) {
+ parser->getBookmark(frameStart);
+ }
+ } else {
+ if (loopCount) {
+ frameStart = lastFrameStart;
+ parser->setBookmark(frameStart);
+ call = parser->parse_call();
+ if (loopCount > 0) {
+ --loopCount;
+ }
+ }
+ }
+
+ return call;
+}
+
+
+AbstractParser *
+lastFrameLoopParser(AbstractParser *parser, int loopCount)
+{
+ return new LastFrameLoopParser(parser, loopCount);
+}
+
+
+} /* namespace trace */
diff --git a/lib/trace/trace_profiler.cpp b/lib/trace/trace_profiler.cpp
new file mode 100644
index 00000000..65c91aad
--- /dev/null
+++ b/lib/trace/trace_profiler.cpp
@@ -0,0 +1,259 @@
+/**************************************************************************
+ *
+ * Copyright 2012 VMware, Inc.
+ * Copyright 2013 Intel, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "trace_profiler.hpp"
+#include "os_time.hpp"
+#include <iostream>
+#include <string.h>
+#include <sstream>
+
+namespace trace {
+Profiler::Profiler()
+ : baseGpuTime(0),
+ baseCpuTime(0),
+ minCpuTime(1000),
+ baseVsizeUsage(0),
+ baseRssUsage(0),
+ cpuTimes(false),
+ gpuTimes(true),
+ pixelsDrawn(false),
+ memoryUsage(false)
+{
+}
+
+Profiler::~Profiler()
+{
+}
+
+void Profiler::setup(bool cpuTimes_, bool gpuTimes_, bool pixelsDrawn_, bool memoryUsage_)
+{
+ cpuTimes = cpuTimes_;
+ gpuTimes = gpuTimes_;
+ pixelsDrawn = pixelsDrawn_;
+ memoryUsage = memoryUsage_;
+
+ std::cout << "# call no gpu_start gpu_dura cpu_start cpu_dura vsize_start vsize_dura rss_start rss_dura pixels program name" << std::endl;
+}
+
+int64_t Profiler::getBaseCpuTime()
+{
+ return baseCpuTime;
+}
+
+int64_t Profiler::getBaseGpuTime()
+{
+ return baseGpuTime;
+}
+
+int64_t Profiler::getBaseVsizeUsage()
+{
+ return baseVsizeUsage;
+}
+
+int64_t Profiler::getBaseRssUsage()
+{
+ return baseRssUsage;
+}
+
+void Profiler::setBaseCpuTime(int64_t cpuStart)
+{
+ baseCpuTime = cpuStart;
+}
+
+void Profiler::setBaseGpuTime(int64_t gpuStart)
+{
+ baseGpuTime = gpuStart;
+}
+
+void Profiler::setBaseVsizeUsage(int64_t vsizeStart)
+{
+ baseVsizeUsage = vsizeStart;
+}
+
+void Profiler::setBaseRssUsage(int64_t rssStart)
+{
+ baseRssUsage = rssStart;
+}
+
+bool Profiler::hasBaseTimes()
+{
+ return baseCpuTime != 0 || baseGpuTime != 0;
+}
+
+void Profiler::addCall(unsigned no,
+ const char *name,
+ unsigned program,
+ int64_t pixels,
+ int64_t gpuStart, int64_t gpuDuration,
+ int64_t cpuStart, int64_t cpuDuration,
+ int64_t vsizeStart, int64_t vsizeDuration,
+ int64_t rssStart, int64_t rssDuration)
+{
+ if (gpuTimes && gpuStart) {
+ gpuStart -= baseGpuTime;
+ } else {
+ gpuStart = 0;
+ }
+
+ if (cpuTimes && cpuStart) {
+ cpuStart = cpuStart - baseCpuTime;
+
+ if (cpuDuration < minCpuTime) {
+ return;
+ }
+ } else {
+ cpuStart = 0;
+ cpuDuration = 0;
+ }
+
+ if (!pixelsDrawn) {
+ pixels = 0;
+ }
+
+ if (!memoryUsage || !vsizeStart || !rssStart) {
+ vsizeStart = 0;
+ vsizeDuration = 0;
+ rssStart = 0;
+ rssDuration = 0;
+ }
+
+ std::cout << "call"
+ << " " << no
+ << " " << gpuStart
+ << " " << gpuDuration
+ << " " << cpuStart
+ << " " << cpuDuration
+ << " " << vsizeStart
+ << " " << vsizeDuration
+ << " " << rssStart
+ << " " << rssDuration
+ << " " << pixels
+ << " " << program
+ << " " << name
+ << std::endl;
+}
+
+void Profiler::addFrameEnd()
+{
+ std::cout << "frame_end" << std::endl;
+}
+
+void Profiler::parseLine(const char* in, Profile* profile)
+{
+ std::stringstream line(in, std::ios_base::in);
+ std::string type;
+ static int64_t lastGpuTime;
+ static int64_t lastCpuTime;
+ static int64_t lastVsizeUsage;
+ static int64_t lastRssUsage;
+
+ if (in[0] == '#' || strlen(in) < 4)
+ return;
+
+ if (profile->programs.size() == 0 && profile->calls.size() == 0 && profile->frames.size() == 0) {
+ lastGpuTime = 0;
+ lastCpuTime = 0;
+ lastVsizeUsage = 0;
+ lastRssUsage = 0;
+ }
+
+ line >> type;
+
+ if (type.compare("call") == 0) {
+ Profile::Call call;
+
+ line >> call.no
+ >> call.gpuStart
+ >> call.gpuDuration
+ >> call.cpuStart
+ >> call.cpuDuration
+ >> call.vsizeStart
+ >> call.vsizeDuration
+ >> call.rssStart
+ >> call.rssDuration
+ >> call.pixels
+ >> call.program
+ >> call.name;
+
+ if (lastGpuTime < call.gpuStart + call.gpuDuration) {
+ lastGpuTime = call.gpuStart + call.gpuDuration;
+ }
+
+ if (lastCpuTime < call.cpuStart + call.cpuDuration) {
+ lastCpuTime = call.cpuStart + call.cpuDuration;
+ }
+
+ if (lastVsizeUsage < call.vsizeStart + call.vsizeDuration) {
+ lastVsizeUsage = call.vsizeStart + call.vsizeDuration;
+ }
+
+ if (lastRssUsage < call.rssStart + call.rssDuration) {
+ lastRssUsage = call.rssStart + call.rssDuration;
+ }
+
+ profile->calls.push_back(call);
+
+ if (call.pixels >= 0) {
+ if (profile->programs.size() <= call.program) {
+ profile->programs.resize(call.program + 1);
+ }
+
+ Profile::Program& program = profile->programs[call.program];
+ program.cpuTotal += call.cpuDuration;
+ program.gpuTotal += call.gpuDuration;
+ program.pixelTotal += call.pixels;
+ program.vsizeTotal += call.vsizeDuration;
+ program.rssTotal += call.rssDuration;
+ program.calls.push_back((unsigned int)(profile->calls.size() - 1));
+ }
+ } else if (type.compare("frame_end") == 0) {
+ Profile::Frame frame;
+ frame.no = unsigned(profile->frames.size());
+
+ if (frame.no == 0) {
+ frame.gpuStart = 0;
+ frame.cpuStart = 0;
+ frame.vsizeStart = 0;
+ frame.rssStart = 0;
+ frame.calls.begin = 0;
+ } else {
+ frame.gpuStart = profile->frames.back().gpuStart + profile->frames.back().gpuDuration;
+ frame.cpuStart = profile->frames.back().cpuStart + profile->frames.back().cpuDuration;
+ frame.vsizeStart = profile->frames.back().vsizeStart + profile->frames.back().vsizeDuration;
+ frame.rssStart = profile->frames.back().rssStart + profile->frames.back().rssDuration;
+ frame.calls.begin = profile->frames.back().calls.end + 1;
+ }
+
+ frame.gpuDuration = lastGpuTime - frame.gpuStart;
+ frame.cpuDuration = lastCpuTime - frame.cpuStart;
+ frame.vsizeDuration = lastVsizeUsage - frame.vsizeStart;
+ frame.rssDuration = lastRssUsage - frame.rssStart;
+ frame.calls.end = (unsigned int)(profile->calls.size() - 1);
+
+ profile->frames.push_back(frame);
+ }
+}
+}
diff --git a/lib/trace/trace_profiler.hpp b/lib/trace/trace_profiler.hpp
new file mode 100644
index 00000000..ebe28b46
--- /dev/null
+++ b/lib/trace/trace_profiler.hpp
@@ -0,0 +1,143 @@
+/**************************************************************************
+ *
+ * Copyright 2012 VMware, Inc.
+ * Copyright 2013 Intel, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+namespace trace
+{
+
+struct Profile {
+ struct Call {
+ unsigned no;
+
+ unsigned program;
+
+ int64_t gpuStart;
+ int64_t gpuDuration;
+
+ int64_t cpuStart;
+ int64_t cpuDuration;
+
+ int64_t vsizeStart;
+ int64_t vsizeDuration;
+ int64_t rssStart;
+ int64_t rssDuration;
+
+ int64_t pixels;
+
+ std::string name;
+ };
+
+ struct Frame {
+ unsigned no;
+
+ int64_t gpuStart;
+ int64_t gpuDuration;
+
+ int64_t cpuStart;
+ int64_t cpuDuration;
+
+ int64_t vsizeStart;
+ int64_t vsizeDuration;
+ int64_t rssStart;
+ int64_t rssDuration;
+
+ /* Indices to profile->calls array */
+ struct {
+ unsigned begin;
+ unsigned end;
+ } calls;
+ };
+
+ struct Program {
+ Program() : gpuTotal(0), cpuTotal(0), pixelTotal(0) {}
+
+ uint64_t gpuTotal;
+ uint64_t cpuTotal;
+ uint64_t pixelTotal;
+ int64_t vsizeTotal;
+ int64_t rssTotal;
+
+ /* Indices to profile->calls array */
+ std::vector<unsigned> calls;
+ };
+
+ std::vector<Call> calls;
+ std::vector<Frame> frames;
+ std::vector<Program> programs;
+};
+
+class Profiler
+{
+public:
+ Profiler();
+ ~Profiler();
+
+ void setup(bool cpuTimes_, bool gpuTimes_, bool pixelsDrawn_, bool memoryUsage_);
+
+ void addCall(unsigned no,
+ const char* name,
+ unsigned program,
+ int64_t pixels,
+ int64_t gpuStart, int64_t gpuDuration,
+ int64_t cpuStart, int64_t cpuDuration,
+ int64_t vsizeStart, int64_t vsizeDuration,
+ int64_t rssStart, int64_t rssDuration);
+
+ void addFrameEnd();
+
+ bool hasBaseTimes();
+
+ void setBaseCpuTime(int64_t cpuStart);
+ void setBaseGpuTime(int64_t gpuStart);
+ void setBaseVsizeUsage(int64_t vsizeStart);
+ void setBaseRssUsage(int64_t rssStart);
+
+ int64_t getBaseCpuTime();
+ int64_t getBaseGpuTime();
+ int64_t getBaseVsizeUsage();
+ int64_t getBaseRssUsage();
+
+ static void parseLine(const char* line, Profile* profile);
+
+private:
+ int64_t baseGpuTime;
+ int64_t baseCpuTime;
+ int64_t minCpuTime;
+ int64_t baseVsizeUsage;
+ int64_t baseRssUsage;
+
+ bool cpuTimes;
+ bool gpuTimes;
+ bool pixelsDrawn;
+ bool memoryUsage;
+};
+}
+
diff --git a/lib/trace/trace_snappy.hpp b/lib/trace/trace_snappy.hpp
new file mode 100644
index 00000000..5d82c746
--- /dev/null
+++ b/lib/trace/trace_snappy.hpp
@@ -0,0 +1,34 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc
+ * Copyright 2011 Zack Rusin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#pragma once
+
+
+#define SNAPPY_BYTE1 'a'
+#define SNAPPY_BYTE2 't'
+
+
diff --git a/lib/trace/trace_writer.cpp b/lib/trace/trace_writer.cpp
new file mode 100644
index 00000000..3ad06e4f
--- /dev/null
+++ b/lib/trace/trace_writer.cpp
@@ -0,0 +1,372 @@
+/**************************************************************************
+ *
+ * Copyright 2007-2009 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <vector>
+
+#include "os.hpp"
+#include "trace_ostream.hpp"
+#include "trace_writer.hpp"
+#include "trace_format.hpp"
+
+namespace trace {
+
+
+Writer::Writer() :
+ call_no(0)
+{
+ m_file = nullptr;
+}
+
+Writer::~Writer()
+{
+ close();
+}
+
+void
+Writer::close(void) {
+ delete m_file;
+ m_file = nullptr;
+}
+
+bool
+Writer::open(const char *filename) {
+ close();
+
+ m_file = createSnappyStream(filename);
+ if (!m_file) {
+ return false;
+ }
+
+ call_no = 0;
+ functions.clear();
+ structs.clear();
+ enums.clear();
+ bitmasks.clear();
+ frames.clear();
+
+ _writeUInt(TRACE_VERSION);
+
+ return true;
+}
+
+void inline
+Writer::_write(const void *sBuffer, size_t dwBytesToWrite) {
+ m_file->write(sBuffer, dwBytesToWrite);
+}
+
+void inline
+Writer::_writeByte(char c) {
+ _write(&c, 1);
+}
+
+void inline
+Writer::_writeUInt(unsigned long long value) {
+ char buf[2 * sizeof value];
+ unsigned len;
+
+ len = 0;
+ do {
+ assert(len < sizeof buf);
+ buf[len] = 0x80 | (value & 0x7f);
+ value >>= 7;
+ ++len;
+ } while (value);
+
+ assert(len);
+ buf[len - 1] &= 0x7f;
+
+ _write(buf, len);
+}
+
+void inline
+Writer::_writeFloat(float value) {
+ static_assert(sizeof value == 4, "float is not 4 bytes");
+ _write((const char *)&value, sizeof value);
+}
+
+void inline
+Writer::_writeDouble(double value) {
+ static_assert(sizeof value == 8, "double is not 8 bytes");
+ _write((const char *)&value, sizeof value);
+}
+
+void inline
+Writer::_writeString(const char *str) {
+ size_t len = strlen(str);
+ _writeUInt(len);
+ _write(str, len);
+}
+
+inline bool lookup(std::vector<bool> &map, size_t index) {
+ if (index >= map.size()) {
+ map.resize(index + 1);
+ return false;
+ } else {
+ return map[index];
+ }
+}
+
+void Writer::beginBacktrace(unsigned num_frames) {
+ if (num_frames) {
+ _writeByte(trace::CALL_BACKTRACE);
+ _writeUInt(num_frames);
+ }
+}
+
+void Writer::writeStackFrame(const RawStackFrame *frame) {
+ _writeUInt(frame->id);
+ if (!lookup(frames, frame->id)) {
+ if (frame->module != NULL) {
+ _writeByte(trace::BACKTRACE_MODULE);
+ _writeString(frame->module);
+ }
+ if (frame->function != NULL) {
+ _writeByte(trace::BACKTRACE_FUNCTION);
+ _writeString(frame->function);
+ }
+ if (frame->filename != NULL) {
+ _writeByte(trace::BACKTRACE_FILENAME);
+ _writeString(frame->filename);
+ }
+ if (frame->linenumber >= 0) {
+ _writeByte(trace::BACKTRACE_LINENUMBER);
+ _writeUInt(frame->linenumber);
+ }
+ if (frame->offset >= 0) {
+ _writeByte(trace::BACKTRACE_OFFSET);
+ _writeUInt(frame->offset);
+ }
+ _writeByte(trace::BACKTRACE_END);
+ frames[frame->id] = true;
+ }
+}
+
+unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) {
+ _writeByte(trace::EVENT_ENTER);
+ _writeUInt(thread_id);
+ _writeUInt(sig->id);
+ if (!lookup(functions, sig->id)) {
+ _writeString(sig->name);
+ _writeUInt(sig->num_args);
+ for (unsigned i = 0; i < sig->num_args; ++i) {
+ _writeString(sig->arg_names[i]);
+ }
+ functions[sig->id] = true;
+ }
+
+ return call_no++;
+}
+
+void Writer::endEnter(void) {
+ _writeByte(trace::CALL_END);
+}
+
+void Writer::beginLeave(unsigned call) {
+ _writeByte(trace::EVENT_LEAVE);
+ _writeUInt(call);
+}
+
+void Writer::endLeave(void) {
+ _writeByte(trace::CALL_END);
+}
+
+void Writer::beginArg(unsigned index) {
+ _writeByte(trace::CALL_ARG);
+ _writeUInt(index);
+}
+
+void Writer::beginReturn(void) {
+ _writeByte(trace::CALL_RET);
+}
+
+void Writer::beginArray(size_t length) {
+ _writeByte(trace::TYPE_ARRAY);
+ _writeUInt(length);
+}
+
+void Writer::beginStruct(const StructSig *sig) {
+ _writeByte(trace::TYPE_STRUCT);
+ _writeUInt(sig->id);
+ if (!lookup(structs, sig->id)) {
+ _writeString(sig->name);
+ _writeUInt(sig->num_members);
+ for (unsigned i = 0; i < sig->num_members; ++i) {
+ _writeString(sig->member_names[i]);
+ }
+ structs[sig->id] = true;
+ }
+}
+
+void Writer::beginRepr(void) {
+ _writeByte(trace::TYPE_REPR);
+}
+
+void Writer::writeBool(bool value) {
+ _writeByte(value ? trace::TYPE_TRUE : trace::TYPE_FALSE);
+}
+
+void Writer::writeSInt(signed long long value) {
+ if (value < 0) {
+ _writeByte(trace::TYPE_SINT);
+ _writeUInt(-value);
+ } else {
+ _writeByte(trace::TYPE_UINT);
+ _writeUInt(value);
+ }
+}
+
+void Writer::writeUInt(unsigned long long value) {
+ _writeByte(trace::TYPE_UINT);
+ _writeUInt(value);
+}
+
+void Writer::writeFloat(float value) {
+ _writeByte(trace::TYPE_FLOAT);
+ _writeFloat(value);
+}
+
+void Writer::writeDouble(double value) {
+ _writeByte(trace::TYPE_DOUBLE);
+ _writeDouble(value);
+}
+
+void Writer::writeString(const char *str) {
+ if (!str) {
+ Writer::writeNull();
+ return;
+ }
+ _writeByte(trace::TYPE_STRING);
+ _writeString(str);
+}
+
+void Writer::writeString(const char *str, size_t len) {
+ if (!str) {
+ Writer::writeNull();
+ return;
+ }
+ _writeByte(trace::TYPE_STRING);
+ _writeUInt(len);
+ _write(str, len);
+}
+
+void Writer::writeWString(const wchar_t *str, size_t len) {
+ if (!str) {
+ Writer::writeNull();
+ return;
+ }
+ /* XXX: Encode wide-strings as ASCII for now, to avoid introducing a trace format version bump. */
+#if 0
+ _writeByte(trace::TYPE_WSTRING);
+ size_t len = wcslen(str);
+ _writeUInt(len);
+ for (size_t i = 0; i < len; ++i) {
+ _writeUInt(str[i]);
+ }
+#else
+ _writeByte(trace::TYPE_STRING);
+ _writeUInt(len);
+ for (size_t i = 0; i < len; ++i) {
+ wchar_t wc = str[i];
+ char c = wc >= 0 && wc < 0x80 ? (char)wc : '?';
+ _writeByte(c);
+ }
+#endif
+}
+
+void Writer::writeWString(const wchar_t *str) {
+ if (!str) {
+ Writer::writeNull();
+ return;
+ }
+ size_t len = wcslen(str);
+ writeWString(str, len);
+}
+
+void Writer::writeBlob(const void *data, size_t size) {
+ if (!data) {
+ Writer::writeNull();
+ return;
+ }
+ _writeByte(trace::TYPE_BLOB);
+ _writeUInt(size);
+ if (size) {
+ _write(data, size);
+ }
+}
+
+void Writer::writeEnum(const EnumSig *sig, signed long long value) {
+ _writeByte(trace::TYPE_ENUM);
+ _writeUInt(sig->id);
+ if (!lookup(enums, sig->id)) {
+ _writeUInt(sig->num_values);
+ for (unsigned i = 0; i < sig->num_values; ++i) {
+ _writeString(sig->values[i].name);
+ writeSInt(sig->values[i].value);
+ }
+ enums[sig->id] = true;
+ }
+ writeSInt(value);
+}
+
+void Writer::writeBitmask(const BitmaskSig *sig, unsigned long long value) {
+ _writeByte(trace::TYPE_BITMASK);
+ _writeUInt(sig->id);
+ if (!lookup(bitmasks, sig->id)) {
+ _writeUInt(sig->num_flags);
+ for (unsigned i = 0; i < sig->num_flags; ++i) {
+ if (i != 0 && sig->flags[i].value == 0) {
+ os::log("apitrace: warning: sig %s is zero but is not first flag\n", sig->flags[i].name);
+ }
+ _writeString(sig->flags[i].name);
+ _writeUInt(sig->flags[i].value);
+ }
+ bitmasks[sig->id] = true;
+ }
+ _writeUInt(value);
+}
+
+void Writer::writeNull(void) {
+ _writeByte(trace::TYPE_NULL);
+}
+
+void Writer::writePointer(unsigned long long addr) {
+ if (!addr) {
+ Writer::writeNull();
+ return;
+ }
+ _writeByte(trace::TYPE_OPAQUE);
+ _writeUInt(addr);
+}
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_writer.hpp b/lib/trace/trace_writer.hpp
new file mode 100644
index 00000000..e293a938
--- /dev/null
+++ b/lib/trace/trace_writer.hpp
@@ -0,0 +1,116 @@
+/**************************************************************************
+ *
+ * Copyright 2007-2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Trace writing functions.
+ */
+
+#pragma once
+
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "trace_model.hpp"
+
+namespace trace {
+ class OutStream;
+
+ class Writer {
+ protected:
+ OutStream *m_file;
+ unsigned call_no;
+
+ std::vector<bool> functions;
+ std::vector<bool> structs;
+ std::vector<bool> enums;
+ std::vector<bool> bitmasks;
+ std::vector<bool> frames;
+
+ public:
+ Writer();
+ ~Writer();
+
+ bool open(const char *filename);
+ void close(void);
+
+ unsigned beginEnter(const FunctionSig *sig, unsigned thread_id);
+ void endEnter(void);
+
+ void beginLeave(unsigned call);
+ void endLeave(void);
+
+ void beginArg(unsigned index);
+ inline void endArg(void) {}
+
+ void beginReturn(void);
+ inline void endReturn(void) {}
+
+ void beginBacktrace(unsigned num_frames);
+ void writeStackFrame(const RawStackFrame *frame);
+ inline void endBacktrace(void) {}
+
+ void beginArray(size_t length);
+ inline void endArray(void) {}
+
+ inline void beginElement(void) {}
+ inline void endElement(void) {}
+
+ void beginStruct(const StructSig *sig);
+ inline void endStruct(void) {}
+
+ void beginRepr(void);
+ inline void endRepr(void) {}
+
+ void writeBool(bool value);
+ void writeSInt(signed long long value);
+ void writeUInt(unsigned long long value);
+ void writeFloat(float value);
+ void writeDouble(double value);
+ void writeString(const char *str);
+ void writeString(const char *str, size_t size);
+ void writeWString(const wchar_t *str);
+ void writeWString(const wchar_t *str, size_t size);
+ void writeBlob(const void *data, size_t size);
+ void writeEnum(const EnumSig *sig, signed long long value);
+ void writeBitmask(const BitmaskSig *sig, unsigned long long value);
+ void writeNull(void);
+ void writePointer(unsigned long long addr);
+
+ void writeCall(Call *call);
+
+ protected:
+ void inline _write(const void *sBuffer, size_t dwBytesToWrite);
+ void inline _writeByte(char c);
+ void inline _writeUInt(unsigned long long value);
+ void inline _writeFloat(float value);
+ void inline _writeDouble(double value);
+ void inline _writeString(const char *str);
+
+ };
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_writer_local.cpp b/lib/trace/trace_writer_local.cpp
new file mode 100644
index 00000000..79ff962b
--- /dev/null
+++ b/lib/trace/trace_writer_local.cpp
@@ -0,0 +1,287 @@
+/**************************************************************************
+ *
+ * Copyright 2007-2011 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "os.hpp"
+#include "os_thread.hpp"
+#include "os_string.hpp"
+#include "os_version.hpp"
+#include "trace_ostream.hpp"
+#include "trace_writer_local.hpp"
+#include "trace_format.hpp"
+#include "os_backtrace.hpp"
+
+
+namespace trace {
+
+
+static const char *memcpy_args[3] = {"dest", "src", "n"};
+const FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args};
+
+static const char *malloc_args[1] = {"size"};
+const FunctionSig malloc_sig = {1, "malloc", 1, malloc_args};
+
+static const char *free_args[1] = {"ptr"};
+const FunctionSig free_sig = {2, "free", 1, free_args};
+
+static const char *realloc_args[2] = {"ptr", "size"};
+const FunctionSig realloc_sig = {3, "realloc", 2, realloc_args};
+
+
+static void exceptionCallback(void)
+{
+ localWriter.flush();
+}
+
+
+LocalWriter::LocalWriter() :
+ acquired(0)
+{
+ os::String process = os::getProcessName();
+ os::log("apitrace: loaded into %s\n", process.str());
+
+ // Install the signal handlers as early as possible, to prevent
+ // interfering with the application's signal handling.
+ os::setExceptionCallback(exceptionCallback);
+}
+
+LocalWriter::~LocalWriter()
+{
+ os::resetExceptionCallback();
+ checkProcessId();
+
+ os::String process = os::getProcessName();
+ os::log("apitrace: unloaded from %s\n", process.str());
+}
+
+void
+LocalWriter::open(void) {
+ os::String szFileName;
+
+ const char *lpFileName;
+
+ lpFileName = getenv("TRACE_FILE");
+ if (!lpFileName) {
+ static unsigned dwCounter = 0;
+
+ os::String process = os::getProcessName();
+#ifdef _WIN32
+ process.trimExtension();
+#endif
+ process.trimDirectory();
+
+#ifdef ANDROID
+ os::String prefix = "/data/data";
+ prefix.join(process);
+#else
+ os::String prefix = os::getCurrentDir();
+#ifdef _WIN32
+ // Avoid writing into Windows' system directory as quite often access
+ // will be denied.
+ if (IsWindows8OrGreater()) {
+ char szDirectory[MAX_PATH + 1];
+ GetSystemDirectoryA(szDirectory, sizeof szDirectory);
+ if (stricmp(prefix, szDirectory) == 0) {
+ GetTempPathA(sizeof szDirectory, szDirectory);
+ prefix = szDirectory;
+ }
+ }
+#endif
+#endif
+ prefix.join(process);
+
+ for (;;) {
+ FILE *file;
+
+ if (dwCounter)
+ szFileName = os::String::format("%s.%u.trace", prefix.str(), dwCounter);
+ else
+ szFileName = os::String::format("%s.trace", prefix.str());
+
+ lpFileName = szFileName;
+ file = fopen(lpFileName, "rb");
+ if (file == NULL)
+ break;
+
+ fclose(file);
+
+ ++dwCounter;
+ }
+ }
+
+ os::log("apitrace: tracing to %s\n", lpFileName);
+
+ if (!Writer::open(lpFileName)) {
+ os::log("apitrace: error: failed to open %s\n", lpFileName);
+ os::abort();
+ }
+
+ pid = os::getCurrentProcessId();
+
+#if 0
+ // For debugging the exception handler
+ *((int *)0) = 0;
+#endif
+}
+
+static uintptr_t next_thread_num = 1;
+
+static OS_THREAD_SPECIFIC(uintptr_t)
+thread_num;
+
+void LocalWriter::checkProcessId(void) {
+ if (m_file &&
+ os::getCurrentProcessId() != pid) {
+ // We are a forked child process that inherited the trace file, so
+ // create a new file. We can't call any method of the current
+ // file, as it may cause it to flush and corrupt the parent's
+ // trace, so we effectively leak the old file object.
+ close();
+ // Don't want to open the same file again
+ os::unsetEnvironment("TRACE_FILE");
+ open();
+ }
+}
+
+unsigned LocalWriter::beginEnter(const FunctionSig *sig, bool fake) {
+ mutex.lock();
+ ++acquired;
+
+ checkProcessId();
+ if (!m_file) {
+ open();
+ }
+
+ uintptr_t this_thread_num = thread_num;
+ if (!this_thread_num) {
+ this_thread_num = next_thread_num++;
+ thread_num = this_thread_num;
+ }
+
+ assert(this_thread_num);
+ unsigned thread_id = this_thread_num - 1;
+ unsigned call_no = Writer::beginEnter(sig, thread_id);
+ if (!fake && os::backtrace_is_needed(sig->name)) {
+ std::vector<RawStackFrame> backtrace = os::get_backtrace();
+ beginBacktrace(backtrace.size());
+ for (auto & frame : backtrace) {
+ writeStackFrame(&frame);
+ }
+ endBacktrace();
+ }
+ return call_no;
+}
+
+void LocalWriter::endEnter(void) {
+ Writer::endEnter();
+ --acquired;
+ mutex.unlock();
+}
+
+void LocalWriter::beginLeave(unsigned call) {
+ mutex.lock();
+ ++acquired;
+ Writer::beginLeave(call);
+}
+
+void LocalWriter::endLeave(void) {
+ Writer::endLeave();
+ --acquired;
+ mutex.unlock();
+}
+
+void LocalWriter::flush(void) {
+ /*
+ * Do nothing if the mutex is already acquired (e.g., if a segfault happen
+ * while writing the file) as state could be inconsistent, therefore yield
+ * inconsistent trace files and/or repeated segfaults till infinity.
+ */
+
+ mutex.lock();
+ if (acquired) {
+ os::log("apitrace: ignoring exception while tracing\n");
+ } else {
+ ++acquired;
+ if (m_file) {
+ if (os::getCurrentProcessId() != pid) {
+ os::log("apitrace: ignoring exception in child process\n");
+ } else {
+ os::log("apitrace: flushing trace due to an exception\n");
+ m_file->flush();
+ }
+ }
+ --acquired;
+ }
+ mutex.unlock();
+}
+
+
+LocalWriter localWriter;
+
+
+void fakeMemcpy(const void *ptr, size_t size) {
+ assert(ptr);
+ if (!size) {
+ return;
+ }
+
+ unsigned _call = localWriter.beginEnter(&memcpy_sig, true);
+
+#if defined(_WIN32) && !defined(NDEBUG)
+ size_t maxSize = 0;
+ MEMORY_BASIC_INFORMATION mi;
+ while (VirtualQuery((const uint8_t *)ptr + maxSize, &mi, sizeof mi) == sizeof mi &&
+ mi.Protect & (PAGE_READONLY|PAGE_READWRITE)) {
+ maxSize = (const uint8_t *)mi.BaseAddress + mi.RegionSize - (const uint8_t *)ptr;
+ }
+ if (maxSize < size) {
+ os::log("apitrace: warning: %u: clamping size from %Iu to %Iu\n", _call, size, maxSize);
+ size = maxSize;
+ }
+#endif
+
+ localWriter.beginArg(0);
+ localWriter.writePointer((uintptr_t)ptr);
+ localWriter.endArg();
+ localWriter.beginArg(1);
+ localWriter.writeBlob(ptr, size);
+ localWriter.endArg();
+ localWriter.beginArg(2);
+ localWriter.writeUInt(size);
+ localWriter.endArg();
+ localWriter.endEnter();
+ localWriter.beginLeave(_call);
+ localWriter.endLeave();
+}
+
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_writer_local.hpp b/lib/trace/trace_writer_local.hpp
new file mode 100644
index 00000000..5d55bd9d
--- /dev/null
+++ b/lib/trace/trace_writer_local.hpp
@@ -0,0 +1,121 @@
+/**************************************************************************
+ *
+ * Copyright 2007-2013 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Trace writing functions, used to trace calls in the current process.
+ */
+
+#pragma once
+
+
+#include <stdint.h>
+
+#include "os_thread.hpp"
+#include "os_process.hpp"
+#include "trace_writer.hpp"
+
+
+namespace trace {
+
+ extern const FunctionSig memcpy_sig;
+ extern const FunctionSig malloc_sig;
+ extern const FunctionSig free_sig;
+ extern const FunctionSig realloc_sig;
+
+ /**
+ * A specialized Writer class, mean to trace the current process.
+ *
+ * In particular:
+ * - it creates a trace file based on the current process name
+ * - uses mutexes to allow tracing from multiple threades
+ * - flushes the output to ensure the last call is traced in event of
+ * abnormal termination
+ */
+ class LocalWriter : public Writer {
+ protected:
+ /**
+ * This mutex guarantees that only one thread writes to the trace file
+ * at one given instance.
+ *
+ * We need a recursive mutex so that we dont't dead lock in the event
+ * of a segfault happens while the mutex is held.
+ *
+ * To prevent deadlocks, the call for the real function (the one being
+ * traced) should not be done with the mutex held. That is, it should
+ * be done outside the beginEnter/endEnter and beginLeave/endLeave
+ * pairs. Preferably between these two pairs.
+ */
+ os::recursive_mutex mutex;
+ int acquired;
+
+ /**
+ * ID of the processed that opened the trace file.
+ */
+ os::ProcessId pid;
+
+ void checkProcessId();
+
+ public:
+ /**
+ * Should never called directly -- use localWriter singleton below
+ * instead.
+ */
+ LocalWriter();
+ ~LocalWriter();
+
+ void open(void);
+
+ /**
+ * It will acquire the mutex.
+ */
+ unsigned beginEnter(const FunctionSig *sig, bool fake = false);
+
+ /**
+ * It will release the mutex.
+ */
+ void endEnter(void);
+
+ /**
+ * It will acquire the mutex.
+ */
+ void beginLeave(unsigned call);
+
+ /**
+ * It will release the mutex.
+ */
+ void endLeave(void);
+
+ void flush(void);
+ };
+
+ /**
+ * Singleton.
+ */
+ extern LocalWriter localWriter;
+
+ void fakeMemcpy(const void *ptr, size_t size);
+
+} /* namespace trace */
+
diff --git a/lib/trace/trace_writer_model.cpp b/lib/trace/trace_writer_model.cpp
new file mode 100644
index 00000000..d497e5b9
--- /dev/null
+++ b/lib/trace/trace_writer_model.cpp
@@ -0,0 +1,149 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "trace_writer.hpp"
+
+
+namespace trace {
+
+
+class ModelWriter : public Visitor
+{
+protected:
+ Writer &writer;
+
+public:
+ ModelWriter(Writer &_writer) :
+ writer(_writer) {
+ }
+
+ void visit(Null *) override {
+ writer.writeNull();
+ }
+
+ void visit(Bool *node) override {
+ writer.writeBool(node->value);
+ }
+
+ void visit(SInt *node) override {
+ writer.writeSInt(node->value);
+ }
+
+ void visit(UInt *node) override {
+ writer.writeUInt(node->value);
+ }
+
+ void visit(Float *node) override {
+ writer.writeFloat(node->value);
+ }
+
+ void visit(Double *node) override {
+ writer.writeDouble(node->value);
+ }
+
+ void visit(String *node) override {
+ writer.writeString(node->value);
+ }
+
+ void visit(WString *node) override {
+ writer.writeWString(node->value);
+ }
+
+ void visit(Enum *node) override {
+ writer.writeEnum(node->sig, node->value);
+ }
+
+ void visit(Bitmask *node) override {
+ writer.writeBitmask(node->sig, node->value);
+ }
+
+ void visit(Struct *node) override {
+ writer.beginStruct(node->sig);
+ for (unsigned i = 0; i < node->sig->num_members; ++i) {
+ _visit(node->members[i]);
+ }
+ writer.endStruct();
+ }
+
+ void visit(Array *node) override {
+ writer.beginArray(node->values.size());
+ for (auto & value : node->values) {
+ _visit(value);
+ }
+ writer.endArray();
+ }
+
+ void visit(Blob *node) override {
+ writer.writeBlob(node->buf, node->size);
+ }
+
+ void visit(Pointer *node) override {
+ writer.writePointer(node->value);
+ }
+
+ void visit(Repr *node) override {
+ writer.beginRepr();
+ _visit(node->humanValue);
+ _visit(node->machineValue);
+ writer.endRepr();
+ }
+
+ void visit(Call *call) {
+ unsigned call_no = writer.beginEnter(call->sig, call->thread_id);
+ if (call->backtrace != NULL) {
+ writer.beginBacktrace(call->backtrace->size());
+ for (auto & frame : *call->backtrace) {
+ writer.writeStackFrame(frame);
+ }
+ writer.endBacktrace();
+ }
+ for (unsigned i = 0; i < call->args.size(); ++i) {
+ if (call->args[i].value) {
+ writer.beginArg(i);
+ _visit(call->args[i].value);
+ writer.endArg();
+ }
+ }
+ writer.endEnter();
+ writer.beginLeave(call_no);
+ if (call->ret) {
+ writer.beginReturn();
+ _visit(call->ret);
+ writer.endReturn();
+ }
+ writer.endLeave();
+ }
+};
+
+
+void Writer::writeCall(Call *call) {
+ ModelWriter visitor(*this);
+ visitor.visit(call);
+}
+
+
+} /* namespace trace */
+