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

Commit 93a4a7f0 authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Use libcorkscrew for stack unwinding."

parents d706882f e27d62bb
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