|  | /* | 
|  | * 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 |