Use libcorkscrew for stack unwinding.
Change-Id: Iee1ee5a2018ab8cfc1ce12ec2a124809245eaa02
This commit is contained in:
parent
f17f5a24c7
commit
e27d62bbf2
@ -21,6 +21,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <utils/String8.h>
|
#include <utils/String8.h>
|
||||||
|
#include <corkscrew/backtrace.h>
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -61,11 +62,8 @@ public:
|
|||||||
size_t size() const { return mCount; }
|
size_t size() const { return mCount; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Internal helper function
|
size_t mCount;
|
||||||
String8 toStringSingleLevel(const char* prefix, int32_t level) const;
|
backtrace_frame_t mStack[MAX_DEPTH];
|
||||||
|
|
||||||
size_t mCount;
|
|
||||||
const void* mStack[MAX_DEPTH];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace android
|
}; // namespace android
|
||||||
|
@ -105,7 +105,8 @@ LOCAL_SHARED_LIBRARIES := \
|
|||||||
libz \
|
libz \
|
||||||
liblog \
|
liblog \
|
||||||
libcutils \
|
libcutils \
|
||||||
libdl
|
libdl \
|
||||||
|
libcorkscrew
|
||||||
|
|
||||||
LOCAL_MODULE:= libutils
|
LOCAL_MODULE:= libutils
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
@ -17,218 +17,33 @@
|
|||||||
#define LOG_TAG "CallStack"
|
#define LOG_TAG "CallStack"
|
||||||
|
|
||||||
#include <string.h>
|
#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/Log.h>
|
||||||
#include <utils/Errors.h>
|
#include <utils/Errors.h>
|
||||||
#include <utils/CallStack.h>
|
#include <utils/CallStack.h>
|
||||||
#include <utils/threads.h>
|
#include <corkscrew/backtrace.h>
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
|
CallStack::CallStack() :
|
||||||
typedef struct {
|
mCount(0) {
|
||||||
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
|
CallStack::CallStack(const CallStack& rhs) :
|
||||||
int backtrace(const void** addrs, size_t ignore, size_t size)
|
mCount(rhs.mCount) {
|
||||||
{
|
|
||||||
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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~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)
|
|
||||||
{
|
|
||||||
if (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;
|
mCount = rhs.mCount;
|
||||||
if (mCount) {
|
if (mCount) {
|
||||||
memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
|
memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -236,7 +51,7 @@ CallStack& CallStack::operator = (const CallStack& rhs)
|
|||||||
bool CallStack::operator == (const CallStack& rhs) const {
|
bool CallStack::operator == (const CallStack& rhs) const {
|
||||||
if (mCount != rhs.mCount)
|
if (mCount != rhs.mCount)
|
||||||
return false;
|
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 {
|
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 {
|
bool CallStack::operator < (const CallStack& rhs) const {
|
||||||
if (mCount != rhs.mCount)
|
if (mCount != rhs.mCount)
|
||||||
return 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 {
|
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 {
|
bool CallStack::operator > (const CallStack& rhs) const {
|
||||||
if (mCount != rhs.mCount)
|
if (mCount != rhs.mCount)
|
||||||
return 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 {
|
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 {
|
const void* CallStack::operator [] (int index) const {
|
||||||
if (index >= int(mCount))
|
if (index >= int(mCount))
|
||||||
return 0;
|
return 0;
|
||||||
return mStack[index];
|
return reinterpret_cast<const void*>(mStack[index].absolute_pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallStack::clear() {
|
||||||
void CallStack::clear()
|
|
||||||
{
|
|
||||||
mCount = 0;
|
mCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallStack::update(int32_t ignoreDepth, int32_t maxDepth)
|
void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
|
||||||
{
|
if (maxDepth > MAX_DEPTH) {
|
||||||
if (maxDepth > MAX_DEPTH)
|
|
||||||
maxDepth = MAX_DEPTH;
|
maxDepth = MAX_DEPTH;
|
||||||
mCount = backtrace(mStack, ignoreDepth, maxDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
} 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");
|
ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
|
||||||
|
mCount = count > 0 ? count : 0;
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump a stack trace to the log
|
void CallStack::dump(const char* prefix) const {
|
||||||
void CallStack::dump(const char* prefix) const
|
backtrace_symbol_t symbols[mCount];
|
||||||
{
|
|
||||||
/*
|
get_backtrace_symbols(mStack, mCount, symbols);
|
||||||
* Sending a single long log may be truncated since the stack levels can
|
for (size_t i = 0; i < mCount; i++) {
|
||||||
* get very deep. So we request function names of each frame individually.
|
const backtrace_frame_t& frame = mStack[i];
|
||||||
*/
|
const backtrace_symbol_t& symbol = symbols[i];
|
||||||
for (int i=0; i<int(mCount); i++) {
|
const char* mapName = symbol.map_info ? symbol.map_info->name : "<unknown>";
|
||||||
LOGD("%s", toStringSingleLevel(prefix, i).string());
|
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 {
|
||||||
|
LOGD("%s#%02d pc %08x %s\n", prefix,
|
||||||
|
int(i), uint32_t(symbol.relative_pc), mapName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
free_backtrace_symbols(symbols, mCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a string (possibly very long) containing the complete stack trace
|
String8 CallStack::toString(const char* prefix) const {
|
||||||
String8 CallStack::toString(const char* prefix) const
|
String8 str;
|
||||||
{
|
backtrace_symbol_t symbols[mCount];
|
||||||
String8 res;
|
|
||||||
|
|
||||||
for (int i=0; i<int(mCount); i++) {
|
get_backtrace_symbols(mStack, mCount, symbols);
|
||||||
res.append(toStringSingleLevel(prefix, i).string());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
free_backtrace_symbols(symbols, mCount);
|
||||||
return res;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
}; // namespace android
|
}; // namespace android
|
||||||
|
Loading…
Reference in New Issue
Block a user