Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e27d62bb authored by Jeff Brown's avatar Jeff Brown
Browse files

Use libcorkscrew for stack unwinding.

Change-Id: Iee1ee5a2018ab8cfc1ce12ec2a124809245eaa02
parent f17f5a24
Loading
Loading
Loading
Loading
+3 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <sys/types.h>

#include <utils/String8.h>
#include <corkscrew/backtrace.h>

// ---------------------------------------------------------------------------

@@ -61,11 +62,8 @@ public:
    size_t size() const { return mCount; }

private:
    // Internal helper function
    String8 toStringSingleLevel(const char* prefix, int32_t level) const;

    size_t mCount;
    const void* mStack[MAX_DEPTH];
    backtrace_frame_t mStack[MAX_DEPTH];
};

}; // namespace android
+2 −1
Original line number Diff line number Diff line
@@ -105,7 +105,8 @@ LOCAL_SHARED_LIBRARIES := \
	libz \
	liblog \
	libcutils \
	libdl
	libdl \
	libcorkscrew

LOCAL_MODULE:= libutils
include $(BUILD_SHARED_LIBRARY)
+58 −266
Original line number Diff line number Diff line
@@ -17,218 +17,33 @@
#define LOG_TAG "CallStack"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#if HAVE_DLADDR
#include <dlfcn.h>
#endif

#if HAVE_CXXABI
#include <cxxabi.h>
#endif

#include <unwind.h>

#include <utils/Log.h>
#include <utils/Errors.h>
#include <utils/CallStack.h>
#include <utils/threads.h>

#include <corkscrew/backtrace.h>

/*****************************************************************************/
namespace android {


typedef struct {
    size_t count;
    size_t ignore;
    const void** addrs;
} stack_crawl_state_t;

static
_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg)
{
    stack_crawl_state_t* state = (stack_crawl_state_t*)arg;
    if (state->count) {
        void* ip = (void*)_Unwind_GetIP(context);
        if (ip) {
            if (state->ignore) {
                state->ignore--;
            } else {
                state->addrs[0] = ip; 
                state->addrs++;
                state->count--;
            }
        }
    }
    return _URC_NO_REASON;
}

static
int backtrace(const void** addrs, size_t ignore, size_t size)
{
    stack_crawl_state_t state;
    state.count = size;
    state.ignore = ignore;
    state.addrs = addrs;
    _Unwind_Backtrace(trace_function, (void*)&state);
    return size - state.count;
}

/*****************************************************************************/

static 
const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize)
{
#if HAVE_DLADDR
    Dl_info info;
    if (dladdr(addr, &info)) {
        *offset = info.dli_saddr;
        return info.dli_sname;
    }
#endif
    return NULL;
}

static 
int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize)
{
    size_t out_len = 0;
#if HAVE_CXXABI
    int status = 0;
    char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status);
    if (status == 0) {
        // OK
        if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len);
        else out_len = 0;
        free(demangled);
    } else {
        out_len = 0;
    }
#endif
    return out_len;
}

/*****************************************************************************/

class MapInfo {
    struct mapinfo {
        struct mapinfo *next;
        uint64_t start;
        uint64_t end;
        char name[];
    };

    const char *map_to_name(uint64_t pc, const char* def, uint64_t* start) {
        mapinfo* mi = getMapInfoList();
        while(mi) {
            if ((pc >= mi->start) && (pc < mi->end)) {
                if (start) 
                    *start = mi->start;
                return mi->name;
            }
            mi = mi->next;
        }
        if (start) 
            *start = 0;
        return def;
    }

    mapinfo *parse_maps_line(char *line) {
        mapinfo *mi;
        int len = strlen(line);
        if (len < 1) return 0;
        line[--len] = 0;
        if (len < 50) return 0;
        if (line[20] != 'x') return 0;
        mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47));
        if (mi == 0) return 0;
        mi->start = strtoull(line, 0, 16);
        mi->end = strtoull(line + 9, 0, 16);
        mi->next = 0;
        strcpy(mi->name, line + 49);
        return mi;
    }

    mapinfo* getMapInfoList() {
        Mutex::Autolock _l(mLock);
        if (milist == 0) {
            char data[1024];
            FILE *fp;
            sprintf(data, "/proc/%d/maps", getpid());
            fp = fopen(data, "r");
            if (fp) {
                while(fgets(data, 1024, fp)) {
                    mapinfo *mi = parse_maps_line(data);
                    if(mi) {
                        mi->next = milist;
                        milist = mi;
                    }
                }
                fclose(fp);
            }
        }
        return milist;
    }
    mapinfo*    milist;
    Mutex       mLock;
    static MapInfo sMapInfo;

public:
    MapInfo()
     : milist(0) {
CallStack::CallStack() :
        mCount(0) {
}

    ~MapInfo() {
        while (milist) {
            mapinfo *next = milist->next;
            free(milist);
            milist = next;
        }
    }
    
    static const char *mapAddressToName(const void* pc, const char* def,
            void const** start) 
    {
        uint64_t s;
        char const* name = sMapInfo.map_to_name(uint64_t(uintptr_t(pc)), def, &s);
        if (start) {
            *start = (void*)s;
        }
        return name;
    }

};

/*****************************************************************************/

MapInfo MapInfo::sMapInfo;

/*****************************************************************************/

CallStack::CallStack()
    : mCount(0)
{
}

