[PATCH] Backtrace for android and linux

Eugene Velesevich evel at ispras.ru
Thu Apr 4 05:02:52 PDT 2013


Hello,

This patch implements call stack recording during tracing. The symbolic
backtrace is recorded as a string returned from a new fake function,
glFakeBacktraceFunction.

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 one get backtrace via glibc backtrace().
This way doesn't yeild file name and line number information.
But one may use gcc libbacktrace:
http://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libbacktrace
Function set is specified via APITRACE_BT_FUNCTIONS environment variable.

I'm not aware how to get the backtrace on MAC OS.

---
 CMakeLists.txt                |    1 +
 cli/cli_trim.cpp              |    9 +++
 common/trace_backtrace.hpp    |  163 +++++++++++++++++++++++++++++++++++++++
 common/trace_dump.cpp         |    3 +
 common/trace_model.hpp        |    8 +-
 common/trace_parser.cpp       |   22 +++++-
 common/trace_parser.hpp       |    6 ++
 common/trace_writer.cpp       |   11 ++-
 common/trace_writer.hpp       |    5 +-
 common/trace_writer_local.cpp |   32 ++++++--
 common/trace_writer_local.hpp |   14 ++--
 common/trace_writer_model.cpp |    2 +-
 gui/apitracecall.cpp          |   15 ++++
 gui/apitracecall.h            |    5 ++
 gui/mainwindow.cpp            |   10 +++
 gui/ui/mainwindow.ui          |   19 +++++
 wrappers/CMakeLists.txt       |    4 +-
 wrappers/gltrace.py           |    2 +-
 wrappers/trace.cpp            |  171 +++++++++++++++++++++++++++++++++++++++++
 wrappers/trace.hpp            |   26 +++++++
 wrappers/trace.py             |    8 +-
 21 files changed, 510 insertions(+), 26 deletions(-)
 create mode 100644 common/trace_backtrace.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ccaddd0..332cffb 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)
diff --git a/cli/cli_trim.cpp b/cli/cli_trim.cpp
index 5625a2c..26f3a34 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->bt.empty()) {
+                char* bt = new char [call->bt.length()];
+                strcpy(bt, call->bt.c_str());
+                trace::Call* btCall = new trace::Call(&trace::glFakeBacktraceFunction_sig, 0, call->no);
+                btCall->thread_id = call->thread_id;
+                btCall->ret = new trace::String(bt);
+                writer.writeCall(btCall);
+                delete btCall;
+            }
             writer.writeCall(call);
 
             if (options->print_callset) {
diff --git a/common/trace_backtrace.hpp b/common/trace_backtrace.hpp
new file mode 100644
index 0000000..881e7a5
--- /dev/null
+++ b/common/trace_backtrace.hpp
@@ -0,0 +1,163 @@
+/**************************************************************************
+ *
+ * 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
+ */
+
+
+#ifndef _TRACE_BACKTRACE_HPP_
+#define _TRACE_BACKTRACE_HPP_
+
+#ifdef ANDROID
+
+#include <dlfcn.h>
+
+
+/* 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;
+};
+
+static int SOopened = 0;
+static void* (*threadself)(void);
+static FILE* log;
+static char* buf;
+static size_t size;
+static void (*cTarget)(DebugOutputTarget*, FILE*);
+static void (*dumpSelf)(const DebugOutputTarget*, void*);
+static FILE* (*open_memstream_exp)(char**, size_t*);
+static DebugOutputTarget t;
+
+static char* get_backtrace() {
+    if(!SOopened) {
+        void* handle = dlopen("/system/lib/libdvm.so", 0);
+        if(!handle) {
+          os::log("dlopen failed\n");
+          return NULL;
+        }
+        threadself = (void* (*)())dlsym(handle, "_Z13dvmThreadSelfv");
+        if(threadself == NULL) {
+          os::log("dlsym ThreadSelf failed\n");
+          return NULL;
+        }
+        cTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE");
+        if(cTarget == NULL) {
+          os::log("dlsym CreateFileOutput failed\n");
+          return NULL;
+        }
+        dumpSelf = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread");
+        if(dumpSelf == NULL) {
+          os::log("dlsym DumpThreadStack failed\n");
+          return NULL;
+        }
+        void* handle2 = dlopen("/system/lib/libcutils.so", 0);
+        if(!handle2) {
+          os::log("dlopen failed\n");
+          return NULL;
+        }
+        open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");
+        if(open_memstream_exp == NULL) {
+          os::log("dlsym open_memstream failed\n");
+          return NULL;
+        }
+        log = open_memstream_exp(&buf, &size);
+        if(log == NULL) {
+          os::log("open_memstream failed\n");
+          return NULL;
+        }
+        cTarget(&t, log);
+        SOopened = true;
+    }
+    rewind(log);
+    dumpSelf(&t, threadself());
+    fflush(log);
+    return buf;
+}
+
+#elif defined __linux__
+
+#include <execinfo.h>
+
+
+/*
+ * The number of apitrace functions on stack
+ */
+#define NESTED_FUNC_N 2
+
+#define BT_DEPTH 10
+
+
+static 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;
+}
+
+#else
+
+static inline char* get_backtrace() {
+    return NULL;
+}
+
+#endif
+#endif
diff --git a/common/trace_dump.cpp b/common/trace_dump.cpp
index e6810b6..bff9204 100644
--- a/common/trace_dump.cpp
+++ b/common/trace_dump.cpp
@@ -271,6 +271,9 @@ public:
         
         os << "\n";
 
