[PATCH v3] Backtrace via call detail

Eugene Velesevich evel at ispras.ru
Tue Apr 16 03:20:29 PDT 2013


Hello,

Changes from v2: Now backtrace is parsed and is recorded as a call detail.

This patch implements backtrace recording during tracing, and adds support in
'apitrace dump' and QApitrace. Backtrace is obtained via platform-specific
functions (and, internally, in platform-specific format). Then it is parsed to
produce an std::vector of stack frame structs: { char *module, *function,
*filename, *linenumber, *offset } (some fields may be NULL) and is written
into the trace file in the Enter call section as an Array with specific call
detail flag previously emitted.

A platform-dependent mechanism is provided to specify a set of traced
calls for which backtraces will be recorded. It is possible to specify
either function names, or prefixes of names by appending a '*' (e.g.
"glUniform*").

On Android the backtrace is retrieved from Dalvik via libdvm functions
imported at runtime.
Function set is specified in /data/apitrace.fnames, one per line.

On Linux the backtrace is retrieved via glibc backtrace(), and will not always
yield filename:linenumber information.
Function set is specified via APITRACE_BT_FUNCTIONS environment variable.

On other platforms, obtaining a backtrace is not implemented by this patch.

---
 CMakeLists.txt                |    1 +
 common/trace_backtrace.cpp    |  447 +++++++++++++++++++++++++++++++++++++++++
 common/trace_backtrace.hpp    |   75 +++++++
 common/trace_dump.cpp         |   33 +++
 common/trace_format.hpp       |    6 +-
 common/trace_model.hpp        |    9 +-
 common/trace_parser.cpp       |    8 +-
 common/trace_writer.cpp       |   27 ++-
 common/trace_writer.hpp       |    9 +-
 common/trace_writer_local.cpp |   21 +-
 common/trace_writer_local.hpp |   12 +-
 common/trace_writer_model.cpp |    5 +
 gui/apitracecall.cpp          |   41 ++++
 gui/apitracecall.h            |    5 +
 gui/mainwindow.cpp            |   10 +
 gui/ui/mainwindow.ui          |   19 ++
 wrappers/gltrace.py           |    2 +-
 wrappers/trace.py             |    4 +-
 18 files changed, 710 insertions(+), 24 deletions(-)
 create mode 100644 common/trace_backtrace.cpp
 create mode 100644 common/trace_backtrace.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9394282..6ac09ee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -314,6 +314,7 @@ add_library (common STATIC
     common/trace_profiler.cpp
     common/trace_option.cpp
     common/${os}
+    common/trace_backtrace.cpp
 )
 
 set_target_properties (common PROPERTIES
diff --git a/common/trace_backtrace.cpp b/common/trace_backtrace.cpp
new file mode 100644
index 0000000..23f96f6
--- /dev/null
+++ b/common/trace_backtrace.cpp
@@ -0,0 +1,447 @@
+/**************************************************************************
+ *
+ * Copyright 2013 Samsung
+ * Contributed by Eugene Velesevich
+ * 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.
+ *
+ **************************************************************************/
+
+/*
+ *  Wrapper for platform-specific code for obtaining symbolic backtraces
+ *  on Android and Linux
+ */
+
+
+
+#include "trace_backtrace.hpp"
+
+#if defined(ANDROID) or defined(__linux__)
+
+#include <set>
+#include "os.hpp"
+
+
+namespace trace {
+
+/*
+ * Pascal string (with zero terminator optionally omitted)
+ * This is a helper class for storing a set of exact strings or prefixes
+ * to match a zero-terminated string against later.
+ * Two zero-terminated pstrings compare equal iff they are the same.
+ * Otherwise, they compare equal iff one is a prefix of the other
+ * (a zero-terminated pstring cannot be a prefix)
+ */
+
+struct pstring {
+    const char* s;
+    int n;
+    pstring(const char* s, int n)
+    {
+        this->s = s;
+        this->n = n;
+    }
+    bool operator<(const pstring q2) const
+    {
+        return memcmp(s, q2.s, n < q2.n? n : q2.n) < 0;
+    }
+};
+
+
+#define PREFIX_BUF_SIZE (PREFIX_MAX_FUNC_NAME * MAX_BT_FUNC)
+
+class StringPrefixes {
+private:
+    std::set<pstring> pset;
+    char* buf;
+private:
+    void addPrefix(char* startbuf, int n) {
+        std::set<pstring>::iterator elem = pset.find(pstring(startbuf, n));
+        bool replace = elem != pset.end() && n < elem->n;
+        if (replace) {
+            pset.erase(elem);
+        }
+        if (replace || elem == pset.end()) {
+            pset.insert(pstring(startbuf, n));
+        }
+    }
+public:
+    StringPrefixes(const char* source);
+
+    bool contain(const char* s) {
+        if (pset.find(pstring(s, strlen(s) + 1)) != pset.end()) {
+            os::log("Backtrace for %s is enabled", s);
+            return true;
+        }
+        return false;
+    }
+};
+
+bool backtrace_is_needed(const char* fname) {
+    static StringPrefixes backtraceFunctionNamePrefixes(APITRACE_FNAMES_SOURCE);
+    return backtraceFunctionNamePrefixes.contain(fname);
+}
+
+} /* namespace trace */
+
+#ifdef ANDROID
+
+#include <dlfcn.h>
+#include "os.hpp"
+#include <vector>
+
+namespace trace {
+
+StringPrefixes::StringPrefixes(const char* source) {
+    buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);
+    char* startbuf = buf;
+    int n = 0;
+    FILE* f = fopen(source, "r");
+    if (f == NULL) {
+        os::log("Cannot open " APITRACE_FNAMES_FILE);
+    }
+    while ((startbuf = fgets(startbuf, PREFIX_MAX_FUNC_NAME, f))) {
+        n = strlen(startbuf);
+        if (startbuf[n - 1] == '\n') {
+            n--;
+        }
+        if (n > 2 && startbuf[0] != '#') {
+            int psize;
+            if (startbuf[n - 1] != '*') {
+                startbuf[n] = '\0';
+                psize = n + 1;
+            }
+            else {
+                psize = n - 1;
+            }
+            addPrefix(startbuf, psize);
+            startbuf += n + 1;
+            n = 0;
+        }
+    }
+    fclose(f);
+}
+
+
+/* The following two declarations are copied from Android sources */
+
+enum DebugTargetKind {
+    kDebugTargetUnknown = 0,
+    kDebugTargetLog,
+    kDebugTargetFile,
+};
+
+struct DebugOutputTarget {
+    DebugTargetKind which;
+
+    union {
+        struct {
+            int priority;
+            const char* tag;
+        } log;
+        struct {
+            FILE* fp;
+        } file;
+    } data;
+};
+
+#define THREAD_SELF_NAME "_Z13dvmThreadSelfv"
+#define CREATE_DEBUG_TARGET_NAME "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE"
+#define DUMP_BACKTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"
+
+
+class DalvikBacktraceProvider {
+private:
+    bool errorOccured;
+    void* (*threadself)(void);
+    FILE* streamInMemory;
+    char* buf;
+    size_t bufSize;
+    void (*dumpBacktrace)(const DebugOutputTarget*, void*);
+    DebugOutputTarget debugTarget;
+public:
+    DalvikBacktraceProvider() {
+        FILE* (*open_memstream_exp)(char**, size_t*);
+        void (*createDebugTarget)(DebugOutputTarget*, FILE*);
+        void* handle = dlopen("/system/lib/libdvm.so", 0);
+        errorOccured = true;
+        if (!handle) {
+            os::log("dlopen failed\n");
+            return;
+        }
+        threadself = (void* (*)())dlsym(handle, THREAD_SELF_NAME);
+        if (threadself == NULL) {
+            os::log("dlsym ThreadSelf failed\n");
+            return;
+        }
+        createDebugTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, CREATE_DEBUG_TARGET_NAME);
+        if (createDebugTarget == NULL) {
+            os::log("dlsym CreateFileOutput failed\n");
+            return;
+        }
+        dumpBacktrace = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, DUMP_BACKTRACE_NAME);
+        if (dumpBacktrace == NULL) {
+            os::log("dlsym DumpThreadStack failed\n");
+            return;
+        }
+        void* handle2 = dlopen("/system/lib/libcutils.so", 0);
+        if (!handle2) {
+            os::log("dlopen failed\n");
+            return;
+        }
+        open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");
+        if (open_memstream_exp == NULL) {
+            os::log("dlsym open_memstream failed\n");
+            return;
+        }
+        streamInMemory = open_memstream_exp(&buf, &bufSize);
+        if (streamInMemory == NULL) {
+            os::log("open_memstream failed\n");
+            return;
+        }
+        createDebugTarget(&debugTarget, streamInMemory);
+        errorOccured = false;
+    }
+
+    inline char* getBacktrace() {
+        if (errorOccured) {
+            return NULL;
+        }
+        rewind(streamInMemory);
+        dumpBacktrace(&debugTarget, threadself());
+        fflush(streamInMemory);
+        return buf;
+    }
+/*
+ * Parse a stack frame, expecting:
+ * "  at android.view.HardwareRenderer$GlRenderer.initializeEgl(HardwareRenderer.java:547)"
+ * or
+ * "  at android.view.HardwareRenderer$GlRenderer.initializeEgl(Native Method)"
+ */
+    std::vector<StackFrame> parseBacktrace(char *rawBacktrace) {
+        std::vector<StackFrame> parsedBacktrace;
+        char* rawBacktrace_it = rawBacktrace;
+        while (*rawBacktrace_it != '\0') {
+            StackFrame stackFrame;
+            /* skip leading space */
+            while (*rawBacktrace_it == ' ') {
+                rawBacktrace_it++;
+            }
+            /* Skip "at " */
+            rawBacktrace_it += 3;
+            stackFrame.function = rawBacktrace_it;
+            while (*rawBacktrace_it != '(') {
+                rawBacktrace_it++;
+            }
+            *rawBacktrace_it = '\0';
+            stackFrame.filename = rawBacktrace_it + 1;
+            while (*rawBacktrace_it != ':' && *rawBacktrace_it != ')') {
+                rawBacktrace_it++;
+            }
+            if (*rawBacktrace_it == ':') {
+                stackFrame.linenumber = rawBacktrace_it + 1;
+                *rawBacktrace_it = '\0';
+                while (*rawBacktrace_it != ')') {
+                    rawBacktrace_it++;
+                }
+                *rawBacktrace_it = '\0';
+                rawBacktrace_it++;
+            }
+            else {
+                stackFrame.filename = NULL;
+                while (*rawBacktrace_it != '\n') {
+                    rawBacktrace_it++;
+                }
+            }
+            while (*rawBacktrace_it == '\n' || *rawBacktrace_it == ' ') {
+                    rawBacktrace_it++;
+            }
+            parsedBacktrace.push_back(stackFrame); /* module */
+        }
+        return parsedBacktrace;
+    }
+};
+
+std::vector<StackFrame> get_backtrace() {
+    static DalvikBacktraceProvider backtraceProvider;
+    return backtraceProvider.parseBacktrace(backtraceProvider.getBacktrace());
+}
+
+/* end ANDROID */
+#elif defined __linux__
+
+#include <execinfo.h>
+#include <string.h>
+#include <stdlib.h>
+#include <map>
+#include <vector>
+#include <stdio.h>
+
+namespace trace {
+
+
+StringPrefixes::StringPrefixes(const char* source) {
+    buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);
+    char* startbuf = buf;
+    int n = 0;
+    char* s = getenv(source);
+    char end = ';';
+    if (s == NULL) {
+        return;
+    }
+    *buf = ';';
+    strncpy(buf + 1, s, PREFIX_BUF_SIZE - 2);
+    while (end != '\0') {
+        startbuf++;
+        while (*(startbuf + n) != ';' && *(startbuf + n) != '\0') {
+            n++;
+        }
+        end = startbuf[n];
+        if (n > 2 && startbuf[0] != '#') {
+            int psize;
+            if (startbuf[n - 1] != '*') {
+                startbuf[n] = '\0';
+                psize = n + 1;
+            }
+            else {
+                psize = n - 1;
+            }
+            addPrefix(startbuf, psize);
+            startbuf += n;
+            n = 0;
+        }
+    }
+}
+
+
+#define BT_DEPTH 10
+
+class GlibcBacktraceProvider {
+private:
+    std::map<void*, StackFrame*> cache;
+    /*
+     * Backtrace being returned by glibc backtrace() contains stack frames
+     * belonging to apitrace wrapper module. We count the number of apitrace
+     * functions on the stack to avoid recording these frames.
+     */
+    int numOfNestedFunctions;
+private:
+/*
+ * Parse a stack frame, expecting:
+ * /lib/libc.so.6.1(__libc_start_main+0x50308) [0x2000000000097630]
+ * or
+ * /lib/libc.so.6.1(+0x50308) [0x2000000000097630]
+ * or
+ * /lib/libc.so.6.1() [0x2000000000097630]
+ */
+    StackFrame* parseFrame(void* frame, char* frame_symbol) {
+        if (cache.find(frame) == cache.end()) {
+            char* frame_symbol_copy = new char[strlen(frame_symbol) + 1];
+            strcpy(frame_symbol_copy, frame_symbol);
+            StackFrame* parsedFrame = new StackFrame;
+            char* frame_it = frame_symbol_copy;
+            parsedFrame->module = frame_it;
+            while (true) {
+                switch (*frame_it) {
+                case '(':
+                    *frame_it = '\0';
+                    frame_it++;
+                    if (*frame_it != ')' && *frame_it != '+') {
+                        parsedFrame->function = frame_it;
+                        while (*frame_it != '+' && *frame_it != ')') {
+                            frame_it++;
+                        }
+                        *frame_it = '\0';
+                        frame_it++;
+                    }
+                    break;
+                case '[':
+                    *frame_it = '\0';
+                    frame_it++;
+                    parsedFrame->offset = frame_it;
+                    break;
+                case ']':
+                    *frame_it = '\0';
+                    cache[frame] = parsedFrame;
+                    return parsedFrame;
+                case '\0':
+                    cache[frame] = NULL;
+                    delete[] frame_symbol_copy;
+                    delete[] parsedFrame;
+                    return NULL;
+                default:
+                    frame_it++;
+                }
+            }
+        }
+        else {
+            return cache[frame];
+        }
+    }
+public:
+    GlibcBacktraceProvider() :
+      numOfNestedFunctions(0) {
+    }
+    std::vector<StackFrame> getParsedBacktrace() {
+        std::vector<StackFrame> parsedBacktrace;
+        void *array[numOfNestedFunctions + BT_DEPTH];
+        size_t size;
+        char **strings;
+        size_t i;
+        const char* firstModule;
+        size = backtrace(array, numOfNestedFunctions + BT_DEPTH);
+        strings = backtrace_symbols(array, size);
+        for (i = numOfNestedFunctions; i < size; i++) {
+            StackFrame* parsedFrame = parseFrame(array[i], strings[i]);
+            if (numOfNestedFunctions == 0) {
+                if (i == 0) {
+                    firstModule = parsedFrame->module;
+                }
+                else {
+                    if (strcmp(firstModule, parsedFrame->module)) {
+                        numOfNestedFunctions = i;
+                        free(strings);
+                        parsedBacktrace = getParsedBacktrace();
+                        numOfNestedFunctions--;
+                        return parsedBacktrace;
+                    }
+                }
+            } else {
+                if (parsedFrame != NULL) {
+                    parsedBacktrace.push_back(*parsedFrame);
+                }
+            }
+        }
+        free(strings);
+        return parsedBacktrace;
+    }
+};
+
+
+std::vector<StackFrame> get_backtrace() {
+    static GlibcBacktraceProvider backtraceProvider;
+    return backtraceProvider.getParsedBacktrace();
+}
+
+#endif /* LINUX */
+
+} /* namespace trace */
+
+#endif /* ANDROID or LINUX */
diff --git a/common/trace_backtrace.hpp b/common/trace_backtrace.hpp
new file mode 100644
index 0000000..4fef75e
--- /dev/null
+++ b/common/trace_backtrace.hpp
@@ -0,0 +1,75 @@
+#ifndef _TRACE_BACKTRACE_HPP_
+#define _TRACE_BACKTRACE_HPP_
+
+#include <vector>
+
+#include "trace_model.hpp"
+
+namespace trace {
+
+
+struct StackFrame {
+    char* module;
+    char* function;
+    char* filename;
+    char* linenumber;
+    char* offset;
+    StackFrame() :
+      module(0),
+      function(0),
+      filename(0),
+      linenumber(0),
+      offset(0)
+    {
+    }
+};
+
+static const char* stackframe_members[5] = {
+    "module",
+    "function",
+    "filename",
+    "linenumber",
+    "offset"
+};
+
+static const trace::StructSig stackframe_sig = {
+    2, "backtrace", 5, stackframe_members
+};
+
+
+#if defined(ANDROID) or defined(__linux__)
+
+std::vector<StackFrame> get_backtrace();
+bool backtrace_is_needed(const char* fname);
+
+#if defined(ANDROID)
+
+#define MAX_BT_FUNC 20
+#define PREFIX_MAX_FUNC_NAME 100
+#define APITRACE_FNAMES_FILE "/data/apitrace.fnames"
+#define APITRACE_FNAMES_SOURCE APITRACE_FNAMES_FILE
+
+#elif defined(__linux__)
+
+#define MAX_BT_FUNC 20
+#define PREFIX_MAX_FUNC_NAME 100
+#define APITRACE_FNAMES_ENV "APITRACE_BT_FUNCTIONS"
+#define APITRACE_FNAMES_SOURCE APITRACE_FNAMES_ENV
+
+#endif
+
+#else /* !__linux__ && !ANDROID */
+
+static inline std::vector<StackFrame> get_backtrace() {
+    return std::vector<StackFrame>();
+}
+
+static inline bool backtrace_is_needed(const char*) {
+    return false;
+}
+
+#endif
+
+} /* namespace trace */
+
+#endif
diff --git a/common/trace_dump.cpp b/common/trace_dump.cpp
index e6810b6..ff8bb2b 100644
--- a/common/trace_dump.cpp
+++ b/common/trace_dump.cpp
@@ -228,6 +228,35 @@ public:
         _visit(r->humanValue);
     }
 
