[PATCH v3] Backtrace via call detail

José Fonseca jose.r.fonseca at gmail.com
Tue Apr 23 00:40:04 PDT 2013


Thanks. It's much closer of what I had in mind.

However, instead of encoding the stack traces as an array of structures,
I'd really prefer encoding the stack traces with the scheme I illustrated
earlier (where symbol, filename, lineno are optional details of a stack
frame node). It's fine if when you parse the traces you encode them with
arrays of structures (though I don't see much benefit). My main concern is
just the file format, as everything else can be improved later on.

Jose


On Tue, Apr 16, 2013 at 11:20 AM, Eugene Velesevich <evel at ispras.ru> wrote:

> 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
>
> _______________________________________________
> apitrace mailing list
> apitrace at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/apitrace
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/apitrace/attachments/20130423/c1cc43e3/attachment-0001.html>


More information about the apitrace mailing list