+        if(!call->bt.empty()) {
+            os << red << bold << "Backtrace:\n" << call->bt << normal;
+        }
         if (callFlags & CALL_FLAG_END_FRAME) {
             os << "\n";
         }
diff --git a/common/trace_model.hpp b/common/trace_model.hpp
index bb8936e..16c38b4 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 bt;
 };
 
 
@@ -463,18 +465,20 @@ class Call
 public:
     unsigned thread_id;
     unsigned no;
-    const FunctionSig *sig;
+    FunctionSig *sig;
     std::vector<Arg> args;
     Value *ret;
 
     CallFlags flags;
+    std::string bt;
 
     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),
+        bt() {
     }
 
     ~Call();
diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp
index 095af67..ec70610 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,15 @@ Call *Parser::parse_call(Mode mode) {
 #endif
             call = parse_leave(mode);
             adjust_call_flags(call);
+            Call* call_bt;
+            if(call->sig->id == btFunctionSigId) {
+                call_bt = parse_call(mode);
+                if(mode != SCAN) {
+                    call_bt->bt = ((String*)call->ret)->value;
+                }
+                return call_bt;
+            }
+            call->no -= num_fake_calls;
             return call;
         default:
             std::cerr << "error: unknown event " << c << "\n";
@@ -231,6 +245,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 +454,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..b00814a 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::writeBtCall(const char* bt) {
+    unsigned call = beginEnter(&glFakeBacktraceFunction_sig);
+    endEnter();
+    beginLeave(call);
+    beginReturn();
+    writeString(bt);
+    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,13 @@ 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->bt = egl_func_backtrace_is_needed(sig->name);
+    if(sig->bt)
+        writeBtCall(get_backtrace());
+
+
     unsigned this_thread_num = thread_num;
     if (!this_thread_num) {
         this_thread_num = thread_num = next_thread_num++;
@@ -146,7 +166,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..3b78974 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 writeBtCall(const char *bt);
+
         /**
          * 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..05d3b03 100644
--- a/gui/apitracecall.cpp
+++ b/gui/apitracecall.cpp
@@ -685,6 +685,10 @@ ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,
     }
     m_argValues.squeeze();
     m_flags = call->flags;
+    if(!call->bt.empty())
+    {
+        this->setbt(QString(call->bt.c_str()));
+    }
 }
 
 ApiTraceCall::~ApiTraceCall()
@@ -820,6 +824,17 @@ int ApiTraceCall::binaryDataIndex() const
     return m_binaryDataIndex;
 }
 
+QString ApiTraceCall::bt() const
+{
+    return m_bt;
+}
+
+void ApiTraceCall::setbt(QString bt)
+{
+    m_bt = bt;
+}
+
+
 QStaticText ApiTraceCall::staticText() const
 {
     if (m_staticText && !m_staticText->text().isEmpty())
diff --git a/gui/apitracecall.h b/gui/apitracecall.h
index 6c6b607..1d83de9 100644
--- a/gui/apitracecall.h
+++ b/gui/apitracecall.h
@@ -279,6 +279,9 @@ public:
     int numChildren() const;
     bool hasBinaryData() const;
     int binaryDataIndex() const;
+
+    QString bt() const;
+    void setbt(QString bt);
 private:
     int m_index;
     ApiTraceCallSignature *m_signature;
@@ -291,6 +294,8 @@ private:
 
     QString m_error;
 
+    QString m_bt;
+
     mutable QString m_richText;
     mutable QString m_searchText;
 };
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp
index d6ebd2f..b4d6051 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->bt().isNull())
+        {
+            m_ui.btBrowser->setText(call->bt());
+            m_ui.btDock->show();
+        }
+        else
+            m_ui.btDock->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.btDock->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.btDock->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.btDock);
 
     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
 
diff --git a/gui/ui/mainwindow.ui b/gui/ui/mainwindow.ui
index 06f4503..45c97d7 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="btDock">
+   <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="btBrowser"/>
+     </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>btDock</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..00761b1 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,176 @@ isTracingEnabled(void)
     return enabled;
 }
 
+#endif
+
+
+#if defined(ANDROID) or defined(__linux__)
+
+
+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; /* return true, if strlen(s); */
+    }
+};
+
+
+#define PREFIX_BUF_SIZE (PREFIX_MAX_FUNC_NAME * MAX_BT_FUNC)
+
+class Prefixes {
+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
+    Prefixes(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__ */
+    Prefixes(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;
+                    os::log("%s:%i\n", startbuf, psize);
+                }
+                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;
+    }
+};
+
+static Prefixes prefixes(APITRACE_FNAMES_SOURCE);
+
+bool egl_func_backtrace_is_needed(const char* fname)
+{
+    return prefixes.contain(fname);
+    /*
+    static bool nofirst = false;
+    if(nofirst)
+    {
+        if(prefixes.find(pstring(fname, PREFIX_NO_STRING_SIZE)) != prefixes.end())
+        {
+            os::log("Backtrace for %s is enabled", fname);
+            return true;
+        }
+        return false;
+    }
+    nofirst = true;
+    FILE* f = fopen(APITRACE_FNAMES_FILE, "r");
+    prefixes_buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);
+    if(f == NULL)
+    {
+        os::log("Cannot open " APITRACE_FNAMES_FILE);
+        return false;
+    }
+    char* startbuf = prefixes_buf;
+    while((startbuf = fgets(startbuf, PREFIX_MAX_FUNC_NAME, f)))
+    {
+        int n = strlen(startbuf) - 1;
+        if(n > 2 && startbuf[0] != '#')
+        {
+            std::set<pstring>::iterator elem = prefixes.find(pstring(startbuf, n));
+            bool replace = elem != prefixes.end() && n < elem->n;
+            if(replace)
+            {
+                prefixes.erase(elem);
+            }
+            if(replace || elem == prefixes.end())
+            {
+                prefixes.insert(pstring(startbuf, n));
+            }
+            startbuf += n;
+        }
+    }
+    fclose(f);
+    return egl_func_backtrace_is_needed(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..d02e8c7 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, eglFakeBacktrace
+    __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