/* * 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", long(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