[PATCH v4] Backtrace via call detail

Eugene Velesevich evel at ispras.ru
Tue Apr 23 06:56:14 PDT 2013


Hello,

Changes from v3: Instead of writing the backtrace as Array, the backtrace is
now recorded as a list of stack frame nodes with optional stack frame details
(the scheme is below).

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 a call detail:

BACKTRACE
    FRAME
        MODULE "module"
        FUNCTION "Foo"
        FILENAME "foo.cpp"
        LINENUMBER "1234"
        OFFSET "0xSDF"
    FRAME
        FUNCTION "Boo"
        // no filename line info available for this frame
END_BACKTRACE

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    |   62 ++++++
 common/trace_dump.cpp         |   37 ++++
 common/trace_format.hpp       |   16 +-
 common/trace_model.cpp        |   28 +++
 common/trace_model.hpp        |   35 +++-
 common/trace_parser.cpp       |   50 ++++-
 common/trace_parser.hpp       |    2 +
 common/trace_writer.cpp       |   71 ++++++-
 common/trace_writer.hpp       |   20 +-
 common/trace_writer_local.cpp |   20 +-
 common/trace_writer_local.hpp |   12 +-
 common/trace_writer_model.cpp |   41 ++++
 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 +-
 20 files changed, 898 insertions(+), 25 deletions(-)
 create mode 100644 common/trace_backtrace.cpp
 create mode 100644 common/trace_backtrace.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3b45bae..98cae55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -312,6 +312,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..9a837da
--- /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<RawStackFrame> parseBacktrace(char *rawBacktrace) {
+        std::vector<RawStackFrame> parsedBacktrace;
+        char* rawBacktrace_it = rawBacktrace;
+        while (*rawBacktrace_it != '\0') {
+            RawStackFrame 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<RawStackFrame> 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*, RawStackFrame*> 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]
+ */
+    RawStackFrame* 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);
+            RawStackFrame* parsedFrame = new RawStackFrame;
+            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<RawStackFrame> getParsedBacktrace() {
+        std::vector<RawStackFrame> 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++) {
+            RawStackFrame* 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<RawStackFrame> 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..e3c726c
--- /dev/null
+++ b/common/trace_backtrace.hpp
@@ -0,0 +1,62 @@
+#ifndef _TRACE_BACKTRACE_HPP_
+#define _TRACE_BACKTRACE_HPP_
+
+#include <vector>
+
+#include "trace_model.hpp"
+
+namespace trace {
+
+
+struct RawStackFrame {
+    char* module;
+    char* function;
+    char* filename;
+    char* linenumber;
+    char* offset;
+    RawStackFrame() :
+      module(0),
+      function(0),
+      filename(0),
+      linenumber(0),
+      offset(0)
+    {
+    }
+};
+
+#if defined(ANDROID) or defined(__linux__)
+
+std::vector<RawStackFrame> 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..5d54535 100644
--- a/common/trace_dump.cpp
+++ b/common/trace_dump.cpp
@@ -228,6 +228,39 @@ public:
         _visit(r->humanValue);
     }
 
+    void visit(StackFrame *frame) {
+        String* tmp;
+        tmp = frame->module;
+        if (tmp != NULL) {
+            os << tmp->toString() << " ";
+        }
+        tmp = frame->function;
+        if (tmp != NULL) {
+            os << "at " << tmp->toString() << "() ";
+        }
+        tmp = frame->filename;
+        if (tmp != NULL) {
+            os << "at " << tmp->toString();
+            tmp = frame->linenumber;
+            if (tmp != NULL) {
+                os << ":" << tmp->toString() << " ";
+            }
+        }
+        else {
+            tmp = frame->offset;
+            if (tmp != NULL) {
+                os << "[" << tmp->toString() << "]";
+            }
+        }
+    }
+
+    void visit(Backtrace* backtrace) {
+        for (int i = 0; i < backtrace->frames.size(); i ++) {
+            visit(backtrace->frames[i]);
+            os << "\n";
+        }
+    }
+
     void visit(Call *call) {
         CallFlags callFlags = call->flags;
         
@@ -271,6 +304,10 @@ public:
         
         os << "\n";
 
+        if (call->backtrace != NULL) {
+            os << bold << red << "Backtrace:\n" << normal;
+            visit(call->backtrace);
+        }
         if (callFlags & CALL_FLAG_END_FRAME) {
             os << "\n";
         }
diff --git a/common/trace_format.hpp b/common/trace_format.hpp
index d5fd81b..5557a31 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,17 @@ enum CallDetail {
     CALL_ARG,
     CALL_RET,
     CALL_THREAD,
+    CALL_BACKTRACE,
+};
+
+enum CallBacktrace {
+    CALL_BACKTRACE_FRAME = 0,
+    CALL_BACKTRACE_MODULE,
+    CALL_BACKTRACE_FUNCTION,
+    CALL_BACKTRACE_FILENAME,
+    CALL_BACKTRACE_LINENUMBER,
+    CALL_BACKTRACE_OFFSET,
+    CALL_BACKTRACE_END,
 };
 
 enum Type {
diff --git a/common/trace_model.cpp b/common/trace_model.cpp
index 86527a1..88061f0 100644
--- a/common/trace_model.cpp
+++ b/common/trace_model.cpp
@@ -72,6 +72,29 @@ Blob::~Blob() {
     }
 }
 
+StackFrame::~StackFrame() {
+    if (module != NULL) {
+        delete module;
+    }
+    if (function != NULL) {
+        delete function;
+    }
+    if (filename != NULL) {
+        delete filename;
+    }
+    if (linenumber != NULL) {
+        delete linenumber;
+    }
+    if (offset != NULL) {
+        delete offset;
+    }
+}
+
+Backtrace::~Backtrace() {
+    for (int i = 0; i < frames.size(); i++) {
+        delete frames[i];
+    }
+}
 
 // bool cast
 bool Null   ::toBool(void) const { return false; }
@@ -176,6 +199,9 @@ void Blob   ::visit(Visitor &visitor) { visitor.visit(this); }
 void Pointer::visit(Visitor &visitor) { visitor.visit(this); }
 void Repr   ::visit(Visitor &visitor) { visitor.visit(this); }
 
+void Backtrace::addFrame(StackFrame* frame) {
+    frames.push_back(frame);
+}
 
 void Visitor::visit(Null *) { assert(0); }
 void Visitor::visit(Bool *) { assert(0); }
@@ -191,6 +217,8 @@ void Visitor::visit(Array *) { assert(0); }
 void Visitor::visit(Blob *) { assert(0); }
 void Visitor::visit(Pointer *) { assert(0); }
 void Visitor::visit(Repr *node) { node->machineValue->visit(*this); }
+void Visitor::visit(Backtrace *) { assert(0); }
+void Visitor::visit(StackFrame *) { assert(0); }
 
 
 static Null null;
diff --git a/common/trace_model.hpp b/common/trace_model.hpp
index 9558225..7c4cdc7 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;
 };
 
 
@@ -348,6 +349,29 @@ public:
     void visit(Visitor &visitor);
 };
 
