<div dir="ltr">Thanks. It's much closer of what I had in mind.<div><br></div><div><span style="font-family:arial,sans-serif;font-size:13px">However, instead of encoding the stack traces as an array of structures, I'd really prefer encoding the stack traces with the scheme I illustrated earlier (where symbol, filename, lineno are optional details of a stack frame node). It's fine if when you parse the traces you encode them with arrays of structures (though I don't see much benefit). My main concern is just the file format, as everything else can be improved later on.</span></div>
<div><span style="font-family:arial,sans-serif;font-size:13px"><br></span></div><div style><span style="font-family:arial,sans-serif;font-size:13px">Jose</span></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">
On Tue, Apr 16, 2013 at 11:20 AM, Eugene Velesevich <span dir="ltr"><<a href="mailto:evel@ispras.ru" target="_blank">evel@ispras.ru</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Hello,<br>
<br>
Changes from v2: Now backtrace is parsed and is recorded as a call detail.<br>
<br>
This patch implements backtrace recording during tracing, and adds support in<br>
'apitrace dump' and QApitrace. Backtrace is obtained via platform-specific<br>
functions (and, internally, in platform-specific format). Then it is parsed to<br>
produce an std::vector of stack frame structs: { char *module, *function,<br>
*filename, *linenumber, *offset } (some fields may be NULL) and is written<br>
into the trace file in the Enter call section as an Array with specific call<br>
detail flag previously emitted.<br>
<br>
A platform-dependent mechanism is provided to specify a set of traced<br>
calls for which backtraces will be recorded. It is possible to specify<br>
either function names, or prefixes of names by appending a '*' (e.g.<br>
"glUniform*").<br>
<br>
On Android the backtrace is retrieved from Dalvik via libdvm functions<br>
imported at runtime.<br>
Function set is specified in /data/apitrace.fnames, one per line.<br>
<br>
On Linux the backtrace is retrieved via glibc backtrace(), and will not always<br>
yield filename:linenumber information.<br>
Function set is specified via APITRACE_BT_FUNCTIONS environment variable.<br>
<br>
On other platforms, obtaining a backtrace is not implemented by this patch.<br>
<br>
---<br>
CMakeLists.txt | 1 +<br>
common/trace_backtrace.cpp | 447 +++++++++++++++++++++++++++++++++++++++++<br>
common/trace_backtrace.hpp | 75 +++++++<br>
common/trace_dump.cpp | 33 +++<br>
common/trace_format.hpp | 6 +-<br>
common/trace_model.hpp | 9 +-<br>
common/trace_parser.cpp | 8 +-<br>
common/trace_writer.cpp | 27 ++-<br>
common/trace_writer.hpp | 9 +-<br>
common/trace_writer_local.cpp | 21 +-<br>
common/trace_writer_local.hpp | 12 +-<br>
common/trace_writer_model.cpp | 5 +<br>
gui/apitracecall.cpp | 41 ++++<br>
gui/apitracecall.h | 5 +<br>
gui/mainwindow.cpp | 10 +<br>
gui/ui/mainwindow.ui | 19 ++<br>
wrappers/gltrace.py | 2 +-<br>
wrappers/trace.py | 4 +-<br>
18 files changed, 710 insertions(+), 24 deletions(-)<br>
create mode 100644 common/trace_backtrace.cpp<br>
create mode 100644 common/trace_backtrace.hpp<br>
<br>
diff --git a/CMakeLists.txt b/CMakeLists.txt<br>
index 9394282..6ac09ee 100644<br>
--- a/CMakeLists.txt<br>
+++ b/CMakeLists.txt<br>
@@ -314,6 +314,7 @@ add_library (common STATIC<br>
common/trace_profiler.cpp<br>
common/trace_option.cpp<br>
common/${os}<br>
+ common/trace_backtrace.cpp<br>
)<br>
<br>
set_target_properties (common PROPERTIES<br>
diff --git a/common/trace_backtrace.cpp b/common/trace_backtrace.cpp<br>
new file mode 100644<br>
index 0000000..23f96f6<br>
--- /dev/null<br>
+++ b/common/trace_backtrace.cpp<br>
@@ -0,0 +1,447 @@<br>
+/**************************************************************************<br>
+ *<br>
+ * Copyright 2013 Samsung<br>
+ * Contributed by Eugene Velesevich<br>
+ * All Rights Reserved.<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy<br>
+ * of this software and associated documentation files (the "Software"), to deal<br>
+ * in the Software without restriction, including without limitation the rights<br>
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br>
+ * copies of the Software, and to permit persons to whom the Software is<br>
+ * furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice shall be included in<br>
+ * all copies or substantial portions of the Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br>
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br>
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN<br>
+ * THE SOFTWARE.<br>
+ *<br>
+ **************************************************************************/<br>
+<br>
+/*<br>
+ * Wrapper for platform-specific code for obtaining symbolic backtraces<br>
+ * on Android and Linux<br>
+ */<br>
+<br>
+<br>
+<br>
+#include "trace_backtrace.hpp"<br>
+<br>
+#if defined(ANDROID) or defined(__linux__)<br>
+<br>
+#include <set><br>
+#include "os.hpp"<br>
+<br>
+<br>
+namespace trace {<br>
+<br>
+/*<br>
+ * Pascal string (with zero terminator optionally omitted)<br>
+ * This is a helper class for storing a set of exact strings or prefixes<br>
+ * to match a zero-terminated string against later.<br>
+ * Two zero-terminated pstrings compare equal iff they are the same.<br>
+ * Otherwise, they compare equal iff one is a prefix of the other<br>
+ * (a zero-terminated pstring cannot be a prefix)<br>
+ */<br>
+<br>
+struct pstring {<br>
+ const char* s;<br>
+ int n;<br>
+ pstring(const char* s, int n)<br>
+ {<br>
+ this->s = s;<br>
+ this->n = n;<br>
+ }<br>
+ bool operator<(const pstring q2) const<br>
+ {<br>
+ return memcmp(s, q2.s, n < q2.n? n : q2.n) < 0;<br>
+ }<br>
+};<br>
+<br>
+<br>
+#define PREFIX_BUF_SIZE (PREFIX_MAX_FUNC_NAME * MAX_BT_FUNC)<br>
+<br>
+class StringPrefixes {<br>
+private:<br>
+ std::set<pstring> pset;<br>
+ char* buf;<br>
+private:<br>
+ void addPrefix(char* startbuf, int n) {<br>
+ std::set<pstring>::iterator elem = pset.find(pstring(startbuf, n));<br>
+ bool replace = elem != pset.end() && n < elem->n;<br>
+ if (replace) {<br>
+ pset.erase(elem);<br>
+ }<br>
+ if (replace || elem == pset.end()) {<br>
+ pset.insert(pstring(startbuf, n));<br>
+ }<br>
+ }<br>
+public:<br>
+ StringPrefixes(const char* source);<br>
+<br>
+ bool contain(const char* s) {<br>
+ if (pset.find(pstring(s, strlen(s) + 1)) != pset.end()) {<br>
+ os::log("Backtrace for %s is enabled", s);<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+ }<br>
+};<br>
+<br>
+bool backtrace_is_needed(const char* fname) {<br>
+ static StringPrefixes backtraceFunctionNamePrefixes(APITRACE_FNAMES_SOURCE);<br>
+ return backtraceFunctionNamePrefixes.contain(fname);<br>
+}<br>
+<br>
+} /* namespace trace */<br>
+<br>
+#ifdef ANDROID<br>
+<br>
+#include <dlfcn.h><br>
+#include "os.hpp"<br>
+#include <vector><br>
+<br>
+namespace trace {<br>
+<br>
+StringPrefixes::StringPrefixes(const char* source) {<br>
+ buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);<br>
+ char* startbuf = buf;<br>
+ int n = 0;<br>
+ FILE* f = fopen(source, "r");<br>
+ if (f == NULL) {<br>
+ os::log("Cannot open " APITRACE_FNAMES_FILE);<br>
+ }<br>
+ while ((startbuf = fgets(startbuf, PREFIX_MAX_FUNC_NAME, f))) {<br>
+ n = strlen(startbuf);<br>
+ if (startbuf[n - 1] == '\n') {<br>
+ n--;<br>
+ }<br>
+ if (n > 2 && startbuf[0] != '#') {<br>
+ int psize;<br>
+ if (startbuf[n - 1] != '*') {<br>
+ startbuf[n] = '\0';<br>
+ psize = n + 1;<br>
+ }<br>
+ else {<br>
+ psize = n - 1;<br>
+ }<br>
+ addPrefix(startbuf, psize);<br>
+ startbuf += n + 1;<br>
+ n = 0;<br>
+ }<br>
+ }<br>
+ fclose(f);<br>
+}<br>
+<br>
+<br>
+/* The following two declarations are copied from Android sources */<br>
+<br>
+enum DebugTargetKind {<br>
+ kDebugTargetUnknown = 0,<br>
+ kDebugTargetLog,<br>
+ kDebugTargetFile,<br>
+};<br>
+<br>
+struct DebugOutputTarget {<br>
+ DebugTargetKind which;<br>
+<br>
+ union {<br>
+ struct {<br>
+ int priority;<br>
+ const char* tag;<br>
+ } log;<br>
+ struct {<br>
+ FILE* fp;<br>
+ } file;<br>
+ } data;<br>
+};<br>
+<br>
+#define THREAD_SELF_NAME "_Z13dvmThreadSelfv"<br>
+#define CREATE_DEBUG_TARGET_NAME "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE"<br>
+#define DUMP_BACKTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"<br>
+<br>
+<br>
+class DalvikBacktraceProvider {<br>
+private:<br>
+ bool errorOccured;<br>
+ void* (*threadself)(void);<br>
+ FILE* streamInMemory;<br>
+ char* buf;<br>
+ size_t bufSize;<br>
+ void (*dumpBacktrace)(const DebugOutputTarget*, void*);<br>
+ DebugOutputTarget debugTarget;<br>
+public:<br>
+ DalvikBacktraceProvider() {<br>
+ FILE* (*open_memstream_exp)(char**, size_t*);<br>
+ void (*createDebugTarget)(DebugOutputTarget*, FILE*);<br>
+ void* handle = dlopen("/system/lib/libdvm.so", 0);<br>
+ errorOccured = true;<br>
+ if (!handle) {<br>
+ os::log("dlopen failed\n");<br>
+ return;<br>
+ }<br>
+ threadself = (void* (*)())dlsym(handle, THREAD_SELF_NAME);<br>
+ if (threadself == NULL) {<br>
+ os::log("dlsym ThreadSelf failed\n");<br>
+ return;<br>
+ }<br>
+ createDebugTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, CREATE_DEBUG_TARGET_NAME);<br>
+ if (createDebugTarget == NULL) {<br>
+ os::log("dlsym CreateFileOutput failed\n");<br>
+ return;<br>
+ }<br>
+ dumpBacktrace = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, DUMP_BACKTRACE_NAME);<br>
+ if (dumpBacktrace == NULL) {<br>
+ os::log("dlsym DumpThreadStack failed\n");<br>
+ return;<br>
+ }<br>
+ void* handle2 = dlopen("/system/lib/libcutils.so", 0);<br>
+ if (!handle2) {<br>
+ os::log("dlopen failed\n");<br>
+ return;<br>
+ }<br>
+ open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");<br>
+ if (open_memstream_exp == NULL) {<br>
+ os::log("dlsym open_memstream failed\n");<br>
+ return;<br>
+ }<br>
+ streamInMemory = open_memstream_exp(&buf, &bufSize);<br>
+ if (streamInMemory == NULL) {<br>
+ os::log("open_memstream failed\n");<br>
+ return;<br>
+ }<br>
+ createDebugTarget(&debugTarget, streamInMemory);<br>
+ errorOccured = false;<br>
+ }<br>
+<br>
+ inline char* getBacktrace() {<br>
+ if (errorOccured) {<br>
+ return NULL;<br>
+ }<br>
+ rewind(streamInMemory);<br>
+ dumpBacktrace(&debugTarget, threadself());<br>
+ fflush(streamInMemory);<br>
+ return buf;<br>
+ }<br>
+/*<br>
+ * Parse a stack frame, expecting:<br>
+ * " at android.view.HardwareRenderer$GlRenderer.initializeEgl(HardwareRenderer.java:547)"<br>
+ * or<br>
+ * " at android.view.HardwareRenderer$GlRenderer.initializeEgl(Native Method)"<br>
+ */<br>
+ std::vector<StackFrame> parseBacktrace(char *rawBacktrace) {<br>
+ std::vector<StackFrame> parsedBacktrace;<br>
+ char* rawBacktrace_it = rawBacktrace;<br>
+ while (*rawBacktrace_it != '\0') {<br>
+ StackFrame stackFrame;<br>
+ /* skip leading space */<br>
+ while (*rawBacktrace_it == ' ') {<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ /* Skip "at " */<br>
+ rawBacktrace_it += 3;<br>
+ stackFrame.function = rawBacktrace_it;<br>
+ while (*rawBacktrace_it != '(') {<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ *rawBacktrace_it = '\0';<br>
+ stackFrame.filename = rawBacktrace_it + 1;<br>
+ while (*rawBacktrace_it != ':' && *rawBacktrace_it != ')') {<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ if (*rawBacktrace_it == ':') {<br>
+ stackFrame.linenumber = rawBacktrace_it + 1;<br>
+ *rawBacktrace_it = '\0';<br>
+ while (*rawBacktrace_it != ')') {<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ *rawBacktrace_it = '\0';<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ else {<br>
+ stackFrame.filename = NULL;<br>
+ while (*rawBacktrace_it != '\n') {<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ }<br>
+ while (*rawBacktrace_it == '\n' || *rawBacktrace_it == ' ') {<br>
+ rawBacktrace_it++;<br>
+ }<br>
+ parsedBacktrace.push_back(stackFrame); /* module */<br>
+ }<br>
+ return parsedBacktrace;<br>
+ }<br>
+};<br>
+<br>
+std::vector<StackFrame> get_backtrace() {<br>
+ static DalvikBacktraceProvider backtraceProvider;<br>
+ return backtraceProvider.parseBacktrace(backtraceProvider.getBacktrace());<br>
+}<br>
+<br>
+/* end ANDROID */<br>
+#elif defined __linux__<br>
+<br>
+#include <execinfo.h><br>
+#include <string.h><br>
+#include <stdlib.h><br>
+#include <map><br>
+#include <vector><br>
+#include <stdio.h><br>
+<br>
+namespace trace {<br>
+<br>
+<br>
+StringPrefixes::StringPrefixes(const char* source) {<br>
+ buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);<br>
+ char* startbuf = buf;<br>
+ int n = 0;<br>
+ char* s = getenv(source);<br>
+ char end = ';';<br>
+ if (s == NULL) {<br>
+ return;<br>
+ }<br>
+ *buf = ';';<br>
+ strncpy(buf + 1, s, PREFIX_BUF_SIZE - 2);<br>
+ while (end != '\0') {<br>
+ startbuf++;<br>
+ while (*(startbuf + n) != ';' && *(startbuf + n) != '\0') {<br>
+ n++;<br>
+ }<br>
+ end = startbuf[n];<br>
+ if (n > 2 && startbuf[0] != '#') {<br>
+ int psize;<br>
+ if (startbuf[n - 1] != '*') {<br>
+ startbuf[n] = '\0';<br>
+ psize = n + 1;<br>
+ }<br>
+ else {<br>
+ psize = n - 1;<br>
+ }<br>
+ addPrefix(startbuf, psize);<br>
+ startbuf += n;<br>
+ n = 0;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+<br>
+#define BT_DEPTH 10<br>
+<br>
+class GlibcBacktraceProvider {<br>
+private:<br>
+ std::map<void*, StackFrame*> cache;<br>
+ /*<br>
+ * Backtrace being returned by glibc backtrace() contains stack frames<br>
+ * belonging to apitrace wrapper module. We count the number of apitrace<br>
+ * functions on the stack to avoid recording these frames.<br>
+ */<br>
+ int numOfNestedFunctions;<br>
+private:<br>
+/*<br>
+ * Parse a stack frame, expecting:<br>
+ * /lib/libc.so.6.1(__libc_start_main+0x50308) [0x2000000000097630]<br>
+ * or<br>
+ * /lib/libc.so.6.1(+0x50308) [0x2000000000097630]<br>
+ * or<br>
+ * /lib/libc.so.6.1() [0x2000000000097630]<br>
+ */<br>
+ StackFrame* parseFrame(void* frame, char* frame_symbol) {<br>
+ if (cache.find(frame) == cache.end()) {<br>
+ char* frame_symbol_copy = new char[strlen(frame_symbol) + 1];<br>
+ strcpy(frame_symbol_copy, frame_symbol);<br>
+ StackFrame* parsedFrame = new StackFrame;<br>
+ char* frame_it = frame_symbol_copy;<br>
+ parsedFrame->module = frame_it;<br>
+ while (true) {<br>
+ switch (*frame_it) {<br>
+ case '(':<br>
+ *frame_it = '\0';<br>
+ frame_it++;<br>
+ if (*frame_it != ')' && *frame_it != '+') {<br>
+ parsedFrame->function = frame_it;<br>
+ while (*frame_it != '+' && *frame_it != ')') {<br>
+ frame_it++;<br>
+ }<br>
+ *frame_it = '\0';<br>
+ frame_it++;<br>
+ }<br>
+ break;<br>
+ case '[':<br>
+ *frame_it = '\0';<br>
+ frame_it++;<br>
+ parsedFrame->offset = frame_it;<br>
+ break;<br>
+ case ']':<br>
+ *frame_it = '\0';<br>
+ cache[frame] = parsedFrame;<br>
+ return parsedFrame;<br>
+ case '\0':<br>
+ cache[frame] = NULL;<br>
+ delete[] frame_symbol_copy;<br>
+ delete[] parsedFrame;<br>
+ return NULL;<br>
+ default:<br>
+ frame_it++;<br>
+ }<br>
+ }<br>
+ }<br>
+ else {<br>
+ return cache[frame];<br>
+ }<br>
+ }<br>
+public:<br>
+ GlibcBacktraceProvider() :<br>
+ numOfNestedFunctions(0) {<br>
+ }<br>
+ std::vector<StackFrame> getParsedBacktrace() {<br>
+ std::vector<StackFrame> parsedBacktrace;<br>
+ void *array[numOfNestedFunctions + BT_DEPTH];<br>
+ size_t size;<br>
+ char **strings;<br>
+ size_t i;<br>
+ const char* firstModule;<br>
+ size = backtrace(array, numOfNestedFunctions + BT_DEPTH);<br>
+ strings = backtrace_symbols(array, size);<br>
+ for (i = numOfNestedFunctions; i < size; i++) {<br>
+ StackFrame* parsedFrame = parseFrame(array[i], strings[i]);<br>
+ if (numOfNestedFunctions == 0) {<br>
+ if (i == 0) {<br>
+ firstModule = parsedFrame->module;<br>
+ }<br>
+ else {<br>
+ if (strcmp(firstModule, parsedFrame->module)) {<br>
+ numOfNestedFunctions = i;<br>
+ free(strings);<br>
+ parsedBacktrace = getParsedBacktrace();<br>
+ numOfNestedFunctions--;<br>
+ return parsedBacktrace;<br>
+ }<br>
+ }<br>
+ } else {<br>
+ if (parsedFrame != NULL) {<br>
+ parsedBacktrace.push_back(*parsedFrame);<br>
+ }<br>
+ }<br>
+ }<br>
+ free(strings);<br>
+ return parsedBacktrace;<br>
+ }<br>
+};<br>
+<br>
+<br>
+std::vector<StackFrame> get_backtrace() {<br>
+ static GlibcBacktraceProvider backtraceProvider;<br>
+ return backtraceProvider.getParsedBacktrace();<br>
+}<br>
+<br>
+#endif /* LINUX */<br>
+<br>
+} /* namespace trace */<br>
+<br>
+#endif /* ANDROID or LINUX */<br>
diff --git a/common/trace_backtrace.hpp b/common/trace_backtrace.hpp<br>
new file mode 100644<br>
index 0000000..4fef75e<br>
--- /dev/null<br>
+++ b/common/trace_backtrace.hpp<br>
@@ -0,0 +1,75 @@<br>
+#ifndef _TRACE_BACKTRACE_HPP_<br>
+#define _TRACE_BACKTRACE_HPP_<br>
+<br>
+#include <vector><br>
+<br>
+#include "trace_model.hpp"<br>
+<br>
+namespace trace {<br>
+<br>
+<br>
+struct StackFrame {<br>
+ char* module;<br>
+ char* function;<br>
+ char* filename;<br>
+ char* linenumber;<br>
+ char* offset;<br>
+ StackFrame() :<br>
+ module(0),<br>
+ function(0),<br>
+ filename(0),<br>
+ linenumber(0),<br>
+ offset(0)<br>
+ {<br>
+ }<br>
+};<br>
+<br>
+static const char* stackframe_members[5] = {<br>
+ "module",<br>
+ "function",<br>
+ "filename",<br>
+ "linenumber",<br>
+ "offset"<br>
+};<br>
+<br>
+static const trace::StructSig stackframe_sig = {<br>
+ 2, "backtrace", 5, stackframe_members<br>
+};<br>
+<br>
+<br>
+#if defined(ANDROID) or defined(__linux__)<br>
+<br>
+std::vector<StackFrame> get_backtrace();<br>
+bool backtrace_is_needed(const char* fname);<br>
+<br>
+#if defined(ANDROID)<br>
+<br>
+#define MAX_BT_FUNC 20<br>
+#define PREFIX_MAX_FUNC_NAME 100<br>
+#define APITRACE_FNAMES_FILE "/data/apitrace.fnames"<br>
+#define APITRACE_FNAMES_SOURCE APITRACE_FNAMES_FILE<br>
+<br>
+#elif defined(__linux__)<br>
+<br>
+#define MAX_BT_FUNC 20<br>
+#define PREFIX_MAX_FUNC_NAME 100<br>
+#define APITRACE_FNAMES_ENV "APITRACE_BT_FUNCTIONS"<br>
+#define APITRACE_FNAMES_SOURCE APITRACE_FNAMES_ENV<br>
+<br>
+#endif<br>
+<br>
+#else /* !__linux__ && !ANDROID */<br>
+<br>
+static inline std::vector<StackFrame> get_backtrace() {<br>
+ return std::vector<StackFrame>();<br>
+}<br>
+<br>
+static inline bool backtrace_is_needed(const char*) {<br>
+ return false;<br>
+}<br>
+<br>
+#endif<br>
+<br>
+} /* namespace trace */<br>
+<br>
+#endif<br>
diff --git a/common/trace_dump.cpp b/common/trace_dump.cpp<br>
index e6810b6..ff8bb2b 100644<br>
--- a/common/trace_dump.cpp<br>
+++ b/common/trace_dump.cpp<br>
@@ -228,6 +228,35 @@ public:<br>
_visit(r->humanValue);<br>
}<br>
<br>
+ void visitBacktrace(std::vector<Value*> backtrace) {<br>
+ for (int i = 0; i < backtrace.size(); i ++) {<br>
+ Struct* frame = dynamic_cast<Struct*>(backtrace[i]);<br>
+ const char* tmp = frame->members[0]->toString(); /* module */<br>
+ if (tmp != NULL) {<br>
+ os << tmp << " ";<br>
+ }<br>
+ tmp = frame->members[1]->toString(); /* function */<br>
+ if (tmp != NULL) {<br>
+ os << "at " << tmp << "() ";<br>
+ }<br>
+ tmp = frame->members[2]->toString(); /* file */<br>
+ if (tmp != NULL) {<br>
+ os << "at " << tmp;<br>
+ tmp = frame->members[3]->toString(); /* line */<br>
+ if (tmp != NULL) {<br>
+ os << ":" << tmp << " ";<br>
+ }<br>
+ }<br>
+ else {<br>
+ tmp = frame->members[4]->toString(); /* offset */<br>
+ if (tmp != NULL) {<br>
+ os << "[" << tmp << "]";<br>
+ }<br>
+ }<br>
+ os << "\n";<br>
+ }<br>
+ }<br>
+<br>
void visit(Call *call) {<br>
CallFlags callFlags = call->flags;<br>
<br>
@@ -271,6 +300,10 @@ public:<br>
<br>
os << "\n";<br>
<br>
+ if (call->backtrace != NULL) {<br>
+ os << bold << red << "Backtrace:\n" << normal;<br>
+ visitBacktrace(call->backtrace->values);<br>
+ }<br>
if (callFlags & CALL_FLAG_END_FRAME) {<br>
os << "\n";<br>
}<br>
diff --git a/common/trace_format.hpp b/common/trace_format.hpp<br>
index d5fd81b..0bb8ee0 100644<br>
--- a/common/trace_format.hpp<br>
+++ b/common/trace_format.hpp<br>
@@ -69,8 +69,11 @@ namespace trace {<br>
*<br>
* - version 4:<br>
* - call enter events include thread ID<br>
+ *<br>
+ * - version 5:<br>
+ * - new call detail flag CALL_BACKTRACE<br>
*/<br>
-#define TRACE_VERSION 4<br>
+#define TRACE_VERSION 5<br>
<br>
<br>
/*<br>
@@ -127,6 +130,7 @@ enum CallDetail {<br>
CALL_ARG,<br>
CALL_RET,<br>
CALL_THREAD,<br>
+ CALL_BACKTRACE,<br>
};<br>
<br>
enum Type {<br>
diff --git a/common/trace_model.hpp b/common/trace_model.hpp<br>
index 9558225..b47b692 100644<br>
--- a/common/trace_model.hpp<br>
+++ b/common/trace_model.hpp<br>
@@ -49,6 +49,7 @@ struct FunctionSig {<br>
const char *name;<br>
unsigned num_args;<br>
const char **arg_names;<br>
+ bool backtrace;<br>
};<br>
<br>
<br>
@@ -463,18 +464,20 @@ class Call<br>
public:<br>
unsigned thread_id;<br>
unsigned no;<br>
- const FunctionSig *sig;<br>
+ FunctionSig *sig;<br>
std::vector<Arg> args;<br>
Value *ret;<br>
<br>
CallFlags flags;<br>
+ Array* backtrace;<br>
<br>
- Call(const FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :<br>
+ Call(FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :<br>
thread_id(_thread_id),<br>
sig(_sig),<br>
args(_sig->num_args),<br>
ret(0),<br>
- flags(_flags) {<br>
+ flags(_flags),<br>
+ backtrace(0) {<br>
}<br>
<br>
~Call();<br>
diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp<br>
index c70517e..f699353 100644<br>
--- a/common/trace_parser.cpp<br>
+++ b/common/trace_parser.cpp<br>
@@ -463,7 +463,7 @@ Call *Parser::parse_leave(Mode mode) {<br>
* between two frames. We won't return this call, but we still need to skip<br>
* over its data.<br>
*/<br>
- const FunctionSig sig = {0, NULL, 0, NULL};<br>
+ FunctionSig sig = {0, NULL, 0, NULL};<br>
call = new Call(&sig, 0, 0);<br>
parse_call_details(call, SCAN);<br>
delete call;<br>
@@ -500,6 +500,12 @@ bool Parser::parse_call_details(Call *call, Mode mode) {<br>
#endif<br>
call->ret = parse_value(mode);<br>
break;<br>
+ case trace::CALL_BACKTRACE:<br>
+#if TRACE_VERBOSE<br>
+ std::cerr << "\tCALL_BACKTRACE\n";<br>
+#endif<br>
+ call->backtrace = (Array*)parse_value(mode);<br>
+ break;<br>
default:<br>
std::cerr << "error: ("<<call->name()<< ") unknown call detail "<br>
<< c << "\n";<br>
diff --git a/common/trace_writer.cpp b/common/trace_writer.cpp<br>
index d69e93f..b041d44 100644<br>
--- a/common/trace_writer.cpp<br>
+++ b/common/trace_writer.cpp<br>
@@ -29,12 +29,13 @@<br>
#include <stdio.h><br>
#include <stdlib.h><br>
#include <string.h><br>
+#include <vector><br>
<br>
#include "os.hpp"<br>
#include "trace_file.hpp"<br>
#include "trace_writer.hpp"<br>
#include "trace_format.hpp"<br>
-<br>
+#include "trace_backtrace.hpp"<br>
<br>
namespace trace {<br>
<br>
@@ -134,11 +135,33 @@ inline bool lookup(std::vector<bool> &map, size_t index) {<br>
}<br>
}<br>
<br>
-unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) {<br>
+void Writer::writeBacktrace(std::vector<StackFrame> backtrace) {<br>
+<br>
+ beginArray(backtrace.size());<br>
+ for (int i = 0; i < backtrace.size(); i++) {<br>
+ beginElement();<br>
+ beginStruct(&stackframe_sig);<br>
+ writeString(backtrace[i].module);<br>
+ writeString(backtrace[i].function);<br>
+ writeString(backtrace[i].filename);<br>
+ writeString(backtrace[i].linenumber);<br>
+ writeString(backtrace[i].offset);<br>
+ endStruct();<br>
+ endElement();<br>
+ }<br>
+ endArray();<br>
+}<br>
+<br>
+void Writer::beginBacktrace(void ) {<br>
+ _writeByte(trace::CALL_BACKTRACE);<br>
+}<br>
+<br>
+unsigned Writer::beginEnter(FunctionSig *sig, unsigned thread_id) {<br>
_writeByte(trace::EVENT_ENTER);<br>
_writeUInt(thread_id);<br>
_writeUInt(sig->id);<br>
if (!lookup(functions, sig->id)) {<br>
+ sig->backtrace = backtrace_is_needed(sig->name);<br>
_writeString(sig->name);<br>
_writeUInt(sig->num_args);<br>
for (unsigned i = 0; i < sig->num_args; ++i) {<br>
diff --git a/common/trace_writer.hpp b/common/trace_writer.hpp<br>
index a46b43e..4070052 100644<br>
--- a/common/trace_writer.hpp<br>
+++ b/common/trace_writer.hpp<br>
@@ -36,7 +36,7 @@<br>
#include <vector><br>
<br>
#include "trace_model.hpp"<br>
-<br>
+#include "trace_backtrace.hpp"<br>
<br>
namespace trace {<br>
class File;<br>
@@ -58,7 +58,12 @@ namespace trace {<br>
bool open(const char *filename);<br>
void close(void);<br>
<br>
- unsigned beginEnter(const FunctionSig *sig, unsigned thread_id);<br>
+ void writeBacktrace(std::vector<StackFrame> backtrace);<br>
+ void beginBacktrace(void);<br>
+ inline void endBacktrace(void) {<br>
+ }<br>
+<br>
+ unsigned beginEnter(FunctionSig *sig, unsigned thread_id);<br>
void endEnter(void);<br>
<br>
void beginLeave(unsigned call);<br>
diff --git a/common/trace_writer_local.cpp b/common/trace_writer_local.cpp<br>
index feb5c91..760861d 100644<br>
--- a/common/trace_writer_local.cpp<br>
+++ b/common/trace_writer_local.cpp<br>
@@ -36,22 +36,23 @@<br>
#include "trace_file.hpp"<br>
#include "trace_writer_local.hpp"<br>
#include "trace_format.hpp"<br>
+#include "trace_backtrace.hpp"<br>
<br>
<br>
namespace trace {<br>
<br>
<br>
static const char *memcpy_args[3] = {"dest", "src", "n"};<br>
-const FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args};<br>
+FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args, false};<br>
<br>
static const char *malloc_args[1] = {"size"};<br>
-const FunctionSig malloc_sig = {1, "malloc", 1, malloc_args};<br>
+FunctionSig malloc_sig = {1, "malloc", 1, malloc_args, false};<br>
<br>
static const char *free_args[1] = {"ptr"};<br>
-const FunctionSig free_sig = {2, "free", 1, free_args};<br>
+FunctionSig free_sig = {2, "free", 1, free_args, false};<br>
<br>
static const char *realloc_args[2] = {"ptr", "size"};<br>
-const FunctionSig realloc_sig = {3, "realloc", 2, realloc_args};<br>
+FunctionSig realloc_sig = {3, "realloc", 2, realloc_args, false};<br>
<br>
<br>
static void exceptionCallback(void)<br>
@@ -131,7 +132,7 @@ LocalWriter::open(void) {<br>
static unsigned next_thread_num = 1;<br>
static thread_specific unsigned thread_num = 0;<br>
<br>
-unsigned LocalWriter::beginEnter(const FunctionSig *sig) {<br>
+unsigned LocalWriter::beginEnter(FunctionSig *sig) {<br>
mutex.lock();<br>
++acquired;<br>
<br>
@@ -146,7 +147,15 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {<br>
<br>
assert(thread_num > 0);<br>
unsigned thread_id = thread_num - 1;<br>
- return Writer::beginEnter(sig, thread_id);<br>
+<br>
+ unsigned call_no = Writer::beginEnter(sig, thread_id);<br>
+ if (sig->backtrace) {<br>
+ std::vector<StackFrame> backtrace = get_backtrace();<br>
+ beginBacktrace();<br>
+ writeBacktrace(backtrace);<br>
+ endBacktrace();<br>
+ }<br>
+ return call_no;<br>
}<br>
<br>
void LocalWriter::endEnter(void) {<br>
diff --git a/common/trace_writer_local.hpp b/common/trace_writer_local.hpp<br>
index cc5dda0..1da2250 100644<br>
--- a/common/trace_writer_local.hpp<br>
+++ b/common/trace_writer_local.hpp<br>
@@ -39,10 +39,10 @@<br>
<br>
namespace trace {<br>
<br>
- extern const FunctionSig memcpy_sig;<br>
- extern const FunctionSig malloc_sig;<br>
- extern const FunctionSig free_sig;<br>
- extern const FunctionSig realloc_sig;<br>
+ extern FunctionSig memcpy_sig;<br>
+ extern FunctionSig malloc_sig;<br>
+ extern FunctionSig free_sig;<br>
+ extern FunctionSig realloc_sig;<br>
<br>
/**<br>
* A specialized Writer class, mean to trace the current process.<br>
@@ -83,8 +83,8 @@ namespace trace {<br>
/**<br>
* It will acquire the mutex.<br>
*/<br>
- unsigned beginEnter(const FunctionSig *sig);<br>
-<br>
+ unsigned beginEnter(FunctionSig *sig);<br>
+<br>
/**<br>
* It will release the mutex.<br>
*/<br>
diff --git a/common/trace_writer_model.cpp b/common/trace_writer_model.cpp<br>
index 2e858a2..f369172 100644<br>
--- a/common/trace_writer_model.cpp<br>
+++ b/common/trace_writer_model.cpp<br>
@@ -109,6 +109,11 @@ public:<br>
<br>
void visit(Call *call) {<br>
unsigned call_no = writer.beginEnter(call->sig, call->thread_id);<br>
+ if (call->backtrace != NULL) {<br>
+ writer.beginBacktrace();<br>
+ _visit(call->backtrace);<br>
+ writer.endBacktrace();<br>
+ }<br>
for (unsigned i = 0; i < call->args.size(); ++i) {<br>
if (call->args[i].value) {<br>
writer.beginArg(i);<br>
diff --git a/gui/apitracecall.cpp b/gui/apitracecall.cpp<br>
index 3377486..cc2826f 100644<br>
--- a/gui/apitracecall.cpp<br>
+++ b/gui/apitracecall.cpp<br>
@@ -685,6 +685,37 @@ ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,<br>
}<br>
m_argValues.squeeze();<br>
m_flags = call->flags;<br>
+ if (call->backtrace != NULL) {<br>
+ std::vector<trace::Value*> backtrace = call->backtrace->values;<br>
+ QString qbacktrace;<br>
+ for (int i = 0; i < backtrace.size(); i++) {<br>
+ trace::Struct* frame = dynamic_cast<trace::Struct*>(backtrace[i]);<br>
+ const char* tmp = frame->members[0]->toString(); /* module */<br>
+ if (tmp != NULL) {<br>
+ qbacktrace += QString("%1 ").arg(tmp);<br>
+ }<br>
+ tmp = frame->members[1]->toString(); /* function */<br>
+ if (tmp != NULL) {<br>
+ qbacktrace += QString("at %1() ").arg(tmp);<br>
+ }<br>
+ tmp = frame->members[2]->toString(); /* file */<br>
+ if (tmp != NULL) {<br>
+ qbacktrace += QString("at %1").arg(tmp);<br>
+ tmp = frame->members[3]->toString(); /* line */<br>
+ if (tmp != NULL) {<br>
+ qbacktrace += QString(":%1 ").arg(tmp);<br>
+ }<br>
+ }<br>
+ else {<br>
+ tmp = frame->members[4]->toString(); /* offset */<br>
+ if (tmp != NULL) {<br>
+ qbacktrace += QString("[%1]").arg(tmp);<br>
+ }<br>
+ }<br>
+ qbacktrace += "\n";<br>
+ }<br>
+ this->setBacktrace(qbacktrace);<br>
+ }<br>
}<br>
<br>
ApiTraceCall::~ApiTraceCall()<br>
@@ -820,6 +851,16 @@ int ApiTraceCall::binaryDataIndex() const<br>
return m_binaryDataIndex;<br>
}<br>
<br>
+QString ApiTraceCall::backtrace() const<br>
+{<br>
+ return m_backtrace;<br>
+}<br>
+<br>
+void ApiTraceCall::setBacktrace(QString backtrace)<br>
+{<br>
+ m_backtrace = backtrace;<br>
+}<br>
+<br>
QStaticText ApiTraceCall::staticText() const<br>
{<br>
if (m_staticText && !m_staticText->text().isEmpty())<br>
diff --git a/gui/apitracecall.h b/gui/apitracecall.h<br>
index 6c6b607..8004ced 100644<br>
--- a/gui/apitracecall.h<br>
+++ b/gui/apitracecall.h<br>
@@ -279,6 +279,9 @@ public:<br>
int numChildren() const;<br>
bool hasBinaryData() const;<br>
int binaryDataIndex() const;<br>
+<br>
+ QString backtrace() const;<br>
+ void setBacktrace(QString backtrace);<br>
private:<br>
int m_index;<br>
ApiTraceCallSignature *m_signature;<br>
@@ -291,6 +294,8 @@ private:<br>
<br>
QString m_error;<br>
<br>
+ QString m_backtrace;<br>
+<br>
mutable QString m_richText;<br>
mutable QString m_searchText;<br>
};<br>
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp<br>
index 8905fbb..e46fce6 100644<br>
--- a/gui/mainwindow.cpp<br>
+++ b/gui/mainwindow.cpp<br>
@@ -121,6 +121,13 @@ void MainWindow::callItemSelected(const QModelIndex &index)<br>
<br>
if (event && event->type() == ApiTraceEvent::Call) {<br>
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);<br>
+ if (!call->backtrace().isNull()) {<br>
+ m_ui.backtraceBrowser->setText(call->backtrace());<br>
+ m_ui.backtraceDock->show();<br>
+ }<br>
+ else {<br>
+ m_ui.backtraceDock->hide();<br>
+ }<br>
m_ui.detailsDock->setWindowTitle(<br>
tr("Details View. Frame %1, Call %2")<br>
.arg(call->parentFrame() ? call->parentFrame()->number : 0)<br>
@@ -159,6 +166,7 @@ void MainWindow::callItemSelected(const QModelIndex &index)<br>
m_selectedEvent = 0;<br>
}<br>
m_ui.detailsDock->hide();<br>
+ m_ui.backtraceDock->hide();<br>
m_ui.vertexDataDock->hide();<br>
}<br>
if (m_selectedEvent && m_selectedEvent->hasState()) {<br>
@@ -762,6 +770,7 @@ void MainWindow::initObjects()<br>
m_argsEditor = new ArgumentsEditor(this);<br>
<br>
m_ui.detailsDock->hide();<br>
+ m_ui.backtraceDock->hide();<br>
m_ui.errorsDock->hide();<br>
m_ui.vertexDataDock->hide();<br>
m_ui.stateDock->hide();<br>
@@ -769,6 +778,7 @@ void MainWindow::initObjects()<br>
<br>
tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);<br>
tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);<br>
+ tabifyDockWidget(m_ui.detailsDock, m_ui.backtraceDock);<br>
<br>
m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);<br>
<br>
diff --git a/gui/ui/mainwindow.ui b/gui/ui/mainwindow.ui<br>
index 06f4503..75b345a 100644<br>
--- a/gui/ui/mainwindow.ui<br>
+++ b/gui/ui/mainwindow.ui<br>
@@ -432,6 +432,9 @@<br>
<layout class="QVBoxLayout" name="verticalLayout_4"><br>
<item><br>
<widget class="QTreeWidget" name="errorsTreeWidget"><br>
+ <property name="layoutDirection"><br>
+ <enum>Qt::LeftToRight</enum><br>
+ </property><br>
<column><br>
<property name="text"><br>
<string>Index</string><br>
@@ -452,6 +455,21 @@<br>
</layout><br>
</widget><br>
</widget><br>
+ <widget class="QDockWidget" name="backtraceDock"><br>
+ <property name="windowTitle"><br>
+ <string>Backtrace</string><br>
+ </property><br>
+ <attribute name="dockWidgetArea"><br>
+ <number>8</number><br>
+ </attribute><br>
+ <widget class="QWidget" name="dockWidgetContents"><br>
+ <layout class="QHBoxLayout" name="horizontalLayout"><br>
+ <item><br>
+ <widget class="QTextBrowser" name="backtraceBrowser"/><br>
+ </item><br>
+ </layout><br>
+ </widget><br>
+ </widget><br>
<action name="actionExit"><br>
<property name="text"><br>
<string>Exit</string><br>
@@ -636,6 +654,7 @@<br>
<zorder>stateDock</zorder><br>
<zorder>vertexDataDock</zorder><br>
<zorder>errorsDock</zorder><br>
+ <zorder>backtraceDock</zorder><br>
</widget><br>
<customwidgets><br>
<customwidget><br>
diff --git a/wrappers/gltrace.py b/wrappers/gltrace.py<br>
index c40fbb3..fcb48bb 100644<br>
--- a/wrappers/gltrace.py<br>
+++ b/wrappers/gltrace.py<br>
@@ -520,7 +520,7 @@ class GlTracer(Tracer):<br>
<br>
# Emit a fake function<br>
print ' {'<br>
- print ' static const trace::FunctionSig &_sig = %s ? _glEnableClientState_sig : _glDisableClientState_sig;' % flag_name<br>
+ print ' static trace::FunctionSig &_sig = %s ? _glEnableClientState_sig : _glDisableClientState_sig;' % flag_name<br>
print ' unsigned _call = trace::localWriter.beginEnter(&_sig);'<br>
print ' trace::localWriter.beginArg(0);'<br>
self.serializeValue(glapi.GLenum, enable_name)<br>
diff --git a/wrappers/trace.py b/wrappers/trace.py<br>
index d9c2900..9ff5410 100644<br>
--- a/wrappers/trace.py<br>
+++ b/wrappers/trace.py<br>
@@ -452,7 +452,7 @@ class Tracer:<br>
print 'static const char * _%s_args[%u] = {%s};' % (<a href="http://function.name" target="_blank">function.name</a>, len(function.args), ', '.join(['"%s"' % <a href="http://arg.name" target="_blank">arg.name</a> for arg in function.args]))<br>
else:<br>
print 'static const char ** _%s_args = NULL;' % (<a href="http://function.name" target="_blank">function.name</a>,)<br>
- print 'static const trace::FunctionSig _%s_sig = {%u, "%s", %u, _%s_args};' % (<a href="http://function.name" target="_blank">function.name</a>, self.getFunctionSigId(), <a href="http://function.name" target="_blank">function.name</a>, len(function.args), <a href="http://function.name" target="_blank">function.name</a>)<br>
+ print 'static trace::FunctionSig _%s_sig = {%u, "%s", %u, _%s_args};' % (<a href="http://function.name" target="_blank">function.name</a>, self.getFunctionSigId(), <a href="http://function.name" target="_blank">function.name</a>, len(function.args), <a href="http://function.name" target="_blank">function.name</a>)<br>
print<br>
<br>
def getFunctionSigId(self):<br>
@@ -685,7 +685,7 @@ class Tracer:<br>
assert not method.internal<br>
<br>
print ' static const char * _args[%u] = {%s};' % (len(method.args) + 1, ', '.join(['"this"'] + ['"%s"' % <a href="http://arg.name" target="_blank">arg.name</a> for arg in method.args]))<br>
- print ' static const trace::FunctionSig _sig = {%u, "%s", %u, _args};' % (self.getFunctionSigId(), <a href="http://interface.name" target="_blank">interface.name</a> + '::' + <a href="http://method.name" target="_blank">method.name</a>, len(method.args) + 1)<br>
+ print ' static trace::FunctionSig _sig = {%u, "%s", %u, _args};' % (self.getFunctionSigId(), <a href="http://interface.name" target="_blank">interface.name</a> + '::' + <a href="http://method.name" target="_blank">method.name</a>, len(method.args) + 1)<br>
<br>
print ' %s *_this = static_cast<%s *>(m_pInstance);' % (base, base)<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
1.7.9.5<br>
<br>
_______________________________________________<br>
apitrace mailing list<br>
<a href="mailto:apitrace@lists.freedesktop.org">apitrace@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/apitrace" target="_blank">http://lists.freedesktop.org/mailman/listinfo/apitrace</a><br>
</font></span></blockquote></div><br></div>