diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /libs/utils/CallStack.cpp | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'libs/utils/CallStack.cpp')
-rw-r--r-- | libs/utils/CallStack.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp new file mode 100644 index 0000000..26fb22a --- /dev/null +++ b/libs/utils/CallStack.cpp @@ -0,0 +1,335 @@ +/* + * 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) { + mapinfo* mi = getMapInfoList(); + while(mi) { + if ((pc >= mi->start) && (pc < mi->end)) + return mi->name; + mi = mi->next; + } + 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) { + return sMapInfo.map_to_name((uint64_t)pc, def); + } + +}; + +/*****************************************************************************/ + +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 { + name = MapInfo::mapAddressToName(ip, "<unknown>"); + snprintf(tmp, 256, "pc %p %s", ip, 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 |