+    void visitBacktrace(std::vector<Value*> backtrace) {
+        for (int i = 0; i < backtrace.size(); i ++) {
+            Struct* frame = dynamic_cast<Struct*>(backtrace[i]);
+            const char* tmp = frame->members[0]->toString(); /* module */
+            if (tmp != NULL) {
+                os << tmp << " ";
+            }
+            tmp = frame->members[1]->toString(); /* function */
+            if (tmp != NULL) {
+                os << "at " << tmp << "() ";
+            }
+            tmp = frame->members[2]->toString(); /* file */
+            if (tmp != NULL) {
+                os << "at " << tmp;
+                tmp = frame->members[3]->toString(); /* line */
+                if (tmp != NULL) {
+                    os << ":" << tmp << " ";
+                }
+            }
+            else {
+                tmp = frame->members[4]->toString(); /* offset */
+                if (tmp != NULL) {
+                    os << "[" << tmp << "]";
+                }
+            }
+            os << "\n";
+        }
+    }
+
     void visit(Call *call) {
         CallFlags callFlags = call->flags;
         
@@ -271,6 +300,10 @@ public:
         
         os << "\n";
 
+        if (call->backtrace != NULL) {
+            os << bold << red << "Backtrace:\n" << normal;
+            visitBacktrace(call->backtrace->values);
+        }
         if (callFlags & CALL_FLAG_END_FRAME) {
             os << "\n";
         }
diff --git a/common/trace_format.hpp b/common/trace_format.hpp
index d5fd81b..0bb8ee0 100644
--- a/common/trace_format.hpp
+++ b/common/trace_format.hpp
@@ -69,8 +69,11 @@ namespace trace {
  *
  * - version 4:
  *   - call enter events include thread ID
+ *
+ * - version 5:
+ *   - new call detail flag CALL_BACKTRACE
  */
-#define TRACE_VERSION 4
+#define TRACE_VERSION 5
 
 
 /*
@@ -127,6 +130,7 @@ enum CallDetail {
     CALL_ARG,
     CALL_RET,
     CALL_THREAD,
+    CALL_BACKTRACE,
 };
 
 enum Type {
diff --git a/common/trace_model.hpp b/common/trace_model.hpp
index 9558225..b47b692 100644
--- a/common/trace_model.hpp
+++ b/common/trace_model.hpp
@@ -49,6 +49,7 @@ struct FunctionSig {
     const char *name;
     unsigned num_args;
     const char **arg_names;
+    bool backtrace;
 };
 
 
@@ -463,18 +464,20 @@ class Call
 public:
     unsigned thread_id;
     unsigned no;
-    const FunctionSig *sig;
+    FunctionSig *sig;
     std::vector<Arg> args;
     Value *ret;
 
     CallFlags flags;
+    Array* backtrace;
 
-    Call(const FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :
+    Call(FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :
         thread_id(_thread_id), 
         sig(_sig), 
         args(_sig->num_args), 
         ret(0),
-        flags(_flags) {
+        flags(_flags),
+        backtrace(0) {
     }
 
     ~Call();
diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp
index c70517e..f699353 100644
--- a/common/trace_parser.cpp
+++ b/common/trace_parser.cpp
@@ -463,7 +463,7 @@ Call *Parser::parse_leave(Mode mode) {
          * 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};
+        FunctionSig sig = {0, NULL, 0, NULL};
         call = new Call(&sig, 0, 0);
         parse_call_details(call, SCAN);
         delete call;
@@ -500,6 +500,12 @@ bool Parser::parse_call_details(Call *call, Mode mode) {
 #endif
             call->ret = parse_value(mode);
             break;
+        case trace::CALL_BACKTRACE:
+#if TRACE_VERBOSE
+            std::cerr << "\tCALL_BACKTRACE\n";
+#endif
+            call->backtrace = (Array*)parse_value(mode);
+            break;
         default:
             std::cerr << "error: ("<<call->name()<< ") unknown call detail "
                       << c << "\n";
diff --git a/common/trace_writer.cpp b/common/trace_writer.cpp
index d69e93f..b041d44 100644
--- a/common/trace_writer.cpp
+++ b/common/trace_writer.cpp
@@ -29,12 +29,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <vector>
 
 #include "os.hpp"
 #include "trace_file.hpp"
 #include "trace_writer.hpp"
 #include "trace_format.hpp"
-
+#include "trace_backtrace.hpp"
 
 namespace trace {
 
@@ -134,11 +135,33 @@ inline bool lookup(std::vector<bool> &map, size_t index) {
     }
 }
 
-unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) {
+void Writer::writeBacktrace(std::vector<StackFrame> backtrace) {
+
+    beginArray(backtrace.size());
+    for (int i = 0; i < backtrace.size(); i++) {
+        beginElement();
+        beginStruct(&stackframe_sig);
+        writeString(backtrace[i].module);
+        writeString(backtrace[i].function);
+        writeString(backtrace[i].filename);
+        writeString(backtrace[i].linenumber);
+        writeString(backtrace[i].offset);
+        endStruct();
+        endElement();
+    }
+    endArray();
+}
+
+void Writer::beginBacktrace(void ) {
+    _writeByte(trace::CALL_BACKTRACE);
+}
+
+unsigned Writer::beginEnter(FunctionSig *sig, unsigned thread_id) {
     _writeByte(trace::EVENT_ENTER);
     _writeUInt(thread_id);
     _writeUInt(sig->id);
     if (!lookup(functions, sig->id)) {
+        sig->backtrace = backtrace_is_needed(sig->name);
         _writeString(sig->name);
         _writeUInt(sig->num_args);
         for (unsigned i = 0; i < sig->num_args; ++i) {
diff --git a/common/trace_writer.hpp b/common/trace_writer.hpp
index a46b43e..4070052 100644
--- a/common/trace_writer.hpp
+++ b/common/trace_writer.hpp
@@ -36,7 +36,7 @@
 #include <vector>
 
 #include "trace_model.hpp"
-
+#include "trace_backtrace.hpp"
 
 namespace trace {
     class File;
@@ -58,7 +58,12 @@ namespace trace {
         bool open(const char *filename);
         void close(void);
 
-        unsigned beginEnter(const FunctionSig *sig, unsigned thread_id);
+        void writeBacktrace(std::vector<StackFrame> backtrace);
+        void beginBacktrace(void);
+        inline void endBacktrace(void) {
+        }
+
+        unsigned beginEnter(FunctionSig *sig, unsigned thread_id);
         void endEnter(void);
 
         void beginLeave(unsigned call);
diff --git a/common/trace_writer_local.cpp b/common/trace_writer_local.cpp
index feb5c91..760861d 100644
--- a/common/trace_writer_local.cpp
+++ b/common/trace_writer_local.cpp
@@ -36,22 +36,23 @@
 #include "trace_file.hpp"
 #include "trace_writer_local.hpp"
 #include "trace_format.hpp"
+#include "trace_backtrace.hpp"
 
 
 namespace trace {
 
 
 static const char *memcpy_args[3] = {"dest", "src", "n"};
-const FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args};
+FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args, false};
 
 static const char *malloc_args[1] = {"size"};
-const FunctionSig malloc_sig = {1, "malloc", 1, malloc_args};
+FunctionSig malloc_sig = {1, "malloc", 1, malloc_args, false};
 
 static const char *free_args[1] = {"ptr"};
-const FunctionSig free_sig = {2, "free", 1, free_args};
+FunctionSig free_sig = {2, "free", 1, free_args, false};
 
 static const char *realloc_args[2] = {"ptr", "size"};
-const FunctionSig realloc_sig = {3, "realloc", 2, realloc_args};
+FunctionSig realloc_sig = {3, "realloc", 2, realloc_args, false};
 
 
 static void exceptionCallback(void)
@@ -131,7 +132,7 @@ LocalWriter::open(void) {
 static unsigned next_thread_num = 1;
 static thread_specific unsigned thread_num = 0;
 
-unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
+unsigned LocalWriter::beginEnter(FunctionSig *sig) {
     mutex.lock();
     ++acquired;
 
@@ -146,7 +147,15 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
 
     assert(thread_num > 0);
     unsigned thread_id = thread_num - 1;
-    return Writer::beginEnter(sig, thread_id);
+
+    unsigned call_no = Writer::beginEnter(sig, thread_id);
+    if (sig->backtrace) {
+        std::vector<StackFrame> backtrace = get_backtrace();
+        beginBacktrace();
+        writeBacktrace(backtrace);
+        endBacktrace();
+    }
+    return call_no;
 }
 
 void LocalWriter::endEnter(void) {
diff --git a/common/trace_writer_local.hpp b/common/trace_writer_local.hpp
index cc5dda0..1da2250 100644
--- a/common/trace_writer_local.hpp
+++ b/common/trace_writer_local.hpp
@@ -39,10 +39,10 @@
 
 namespace trace {
 
-    extern const FunctionSig memcpy_sig;
-    extern const FunctionSig malloc_sig;
-    extern const FunctionSig free_sig;
-    extern const FunctionSig realloc_sig;
+    extern FunctionSig memcpy_sig;
+    extern FunctionSig malloc_sig;
+    extern FunctionSig free_sig;
+    extern FunctionSig realloc_sig;
 
     /**
      * A specialized Writer class, mean to trace the current process.
@@ -83,8 +83,8 @@ namespace trace {
         /**
          * It will acquire the mutex.
          */
-        unsigned beginEnter(const FunctionSig *sig);
-        
+        unsigned beginEnter(FunctionSig *sig);
+
         /**
          * It will release the mutex.
          */
diff --git a/common/trace_writer_model.cpp b/common/trace_writer_model.cpp
index 2e858a2..f369172 100644
--- a/common/trace_writer_model.cpp
+++ b/common/trace_writer_model.cpp
@@ -109,6 +109,11 @@ public:
 
     void visit(Call *call) {
         unsigned call_no = writer.beginEnter(call->sig, call->thread_id);
+        if (call->backtrace != NULL) {
+            writer.beginBacktrace();
+            _visit(call->backtrace);
+            writer.endBacktrace();
+        }
         for (unsigned i = 0; i < call->args.size(); ++i) {
             if (call->args[i].value) {
                 writer.beginArg(i);
diff --git a/gui/apitracecall.cpp b/gui/apitracecall.cpp
index 3377486..cc2826f 100644
--- a/gui/apitracecall.cpp
+++ b/gui/apitracecall.cpp
@@ -685,6 +685,37 @@ ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,
     }
     m_argValues.squeeze();
     m_flags = call->flags;
+    if (call->backtrace != NULL) {
+        std::vector<trace::Value*> backtrace = call->backtrace->values;
+        QString qbacktrace;
+        for (int i = 0; i < backtrace.size(); i++) {
+            trace::Struct* frame = dynamic_cast<trace::Struct*>(backtrace[i]);
+            const char* tmp = frame->members[0]->toString(); /* module */
+            if (tmp != NULL) {
+                qbacktrace += QString("%1 ").arg(tmp);
+            }
+            tmp = frame->members[1]->toString(); /* function */
+            if (tmp != NULL) {
+                qbacktrace += QString("at %1() ").arg(tmp);
+            }
+            tmp = frame->members[2]->toString(); /* file */
+            if (tmp != NULL) {
+                qbacktrace += QString("at %1").arg(tmp);
+                tmp = frame->members[3]->toString(); /* line */
+                if (tmp != NULL) {
+                    qbacktrace += QString(":%1 ").arg(tmp);
+                }
+            }
+            else {
+                tmp = frame->members[4]->toString(); /* offset */
+                if (tmp != NULL) {
+                    qbacktrace += QString("[%1]").arg(tmp);
+                }
+            }
+            qbacktrace += "\n";
+        }
+        this->setBacktrace(qbacktrace);
+    }
 }
 
 ApiTraceCall::~ApiTraceCall()
@@ -820,6 +851,16 @@ int ApiTraceCall::binaryDataIndex() const
     return m_binaryDataIndex;
 }
 
+QString ApiTraceCall::backtrace() const
+{
+    return m_backtrace;
+}
+
+void ApiTraceCall::setBacktrace(QString backtrace)
+{
+    m_backtrace = backtrace;
+}
+
 QStaticText ApiTraceCall::staticText() const
 {
     if (m_staticText && !m_staticText->text().isEmpty())
diff --git a/gui/apitracecall.h b/gui/apitracecall.h
index 6c6b607..8004ced 100644
--- a/gui/apitracecall.h
+++ b/gui/apitracecall.h
@@ -279,6 +279,9 @@ public:
     int numChildren() const;
     bool hasBinaryData() const;
     int binaryDataIndex() const;
+
+    QString backtrace() const;
+    void setBacktrace(QString backtrace);
 private:
     int m_index;
     ApiTraceCallSignature *m_signature;
@@ -291,6 +294,8 @@ private:
 
     QString m_error;
 
+    QString m_backtrace;
+
     mutable QString m_richText;
     mutable QString m_searchText;
 };
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp
index 8905fbb..e46fce6 100644
--- a/gui/mainwindow.cpp
+++ b/gui/mainwindow.cpp
@@ -121,6 +121,13 @@ void MainWindow::callItemSelected(const QModelIndex &index)
 
     if (event && event->type() == ApiTraceEvent::Call) {
         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
+        if (!call->backtrace().isNull()) {
+            m_ui.backtraceBrowser->setText(call->backtrace());
+            m_ui.backtraceDock->show();
+        }
+        else {
+            m_ui.backtraceDock->hide();
+        }
         m_ui.detailsDock->setWindowTitle(
             tr("Details View. Frame %1, Call %2")
             .arg(call->parentFrame() ? call->parentFrame()->number : 0)
@@ -159,6 +166,7 @@ void MainWindow::callItemSelected(const QModelIndex &index)
             m_selectedEvent = 0;
         }
         m_ui.detailsDock->hide();
+        m_ui.backtraceDock->hide();
         m_ui.vertexDataDock->hide();
     }
     if (m_selectedEvent && m_selectedEvent->hasState()) {
@@ -762,6 +770,7 @@ void MainWindow::initObjects()
     m_argsEditor = new ArgumentsEditor(this);
 
     m_ui.detailsDock->hide();
+    m_ui.backtraceDock->hide();
     m_ui.errorsDock->hide();
     m_ui.vertexDataDock->hide();
     m_ui.stateDock->hide();
@@ -769,6 +778,7 @@ void MainWindow::initObjects()
 
     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
     tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
+    tabifyDockWidget(m_ui.detailsDock, m_ui.backtraceDock);
 
     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
 
diff --git a/gui/ui/mainwindow.ui b/gui/ui/mainwindow.ui
index 06f4503..75b345a 100644
--- a/gui/ui/mainwindow.ui
+++ b/gui/ui/mainwindow.ui
@@ -432,6 +432,9 @@
     <layout class="QVBoxLayout" name="verticalLayout_4">
      <item>
       <widget class="QTreeWidget" name="errorsTreeWidget">
+       <property name="layoutDirection">
+        <enum>Qt::LeftToRight</enum>
+       </property>
        <column>
         <property name="text">
          <string>Index</string>
@@ -452,6 +455,21 @@
     </layout>
    </widget>
   </widget>
+  <widget class="QDockWidget" name="backtraceDock">
+   <property name="windowTitle">
+    <string>Backtrace</string>
+   </property>
+   <attribute name="dockWidgetArea">
+    <number>8</number>
+   </attribute>
+   <widget class="QWidget" name="dockWidgetContents">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QTextBrowser" name="backtraceBrowser"/>
+     </item>
+    </layout>
+   </widget>
+  </widget>
   <action name="actionExit">
    <property name="text">
     <string>Exit</string>
@@ -636,6 +654,7 @@
   <zorder>stateDock</zorder>
   <zorder>vertexDataDock</zorder>
   <zorder>errorsDock</zorder>
+  <zorder>backtraceDock</zorder>
  </widget>
  <customwidgets>
   <customwidget>
diff --git a/wrappers/gltrace.py b/wrappers/gltrace.py
index c40fbb3..fcb48bb 100644
--- a/wrappers/gltrace.py
+++ b/wrappers/gltrace.py
@@ -520,7 +520,7 @@ class GlTracer(Tracer):
 
                     # Emit a fake function
                     print '        {'
-                    print '            static const trace::FunctionSig &_sig = %s ? _glEnableClientState_sig : _glDisableClientState_sig;' % flag_name
+                    print '            static trace::FunctionSig &_sig = %s ? _glEnableClientState_sig : _glDisableClientState_sig;' % flag_name
                     print '            unsigned _call = trace::localWriter.beginEnter(&_sig);'
                     print '            trace::localWriter.beginArg(0);'
                     self.serializeValue(glapi.GLenum, enable_name)
diff --git a/wrappers/trace.py b/wrappers/trace.py
index d9c2900..9ff5410 100644
--- a/wrappers/trace.py
+++ b/wrappers/trace.py
@@ -452,7 +452,7 @@ class Tracer:
                 print 'static const char * _%s_args[%u] = {%s};' % (function.name, len(function.args), ', '.join(['"%s"' % arg.name for arg in function.args]))
             else:
                 print 'static const char ** _%s_args = NULL;' % (function.name,)
-            print 'static const trace::FunctionSig _%s_sig = {%u, "%s", %u, _%s_args};' % (function.name, self.getFunctionSigId(), function.name, len(function.args), function.name)
+            print 'static trace::FunctionSig _%s_sig = {%u, "%s", %u, _%s_args};' % (function.name, self.getFunctionSigId(), function.name, len(function.args), function.name)
             print
 
     def getFunctionSigId(self):
@@ -685,7 +685,7 @@ class Tracer:
         assert not method.internal
 
         print '    static const char * _args[%u] = {%s};' % (len(method.args) + 1, ', '.join(['"this"'] + ['"%s"' % arg.name for arg in method.args]))
-        print '    static const trace::FunctionSig _sig = {%u, "%s", %u, _args};' % (self.getFunctionSigId(), interface.name + '::' + method.name, len(method.args) + 1)
+        print '    static trace::FunctionSig _sig = {%u, "%s", %u, _args};' % (self.getFunctionSigId(), interface.name + '::' + method.name, len(method.args) + 1)
 
         print '    %s *_this = static_cast<%s *>(m_pInstance);' % (base, base)
 
-- 
1.7.9.5



More information about the apitrace mailing list