diff options
| author | 2012-05-22 15:10:45 -0700 | |
|---|---|---|
| committer | 2012-05-22 16:14:10 -0700 | |
| commit | 00e446e5d912100b831fdcdbc276e7c31447f0c4 (patch) | |
| tree | d21452a2da952a25aa2bd06d4e8f3bd921804252 | |
| parent | 335889ac12794803d6b2d72037b6a90fcdb1081a (diff) | |
Switch to libcorkscrew for native stack traces.
This is both for the current thread (which backtrace(3) could do) and other
threads, which is functionality we didn't have on x86 before.
We jump through the appropriate hoops to get static symbols as well as dynamic
ones.
Also unify the existing native stack dumpers so they can share the best code.
Example x86 check failure:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) fault addr 0xdeadd00d
Registers:
eax: 0x00000000 ebx: 0x55a966f0 ecx: 0x55d523c0 edx: 0x09225a50
edi: 0x559d9b84 esi: 0xfffd8bcc ebp: 0x55a9aba0 esp: 0xfffd8b90
eip: 0x558e2e98 eflags: 0x00010246 [ PF ZF IF ]
cs: 0x00000023 ds: 0x0000002b es: 0x0000002b fs: 0x00000007
gs: 0x00000063 ss: 0x0000002b
Backtrace:
#00 unwind_backtrace_thread+536 [0x55d76bb8] (libcorkscrew.so)
#01 art::DumpNativeStack(std::ostream&, int, char const*, bool)+79 [0x55924fcf] (libartd.so)
#02 art::HandleUnexpectedSignal(int, siginfo*, void*)+1431814659 [0x5597cc23] (libartd.so)
#03 __kernel_rt_sigreturn [0x55573410] ([vdso])
#04 art::Runtime::Abort()+600 [0x558e2e98] (libartd.so)
#05 art::LogMessage::~LogMessage()+1052 [0x5580c16c] (libartd.so)
#06 art::EnsureResolved(art::Class*)+336 [0x5570dad0] (libartd.so)
#07 art::ClassLinker::FindClass(char const*, art::ClassLoader const*)+144 [0x5571bc90] (libartd.so)
#08 art::ClassLinker::FindSystemClass(char const*)+43 [0x5572287b] (libartd.so)
#09 art::ClassLinker::FinishInit()+82 [0x557228d2] (libartd.so)
#10 art::ClassLinker::InitFromImage()+1469 [0x5572451d] (libartd.so)
#11 art::ClassLinker::CreateFromImage(art::InternTable*)+60 [0x5572583c] (libartd.so)
#12 art::Runtime::Init(std::vector<std::pair<std::string, void const*>, std::allocator<std::pair<std::string, void const*> > > const&, bool)+943 [0x558e882f] (libartd.so)
#13 art::Runtime::Create(std::vector<std::pair<std::string, void const*>, std::allocator<std::pair<std::string, void const*> > > const&, bool)+119 [0x558e9057] (libartd.so)
#14 art::Dex2Oat::CreateRuntime(std::vector<std::pair<std::string, void const*>, std::allocator<std::pair<std::string, void const*> > >&, art::InstructionSet)+42 [0x80ac68a] (dex2oatd)
#15 art::Dex2Oat::Create(std::vector<std::pair<std::string, void const*>, std::allocator<std::pair<std::string, void const*> > >&, art::InstructionSet, unsigned int, bool)+60 [0x80ac87c] (dex2oatd)
#16 art::dex2oat(int, char**)+3167 [0x80894cf] (dex2oatd)
#17 main+17 [0x8086a01] (dex2oatd)
#18 __libc_start_main+230 [0x55c12bd6] (libc-2.11.1.so)
Example x86 SIGQUIT dump (note the three stacks, and note the absence of all
the crap that makes dalvik's attempt so unreadable):
"Thread-10" prio=5 tid=11 VmWait
| group="main" sCount=1 dsCount=0 obj=0x612d6620 self=0x884f8f0
| sysTid=30512 nice=0 sched=0/0 cgrp=default handle=1458686832
| schedstat=( 1249502 8995 26 ) utm=0 stm=0 core=14 HZ=100
| stackSize=1044KB stack=0x57905000-0x57906000
kernel: futex_wait_queue_me+0xcd/0x110
kernel: futex_wait+0x1e5/0x310
kernel: do_futex+0x101/0xb00
kernel: compat_sys_futex+0x75/0x160
kernel: sysenter_dispatch+0x7/0x2e
native: __kernel_vsyscall+14 [0x5557342e] ([vdso])
native: pthread_mutex_lock+54 [0x55cd8bf6] (libc-2.11.1.so)
native: art::Mutex::Lock()+86 [0x55823a86] (libartd.so)
at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:75)
at java.lang.StringBuilder.<init>(StringBuilder.java:54)
at java.lang.Thread.create(Thread.java:427)
at java.lang.Thread.<init>(Thread.java:223)
at ThreadStress$1.run(ThreadStress.java:134)
Change-Id: Id263e51a167bcfc623634f910e9d138a1f472db7
| -rw-r--r-- | build/Android.libart-compiler-llvm.mk | 2 | ||||
| -rw-r--r-- | build/Android.libart.mk | 2 | ||||
| -rw-r--r-- | src/runtime_linux.cc | 119 | ||||
| -rw-r--r-- | src/thread.cc | 30 | ||||
| -rw-r--r-- | src/thread.h | 2 | ||||
| -rw-r--r-- | src/thread_android.cc | 43 | ||||
| -rw-r--r-- | src/thread_linux.cc | 4 | ||||
| -rw-r--r-- | src/utils.cc | 208 | ||||
| -rw-r--r-- | src/utils.h | 6 |
9 files changed, 266 insertions, 150 deletions
diff --git a/build/Android.libart-compiler-llvm.mk b/build/Android.libart-compiler-llvm.mk index 05b6080d6d..4172f21f2d 100644 --- a/build/Android.libart-compiler-llvm.mk +++ b/build/Android.libart-compiler-llvm.mk @@ -131,10 +131,10 @@ define build-libart-compiler-llvm libLLVMSupport \ librsloader LOCAL_SHARED_LIBRARIES := liblog libnativehelper + LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libstlport libz libdl LOCAL_SHARED_LIBRARIES += libdynamic_annotations # tsan support - LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support else # host LOCAL_STATIC_LIBRARIES += libcutils LOCAL_SHARED_LIBRARIES += libz-host diff --git a/build/Android.libart.mk b/build/Android.libart.mk index a6a7070f73..6ad1e477a0 100644 --- a/build/Android.libart.mk +++ b/build/Android.libart.mk @@ -95,10 +95,10 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : art/%.h LOCAL_STATIC_LIBRARIES += librsloader libLLVMSupport endif LOCAL_SHARED_LIBRARIES := liblog libnativehelper + LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libstlport libz libdl LOCAL_SHARED_LIBRARIES += libdynamic_annotations # tsan support - LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support else # host LOCAL_STATIC_LIBRARIES += libcutils LOCAL_SHARED_LIBRARIES += libz-host diff --git a/src/runtime_linux.cc b/src/runtime_linux.cc index e2c806db8d..1717bc96c4 100644 --- a/src/runtime_linux.cc +++ b/src/runtime_linux.cc @@ -16,97 +16,18 @@ #include "runtime.h" -#include <cxxabi.h> -#include <execinfo.h> #include <signal.h> #include <string.h> #include "logging.h" #include "stringprintf.h" +#include "utils.h" namespace art { -static std::string Demangle(const std::string& mangled_name) { - if (mangled_name.empty()) { - return "??"; - } - - // http://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html - int status; - char* name(abi::__cxa_demangle(mangled_name.c_str(), NULL, NULL, &status)); - if (name != NULL) { - std::string result(name); - free(name); - return result; - } - - return mangled_name; -} - struct Backtrace { void Dump(std::ostream& os) { - // Get the raw stack frames. - size_t MAX_STACK_FRAMES = 128; - void* frames[MAX_STACK_FRAMES]; - size_t frame_count = backtrace(frames, MAX_STACK_FRAMES); - if (frame_count == 0) { - os << "--- backtrace(3) returned no frames"; - return; - } - - // Turn them into something human-readable with symbols. - char** symbols = backtrace_symbols(frames, frame_count); - if (symbols == NULL) { - os << "--- backtrace_symbols(3) failed"; - return; - } - - - // Parse the backtrace strings and demangle, so we can produce output like this: - // ] #00 art::Runtime::Abort(char const*, int)+0x15b [0xf770dd51] (libartd.so) - for (size_t i = 0; i < frame_count; ++i) { - std::string text(symbols[i]); - std::string filename("???"); - std::string function_name; - -#if defined(__APPLE__) - // backtrace_symbols(3) gives us lines like this on Mac OS: - // "0 libartd.dylib 0x001cd29a _ZN3art9Backtrace4DumpERSo + 40>" - // "3 ??? 0xffffffff 0x0 + 4294967295>" - text.erase(0, 4); - size_t index = text.find(' '); - filename = text.substr(0, index); - text.erase(0, 40 - 4); - index = text.find(' '); - std::string address(text.substr(0, index)); - text.erase(0, index + 1); - index = text.find(' '); - function_name = Demangle(text.substr(0, index)); - text.erase(0, index); - text += " [" + address + "]"; -#else - // backtrace_symbols(3) gives us lines like this on Linux: - // "/usr/local/google/home/enh/a1/out/host/linux-x86/bin/../lib/libartd.so(_ZN3art7Runtime5AbortEPKci+0x15b) [0xf76c5af3]" - // "[0xf7b62057]" - size_t index = text.find('('); - if (index != std::string::npos) { - filename = text.substr(0, index); - text.erase(0, index + 1); - - index = text.find_first_of("+)"); - function_name = Demangle(text.substr(0, index)); - text.erase(0, index); - index = text.find(')'); - text.erase(index, 1); - } -#endif - - const char* last_slash = strrchr(filename.c_str(), '/'); - const char* so_name = (last_slash == NULL) ? filename.c_str() : last_slash + 1; - os << StringPrintf("\t#%02zd ", i) << function_name << text << " (" << so_name << ")\n"; - } - - free(symbols); + DumpNativeStack(os, GetTid(), "\t", true); } }; @@ -213,7 +134,9 @@ struct UContext { os << '\n'; DumpRegister32(os, "eip", context->__ss.__eip); + os << " "; DumpRegister32(os, "eflags", context->__ss.__eflags); + DumpX86Flags(os, context->__ss.__eflags); os << '\n'; DumpRegister32(os, "cs", context->__ss.__cs); @@ -237,7 +160,9 @@ struct UContext { os << '\n'; DumpRegister32(os, "eip", context.gregs[REG_EIP]); + os << " "; DumpRegister32(os, "eflags", context.gregs[REG_EFL]); + DumpX86Flags(os, context.gregs[REG_EFL]); os << '\n'; DumpRegister32(os, "cs", context.gregs[REG_CS]); @@ -254,6 +179,38 @@ struct UContext { os << StringPrintf(" %6s: 0x%08x", name, value); } + void DumpX86Flags(std::ostream& os, uint32_t flags) { + os << " ["; + if ((flags & (1 << 0)) != 0) { + os << " CF"; + } + if ((flags & (1 << 2)) != 0) { + os << " PF"; + } + if ((flags & (1 << 4)) != 0) { + os << " AF"; + } + if ((flags & (1 << 6)) != 0) { + os << " ZF"; + } + if ((flags & (1 << 7)) != 0) { + os << " SF"; + } + if ((flags & (1 << 8)) != 0) { + os << " TF"; + } + if ((flags & (1 << 9)) != 0) { + os << " IF"; + } + if ((flags & (1 << 10)) != 0) { + os << " DF"; + } + if ((flags & (1 << 11)) != 0) { + os << " OF"; + } + os << " ]"; + } + mcontext_t& context; }; diff --git a/src/thread.cc b/src/thread.cc index 1e84d8a9b5..c5677c9b3d 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -29,9 +29,9 @@ #include <iostream> #include <list> -#include "debugger.h" #include "class_linker.h" #include "class_loader.h" +#include "debugger.h" #include "heap.h" #include "jni_internal.h" #include "monitor.h" @@ -41,8 +41,8 @@ #include "reflection.h" #include "runtime.h" #include "runtime_support.h" -#include "ScopedLocalRef.h" #include "scoped_jni_thread_state.h" +#include "ScopedLocalRef.h" #include "shadow_frame.h" #include "space.h" #include "stack.h" @@ -616,35 +616,13 @@ struct StackDumpVisitor : public Thread::StackVisitor { void Thread::DumpStack(std::ostream& os) const { // If we're currently in native code, dump that stack before dumping the managed stack. if (GetState() == kNative || GetState() == kVmWait) { - DumpKernelStack(os); - DumpNativeStack(os); + DumpKernelStack(os, GetTid(), " kernel: ", false); + DumpNativeStack(os, GetTid(), " native: ", false); } StackDumpVisitor dumper(os, this); WalkStack(&dumper); } -#if !defined(__APPLE__) -void Thread::DumpKernelStack(std::ostream& os) const { - std::string kernel_stack_filename(StringPrintf("/proc/self/task/%d/stack", GetTid())); - std::string kernel_stack; - if (!ReadFileToString(kernel_stack_filename, &kernel_stack)) { - os << " (couldn't read " << kernel_stack_filename << ")"; - } - - std::vector<std::string> kernel_stack_frames; - Split(kernel_stack, '\n', kernel_stack_frames); - // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff", - // which looking at the source appears to be the kernel's way of saying "that's all, folks!". - kernel_stack_frames.pop_back(); - for (size_t i = 0; i < kernel_stack_frames.size(); ++i) { - os << " kernel: " << kernel_stack_frames[i] << "\n"; - } -} -#else -// TODO: can we get the kernel stack on Mac OS? -void Thread::DumpKernelStack(std::ostream&) const {} -#endif - void Thread::SetStateWithoutSuspendCheck(ThreadState new_state) { volatile void* raw = reinterpret_cast<volatile void*>(&state_); volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw); diff --git a/src/thread.h b/src/thread.h index 91ea568f4b..c429ed5961 100644 --- a/src/thread.h +++ b/src/thread.h @@ -486,8 +486,6 @@ class PACKED Thread { void DumpState(std::ostream& os) const; void DumpStack(std::ostream& os) const; - void DumpKernelStack(std::ostream& os) const; - void DumpNativeStack(std::ostream& os) const; // Out-of-line conveniences for debugging in gdb. static Thread* CurrentFromGdb(); // Like Thread::Current. diff --git a/src/thread_android.cc b/src/thread_android.cc index 4d982b1f7f..f86d0f461d 100644 --- a/src/thread_android.cc +++ b/src/thread_android.cc @@ -21,7 +21,6 @@ #include <limits.h> #include <errno.h> -#include <corkscrew/backtrace.h> #include <cutils/sched_policy.h> #include <utils/threads.h> @@ -29,23 +28,22 @@ namespace art { -/* - * Conversion map for "nice" values. - * - * We use Android thread priority constants to be consistent with the rest - * of the system. In some cases adjacent entries may overlap. - */ +// Conversion map for "nice" values. +// +// We use Android thread priority constants to be consistent with the rest +// of the system. In some cases adjacent entries may overlap. +// static const int kNiceValues[10] = { - ANDROID_PRIORITY_LOWEST, /* 1 (MIN_PRIORITY) */ + ANDROID_PRIORITY_LOWEST, // 1 (MIN_PRIORITY) ANDROID_PRIORITY_BACKGROUND + 6, ANDROID_PRIORITY_BACKGROUND + 3, ANDROID_PRIORITY_BACKGROUND, - ANDROID_PRIORITY_NORMAL, /* 5 (NORM_PRIORITY) */ + ANDROID_PRIORITY_NORMAL, // 5 (NORM_PRIORITY) ANDROID_PRIORITY_NORMAL - 2, ANDROID_PRIORITY_NORMAL - 4, ANDROID_PRIORITY_URGENT_DISPLAY + 3, ANDROID_PRIORITY_URGENT_DISPLAY + 2, - ANDROID_PRIORITY_URGENT_DISPLAY /* 10 (MAX_PRIORITY) */ + ANDROID_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY) }; void Thread::SetNativePriority(int newPriority) { @@ -89,29 +87,4 @@ int Thread::GetNativePriority() { return managed_priority; } -void Thread::DumpNativeStack(std::ostream& os) const { - const size_t MAX_DEPTH = 32; - UniquePtr<backtrace_frame_t[]> backtrace(new backtrace_frame_t[MAX_DEPTH]); - ssize_t frame_count = unwind_backtrace_thread(GetTid(), backtrace.get(), 0, MAX_DEPTH); - if (frame_count == -1) { - os << " (unwind_backtrace_thread failed for thread " << GetTid() << ".)"; - return; - } else if (frame_count == 0) { - os << " (no native stack frames)"; - return; - } - - UniquePtr<backtrace_symbol_t[]> backtrace_symbols(new backtrace_symbol_t[frame_count]); - get_backtrace_symbols(backtrace.get(), frame_count, backtrace_symbols.get()); - - for (size_t i = 0; i < static_cast<size_t>(frame_count); ++i) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - os << " " << line << "\n"; - } - - free_backtrace_symbols(backtrace_symbols.get(), frame_count); -} - } // namespace art diff --git a/src/thread_linux.cc b/src/thread_linux.cc index 8eb8813722..6f4b75dac1 100644 --- a/src/thread_linux.cc +++ b/src/thread_linux.cc @@ -18,10 +18,6 @@ namespace art { -void Thread::DumpNativeStack(std::ostream&) const { - // TODO: use libcorkscrew; backtrace(3) only works for the calling thread. -} - void Thread::SetNativePriority(int) { // Do nothing. } diff --git a/src/utils.cc b/src/utils.cc index 4e35cd2867..7dea1108ff 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -41,9 +41,16 @@ #if defined(__APPLE__) #include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED #include <sys/syscall.h> + +#include <cxxabi.h> // For DumpNativeStack. +#include <execinfo.h> // For DumpNativeStack. + #endif #if defined(__linux__) +#include <corkscrew/backtrace.h> // For DumpNativeStack. +#include <corkscrew/demangle.h> // For DumpNativeStack. + #include <linux/unistd.h> #endif @@ -906,6 +913,207 @@ std::string GetSchedulerGroupName(pid_t tid) { return ""; } +#if defined(__APPLE__) + +static std::string Demangle(const std::string& mangled_name) { + if (mangled_name.empty()) { + return "??"; + } + + // http://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html + int status; + char* name(abi::__cxa_demangle(mangled_name.c_str(), NULL, NULL, &status)); + if (name != NULL) { + std::string result(name); + free(name); + return result; + } + + return mangled_name; +} + +// TODO: port libcorkscrew to Mac OS (or find an equivalent). +void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) { + if (tid != GetTid()) { + // backtrace(3) only works for the current thread. + return; + } + + // Get the raw stack frames. + size_t MAX_STACK_FRAMES = 128; + void* frames[MAX_STACK_FRAMES]; + size_t frame_count = backtrace(frames, MAX_STACK_FRAMES); + if (frame_count == 0) { + os << prefix << "--- backtrace(3) returned no frames"; + return; + } + + // Turn them into something human-readable with symbols. + char** symbols = backtrace_symbols(frames, frame_count); + if (symbols == NULL) { + os << prefix << "--- backtrace_symbols(3) failed"; + return; + } + + // Parse the backtrace strings and demangle, so we can produce output like this: + // ] #00 art::Runtime::Abort(char const*, int)+0x15b [0xf770dd51] (libartd.so) + for (size_t i = 0; i < frame_count; ++i) { + std::string text(symbols[i]); + std::string filename("???"); + std::string function_name; + + // backtrace_symbols(3) gives us lines like this on Mac OS: + // "0 libartd.dylib 0x001cd29a _ZN3art9Backtrace4DumpERSo + 40>" + // "3 ??? 0xffffffff 0x0 + 4294967295>" + text.erase(0, 4); + size_t index = text.find(' '); + filename = text.substr(0, index); + text.erase(0, 40 - 4); + index = text.find(' '); + std::string address(text.substr(0, index)); + text.erase(0, index + 1); + index = text.find(' '); + function_name = Demangle(text.substr(0, index)); + text.erase(0, index); + text += " [" + address + "]"; + + const char* last_slash = strrchr(filename.c_str(), '/'); + const char* so_name = (last_slash == NULL) ? filename.c_str() : last_slash + 1; + os << prefix; + if (include_count) { + os << StringPrintf("\t#%02zd ", i); + } + os << function_name << text << " (" << so_name << ")\n"; + } + + free(symbols); +} + +// TODO: is there any way to get the kernel stack on Mac OS? +void DumpKernelStack(std::ostream&, pid_t, const char*, bool) {} + +#else + +static const char* CleanMapName(const backtrace_symbol_t* symbol) { + const char* map_name = symbol->map_name; + if (map_name == NULL) { + map_name = "???"; + } + // Turn "/usr/local/google/home/enh/clean-dalvik-dev/out/host/linux-x86/lib/libartd.so" + // into "libartd.so". + const char* last_slash = strrchr(map_name, '/'); + if (last_slash != NULL) { + map_name = last_slash + 1; + } + return map_name; +} + +static void FindSymbolInElf(const backtrace_frame_t* frame, const backtrace_symbol_t* symbol, + std::string& symbol_name, uint32_t& pc_offset) { + symbol_table_t* symbol_table = NULL; + if (symbol->map_name != NULL) { + symbol_table = load_symbol_table(symbol->map_name); + } + const symbol_t* elf_symbol = NULL; + if (symbol_table != NULL) { + elf_symbol = find_symbol(symbol_table, symbol->relative_pc); + if (elf_symbol == NULL) { + elf_symbol = find_symbol(symbol_table, frame->absolute_pc); + } + } + if (elf_symbol != NULL) { + const char* demangled_symbol_name = demangle_symbol_name(elf_symbol->name); + if (demangled_symbol_name != NULL) { + symbol_name = demangled_symbol_name; + } else { + symbol_name = elf_symbol->name; + } + pc_offset = frame->absolute_pc - elf_symbol->start; + } else { + symbol_name = "???"; + } + free_symbol_table(symbol_table); +} + +void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) { + const size_t MAX_DEPTH = 32; + UniquePtr<backtrace_frame_t[]> frames(new backtrace_frame_t[MAX_DEPTH]); + ssize_t frame_count = unwind_backtrace_thread(tid, frames.get(), 0, MAX_DEPTH); + if (frame_count == -1) { + os << prefix << "(unwind_backtrace_thread failed for thread " << tid << ".)"; + return; + } else if (frame_count == 0) { + os << prefix << "(no native stack frames)"; + return; + } + + UniquePtr<backtrace_symbol_t[]> backtrace_symbols(new backtrace_symbol_t[frame_count]); + get_backtrace_symbols(frames.get(), frame_count, backtrace_symbols.get()); + + for (size_t i = 0; i < static_cast<size_t>(frame_count); ++i) { + const backtrace_frame_t* frame = &frames[i]; + const backtrace_symbol_t* symbol = &backtrace_symbols[i]; + + // We produce output like this: + // ] #00 unwind_backtrace_thread+536 [0x55d75bb8] (libcorkscrew.so) + + std::string symbol_name; + uint32_t pc_offset = 0; + if (symbol->demangled_name != NULL) { + symbol_name = symbol->demangled_name; + pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; + } else if (symbol->symbol_name != NULL) { + symbol_name = symbol->symbol_name; + pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; + } else { + // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... + FindSymbolInElf(frame, symbol, symbol_name, pc_offset); + } + + os << prefix; + if (include_count) { + os << StringPrintf("#%02zd ", i); + } + os << symbol_name; + if (pc_offset != 0) { + os << "+" << pc_offset; + } + os << StringPrintf(" [%p] (%s)\n", + reinterpret_cast<void*>(frame->absolute_pc), CleanMapName(symbol)); + } + + free_backtrace_symbols(backtrace_symbols.get(), frame_count); +} + +void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) { + std::string kernel_stack_filename(StringPrintf("/proc/self/task/%d/stack", tid)); + std::string kernel_stack; + if (!ReadFileToString(kernel_stack_filename, &kernel_stack)) { + os << " (couldn't read " << kernel_stack_filename << ")"; + } + + std::vector<std::string> kernel_stack_frames; + Split(kernel_stack, '\n', kernel_stack_frames); + // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff", + // which looking at the source appears to be the kernel's way of saying "that's all, folks!". + kernel_stack_frames.pop_back(); + for (size_t i = 0; i < kernel_stack_frames.size(); ++i) { + // Turn "[<ffffffff8109156d>] futex_wait_queue_me+0xcd/0x110" into "futex_wait_queue_me+0xcd/0x110". + const char* text = kernel_stack_frames[i].c_str(); + const char* close_bracket = strchr(text, ']'); + if (close_bracket != NULL) { + text = close_bracket + 2; + } + os << prefix; + if (include_count) { + os << StringPrintf("#%02zd ", i); + } + os << text << "\n"; + } +} + +#endif + const char* GetAndroidRoot() { const char* android_root = getenv("ANDROID_ROOT"); if (android_root == NULL) { diff --git a/src/utils.h b/src/utils.h index a26358a3ab..e1c00accf0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -280,6 +280,12 @@ std::string GetSchedulerGroupName(pid_t tid); // implementation-defined limit. void SetThreadName(const char* thread_name); +// Dumps the native stack for thread 'tid' to 'os'. +void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true); + +// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86. +void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true); + // Find $ANDROID_ROOT, /system, or abort const char* GetAndroidRoot(); |