<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>