[PATCH v2] Backtrace for android and linux

Eugene Velesevich evel at ispras.ru
Mon Apr 8 08:02:52 PDT 2013


Hello,

The following patch addresses all issues pointed out by Zack (except for
backtrace parsing/storage format).

I'd like to request a clarification regarding what needs to be changed with
regards to how the backtrace is stored in the trace files for this patch to be
accepted.  I can work on using call detail flags instead of a fake function
(the reason to use fake functions in the first place was preserving trace file
compatibility).  However, I'd like to ask if storing the backtrace without
parsing is OK for now: this patch is already an improvement compared to the
current situation, and I don't see great benefits in parsing the whole string
into stack frames and then into file:line:function, since it is not intended
for machine processing -- only to be viewed by a human.

---
 CMakeLists.txt                |    2 +
 cli/cli_trim.cpp              |    9 +++
 common/trace_backtrace.cpp    |  172 +++++++++++++++++++++++++++++++++++++++++
 common/trace_backtrace.hpp    |   17 ++++
 common/trace_dump.cpp         |    3 +
 common/trace_model.hpp        |    5 +-
 common/trace_parser.cpp       |   23 +++++-
 common/trace_parser.hpp       |    6 ++
 common/trace_writer.cpp       |   11 ++-
 common/trace_writer.hpp       |    5 +-
 common/trace_writer_local.cpp |   33 ++++++--
 common/trace_writer_local.hpp |   14 ++--
 common/trace_writer_model.cpp |    2 +-
 gui/apitracecall.cpp          |   13 ++++
 gui/apitracecall.h            |    5 ++
 gui/mainwindow.cpp            |   10 +++
 gui/ui/mainwindow.ui          |   19 +++++
 wrappers/CMakeLists.txt       |    4 +-
 wrappers/gltrace.py           |    2 +-
 wrappers/trace.cpp            |  132 +++++++++++++++++++++++++++++++
 wrappers/trace.hpp            |   26 +++++++
 wrappers/trace.py             |    8 +-
 22 files changed, 496 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 ccaddd0..df688d6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -282,6 +282,7 @@ include_directories (
     ${CMAKE_CURRENT_BINARY_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR}/common
+    ${CMAKE_CURRENT_SOURCE_DIR}/wrappers
 )
 
 if (WIN32)
