]> git.cworth.org Git - apitrace/commitdiff
Backtrace via call detail
authorEugene Velesevich <evel@ispras.ru>
Tue, 23 Apr 2013 13:56:14 +0000 (17:56 +0400)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Sat, 4 May 2013 09:11:06 +0000 (10:11 +0100)
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.

20 files changed:
CMakeLists.txt
common/trace_backtrace.cpp [new file with mode: 0644]
common/trace_backtrace.hpp [new file with mode: 0644]
common/trace_dump.cpp
common/trace_format.hpp
common/trace_model.cpp
common/trace_model.hpp
common/trace_parser.cpp
common/trace_parser.hpp
common/trace_writer.cpp
common/trace_writer.hpp
common/trace_writer_local.cpp
common/trace_writer_local.hpp
common/trace_writer_model.cpp
gui/apitracecall.cpp
gui/apitracecall.h
gui/mainwindow.cpp
gui/ui/mainwindow.ui
wrappers/gltrace.py
wrappers/trace.py

index 2bfa07ef24d2d882dd538baa1eaa209fd7b23c48..27a384a06109fa93a25e5a943f2abe280d060fea 100644 (file)
@@ -318,6 +318,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 (file)
index 0000000..9a837da
--- /dev/null
@@ -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 (file)
index 0000000..e3c726c
--- /dev/null
@@ -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
index e6810b6afa63e209e156b096bb8a5197b2dc32ce..5d545353e692178132ad235d2a743a3aa047aa23 100644 (file)
@@ -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";
         }
index d5fd81b5b9eeacd6f9a6f2da06ac78a90c5353d9..5557a3148bb3a4c9f534b60ba435f3b682d2af1e 100644 (file)
@@ -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 {
index 86527a1f900f59959691ca740656199c417b5280..88061f0b3d7beaca3593e52c77f9744fce54058d 100644 (file)
@@ -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;
index 95582253190687446a4c0d98e529047c33e98155..7c4cdc78f221528e1b0fa7df7dd7b326b790c24c 100644 (file)
@@ -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();
index c70517e2461658cf18e8e6ca30c0d50b83c49c74..8ba9bdcdacd6b6003bda0380d5747d9796d8da80 100644 (file)
@@ -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.
index 0ae3a28d682b80ad6034e1c8daa88fec91362064..881b025aaf143c26467535718b50dd9add9f4f1c 100644 (file)
@@ -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);
index d69e93fc9650211ca8d6418d173b027a4fa3cc26..2dc7a00ea8c7502de88304abdbbed2239f425001 100644 (file)
 #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) {
index a46b43edc487f15faff6f22bc68fade0a57924fa..6440ab9cec41c19375ffb44bcc52b8e56377c065 100644 (file)
@@ -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);
index 757e9c0fc328ce99dc94d9c7e33beaba1e49d158..b894bf9d095e775f86fee36f1bd648b55f04808d 100644 (file)
 #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) {
index cc5dda0115eb425e462d12203bbb2c2bc4e65cd1..1da22501d41dbe52291bacd5b1679d3073734885 100644 (file)
 
 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.
          */
index 2e858a233971e3434410b8daba6fb5601c66e3a9..cdf9783b4ba1655e0e02ecfacab062a65386a3e5 100644 (file)
@@ -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);
index 33774861013cc99165c323800a09f6cbef0fc1ac..c3a1deebed5bc01e4ef2cb13956ef06c0cc0a387 100644 (file)
@@ -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())
index 6c6b6074b6dcae2799e1f4a750cdc293120c3f53..8004cedebe663ec8fa8da969c613df968ea8b368 100644 (file)
@@ -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;
 };
index 8905fbb05047c5f4ba2f76165fe978f4825fc554..e46fce6a095ece39d12f698464cca740a8017bb5 100644 (file)
@@ -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);
 
index 06f4503af50d92cf3015ce7baaecb3aaed588252..75b345a8f4ffa93659e4e83edf6e1a57bcc0f619 100644 (file)
     <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>
     </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>
   <zorder>stateDock</zorder>
   <zorder>vertexDataDock</zorder>
   <zorder>errorsDock</zorder>
+  <zorder>backtraceDock</zorder>
  </widget>
  <customwidgets>
   <customwidget>
index c40fbb36de62aaac143a13ab1acf9b4e894f659f..fcb48bba5d71c2ceedffd0a8ce6bd7a0d28450e1 100644 (file)
@@ -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)
index d9c2900936714e867f1db5c984a86d3d32104ae1..9ff541049fcfc93abaf46c081f625aeeb1a230a4 100644 (file)
@@ -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)