/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#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>
/*****************************************************************************/
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) {
}
~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) {
memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
}
}
CallStack::~CallStack()
{
}
CallStack& CallStack::operator = (const CallStack& rhs)
{
mCount = rhs.mCount;
if (mCount) {
memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
}
return *this;
}
bool CallStack::operator == (const CallStack& rhs) const {
if (mCount != rhs.mCount)
return false;
return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0);
}
bool CallStack::operator != (const CallStack& rhs) const {
return !operator == (rhs);
}
bool CallStack::operator < (const CallStack& rhs) const {
if (mCount != rhs.mCount)
return mCount < rhs.mCount;
return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0;
}
bool CallStack::operator >= (const CallStack& rhs) const {
return !operator < (rhs);
}
bool CallStack::operator > (const CallStack& rhs) const {
if (mCount != rhs.mCount)
return mCount > rhs.mCount;
return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0;
}
bool CallStack::operator <= (const CallStack& rhs) const {
return !operator > (rhs);
}
const void* CallStack::operator [] (int index) const {
if (index >= int(mCount))
return 0;
return mStack[index];
}
void CallStack::clear()
{
mCount = 0;
}
void CallStack::update(int32_t ignoreDepth, int32_t maxDepth)
{
if (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", uintptr_t(ip)-uintptr_t(start), name);
res.append(tmp);
}
res.append("\n");
return res;
}
// 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());
}
}
// Return a string (possibly very long) containing the complete stack trace
String8 CallStack::toString(const char* prefix) const
{
String8 res;
for (int i=0; i<int(mCount); i++) {
res.append(toStringSingleLevel(prefix, i).string());
}
return res;
}
/*****************************************************************************/
}; // namespace android