CallStack::CallStack(const CallStack& rhs)
    : mCount(rhs.mCount)
{
CallStack::CallStack(const CallStack& rhs) :
        mCount(rhs.mCount) {
    if (mCount) {
        memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
        memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
    }
}

CallStack::~CallStack()
{
CallStack::~CallStack() {
}

CallStack& CallStack::operator = (const CallStack& rhs)
{
CallStack& CallStack::operator = (const CallStack& rhs) {
    mCount = rhs.mCount;
    if (mCount) {
        memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
        memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
    }
    return *this;
}
@@ -236,7 +51,7 @@ CallStack& CallStack::operator = (const CallStack& rhs)
bool CallStack::operator == (const CallStack& rhs) const {
    if (mCount != rhs.mCount)
        return false;
    return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0);
    return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0;
}

bool CallStack::operator != (const CallStack& rhs) const {
@@ -246,7 +61,7 @@ bool CallStack::operator != (const CallStack& rhs) const {
bool CallStack::operator < (const CallStack& rhs) const {
    if (mCount != rhs.mCount)
        return mCount < rhs.mCount;
    return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0;
    return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0;
}

bool CallStack::operator >= (const CallStack& rhs) const {
@@ -256,7 +71,7 @@ bool CallStack::operator >= (const CallStack& rhs) const {
bool CallStack::operator > (const CallStack& rhs) const {
    if (mCount != rhs.mCount)
        return mCount > rhs.mCount;
    return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0;
    return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0;
}

bool CallStack::operator <= (const CallStack& rhs) const {
@@ -266,84 +81,61 @@ bool CallStack::operator <= (const CallStack& rhs) const {
const void* CallStack::operator [] (int index) const {
    if (index >= int(mCount))
        return 0;
    return mStack[index];
    return reinterpret_cast<const void*>(mStack[index].absolute_pc);
}


void CallStack::clear()
{
void CallStack::clear() {
    mCount = 0;
}

void CallStack::update(int32_t ignoreDepth, int32_t maxDepth)
{
    if (maxDepth > MAX_DEPTH)
void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
    if (maxDepth > MAX_DEPTH) {
        maxDepth = MAX_DEPTH;
    mCount = backtrace(mStack, ignoreDepth, maxDepth);
    }
    ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
    mCount = count > 0 ? count : 0;
}

// Return the stack frame name on the designated level
String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const
{
    String8 res;
    char namebuf[1024];
    char tmp[256];
    char tmp1[32];
    char tmp2[32];
    void *offs;

    const void* ip = mStack[level];
    if (!ip) return res;

    if (prefix) res.append(prefix);
    snprintf(tmp1, 32, "#%02d  ", level);
    res.append(tmp1);
void CallStack::dump(const char* prefix) const {
    backtrace_symbol_t symbols[mCount];

    const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf));
    if (name) {
        if (linux_gcc_demangler(name, tmp, 256) != 0)
            name = tmp;
        snprintf(tmp1, 32, "0x%p: <", ip);
        snprintf(tmp2, 32, ">+0x%p", offs);
        res.append(tmp1);
        res.append(name);
        res.append(tmp2);
    get_backtrace_symbols(mStack, mCount, symbols);
    for (size_t i = 0; i < mCount; i++) {
        const backtrace_frame_t& frame = mStack[i];
        const backtrace_symbol_t& symbol = symbols[i];
        const char* mapName = symbol.map_info ? symbol.map_info->name : "<unknown>";
        const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name;
        if (symbolName) {
            LOGD("%s#%02d  pc %08x  %s (%s)\n", prefix,
                    int(i), uint32_t(symbol.relative_pc), mapName, symbolName);
        } else {
        void const* start = 0;
        name = MapInfo::mapAddressToName(ip, "<unknown>", &start);
        snprintf(tmp, 256, "pc %08lx  %s", 
                long(uintptr_t(ip)-uintptr_t(start)), name);
        res.append(tmp);
    }
    res.append("\n");

    return res;
            LOGD("%s#%02d  pc %08x  %s\n", prefix,
                    int(i), uint32_t(symbol.relative_pc), mapName);
        }

// Dump a stack trace to the log
void CallStack::dump(const char* prefix) const
{
    /* 
     * Sending a single long log may be truncated since the stack levels can
     * get very deep. So we request function names of each frame individually.
     */
    for (int i=0; i<int(mCount); i++) {
        LOGD("%s", toStringSingleLevel(prefix, i).string());
    }
    free_backtrace_symbols(symbols, mCount);
}

// Return a string (possibly very long) containing the complete stack trace
String8 CallStack::toString(const char* prefix) const
{
    String8 res;
String8 CallStack::toString(const char* prefix) const {
    String8 str;
    backtrace_symbol_t symbols[mCount];

    for (int i=0; i<int(mCount); i++) {
        res.append(toStringSingleLevel(prefix, i).string());
    get_backtrace_symbols(mStack, mCount, symbols);
    for (size_t i = 0; i < mCount; i++) {
        const backtrace_frame_t& frame = mStack[i];
        const backtrace_symbol_t& symbol = symbols[i];
        const char* mapName = symbol.map_info ? symbol.map_info->name : "<unknown>";
        const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name;
        if (symbolName) {
            str.appendFormat("%s#%02d  pc %08x  %s (%s)\n", prefix,
                    int(i), uint32_t(symbol.relative_pc), mapName, symbolName);
        } else {
            str.appendFormat("%s#%02d  pc %08x  %s\n", prefix,
                    int(i), uint32_t(symbol.relative_pc), mapName);
        }

    return res;
    }

/*****************************************************************************/
    free_backtrace_symbols(symbols, mCount);
    return str;
}

}; // namespace android