@@ -309,6 +310,7 @@ add_library (common STATIC
     common/trace_parser_flags.cpp
     common/trace_writer.cpp
     common/trace_writer_local.cpp
+    common/trace_backtrace.cpp
     common/trace_writer_model.cpp
     common/trace_loader.cpp
     common/trace_profiler.cpp
diff --git a/cli/cli_trim.cpp b/cli/cli_trim.cpp
index 5625a2c..9544616 100644
--- a/cli/cli_trim.cpp
+++ b/cli/cli_trim.cpp
@@ -273,6 +273,15 @@ trim_trace(const char *filename, struct trim_options *options)
         }
 
         if (required->find(call->no) != required->end()) {
+            if (!call->backtrace.empty()) {
+                char* backtrace = new char [call->backtrace.length()];
+                strcpy(backtrace, call->backtrace.c_str());
+                trace::Call* backtraceCall = new trace::Call(&trace::glFakeBacktraceFunction_sig, 0, call->no);
+                backtraceCall->thread_id = call->thread_id;
+                backtraceCall->ret = new trace::String(backtrace);
+                writer.writeCall(backtraceCall);
+                delete backtraceCall;
+            }
             writer.writeCall(call);
 
             if (options->print_callset) {
diff --git a/common/trace_backtrace.cpp b/common/trace_backtrace.cpp
new file mode 100644
index 0000000..eabe083
--- /dev/null
+++ b/common/trace_backtrace.cpp
@@ -0,0 +1,172 @@
+/**************************************************************************
+ *
+ * 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"
+
+#ifdef ANDROID
+
+#include <dlfcn.h>
+#include "os.hpp"
+
+/* The following two declarations are copied from Androis 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_BACTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"
+
+
+class BacktraceProvider {
+private:
+    bool errorOccured;
+    void* (*threadself)(void);
+    FILE* streamInMemory;
+    char* buf;
+    size_t bufSize;
+    void (*dumpBacktrace)(const DebugOutputTarget*, void*);
+    DebugOutputTarget debugTarget;
+public:
+    BacktraceProvider() {
+        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_BACTRACE_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;
+    }
+};
+
+char* get_backtrace() {
+    static BacktraceProvider backtraceProvider;
+    return backtraceProvider.getBacktrace();
+}
+
+#elif defined __linux__
+
+#include <execinfo.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * The number of apitrace functions on stack
+ */
+#define NESTED_FUNC_N 2
+
+#define BT_DEPTH 10
+
+char* get_backtrace() {
+    void *array[NESTED_FUNC_N + BT_DEPTH];
+    static char* buf = (char*)malloc(2000 * sizeof(char));
+    buf[0] = '\0';
+    int buf_data_size = 0;
+    size_t size;
+    char **strings;
+    size_t i;
+    size = backtrace(array, NESTED_FUNC_N + BT_DEPTH);
+    strings = backtrace_symbols(array, size);;
+    for (i = NESTED_FUNC_N; i < size; i++) {
+        buf_data_size += strlen(strings[i]) + 1;
+        if (buf_data_size < 2000 - 2) {
+            strcat(buf, strings[i]);
+            strcat(buf, "\n");
+        }
+    }
+    strcat(buf, "\n");
+    free(strings);
+    return buf;
+}
+
+#endif
diff --git a/common/trace_backtrace.hpp b/common/trace_backtrace.hpp
new file mode 100644
index 0000000..ba104c6
--- /dev/null
+++ b/common/trace_backtrace.hpp
@@ -0,0 +1,17 @@
+#ifndef _TRACE_BACKTRACE_HPP_
+#define _TRACE_BACKTRACE_HPP_
+
+#if defined(ANDROID) or defined(__linux__)
+
+char* get_backtrace();
+
+#else
+
+static inline char* get_backtrace() {
+    return NULL;
+}
+
+#endif
+
+#endif
+
diff --git a/common/trace_dump.cpp b/common/trace_dump.cpp
index e6810b6..dae5fea 100644
--- a/common/trace_dump.cpp
+++ b/common/trace_dump.cpp
@@ -271,6 +271,9 @@ public:
         
         os << "\n";
 
+        if (!call->backtrace.empty()) {
+            os << red << bold << "Backtrace:\n" << call->backtrace << normal;
+        }
         if (callFlags & CALL_FLAG_END_FRAME) {
             os << "\n";
         }
diff --git a/common/trace_model.hpp b/common/trace_model.hpp
index bb8936e..78436ef 100644
--- a/common/trace_model.hpp
+++ b/common/trace_model.hpp
@@ -36,6 +36,7 @@
 
 #include <map>
 #include <vector>
+#include <string>
 
 
 namespace trace {
@@ -49,6 +50,7 @@ struct FunctionSig {
     const char *name;
     unsigned num_args;
     const char **arg_names;
+    bool backtrace;
 };
 
 
@@ -463,11 +465,12 @@ class Call
 public:
     unsigned thread_id;
     unsigned no;
-    const FunctionSig *sig;
+    FunctionSig *sig;
     std::vector<Arg> args;
     Value *ret;
 
     CallFlags flags;
+    std::string backtrace;
 
     Call(FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :
         thread_id(_thread_id), 
diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp
index 095af67..110c3b3 100644
--- a/common/trace_parser.cpp
+++ b/common/trace_parser.cpp
@@ -43,10 +43,12 @@ namespace trace {
 Parser::Parser() {
     file = NULL;
     next_call_no = 0;
+    num_fake_calls = 0;
     version = 0;
     api = API_UNKNOWN;
 
     glGetErrorSig = NULL;
+    btFunctionSigId = (size_t)-1;
 }
 
 
@@ -57,6 +59,7 @@ Parser::~Parser() {
 
 bool Parser::open(const char *filename) {
     assert(!file);
+    num_fake_calls = 0;
     file = File::createForRead(filename);
     if (!file) {
         return false;
@@ -159,13 +162,15 @@ void Parser::close(void) {
 void Parser::getBookmark(ParseBookmark &bookmark) {
     bookmark.offset = file->currentOffset();
     bookmark.next_call_no = next_call_no;
+    bookmark.num_fake_calls = num_fake_calls;
 }
 
 
 void Parser::setBookmark(const ParseBookmark &bookmark) {
     file->setCurrentOffset(bookmark.offset);
     next_call_no = bookmark.next_call_no;
-    
+    num_fake_calls  = bookmark.num_fake_calls;
+
     // Simply ignore all pending calls
     deleteAll(calls);
 }
@@ -188,6 +193,16 @@ Call *Parser::parse_call(Mode mode) {
 #endif
             call = parse_leave(mode);
             adjust_call_flags(call);
+            if (call->sig->id == btFunctionSigId) {
+                Call* call_with_backtrace;
+                call_with_backtrace = parse_call(mode);
+                const char *backtrace = call->ret->toString();
+                if (mode != SCAN && backtrace != NULL) {
+                    call_with_backtrace->backtrace = backtrace;
+                }
+                return call_with_backtrace;
+            }
+            call->no -= num_fake_calls;
             return call;
         default:
             std::cerr << "error: unknown event " << c << "\n";
@@ -231,6 +246,9 @@ Parser::parse_function_sig(void) {
         sig = new FunctionSigState;
         sig->id = id;
         sig->name = read_string();
+        if (!strcmp(sig->name, "glFakeBacktraceFunction")) {
+            btFunctionSigId = sig->id;
+        }
         sig->num_args = read_uint();
         const char **arg_names = new const char *[sig->num_args];
         for (unsigned i = 0; i < sig->num_args; ++i) {
@@ -437,6 +455,9 @@ void Parser::parse_enter(Mode mode) {
 
     call->no = next_call_no++;
 
+    if (sig->id == btFunctionSigId) {
+        num_fake_calls++;
+    }
     if (parse_call_details(call, mode)) {
         calls.push_back(call);
     } else {
diff --git a/common/trace_parser.hpp b/common/trace_parser.hpp
index 0ae3a28..a24819a 100644
--- a/common/trace_parser.hpp
+++ b/common/trace_parser.hpp
@@ -43,6 +43,7 @@ struct ParseBookmark
 {
     File::Offset offset;
     unsigned next_call_no;
+    unsigned num_fake_calls;
 };
 
 
@@ -93,9 +94,14 @@ protected:
 
     unsigned next_call_no;
 
+    /*
+     *  This is needed in order to not put out the call numeration
+     */
+    unsigned num_fake_calls;
 public:
     unsigned long long version;
     API api;
+    size_t btFunctionSigId;
 
     Parser();
 
diff --git a/common/trace_writer.cpp b/common/trace_writer.cpp
index d69e93f..5324316 100644
--- a/common/trace_writer.cpp
+++ b/common/trace_writer.cpp
@@ -38,6 +38,9 @@
 
 namespace trace {
 
+static const char *glFakeBacktraceFunction_args[0] = {};
+FunctionSig glFakeBacktraceFunction_sig = {4, "glFakeBacktraceFunction", 0, glFakeBacktraceFunction_args, false};
+
 
 Writer::Writer() :
     call_no(0)
@@ -134,11 +137,15 @@ inline bool lookup(std::vector<bool> &map, size_t index) {
     }
 }
 
-unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) {
+bool Writer::_lookup(size_t index) {
+    return lookup(functions, index);
+}
+
+unsigned Writer::beginEnter(FunctionSig *sig, unsigned thread_id, bool sig_was_written) {
     _writeByte(trace::EVENT_ENTER);
     _writeUInt(thread_id);
     _writeUInt(sig->id);
-    if (!lookup(functions, sig->id)) {
+    if (!sig_was_written) {
         _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..9fdecbb 100644
--- a/common/trace_writer.hpp
+++ b/common/trace_writer.hpp
@@ -39,6 +39,8 @@
 
 
 namespace trace {
+    extern FunctionSig glFakeBacktraceFunction_sig;
+
     class File;
 
     class Writer {
@@ -58,7 +60,7 @@ namespace trace {
         bool open(const char *filename);
         void close(void);
 
-        unsigned beginEnter(const FunctionSig *sig, unsigned thread_id);
+        unsigned beginEnter(FunctionSig *sig, unsigned thread_id, bool sig_was_written);
         void endEnter(void);
 
         void beginLeave(unsigned call);
@@ -98,6 +100,7 @@ namespace trace {
 
         void writeCall(Call *call);
 
+        bool _lookup(size_t index);
     protected:
         void inline _write(const void *sBuffer, size_t dwBytesToWrite);
         void inline _writeByte(char c);
diff --git a/common/trace_writer_local.cpp b/common/trace_writer_local.cpp
index feb5c91..e87b544 100644
--- a/common/trace_writer_local.cpp
+++ b/common/trace_writer_local.cpp
@@ -31,27 +31,29 @@
 #include <string.h>
 
 #include "os.hpp"
+#include "trace.hpp"
 #include "os_thread.hpp"
 #include "os_string.hpp"
 #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)
@@ -128,10 +130,21 @@ LocalWriter::open(void) {
 #endif
 }
 
+void LocalWriter::writeBacktraceCall(const char* backtrace) {
+    unsigned call = beginEnter(&glFakeBacktraceFunction_sig);
+    endEnter();
+    beginLeave(call);
+    beginReturn();
+    writeString(backtrace);
+    endReturn();
+    endLeave();
+    return;
+}
+
 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;
 
@@ -139,6 +152,14 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
         open();
     }
 
+    bool sig_was_written = Writer::_lookup(sig->id);
+    if (!sig_was_written && sig->id != glFakeBacktraceFunction_sig.id) {
+        sig->backtrace = egl_func_backtrace_is_needed(sig->name);
+    }
+    if (sig->backtrace) {
+        writeBacktraceCall(get_backtrace());
+    }
+
     unsigned this_thread_num = thread_num;
     if (!this_thread_num) {
         this_thread_num = thread_num = next_thread_num++;
@@ -146,7 +167,7 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
 
     assert(thread_num > 0);
     unsigned thread_id = thread_num - 1;
-    return Writer::beginEnter(sig, thread_id);
+    return Writer::beginEnter(sig, thread_id, sig_was_written);
 }
 
 void LocalWriter::endEnter(void) {
diff --git a/common/trace_writer_local.hpp b/common/trace_writer_local.hpp
index cc5dda0..5c10ae2 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.
@@ -80,11 +80,13 @@ namespace trace {
 
         void open(void);
 
+        void writeBacktraceCall(const char *backtrace);
+
         /**
          * 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..d204edf 100644
--- a/common/trace_writer_model.cpp
+++ b/common/trace_writer_model.cpp
@@ -108,7 +108,7 @@ public:
     }
 
     void visit(Call *call) {
-        unsigned call_no = writer.beginEnter(call->sig, call->thread_id);
+        unsigned call_no = writer.beginEnter(call->sig, call->thread_id, writer._lookup(call->sig->id));
         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..dd4660f 100644
--- a/gui/apitracecall.cpp
+++ b/gui/apitracecall.cpp
@@ -685,6 +685,9 @@ ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,
     }
     m_argValues.squeeze();
     m_flags = call->flags;
+    if (!call->backtrace.empty()) {
+        this->setBacktrace(QString(call->backtrace.c_str()));
+    }
 }
 
 ApiTraceCall::~ApiTraceCall()
@@ -820,6 +823,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 d6ebd2f..b0cf8c1 100644
--- a/gui/mainwindow.cpp
+++ b/gui/mainwindow.cpp
@@ -116,6 +116,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)
@@ -154,6 +161,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()) {
@@ -757,6 +765,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();
@@ -764,6 +773,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/CMakeLists.txt b/wrappers/CMakeLists.txt
index 6b76e58..0e972a4 100644
--- a/wrappers/CMakeLists.txt
+++ b/wrappers/CMakeLists.txt
@@ -417,8 +417,8 @@ elseif (X11_FOUND)
 
     target_link_libraries (glxtrace
         glproc_gl
-        common_trace
         common
+        common_trace
         ${ZLIB_LIBRARIES}
         ${SNAPPY_LIBRARIES}
         ${CMAKE_THREAD_LIBS_INIT}
@@ -465,8 +465,8 @@ if (ENABLE_EGL AND NOT WIN32 AND NOT APPLE)
 
     target_link_libraries (egltrace
         glproc_egl
-        common_trace
         common
+        common_trace
         ${ZLIB_LIBRARIES}
         ${SNAPPY_LIBRARIES}
         ${CMAKE_THREAD_LIBS_INIT}
diff --git a/wrappers/gltrace.py b/wrappers/gltrace.py
index 853a4bc..d41ab8a 100644
--- a/wrappers/gltrace.py
+++ b/wrappers/gltrace.py
@@ -516,7 +516,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.cpp b/wrappers/trace.cpp
index eb68934..3ddc1df 100644
--- a/wrappers/trace.cpp
+++ b/wrappers/trace.cpp
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <set>
 
 #ifdef ANDROID
 #include <sys/types.h>
@@ -108,6 +109,137 @@ isTracingEnabled(void)
     return enabled;
 }
 
+#endif
+
+
+#if defined(ANDROID) or defined(__linux__)
+
+
+/*
+ * 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:
+#ifdef ANDROID
+    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;
+                    os::log("%s:%i\n", startbuf, psize);
+                }
+                else {
+                    psize = n - 1;
+                }
+                addPrefix(startbuf, psize);
+                startbuf += n + 1;
+                n = 0;
+            }
+        }
+        fclose(f);
+        os::log("Bt options have been read");
+    }
+
+#else /* __linux__ */
+    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;
+            }
+        }
+        os::log("Bt options have been read\n");
+    }
+#endif
+
+    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 egl_func_backtrace_is_needed(const char* fname) {
+    static StringPrefixes backtraceFunctionNamePrefixes(APITRACE_FNAMES_SOURCE);
+    return backtraceFunctionNamePrefixes.contain(fname);
+}
+
 #endif /* ANDROID */
 
 
diff --git a/wrappers/trace.hpp b/wrappers/trace.hpp
index 997617e..15f785b 100644
--- a/wrappers/trace.hpp
+++ b/wrappers/trace.hpp
@@ -49,6 +49,32 @@ isTracingEnabled(void) {
 
 #endif /* !ANDROID */
 
+#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
+
+bool egl_func_backtrace_is_needed(const char*);
+
+#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
+
+bool egl_func_backtrace_is_needed(const char*);
+
+#else /* !ANDROID and !LINUX */
+
+static inline bool
+egl_func_backtrace_is_needed(const char*) {
+    return false;
+}
+
+#endif
 
 } /* namespace trace */
 
diff --git a/wrappers/trace.py b/wrappers/trace.py
index 61a2cb9..8279fa3 100644
--- a/wrappers/trace.py
+++ b/wrappers/trace.py
@@ -382,8 +382,8 @@ class ValueUnwrapper(ValueWrapper):
 class Tracer:
     '''Base class to orchestrate the code generation of API tracing.'''
 
-    # 0-3 are reserved to memcpy, malloc, free, and realloc
-    __id = 4
+    # 0-4 are reserved to memcpy, malloc, free, realloc, glFakeBacktrace
+    __id = 5
 
     def __init__(self):
         self.api = None
@@ -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, false};' % (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, false};' % (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