+class StackFrame {
+public:
+    String* module;
+    String* function;
+    String* filename;
+    String* linenumber;
+    String* offset;
+    StackFrame() :
+        module(NULL),
+        function(NULL),
+        filename(NULL),
+        linenumber(NULL),
+        offset(NULL)
+    {}
+    ~StackFrame();
+};
+
+class Backtrace {
+public:
+    std::vector<StackFrame*> frames;
+    ~Backtrace();
+    void addFrame(StackFrame* frame);
+};
 
 class Visitor
 {
@@ -366,7 +390,8 @@ public:
     virtual void visit(Blob *);
     virtual void visit(Pointer *);
     virtual void visit(Repr *);
-
+    virtual void visit(Backtrace *);
+    virtual void visit(StackFrame *);
 protected:
     inline void _visit(Value *value) {
         if (value) { 
@@ -463,18 +488,20 @@ class Call
 public:
     unsigned thread_id;
     unsigned no;
-    const FunctionSig *sig;
+    FunctionSig *sig;
     std::vector<Arg> args;
     Value *ret;
 
     CallFlags flags;
+    Backtrace* 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..8ba9bdc 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
+            parse_call_backtrace(call, mode);
+            break;
         default:
             std::cerr << "error: ("<<call->name()<< ") unknown call detail "
                       << c << "\n";
@@ -510,6 +516,48 @@ bool Parser::parse_call_details(Call *call, Mode mode) {
     } while(true);
 }
 
+bool Parser::parse_call_backtrace(Call *call, Mode mode) {
+    Backtrace* backtrace = new Backtrace();
+    StackFrame* frame = NULL;
+    do {
+        int c = read_byte();
+        switch (c) {
+        case trace::CALL_BACKTRACE_FRAME:
+            if (frame != NULL) {
+                backtrace->addFrame(frame);
+            }
+            frame = new StackFrame();
+            break;
+        case trace::CALL_BACKTRACE_END:
+            if (frame != NULL) {
+                backtrace->addFrame(frame);
+            }
+            call->backtrace = backtrace;
+            return true;
+        case trace::CALL_BACKTRACE_MODULE:
+            frame->module = static_cast<String*>(parse_value(mode));
+            break;
+        case trace::CALL_BACKTRACE_FUNCTION:
+            frame->function = static_cast<String*>(parse_value(mode));
+            break;
+        case trace::CALL_BACKTRACE_FILENAME:
+            frame->filename = static_cast<String*>(parse_value(mode));
+            break;
+        case trace::CALL_BACKTRACE_LINENUMBER:
+            frame->linenumber = static_cast<String*>(parse_value(mode));
+            break;
+        case trace::CALL_BACKTRACE_OFFSET:
+            frame->offset = static_cast<String*>(parse_value(mode));
+            break;
+        default:
+            std::cerr << "error: ("<< call->name() << ") unknown call backtrace detail "
+                      << c << "\n";
+            exit(1);
+        case -1:
+            return false;
+        }
+    } while(true);
+}
 
 /**
  * Make adjustments to this particular call flags.
diff --git a/common/trace_parser.hpp b/common/trace_parser.hpp
index 0ae3a28..881b025 100644
--- a/common/trace_parser.hpp
+++ b/common/trace_parser.hpp
@@ -147,6 +147,8 @@ protected:
 
     bool parse_call_details(Call *call, Mode mode);
 
+    bool parse_call_backtrace(Call *call, Mode mode);
+
     void adjust_call_flags(Call *call);
 
     void parse_arg(Call *call, Mode mode);
diff --git a/common/trace_writer.cpp b/common/trace_writer.cpp
index d69e93f..2dc7a00 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,77 @@ inline bool lookup(std::vector<bool> &map, size_t index) {
     }
 }
 
-unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) {
+void Writer::writeBacktrace(std::vector<RawStackFrame> backtrace) {
+
+    for (int i = 0; i < backtrace.size(); i++) {
+        beginStackFrame();
+        if (backtrace[i].module != NULL) {
+            beginStackFrameModule();
+            writeString(backtrace[i].module);
+            endStackFrameModule();
+        }
+        if (backtrace[i].function != NULL) {
+            beginStackFrameFunction();
+            writeString(backtrace[i].function);
+            endStackFrameFunction();
+        }
+        if (backtrace[i].filename != NULL) {
+            beginStackFrameFilename();
+            writeString(backtrace[i].filename);
+            endStackFrameFilename();
+        }
+        if (backtrace[i].linenumber != NULL) {
+            beginStackFrameLinenumber();
+            writeString(backtrace[i].linenumber);
+            endStackFrameLinenumber();
+        }
+        if (backtrace[i].offset != NULL) {
+            beginStackFrameOffset();
+            writeString(backtrace[i].offset);
+            endStackFrameOffset();
+        }
+        endStackFrame();
+    }
+}
+
+void Writer::beginBacktrace(void ) {
+    _writeByte(trace::CALL_BACKTRACE);
+}
+
+void Writer::endBacktrace(void ) {
+    _writeByte(trace::CALL_BACKTRACE_END);
+}
+
+void Writer::beginStackFrame(void ) {
+    _writeByte(trace::CALL_BACKTRACE_FRAME);
+}
+
+void Writer::beginStackFrameModule(void ) {
+    _writeByte(trace::CALL_BACKTRACE_MODULE);
+}
+
+void Writer::beginStackFrameFunction(void ) {
+    _writeByte(trace::CALL_BACKTRACE_FUNCTION);
+}
+
+void Writer::beginStackFrameFilename(void ) {
+    _writeByte(trace::CALL_BACKTRACE_FILENAME);
+}
+
+void Writer::beginStackFrameLinenumber(void ) {
+    _writeByte(trace::CALL_BACKTRACE_LINENUMBER);
+}
+
+void Writer::beginStackFrameOffset(void ) {
+    _writeByte(trace::CALL_BACKTRACE_OFFSET);
+}
+
+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..6440ab9 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,23 @@ namespace trace {
         bool open(const char *filename);
         void close(void);
 
-        unsigned beginEnter(const FunctionSig *sig, unsigned thread_id);
+        void writeBacktrace(std::vector<RawStackFrame> backtrace);
+        void beginBacktrace(void);
+        void endBacktrace(void);
+        void beginStackFrame(void);
+        inline void endStackFrame(void) {}
+        void beginStackFrameModule(void);
+        inline void endStackFrameModule(void) {}
+        void beginStackFrameFunction(void);
+        inline void endStackFrameFunction(void) {}
+        void beginStackFrameFilename(void);
+        inline void endStackFrameFilename(void) {}
+        void beginStackFrameLinenumber(void);
+        inline void endStackFrameLinenumber(void) {}
+        void beginStackFrameOffset(void);
+        inline void endStackFrameOffset(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 757e9c0..b894bf9 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)
@@ -133,7 +134,7 @@ static uintptr_t next_thread_num = 1;
 static OS_THREAD_SPECIFIC_PTR(void)
 thread_num;
 
-unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
+unsigned LocalWriter::beginEnter(FunctionSig *sig) {
     mutex.lock();
     ++acquired;
 
@@ -151,7 +152,14 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
 
     assert(this_thread_num);
     unsigned thread_id = this_thread_num - 1;
-    return Writer::beginEnter(sig, thread_id);
+    unsigned call_no = Writer::beginEnter(sig, thread_id);
+    if (sig->backtrace) {
+        std::vector<RawStackFrame> 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..cdf9783 100644
--- a/common/trace_writer_model.cpp
+++ b/common/trace_writer_model.cpp
@@ -107,8 +107,49 @@ public:
         writer.endRepr();
     }
 
+    void visit(StackFrame* frame) {
+        writer.beginStackFrame();
+        if (frame->module != NULL) {
+            writer.beginStackFrameModule();
+            _visit(frame->module);
+            writer.endStackFrameModule();
+        }
+        if (frame->function != NULL) {
+            writer.beginStackFrameFunction();
+            _visit(frame->function);
+            writer.endStackFrameFunction();
+        }
+        if (frame->filename != NULL) {
+            writer.beginStackFrameFilename();
+            _visit(frame->filename);
+            writer.endStackFrameFilename();
+        }
+        if (frame->linenumber != NULL) {
+            writer.beginStackFrameLinenumber();
+            _visit(frame->linenumber);
+            writer.endStackFrameLinenumber();
+        }
+        if (frame->offset != NULL) {
+            writer.beginStackFrameOffset();
+            _visit(frame->offset);
+            writer.endStackFrameOffset();
+        }
+        writer.endStackFrame();
+    }
+
+    void visit(Backtrace * backtrace) {
+        writer.beginBacktrace();
+        for (int i =0; i < backtrace->frames.size(); i++) {
+            visit(backtrace->frames[i]);
+        }
+        writer.endBacktrace();
+    }
+
     void visit(Call *call) {
         unsigned call_no = writer.beginEnter(call->sig, call->thread_id);
+        if (call->backtrace != NULL) {
+            visit(call->backtrace);
+        }
         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..c3a1dee 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) {
+        QString qbacktrace;
+        for (int i = 0; i < call->backtrace->frames.size(); i++) {
+            trace::StackFrame* frame = call->backtrace->frames[i];
+            trace::String* tmp;
+            tmp = frame->module;
+            if (tmp != NULL) {
+                qbacktrace += QString("%1 ").arg(tmp->toString());
+            }
+            tmp = frame->function;
+            if (tmp != NULL) {
+                qbacktrace += QString("at %1() ").arg(tmp->toString());
+            }
+            tmp = frame->filename;
+            if (tmp != NULL) {
+                qbacktrace += QString("at %1").arg(tmp->toString());
+                tmp = frame->linenumber;
+                if (tmp != NULL) {
+                    qbacktrace += QString(":%1 ").arg(tmp->toString());
+                }
+            }
+            else {
+                tmp = frame->offset;
+                if (tmp != NULL) {
+                    qbacktrace += QString("[%1]").arg(tmp->toString());
+                }
+            }
+            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