[PATCH v3] Backtrace via call detail
Eugene Velesevich
evel at ispras.ru
Tue Apr 16 03:20:29 PDT 2013
Hello,
Changes from v2: Now backtrace is parsed and is recorded as a call detail.
This patch implements backtrace recording during tracing, and adds support in
'apitrace dump' and QApitrace. Backtrace is obtained via platform-specific
functions (and, internally, in platform-specific format). Then it is parsed to
produce an std::vector of stack frame structs: { char *module, *function,
*filename, *linenumber, *offset } (some fields may be NULL) and is written
into the trace file in the Enter call section as an Array with specific call
detail flag previously emitted.
A platform-dependent mechanism is provided to specify a set of traced
calls for which backtraces will be recorded. It is possible to specify
either function names, or prefixes of names by appending a '*' (e.g.
"glUniform*").
On Android the backtrace is retrieved from Dalvik via libdvm functions
imported at runtime.
Function set is specified in /data/apitrace.fnames, one per line.
On Linux the backtrace is retrieved via glibc backtrace(), and will not always
yield filename:linenumber information.
Function set is specified via APITRACE_BT_FUNCTIONS environment variable.
On other platforms, obtaining a backtrace is not implemented by this patch.
---
CMakeLists.txt | 1 +
common/trace_backtrace.cpp | 447 +++++++++++++++++++++++++++++++++++++++++
common/trace_backtrace.hpp | 75 +++++++
common/trace_dump.cpp | 33 +++
common/trace_format.hpp | 6 +-
common/trace_model.hpp | 9 +-
common/trace_parser.cpp | 8 +-
common/trace_writer.cpp | 27 ++-
common/trace_writer.hpp | 9 +-
common/trace_writer_local.cpp | 21 +-
common/trace_writer_local.hpp | 12 +-
common/trace_writer_model.cpp | 5 +
gui/apitracecall.cpp | 41 ++++
gui/apitracecall.h | 5 +
gui/mainwindow.cpp | 10 +
gui/ui/mainwindow.ui | 19 ++
wrappers/gltrace.py | 2 +-
wrappers/trace.py | 4 +-
18 files changed, 710 insertions(+), 24 deletions(-)
create mode 100644 common/trace_backtrace.cpp
create mode 100644 common/trace_backtrace.hpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9394282..6ac09ee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -314,6 +314,7 @@ add_library (common STATIC
common/trace_profiler.cpp
common/trace_option.cpp
common/${os}
+ common/trace_backtrace.cpp
)
set_target_properties (common PROPERTIES
diff --git a/common/trace_backtrace.cpp b/common/trace_backtrace.cpp
new file mode 100644
index 0000000..23f96f6
--- /dev/null
+++ b/common/trace_backtrace.cpp
@@ -0,0 +1,447 @@
+/**************************************************************************
+ *
+ * Copyright 2013 Samsung
+ * Contributed by Eugene Velesevich
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Wrapper for platform-specific code for obtaining symbolic backtraces
+ * on Android and Linux
+ */
+
+
+
+#include "trace_backtrace.hpp"
+
+#if defined(ANDROID) or defined(__linux__)
+
+#include <set>
+#include "os.hpp"
+
+
+namespace trace {
+
+/*
+ * Pascal string (with zero terminator optionally omitted)
+ * This is a helper class for storing a set of exact strings or prefixes
+ * to match a zero-terminated string against later.
+ * Two zero-terminated pstrings compare equal iff they are the same.
+ * Otherwise, they compare equal iff one is a prefix of the other
+ * (a zero-terminated pstring cannot be a prefix)
+ */
+
+struct pstring {
+ const char* s;
+ int n;
+ pstring(const char* s, int n)
+ {
+ this->s = s;
+ this->n = n;
+ }
+ bool operator<(const pstring q2) const
+ {
+ return memcmp(s, q2.s, n < q2.n? n : q2.n) < 0;
+ }
+};
+
+
+#define PREFIX_BUF_SIZE (PREFIX_MAX_FUNC_NAME * MAX_BT_FUNC)
+
+class StringPrefixes {
+private:
+ std::set<pstring> pset;
+ char* buf;
+private:
+ void addPrefix(char* startbuf, int n) {
+ std::set<pstring>::iterator elem = pset.find(pstring(startbuf, n));
+ bool replace = elem != pset.end() && n < elem->n;
+ if (replace) {
+ pset.erase(elem);
+ }
+ if (replace || elem == pset.end()) {
+ pset.insert(pstring(startbuf, n));
+ }
+ }
+public:
+ StringPrefixes(const char* source);
+
+ bool contain(const char* s) {
+ if (pset.find(pstring(s, strlen(s) + 1)) != pset.end()) {
+ os::log("Backtrace for %s is enabled", s);
+ return true;
+ }
+ return false;
+ }
+};
+
+bool backtrace_is_needed(const char* fname) {
+ static StringPrefixes backtraceFunctionNamePrefixes(APITRACE_FNAMES_SOURCE);
+ return backtraceFunctionNamePrefixes.contain(fname);
+}
+
+} /* namespace trace */
+
+#ifdef ANDROID
+
+#include <dlfcn.h>
+#include "os.hpp"
+#include <vector>
+
+namespace trace {
+
+StringPrefixes::StringPrefixes(const char* source) {
+ buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);
+ char* startbuf = buf;
+ int n = 0;
+ FILE* f = fopen(source, "r");
+ if (f == NULL) {
+ os::log("Cannot open " APITRACE_FNAMES_FILE);
+ }
+ while ((startbuf = fgets(startbuf, PREFIX_MAX_FUNC_NAME, f))) {
+ n = strlen(startbuf);
+ if (startbuf[n - 1] == '\n') {
+ n--;
+ }
+ if (n > 2 && startbuf[0] != '#') {
+ int psize;
+ if (startbuf[n - 1] != '*') {
+ startbuf[n] = '\0';
+ psize = n + 1;
+ }
+ else {
+ psize = n - 1;
+ }
+ addPrefix(startbuf, psize);
+ startbuf += n + 1;
+ n = 0;
+ }
+ }
+ fclose(f);
+}
+
+
+/* The following two declarations are copied from Android sources */
+
+enum DebugTargetKind {
+ kDebugTargetUnknown = 0,
+ kDebugTargetLog,
+ kDebugTargetFile,
+};
+
+struct DebugOutputTarget {
+ DebugTargetKind which;
+
+ union {
+ struct {
+ int priority;
+ const char* tag;
+ } log;
+ struct {
+ FILE* fp;
+ } file;
+ } data;
+};
+
+#define THREAD_SELF_NAME "_Z13dvmThreadSelfv"
+#define CREATE_DEBUG_TARGET_NAME "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE"
+#define DUMP_BACKTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"
+
+
+class DalvikBacktraceProvider {
+private:
+ bool errorOccured;
+ void* (*threadself)(void);
+ FILE* streamInMemory;
+ char* buf;
+ size_t bufSize;
+ void (*dumpBacktrace)(const DebugOutputTarget*, void*);
+ DebugOutputTarget debugTarget;
+public:
+ DalvikBacktraceProvider() {
+ FILE* (*open_memstream_exp)(char**, size_t*);
+ void (*createDebugTarget)(DebugOutputTarget*, FILE*);
+ void* handle = dlopen("/system/lib/libdvm.so", 0);
+ errorOccured = true;
+ if (!handle) {
+ os::log("dlopen failed\n");
+ return;
+ }
+ threadself = (void* (*)())dlsym(handle, THREAD_SELF_NAME);
+ if (threadself == NULL) {
+ os::log("dlsym ThreadSelf failed\n");
+ return;
+ }
+ createDebugTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, CREATE_DEBUG_TARGET_NAME);
+ if (createDebugTarget == NULL) {
+ os::log("dlsym CreateFileOutput failed\n");
+ return;
+ }
+ dumpBacktrace = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, DUMP_BACKTRACE_NAME);
+ if (dumpBacktrace == NULL) {
+ os::log("dlsym DumpThreadStack failed\n");
+ return;
+ }
+ void* handle2 = dlopen("/system/lib/libcutils.so", 0);
+ if (!handle2) {
+ os::log("dlopen failed\n");
+ return;
+ }
+ open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");
+ if (open_memstream_exp == NULL) {
+ os::log("dlsym open_memstream failed\n");
+ return;
+ }
+ streamInMemory = open_memstream_exp(&buf, &bufSize);
+ if (streamInMemory == NULL) {
+ os::log("open_memstream failed\n");
+ return;
+ }
+ createDebugTarget(&debugTarget, streamInMemory);
+ errorOccured = false;
+ }
+
+ inline char* getBacktrace() {
+ if (errorOccured) {
+ return NULL;
+ }
+ rewind(streamInMemory);
+ dumpBacktrace(&debugTarget, threadself());
+ fflush(streamInMemory);
+ return buf;
+ }
+/*
+ * Parse a stack frame, expecting:
+ * " at android.view.HardwareRenderer$GlRenderer.initializeEgl(HardwareRenderer.java:547)"
+ * or
+ * " at android.view.HardwareRenderer$GlRenderer.initializeEgl(Native Method)"
+ */
+ std::vector<StackFrame> parseBacktrace(char *rawBacktrace) {
+ std::vector<StackFrame> parsedBacktrace;
+ char* rawBacktrace_it = rawBacktrace;
+ while (*rawBacktrace_it != '\0') {
+ StackFrame stackFrame;
+ /* skip leading space */
+ while (*rawBacktrace_it == ' ') {
+ rawBacktrace_it++;
+ }
+ /* Skip "at " */
+ rawBacktrace_it += 3;
+ stackFrame.function = rawBacktrace_it;
+ while (*rawBacktrace_it != '(') {
+ rawBacktrace_it++;
+ }
+ *rawBacktrace_it = '\0';
+ stackFrame.filename = rawBacktrace_it + 1;
+ while (*rawBacktrace_it != ':' && *rawBacktrace_it != ')') {
+ rawBacktrace_it++;
+ }
+ if (*rawBacktrace_it == ':') {
+ stackFrame.linenumber = rawBacktrace_it + 1;
+ *rawBacktrace_it = '\0';
+ while (*rawBacktrace_it != ')') {
+ rawBacktrace_it++;
+ }
+ *rawBacktrace_it = '\0';
+ rawBacktrace_it++;
+ }
+ else {
+ stackFrame.filename = NULL;
+ while (*rawBacktrace_it != '\n') {
+ rawBacktrace_it++;
+ }
+ }
+ while (*rawBacktrace_it == '\n' || *rawBacktrace_it == ' ') {
+ rawBacktrace_it++;
+ }
+ parsedBacktrace.push_back(stackFrame); /* module */
+ }
+ return parsedBacktrace;
+ }
+};
+
+std::vector<StackFrame> get_backtrace() {
+ static DalvikBacktraceProvider backtraceProvider;
+ return backtraceProvider.parseBacktrace(backtraceProvider.getBacktrace());
+}
+
+/* end ANDROID */
+#elif defined __linux__
+
+#include <execinfo.h>
+#include <string.h>
+#include <stdlib.h>
+#include <map>
+#include <vector>
+#include <stdio.h>
+
+namespace trace {
+
+
+StringPrefixes::StringPrefixes(const char* source) {
+ buf = (char*)malloc(sizeof(char) * PREFIX_BUF_SIZE);
+ char* startbuf = buf;
+ int n = 0;
+ char* s = getenv(source);
+ char end = ';';
+ if (s == NULL) {
+ return;
+ }
+ *buf = ';';
+ strncpy(buf + 1, s, PREFIX_BUF_SIZE - 2);
+ while (end != '\0') {
+ startbuf++;
+ while (*(startbuf + n) != ';' && *(startbuf + n) != '\0') {
+ n++;
+ }
+ end = startbuf[n];
+ if (n > 2 && startbuf[0] != '#') {
+ int psize;
+ if (startbuf[n - 1] != '*') {
+ startbuf[n] = '\0';
+ psize = n + 1;
+ }
+ else {
+ psize = n - 1;
+ }
+ addPrefix(startbuf, psize);
+ startbuf += n;
+ n = 0;
+ }
+ }
+}
+
+
+#define BT_DEPTH 10
+
+class GlibcBacktraceProvider {
+private:
+ std::map<void*, StackFrame*> cache;
+ /*
+ * Backtrace being returned by glibc backtrace() contains stack frames
+ * belonging to apitrace wrapper module. We count the number of apitrace
+ * functions on the stack to avoid recording these frames.
+ */
+ int numOfNestedFunctions;
+private:
+/*
+ * Parse a stack frame, expecting:
+ * /lib/libc.so.6.1(__libc_start_main+0x50308) [0x2000000000097630]
+ * or
+ * /lib/libc.so.6.1(+0x50308) [0x2000000000097630]
+ * or
+ * /lib/libc.so.6.1() [0x2000000000097630]
+ */
+ StackFrame* parseFrame(void* frame, char* frame_symbol) {
+ if (cache.find(frame) == cache.end()) {
+ char* frame_symbol_copy = new char[strlen(frame_symbol) + 1];
+ strcpy(frame_symbol_copy, frame_symbol);
+ StackFrame* parsedFrame = new StackFrame;
+ char* frame_it = frame_symbol_copy;
+ parsedFrame->module = frame_it;
+ while (true) {
+ switch (*frame_it) {
+ case '(':
+ *frame_it = '\0';
+ frame_it++;
+ if (*frame_it != ')' && *frame_it != '+') {
+ parsedFrame->function = frame_it;
+ while (*frame_it != '+' && *frame_it != ')') {
+ frame_it++;
+ }
+ *frame_it = '\0';
+ frame_it++;
+ }
+ break;
+ case '[':
+ *frame_it = '\0';
+ frame_it++;
+ parsedFrame->offset = frame_it;
+ break;
+ case ']':
+ *frame_it = '\0';
+ cache[frame] = parsedFrame;
+ return parsedFrame;
+ case '\0':
+ cache[frame] = NULL;
+ delete[] frame_symbol_copy;
+ delete[] parsedFrame;
+ return NULL;
+ default:
+ frame_it++;
+ }
+ }
+ }
+ else {
+ return cache[frame];
+ }
+ }
+public:
+ GlibcBacktraceProvider() :
+ numOfNestedFunctions(0) {
+ }
+ std::vector<StackFrame> getParsedBacktrace() {
+ std::vector<StackFrame> parsedBacktrace;
+ void *array[numOfNestedFunctions + BT_DEPTH];
+ size_t size;
+ char **strings;
+ size_t i;
+ const char* firstModule;
+ size = backtrace(array, numOfNestedFunctions + BT_DEPTH);
+ strings = backtrace_symbols(array, size);
+ for (i = numOfNestedFunctions; i < size; i++) {
+ StackFrame* parsedFrame = parseFrame(array[i], strings[i]);
+ if (numOfNestedFunctions == 0) {
+ if (i == 0) {
+ firstModule = parsedFrame->module;
+ }
+ else {
+ if (strcmp(firstModule, parsedFrame->module)) {
+ numOfNestedFunctions = i;
+ free(strings);
+ parsedBacktrace = getParsedBacktrace();
+ numOfNestedFunctions--;
+ return parsedBacktrace;
+ }
+ }
+ } else {
+ if (parsedFrame != NULL) {
+ parsedBacktrace.push_back(*parsedFrame);
+ }
+ }
+ }
+ free(strings);
+ return parsedBacktrace;
+ }
+};
+
+
+std::vector<StackFrame> get_backtrace() {
+ static GlibcBacktraceProvider backtraceProvider;
+ return backtraceProvider.getParsedBacktrace();
+}
+
+#endif /* LINUX */
+
+} /* namespace trace */
+
+#endif /* ANDROID or LINUX */
diff --git a/common/trace_backtrace.hpp b/common/trace_backtrace.hpp
new file mode 100644
index 0000000..4fef75e
--- /dev/null
+++ b/common/trace_backtrace.hpp
@@ -0,0 +1,75 @@
+#ifndef _TRACE_BACKTRACE_HPP_
+#define _TRACE_BACKTRACE_HPP_
+
+#include <vector>
+
+#include "trace_model.hpp"
+
+namespace trace {
+
+
+struct StackFrame {
+ char* module;
+ char* function;
+ char* filename;
+ char* linenumber;
+ char* offset;
+ StackFrame() :
+ module(0),
+ function(0),
+ filename(0),
+ linenumber(0),
+ offset(0)
+ {
+ }
+};
+
+static const char* stackframe_members[5] = {
+ "module",
+ "function",
+ "filename",
+ "linenumber",
+ "offset"
+};
+
+static const trace::StructSig stackframe_sig = {
+ 2, "backtrace", 5, stackframe_members
+};
+
+
+#if defined(ANDROID) or defined(__linux__)
+
+std::vector<StackFrame> get_backtrace();
+bool backtrace_is_needed(const char* fname);
+
+#if defined(ANDROID)
+
+#define MAX_BT_FUNC 20
+#define PREFIX_MAX_FUNC_NAME 100
+#define APITRACE_FNAMES_FILE "/data/apitrace.fnames"
+#define APITRACE_FNAMES_SOURCE APITRACE_FNAMES_FILE
+
+#elif defined(__linux__)
+
+#define MAX_BT_FUNC 20
+#define PREFIX_MAX_FUNC_NAME 100
+#define APITRACE_FNAMES_ENV "APITRACE_BT_FUNCTIONS"
+#define APITRACE_FNAMES_SOURCE APITRACE_FNAMES_ENV
+
+#endif
+
+#else /* !__linux__ && !ANDROID */
+
+static inline std::vector<StackFrame> get_backtrace() {
+ return std::vector<StackFrame>();
+}
+
+static inline bool backtrace_is_needed(const char*) {
+ return false;
+}
+
+#endif
+
+} /* namespace trace */
+
+#endif
diff --git a/common/trace_dump.cpp b/common/trace_dump.cpp
index e6810b6..ff8bb2b 100644
--- a/common/trace_dump.cpp
+++ b/common/trace_dump.cpp
@@ -228,6 +228,35 @@ public:
_visit(r->humanValue);
}
+ void visitBacktrace(std::vector<Value*> backtrace) {
+ for (int i = 0; i < backtrace.size(); i ++) {
+ Struct* frame = dynamic_cast<Struct*>(backtrace[i]);
+ const char* tmp = frame->members[0]->toString(); /* module */
+ if (tmp != NULL) {
+ os << tmp << " ";
+ }
+ tmp = frame->members[1]->toString(); /* function */
+ if (tmp != NULL) {
+ os << "at " << tmp << "() ";
+ }
+ tmp = frame->members[2]->toString(); /* file */
+ if (tmp != NULL) {
+ os << "at " << tmp;
+ tmp = frame->members[3]->toString(); /* line */
+ if (tmp != NULL) {
+ os << ":" << tmp << " ";
+ }
+ }
+ else {
+ tmp = frame->members[4]->toString(); /* offset */
+ if (tmp != NULL) {
+ os << "[" << tmp << "]";
+ }
+ }
+ os << "\n";
+ }
+ }
+
void visit(Call *call) {
CallFlags callFlags = call->flags;
@@ -271,6 +300,10 @@ public:
os << "\n";
+ if (call->backtrace != NULL) {
+ os << bold << red << "Backtrace:\n" << normal;
+ visitBacktrace(call->backtrace->values);
+ }
if (callFlags & CALL_FLAG_END_FRAME) {
os << "\n";
}
diff --git a/common/trace_format.hpp b/common/trace_format.hpp
index d5fd81b..0bb8ee0 100644
--- a/common/trace_format.hpp
+++ b/common/trace_format.hpp
@@ -69,8 +69,11 @@ namespace trace {
*
* - version 4:
* - call enter events include thread ID
+ *
+ * - version 5:
+ * - new call detail flag CALL_BACKTRACE
*/
-#define TRACE_VERSION 4
+#define TRACE_VERSION 5
/*
@@ -127,6 +130,7 @@ enum CallDetail {
CALL_ARG,
CALL_RET,
CALL_THREAD,
+ CALL_BACKTRACE,
};
enum Type {
diff --git a/common/trace_model.hpp b/common/trace_model.hpp
index 9558225..b47b692 100644
--- a/common/trace_model.hpp
+++ b/common/trace_model.hpp
@@ -49,6 +49,7 @@ struct FunctionSig {
const char *name;
unsigned num_args;
const char **arg_names;
+ bool backtrace;
};
@@ -463,18 +464,20 @@ class Call
public:
unsigned thread_id;
unsigned no;
- const FunctionSig *sig;
+ FunctionSig *sig;
std::vector<Arg> args;
Value *ret;
CallFlags flags;
+ Array* backtrace;
- Call(const FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :
+ Call(FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) :
thread_id(_thread_id),
sig(_sig),
args(_sig->num_args),
ret(0),
- flags(_flags) {
+ flags(_flags),
+ backtrace(0) {
}
~Call();
diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp
index c70517e..f699353 100644
--- a/common/trace_parser.cpp
+++ b/common/trace_parser.cpp
@@ -463,7 +463,7 @@ Call *Parser::parse_leave(Mode mode) {
* between two frames. We won't return this call, but we still need to skip
* over its data.
*/
- const FunctionSig sig = {0, NULL, 0, NULL};
+ FunctionSig sig = {0, NULL, 0, NULL};
call = new Call(&sig, 0, 0);
parse_call_details(call, SCAN);
delete call;
@@ -500,6 +500,12 @@ bool Parser::parse_call_details(Call *call, Mode mode) {
#endif
call->ret = parse_value(mode);
break;
+ case trace::CALL_BACKTRACE:
+#if TRACE_VERBOSE
+ std::cerr << "\tCALL_BACKTRACE\n";
+#endif
+ call->backtrace = (Array*)parse_value(mode);
+ break;
default:
std::cerr << "error: ("<<call->name()<< ") unknown call detail "
<< c << "\n";
diff --git a/common/trace_writer.cpp b/common/trace_writer.cpp
index d69e93f..b041d44 100644
--- a/common/trace_writer.cpp
+++ b/common/trace_writer.cpp
@@ -29,12 +29,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <vector>
#include "os.hpp"
#include "trace_file.hpp"
#include "trace_writer.hpp"
#include "trace_format.hpp"
-
+#include "trace_backtrace.hpp"
namespace trace {
@@ -134,11 +135,33 @@ inline bool lookup(std::vector<bool> &map, size_t index) {
}
}
-unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) {
+void Writer::writeBacktrace(std::vector<StackFrame> backtrace) {
+
+ beginArray(backtrace.size());
+ for (int i = 0; i < backtrace.size(); i++) {
+ beginElement();
+ beginStruct(&stackframe_sig);
+ writeString(backtrace[i].module);
+ writeString(backtrace[i].function);
+ writeString(backtrace[i].filename);
+ writeString(backtrace[i].linenumber);
+ writeString(backtrace[i].offset);
+ endStruct();
+ endElement();
+ }
+ endArray();
+}
+
+void Writer::beginBacktrace(void ) {
+ _writeByte(trace::CALL_BACKTRACE);
+}
+
+unsigned Writer::beginEnter(FunctionSig *sig, unsigned thread_id) {
_writeByte(trace::EVENT_ENTER);
_writeUInt(thread_id);
_writeUInt(sig->id);
if (!lookup(functions, sig->id)) {
+ sig->backtrace = backtrace_is_needed(sig->name);
_writeString(sig->name);
_writeUInt(sig->num_args);
for (unsigned i = 0; i < sig->num_args; ++i) {
diff --git a/common/trace_writer.hpp b/common/trace_writer.hpp
index a46b43e..4070052 100644
--- a/common/trace_writer.hpp
+++ b/common/trace_writer.hpp
@@ -36,7 +36,7 @@
#include <vector>
#include "trace_model.hpp"
-
+#include "trace_backtrace.hpp"
namespace trace {
class File;
@@ -58,7 +58,12 @@ namespace trace {
bool open(const char *filename);
void close(void);
- unsigned beginEnter(const FunctionSig *sig, unsigned thread_id);
+ void writeBacktrace(std::vector<StackFrame> backtrace);
+ void beginBacktrace(void);
+ inline void endBacktrace(void) {
+ }
+
+ unsigned beginEnter(FunctionSig *sig, unsigned thread_id);
void endEnter(void);
void beginLeave(unsigned call);
diff --git a/common/trace_writer_local.cpp b/common/trace_writer_local.cpp
index feb5c91..760861d 100644
--- a/common/trace_writer_local.cpp
+++ b/common/trace_writer_local.cpp
@@ -36,22 +36,23 @@
#include "trace_file.hpp"
#include "trace_writer_local.hpp"
#include "trace_format.hpp"
+#include "trace_backtrace.hpp"
namespace trace {
static const char *memcpy_args[3] = {"dest", "src", "n"};
-const FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args};
+FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args, false};
static const char *malloc_args[1] = {"size"};
-const FunctionSig malloc_sig = {1, "malloc", 1, malloc_args};
+FunctionSig malloc_sig = {1, "malloc", 1, malloc_args, false};
static const char *free_args[1] = {"ptr"};
-const FunctionSig free_sig = {2, "free", 1, free_args};
+FunctionSig free_sig = {2, "free", 1, free_args, false};
static const char *realloc_args[2] = {"ptr", "size"};
-const FunctionSig realloc_sig = {3, "realloc", 2, realloc_args};
+FunctionSig realloc_sig = {3, "realloc", 2, realloc_args, false};
static void exceptionCallback(void)
@@ -131,7 +132,7 @@ LocalWriter::open(void) {
static unsigned next_thread_num = 1;
static thread_specific unsigned thread_num = 0;
-unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
+unsigned LocalWriter::beginEnter(FunctionSig *sig) {
mutex.lock();
++acquired;
@@ -146,7 +147,15 @@ unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
assert(thread_num > 0);
unsigned thread_id = thread_num - 1;
- return Writer::beginEnter(sig, thread_id);
+
+ unsigned call_no = Writer::beginEnter(sig, thread_id);
+ if (sig->backtrace) {
+ std::vector<StackFrame> backtrace = get_backtrace();
+ beginBacktrace();
+ writeBacktrace(backtrace);
+ endBacktrace();
+ }
+ return call_no;
}
void LocalWriter::endEnter(void) {
diff --git a/common/trace_writer_local.hpp b/common/trace_writer_local.hpp
index cc5dda0..1da2250 100644
--- a/common/trace_writer_local.hpp
+++ b/common/trace_writer_local.hpp
@@ -39,10 +39,10 @@
namespace trace {
- extern const FunctionSig memcpy_sig;
- extern const FunctionSig malloc_sig;
- extern const FunctionSig free_sig;
- extern const FunctionSig realloc_sig;
+ extern FunctionSig memcpy_sig;
+ extern FunctionSig malloc_sig;
+ extern FunctionSig free_sig;
+ extern FunctionSig realloc_sig;
/**
* A specialized Writer class, mean to trace the current process.
@@ -83,8 +83,8 @@ namespace trace {
/**
* It will acquire the mutex.
*/
- unsigned beginEnter(const FunctionSig *sig);
-
+ unsigned beginEnter(FunctionSig *sig);
+
/**
* It will release the mutex.
*/
diff --git a/common/trace_writer_model.cpp b/common/trace_writer_model.cpp
index 2e858a2..f369172 100644
--- a/common/trace_writer_model.cpp
+++ b/common/trace_writer_model.cpp
@@ -109,6 +109,11 @@ public:
void visit(Call *call) {
unsigned call_no = writer.beginEnter(call->sig, call->thread_id);
+ if (call->backtrace != NULL) {
+ writer.beginBacktrace();
+ _visit(call->backtrace);
+ writer.endBacktrace();
+ }
for (unsigned i = 0; i < call->args.size(); ++i) {
if (call->args[i].value) {
writer.beginArg(i);
diff --git a/gui/apitracecall.cpp b/gui/apitracecall.cpp
index 3377486..cc2826f 100644
--- a/gui/apitracecall.cpp
+++ b/gui/apitracecall.cpp
@@ -685,6 +685,37 @@ ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,
}
m_argValues.squeeze();
m_flags = call->flags;
+ if (call->backtrace != NULL) {
+ std::vector<trace::Value*> backtrace = call->backtrace->values;
+ QString qbacktrace;
+ for (int i = 0; i < backtrace.size(); i++) {
+ trace::Struct* frame = dynamic_cast<trace::Struct*>(backtrace[i]);
+ const char* tmp = frame->members[0]->toString(); /* module */
+ if (tmp != NULL) {
+ qbacktrace += QString("%1 ").arg(tmp);
+ }
+ tmp = frame->members[1]->toString(); /* function */
+ if (tmp != NULL) {
+ qbacktrace += QString("at %1() ").arg(tmp);
+ }
+ tmp = frame->members[2]->toString(); /* file */
+ if (tmp != NULL) {
+ qbacktrace += QString("at %1").arg(tmp);
+ tmp = frame->members[3]->toString(); /* line */
+ if (tmp != NULL) {
+ qbacktrace += QString(":%1 ").arg(tmp);
+ }
+ }
+ else {
+ tmp = frame->members[4]->toString(); /* offset */
+ if (tmp != NULL) {
+ qbacktrace += QString("[%1]").arg(tmp);
+ }
+ }
+ qbacktrace += "\n";
+ }
+ this->setBacktrace(qbacktrace);
+ }
}
ApiTraceCall::~ApiTraceCall()
@@ -820,6 +851,16 @@ int ApiTraceCall::binaryDataIndex() const
return m_binaryDataIndex;
}
+QString ApiTraceCall::backtrace() const
+{
+ return m_backtrace;
+}
+
+void ApiTraceCall::setBacktrace(QString backtrace)
+{
+ m_backtrace = backtrace;
+}
+
QStaticText ApiTraceCall::staticText() const
{
if (m_staticText && !m_staticText->text().isEmpty())
diff --git a/gui/apitracecall.h b/gui/apitracecall.h
index 6c6b607..8004ced 100644
--- a/gui/apitracecall.h
+++ b/gui/apitracecall.h
@@ -279,6 +279,9 @@ public:
int numChildren() const;
bool hasBinaryData() const;
int binaryDataIndex() const;
+
+ QString backtrace() const;
+ void setBacktrace(QString backtrace);
private:
int m_index;
ApiTraceCallSignature *m_signature;
@@ -291,6 +294,8 @@ private:
QString m_error;
+ QString m_backtrace;
+
mutable QString m_richText;
mutable QString m_searchText;
};
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp
index 8905fbb..e46fce6 100644
--- a/gui/mainwindow.cpp
+++ b/gui/mainwindow.cpp
@@ -121,6 +121,13 @@ void MainWindow::callItemSelected(const QModelIndex &index)
if (event && event->type() == ApiTraceEvent::Call) {
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
+ if (!call->backtrace().isNull()) {
+ m_ui.backtraceBrowser->setText(call->backtrace());
+ m_ui.backtraceDock->show();
+ }
+ else {
+ m_ui.backtraceDock->hide();
+ }
m_ui.detailsDock->setWindowTitle(
tr("Details View. Frame %1, Call %2")
.arg(call->parentFrame() ? call->parentFrame()->number : 0)
@@ -159,6 +166,7 @@ void MainWindow::callItemSelected(const QModelIndex &index)
m_selectedEvent = 0;
}
m_ui.detailsDock->hide();
+ m_ui.backtraceDock->hide();
m_ui.vertexDataDock->hide();
}
if (m_selectedEvent && m_selectedEvent->hasState()) {
@@ -762,6 +770,7 @@ void MainWindow::initObjects()
m_argsEditor = new ArgumentsEditor(this);
m_ui.detailsDock->hide();
+ m_ui.backtraceDock->hide();
m_ui.errorsDock->hide();
m_ui.vertexDataDock->hide();
m_ui.stateDock->hide();
@@ -769,6 +778,7 @@ void MainWindow::initObjects()
tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
+ tabifyDockWidget(m_ui.detailsDock, m_ui.backtraceDock);
m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
diff --git a/gui/ui/mainwindow.ui b/gui/ui/mainwindow.ui
index 06f4503..75b345a 100644
--- a/gui/ui/mainwindow.ui
+++ b/gui/ui/mainwindow.ui
@@ -432,6 +432,9 @@
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTreeWidget" name="errorsTreeWidget">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
<column>
<property name="text">
<string>Index</string>
@@ -452,6 +455,21 @@
</layout>
</widget>
</widget>
+ <widget class="QDockWidget" name="backtraceDock">
+ <property name="windowTitle">
+ <string>Backtrace</string>
+ </property>
+ <attribute name="dockWidgetArea">
+ <number>8</number>
+ </attribute>
+ <widget class="QWidget" name="dockWidgetContents">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QTextBrowser" name="backtraceBrowser"/>
+ </item>
+ </layout>
+ </widget>
+ </widget>
<action name="actionExit">
<property name="text">
<string>Exit</string>
@@ -636,6 +654,7 @@
<zorder>stateDock</zorder>
<zorder>vertexDataDock</zorder>
<zorder>errorsDock</zorder>
+ <zorder>backtraceDock</zorder>
</widget>
<customwidgets>
<customwidget>
diff --git a/wrappers/gltrace.py b/wrappers/gltrace.py
index c40fbb3..fcb48bb 100644
--- a/wrappers/gltrace.py
+++ b/wrappers/gltrace.py
@@ -520,7 +520,7 @@ class GlTracer(Tracer):
# Emit a fake function
print ' {'
- print ' static const trace::FunctionSig &_sig = %s ? _glEnableClientState_sig : _glDisableClientState_sig;' % flag_name
+ print ' static trace::FunctionSig &_sig = %s ? _glEnableClientState_sig : _glDisableClientState_sig;' % flag_name
print ' unsigned _call = trace::localWriter.beginEnter(&_sig);'
print ' trace::localWriter.beginArg(0);'
self.serializeValue(glapi.GLenum, enable_name)
diff --git a/wrappers/trace.py b/wrappers/trace.py
index d9c2900..9ff5410 100644
--- a/wrappers/trace.py
+++ b/wrappers/trace.py
@@ -452,7 +452,7 @@ class Tracer:
print 'static const char * _%s_args[%u] = {%s};' % (function.name, len(function.args), ', '.join(['"%s"' % arg.name for arg in function.args]))
else:
print 'static const char ** _%s_args = NULL;' % (function.name,)
- print 'static const trace::FunctionSig _%s_sig = {%u, "%s", %u, _%s_args};' % (function.name, self.getFunctionSigId(), function.name, len(function.args), function.name)
+ print 'static trace::FunctionSig _%s_sig = {%u, "%s", %u, _%s_args};' % (function.name, self.getFunctionSigId(), function.name, len(function.args), function.name)
print
def getFunctionSigId(self):
@@ -685,7 +685,7 @@ class Tracer:
assert not method.internal
print ' static const char * _args[%u] = {%s};' % (len(method.args) + 1, ', '.join(['"this"'] + ['"%s"' % arg.name for arg in method.args]))
- print ' static const trace::FunctionSig _sig = {%u, "%s", %u, _args};' % (self.getFunctionSigId(), interface.name + '::' + method.name, len(method.args) + 1)
+ print ' static trace::FunctionSig _sig = {%u, "%s", %u, _args};' % (self.getFunctionSigId(), interface.name + '::' + method.name, len(method.args) + 1)
print ' %s *_this = static_cast<%s *>(m_pInstance);' % (base, base)
--
1.7.9.5
More information about the apitrace
mailing list