diff options
author | Jose Fonseca <jfonseca@vmware.com> | 2016-05-05 15:20:03 +0100 |
---|---|---|
committer | Jose Fonseca <jfonseca@vmware.com> | 2016-05-05 15:20:03 +0100 |
commit | 5e9a2b11b248aaa2f6f353bbcf9f600d65bcaf51 (patch) | |
tree | b3178d019ae82f3088a75315a6b7534c7a792414 /lib | |
parent | ac4cdf379e076c69c7c5ec9d6766362f15968b66 (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')
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 */ + |