From df2906186b6952c57b1f662bfef0b65c9f8c2e0d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 22 Jan 2014 19:21:07 -0800 Subject: Re-enable libunwind for arm. Update to handle the new optimized way that libunwind works. In addition, a small refactor of the BacktraceMap code. A few new tests of for good measure. Change-Id: I2f9b4f5ad5a0dfe907b31febee76e4b9b94fb76f --- libbacktrace/Android.mk | 5 +- libbacktrace/Backtrace.cpp | 228 --------------------------------------- libbacktrace/Backtrace.h | 72 ------------- libbacktrace/BacktraceImpl.cpp | 208 +++++++++++++++++++++++++++++++++++ libbacktrace/BacktraceImpl.h | 79 ++++++++++++++ libbacktrace/BacktraceMap.cpp | 17 +++ libbacktrace/BacktraceThread.cpp | 4 - libbacktrace/BacktraceThread.h | 4 +- libbacktrace/Corkscrew.cpp | 37 ++++--- libbacktrace/Corkscrew.h | 34 +++--- libbacktrace/UnwindCurrent.cpp | 9 +- libbacktrace/UnwindCurrent.h | 6 +- libbacktrace/UnwindMap.cpp | 110 +++++++++++++++++++ libbacktrace/UnwindMap.h | 39 +++++++ libbacktrace/UnwindPtrace.cpp | 14 ++- libbacktrace/UnwindPtrace.h | 4 +- libbacktrace/backtrace_test.cpp | 53 +++++++-- 17 files changed, 554 insertions(+), 369 deletions(-) delete mode 100644 libbacktrace/Backtrace.cpp delete mode 100644 libbacktrace/Backtrace.h create mode 100644 libbacktrace/BacktraceImpl.cpp create mode 100644 libbacktrace/BacktraceImpl.h create mode 100644 libbacktrace/UnwindMap.cpp create mode 100644 libbacktrace/UnwindMap.h (limited to 'libbacktrace') diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index f23eefd..0ae8839 100644 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH:= $(call my-dir) common_src := \ - Backtrace.cpp \ + BacktraceImpl.cpp \ BacktraceMap.cpp \ BacktraceThread.cpp \ thread_utils.c \ @@ -23,7 +23,7 @@ common_shared_libs := \ liblog \ # To enable using libunwind on each arch, add it to this list. -libunwind_architectures := arm64 +libunwind_architectures := arm arm64 ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures))) @@ -35,6 +35,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ $(common_src) \ UnwindCurrent.cpp \ + UnwindMap.cpp \ UnwindPtrace.cpp \ LOCAL_CFLAGS := \ diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp deleted file mode 100644 index d7b40ab..0000000 --- a/libbacktrace/Backtrace.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#include -#include -#include -#include -#include -#include - -#define __STDC_FORMAT_MACROS -#include - -#include - -#include -#include - -#include "Backtrace.h" -#include "thread_utils.h" - -//------------------------------------------------------------------------- -// Backtrace functions. -//------------------------------------------------------------------------- -Backtrace::Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map) - : pid_(pid), tid_(-1), map_(map), map_shared_(true), impl_(impl) { - impl_->SetParent(this); - - if (map_ == NULL) { - // The map will be created when needed. - map_shared_ = false; - } -} - -Backtrace::~Backtrace() { - if (map_ && !map_shared_) { - delete map_; - map_ = NULL; - } - - if (impl_) { - delete impl_; - impl_ = NULL; - } -} - -bool Backtrace::Unwind(size_t num_ignore_frames) { - return impl_->Unwind(num_ignore_frames); -} - -extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len, - int* status); - -std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) { - std::string func_name = impl_->GetFunctionNameRaw(pc, offset); - if (!func_name.empty()) { -#if defined(__APPLE__) - // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. - if (func_name[0] != '_') { - return func_name; - } -#endif - char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0); - if (name) { - func_name = name; - free(name); - } - } - return func_name; -} - -bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) { - if (ptr & 3) { - BACK_LOGW("invalid pointer %p", (void*)ptr); - *out_value = (uint32_t)-1; - return false; - } - return true; -} - -std::string Backtrace::FormatFrameData(size_t frame_num) { - if (frame_num >= frames_.size()) { - return ""; - } - return FormatFrameData(&frames_[frame_num]); -} - -std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) { - const char* map_name; - if (frame->map && !frame->map->name.empty()) { - map_name = frame->map->name.c_str(); - } else { - map_name = ""; - } - - uintptr_t relative_pc; - if (frame->map) { - relative_pc = frame->pc - frame->map->start; - } else { - relative_pc = frame->pc; - } - - char buf[512]; - if (!frame->func_name.empty() && frame->func_offset) { - snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")", - frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, - frame->func_name.c_str(), frame->func_offset); - } else if (!frame->func_name.empty()) { - snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame->num, - (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name.c_str()); - } else { - snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame->num, - (int)sizeof(uintptr_t)*2, relative_pc, map_name); - } - - return buf; -} - -bool Backtrace::BuildMap() { - map_ = impl_->CreateBacktraceMap(pid_); - if (!map_->Build()) { - BACK_LOGW("Failed to build map for process %d", pid_); - return false; - } - return true; -} - -const backtrace_map_t* Backtrace::FindMap(uintptr_t pc) { - if (map_ == NULL) { - // Lazy eval, time to build the map. - if (!BuildMap()) { - return NULL; - } - } - return map_->Find(pc); -} - -BacktraceMap* Backtrace::TakeMapOwnership() { - map_shared_ = true; - return map_; -} - -//------------------------------------------------------------------------- -// BacktraceCurrent functions. -//------------------------------------------------------------------------- -BacktraceCurrent::BacktraceCurrent( - BacktraceImpl* impl, BacktraceMap* map) : Backtrace(impl, getpid(), map) { -} - -BacktraceCurrent::~BacktraceCurrent() { -} - -bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) { - if (!VerifyReadWordArgs(ptr, out_value)) { - return false; - } - - const backtrace_map_t* map = FindMap(ptr); - if (map && map->flags & PROT_READ) { - *out_value = *reinterpret_cast(ptr); - return true; - } else { - BACK_LOGW("pointer %p not in a readable map", reinterpret_cast(ptr)); - *out_value = static_cast(-1); - return false; - } -} - -//------------------------------------------------------------------------- -// BacktracePtrace functions. -//------------------------------------------------------------------------- -BacktracePtrace::BacktracePtrace( - BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map) - : Backtrace(impl, pid, map) { - tid_ = tid; -} - -BacktracePtrace::~BacktracePtrace() { -} - -bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { - if (!VerifyReadWordArgs(ptr, out_value)) { - return false; - } - -#if defined(__APPLE__) - BACK_LOGW("MacOS does not support reading from another pid."); - return false; -#else - // ptrace() returns -1 and sets errno when the operation fails. - // To disambiguate -1 from a valid result, we clear errno beforehand. - errno = 0; - *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast(ptr), NULL); - if (*out_value == static_cast(-1) && errno) { - BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", - reinterpret_cast(ptr), Tid(), strerror(errno)); - return false; - } - return true; -#endif -} - -Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) { - if (pid == BACKTRACE_CURRENT_PROCESS || pid == getpid()) { - if (tid == BACKTRACE_CURRENT_THREAD || tid == gettid()) { - return CreateCurrentObj(map); - } else { - return CreateThreadObj(tid, map); - } - } else if (tid == BACKTRACE_CURRENT_THREAD) { - return CreatePtraceObj(pid, pid, map); - } else { - return CreatePtraceObj(pid, tid, map); - } -} diff --git a/libbacktrace/Backtrace.h b/libbacktrace/Backtrace.h deleted file mode 100644 index 31fcaf2..0000000 --- a/libbacktrace/Backtrace.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef _LIBBACKTRACE_BACKTRACE_H -#define _LIBBACKTRACE_BACKTRACE_H - -#include -#include - -#include -#include - -// Macro to log the function name along with the warning message. -#define BACK_LOGW(format, ...) \ - ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) - -class BacktraceImpl { -public: - virtual ~BacktraceImpl() { } - - virtual bool Unwind(size_t num_ignore_frames) = 0; - - // The name returned is not demangled, Backtrace::GetFunctionName() - // takes care of demangling the name. - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0; - - void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; } - - virtual BacktraceMap* CreateBacktraceMap(pid_t pid) = 0; - -protected: - inline std::vector* GetFrames() { return &backtrace_obj_->frames_; } - - inline bool BuildMap() { return backtrace_obj_->BuildMap(); } - - Backtrace* backtrace_obj_; -}; - -class BacktraceCurrent : public Backtrace { -public: - BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map); - virtual ~BacktraceCurrent(); - - bool ReadWord(uintptr_t ptr, uint32_t* out_value); -}; - -class BacktracePtrace : public Backtrace { -public: - BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map); - virtual ~BacktracePtrace(); - - bool ReadWord(uintptr_t ptr, uint32_t* out_value); -}; - -Backtrace* CreateCurrentObj(BacktraceMap* map); -Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map); -Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map); - -#endif // _LIBBACKTRACE_BACKTRACE_H diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp new file mode 100644 index 0000000..39296b4 --- /dev/null +++ b/libbacktrace/BacktraceImpl.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2013 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. + */ + +#include +#include +#include +#include +#include +#include + +#define __STDC_FORMAT_MACROS +#include + +#include + +#include +#include + +#include "BacktraceImpl.h" +#include "thread_utils.h" + +//------------------------------------------------------------------------- +// Backtrace functions. +//------------------------------------------------------------------------- +Backtrace::Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map) + : pid_(pid), tid_(-1), map_(map), map_shared_(true), impl_(impl) { + impl_->SetParent(this); + + if (map_ == NULL) { + map_ = BacktraceMap::Create(pid); + map_shared_ = false; + } +} + +Backtrace::~Backtrace() { + if (impl_) { + delete impl_; + impl_ = NULL; + } + + if (map_ && !map_shared_) { + delete map_; + map_ = NULL; + } +} + +bool Backtrace::Unwind(size_t num_ignore_frames) { + return impl_->Unwind(num_ignore_frames); +} + +extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len, + int* status); + +std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) { + std::string func_name = impl_->GetFunctionNameRaw(pc, offset); + if (!func_name.empty()) { +#if defined(__APPLE__) + // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. + if (func_name[0] != '_') { + return func_name; + } +#endif + char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0); + if (name) { + func_name = name; + free(name); + } + } + return func_name; +} + +bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) { + if (ptr & 3) { + BACK_LOGW("invalid pointer %p", (void*)ptr); + *out_value = (uint32_t)-1; + return false; + } + return true; +} + +std::string Backtrace::FormatFrameData(size_t frame_num) { + if (frame_num >= frames_.size()) { + return ""; + } + return FormatFrameData(&frames_[frame_num]); +} + +std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) { + const char* map_name; + if (frame->map && !frame->map->name.empty()) { + map_name = frame->map->name.c_str(); + } else { + map_name = ""; + } + + uintptr_t relative_pc; + if (frame->map) { + relative_pc = frame->pc - frame->map->start; + } else { + relative_pc = frame->pc; + } + + char buf[512]; + if (!frame->func_name.empty() && frame->func_offset) { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")", + frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, + frame->func_name.c_str(), frame->func_offset); + } else if (!frame->func_name.empty()) { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame->num, + (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name.c_str()); + } else { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame->num, + (int)sizeof(uintptr_t)*2, relative_pc, map_name); + } + + return buf; +} + +const backtrace_map_t* Backtrace::FindMap(uintptr_t pc) { + return map_->Find(pc); +} + +//------------------------------------------------------------------------- +// BacktraceCurrent functions. +//------------------------------------------------------------------------- +BacktraceCurrent::BacktraceCurrent( + BacktraceImpl* impl, BacktraceMap* map) : Backtrace(impl, getpid(), map) { +} + +BacktraceCurrent::~BacktraceCurrent() { +} + +bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + + const backtrace_map_t* map = FindMap(ptr); + if (map && map->flags & PROT_READ) { + *out_value = *reinterpret_cast(ptr); + return true; + } else { + BACK_LOGW("pointer %p not in a readable map", reinterpret_cast(ptr)); + *out_value = static_cast(-1); + return false; + } +} + +//------------------------------------------------------------------------- +// BacktracePtrace functions. +//------------------------------------------------------------------------- +BacktracePtrace::BacktracePtrace( + BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map) + : Backtrace(impl, pid, map) { + tid_ = tid; +} + +BacktracePtrace::~BacktracePtrace() { +} + +bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + +#if defined(__APPLE__) + BACK_LOGW("MacOS does not support reading from another pid."); + return false; +#else + // ptrace() returns -1 and sets errno when the operation fails. + // To disambiguate -1 from a valid result, we clear errno beforehand. + errno = 0; + *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast(ptr), NULL); + if (*out_value == static_cast(-1) && errno) { + BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", + reinterpret_cast(ptr), Tid(), strerror(errno)); + return false; + } + return true; +#endif +} + +Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) { + if (pid == BACKTRACE_CURRENT_PROCESS || pid == getpid()) { + if (tid == BACKTRACE_CURRENT_THREAD || tid == gettid()) { + return CreateCurrentObj(map); + } else { + return CreateThreadObj(tid, map); + } + } else if (tid == BACKTRACE_CURRENT_THREAD) { + return CreatePtraceObj(pid, pid, map); + } else { + return CreatePtraceObj(pid, tid, map); + } +} diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h new file mode 100644 index 0000000..af014d5 --- /dev/null +++ b/libbacktrace/BacktraceImpl.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_IMPL_H +#define _LIBBACKTRACE_BACKTRACE_IMPL_H + +#include +#include + +#include +#include + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +class BacktraceImpl { +public: + virtual ~BacktraceImpl() { } + + virtual bool Unwind(size_t num_ignore_frames) = 0; + + // The name returned is not demangled, Backtrace::GetFunctionName() + // takes care of demangling the name. + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0; + + void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; } + + inline pid_t Pid() { return backtrace_obj_->Pid(); } + inline pid_t Tid() { return backtrace_obj_->Tid(); } + + inline const backtrace_map_t* FindMap(uintptr_t addr) { + return backtrace_obj_->FindMap(addr); + } + inline std::string GetFunctionName(uintptr_t pc, uintptr_t* offset) { + return backtrace_obj_->GetFunctionName(pc, offset); + } + inline BacktraceMap* GetMap() { return backtrace_obj_->GetMap(); } + +protected: + inline std::vector* GetFrames() { return &backtrace_obj_->frames_; } + + Backtrace* backtrace_obj_; +}; + +class BacktraceCurrent : public Backtrace { +public: + BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map); + virtual ~BacktraceCurrent(); + + bool ReadWord(uintptr_t ptr, uint32_t* out_value); +}; + +class BacktracePtrace : public Backtrace { +public: + BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map); + virtual ~BacktracePtrace(); + + bool ReadWord(uintptr_t ptr, uint32_t* out_value); +}; + +Backtrace* CreateCurrentObj(BacktraceMap* map); +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map); +Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map); + +#endif // _LIBBACKTRACE_BACKTRACE_IMPL_H diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp index 78f5b6b..6320800 100644 --- a/libbacktrace/BacktraceMap.cpp +++ b/libbacktrace/BacktraceMap.cpp @@ -21,9 +21,13 @@ #include #include +#include #include #include +#include "thread_utils.h" +#include "BacktraceImpl.h" + BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) { if (pid_ < 0) { pid_ = getpid(); @@ -128,3 +132,16 @@ bool BacktraceMap::Build() { return true; } + +#if defined(__APPLE__) +// Corkscrew and libunwind don't compile on the mac, so create a generic +// map object. +BacktraceMap* BacktraceMap::Create(pid_t pid) { + BacktraceMap* map = new BacktraceMap(pid); + if (!map->Build()) { + delete map; + return NULL; + } + return map; +} +#endif diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp index 9953dc1..5ffe516 100644 --- a/libbacktrace/BacktraceThread.cpp +++ b/libbacktrace/BacktraceThread.cpp @@ -180,10 +180,6 @@ bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { } bool BacktraceThread::Unwind(size_t num_ignore_frames) { - if (!thread_intf_->Init()) { - return false; - } - ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( thread_intf_, Pid(), Tid(), num_ignore_frames); if (!entry) { diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h index cae496a..3412d58 100644 --- a/libbacktrace/BacktraceThread.h +++ b/libbacktrace/BacktraceThread.h @@ -20,7 +20,7 @@ #include #include -#include "Backtrace.h" +#include "BacktraceImpl.h" enum state_e { STATE_WAITING = 0, @@ -58,8 +58,6 @@ class BacktraceThreadInterface { public: virtual ~BacktraceThreadInterface() { } - virtual bool Init() = 0; - virtual void ThreadUnwind( siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0; }; diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp index fd31a26..efeee2e 100644 --- a/libbacktrace/Corkscrew.cpp +++ b/libbacktrace/Corkscrew.cpp @@ -65,7 +65,8 @@ bool CorkscrewMap::Build() { } map.name = cur_map->name; - maps_.push_back(map); + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); cur_map = cur_map->next; } @@ -93,8 +94,8 @@ bool CorkscrewCommon::GenerateFrameData( it->stack_size = cork_frames[i].stack_size; it->func_offset = 0; - it->map = backtrace_obj_->FindMap(it->pc); - it->func_name = backtrace_obj_->GetFunctionName(it->pc, &it->func_offset); + it->map = FindMap(it->pc); + it->func_name = GetFunctionName(it->pc, &it->func_offset); } return true; } @@ -119,7 +120,7 @@ std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset *offset = 0; Dl_info info; - const backtrace_map_t* map = backtrace_obj_->FindMap(pc); + const backtrace_map_t* map = FindMap(pc); if (map) { if (dladdr((const void*)pc, &info)) { if (info.dli_sname) { @@ -158,19 +159,10 @@ CorkscrewThread::CorkscrewThread() { CorkscrewThread::~CorkscrewThread() { } -bool CorkscrewThread::Init() { - if (backtrace_obj_->GetMap() == NULL) { - // Trigger the map object creation, which will create the corkscrew - // map information. - return BuildMap(); - } - return true; -} - void CorkscrewThread::ThreadUnwind( siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES]; - CorkscrewMap* map = static_cast(backtrace_obj_->GetMap()); + CorkscrewMap* map = static_cast(GetMap()); ssize_t num_frames = unwind_backtrace_signal_arch( siginfo, sigcontext, map->GetMapInfo(), cork_frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); @@ -204,12 +196,11 @@ CorkscrewPtrace::~CorkscrewPtrace() { } bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { - ptrace_context_ = load_ptrace_context(backtrace_obj_->Tid()); + ptrace_context_ = load_ptrace_context(Tid()); backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; ssize_t num_frames = unwind_backtrace_ptrace( - backtrace_obj_->Tid(), ptrace_context_, frames, num_ignore_frames, - MAX_BACKTRACE_FRAMES); + Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); return GenerateFrameData(frames, num_frames); } @@ -246,3 +237,15 @@ Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { CorkscrewThread* thread_obj = new CorkscrewThread(); return new BacktraceThread(thread_obj, thread_obj, tid, map); } + +//------------------------------------------------------------------------- +// BacktraceMap create function. +//------------------------------------------------------------------------- +BacktraceMap* BacktraceMap::Create(pid_t pid) { + BacktraceMap* map = new CorkscrewMap(pid); + if (!map->Build()) { + delete map; + return NULL; + } + return map; +} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h index 229bb01..1633398 100644 --- a/libbacktrace/Corkscrew.h +++ b/libbacktrace/Corkscrew.h @@ -26,14 +26,25 @@ #include -#include "Backtrace.h" +#include "BacktraceImpl.h" #include "BacktraceThread.h" +class CorkscrewMap : public BacktraceMap { +public: + CorkscrewMap(pid_t pid); + virtual ~CorkscrewMap(); + + virtual bool Build(); + + map_info_t* GetMapInfo() { return map_info_; } + +private: + map_info_t* map_info_; +}; + class CorkscrewCommon : public BacktraceImpl { public: bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); - - virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new BacktraceMap(pid); } }; class CorkscrewCurrent : public CorkscrewCommon { @@ -46,30 +57,13 @@ public: virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); }; -class CorkscrewMap : public BacktraceMap { -public: - CorkscrewMap(pid_t pid); - virtual ~CorkscrewMap(); - - virtual bool Build(); - - map_info_t* GetMapInfo() { return map_info_; } - -private: - map_info_t* map_info_; -}; - class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { public: CorkscrewThread(); virtual ~CorkscrewThread(); - virtual bool Init(); - virtual void ThreadUnwind( siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); - - virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new CorkscrewMap(pid); } }; class CorkscrewPtrace : public CorkscrewCommon { diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index d1195ee..17b71b9 100644 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -25,6 +25,7 @@ #include #include "UnwindCurrent.h" +#include "UnwindMap.h" // Define the ucontext_t structures needed for each supported arch. #if defined(__arm__) @@ -119,8 +120,8 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { } if (resolve) { - frame->func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset); - frame->map = backtrace_obj_->FindMap(frame->pc); + frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); + frame->map = FindMap(frame->pc); } else { frame->map = NULL; frame->func_offset = 0; @@ -171,10 +172,6 @@ UnwindThread::UnwindThread() { UnwindThread::~UnwindThread() { } -bool UnwindThread::Init() { - return true; -} - void UnwindThread::ThreadUnwind( siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { ExtractContext(sigcontext); diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h index 8302c0b..acce110 100644 --- a/libbacktrace/UnwindCurrent.h +++ b/libbacktrace/UnwindCurrent.h @@ -19,7 +19,7 @@ #include -#include "Backtrace.h" +#include "BacktraceImpl.h" #include "BacktraceThread.h" #define UNW_LOCAL_ONLY @@ -38,8 +38,6 @@ public: void ExtractContext(void* sigcontext); - virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new BacktraceMap(pid); } - protected: unw_context_t context_; }; @@ -49,8 +47,6 @@ public: UnwindThread(); virtual ~UnwindThread(); - virtual bool Init(); - virtual void ThreadUnwind( siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); }; diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp new file mode 100644 index 0000000..9c8193b --- /dev/null +++ b/libbacktrace/UnwindMap.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 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 "libbacktrace" + +#include +#include +#include + +#include + +#include + +#include "UnwindMap.h" + +//------------------------------------------------------------------------- +// libunwind has a single shared address space for the current process +// aka local. If multiple maps are created for the current pid, then +// only update the local address space once, and keep a reference count +// of maps using the same map cursor. +//------------------------------------------------------------------------- +static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER; +static unw_map_cursor_t* g_map_cursor = NULL; +static int g_map_references = 0; + +UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) { + map_cursor_.map_list = NULL; +} + +UnwindMap::~UnwindMap() { + if (pid_ == getpid()) { + pthread_mutex_lock(&g_map_mutex); + if (--g_map_references == 0) { + // Clear the local address space map. + unw_map_set(unw_local_addr_space, NULL); + unw_map_cursor_destroy(&map_cursor_); + } + pthread_mutex_unlock(&g_map_mutex); + } else { + unw_map_cursor_destroy(&map_cursor_); + } +} + +bool UnwindMap::Build() { + bool return_value = true; + if (pid_ == getpid()) { + pthread_mutex_lock(&g_map_mutex); + if (g_map_references == 0) { + return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); + if (return_value) { + // Set the local address space to this cursor map. + unw_map_set(unw_local_addr_space, &map_cursor_); + g_map_references = 1; + g_map_cursor = &map_cursor_; + } + } else { + g_map_references++; + map_cursor_ = *g_map_cursor; + } + pthread_mutex_unlock(&g_map_mutex); + } else { + return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); + } + + if (!return_value) + return false; + + // Use the map_cursor information to construct the BacktraceMap data + // rather than reparsing /proc/self/maps. + unw_map_cursor_reset(&map_cursor_); + unw_map_t unw_map; + while (unw_map_cursor_get(&map_cursor_, &unw_map)) { + backtrace_map_t map; + + map.start = unw_map.start; + map.end = unw_map.end; + map.flags = unw_map.flags; + map.name = unw_map.path; + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + } + + return true; +} + +//------------------------------------------------------------------------- +// BacktraceMap create function. +//------------------------------------------------------------------------- +BacktraceMap* BacktraceMap::Create(pid_t pid) { + BacktraceMap* map = new UnwindMap(pid); + if (!map->Build()) { + delete map; + return NULL; + } + return map; +} diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h new file mode 100644 index 0000000..5a874e8 --- /dev/null +++ b/libbacktrace/UnwindMap.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _LIBBACKTRACE_UNWIND_MAP_H +#define _LIBBACKTRACE_UNWIND_MAP_H + +#include + +// The unw_map_cursor_t structure is different depending on whether it is +// the local or remote version. In order to get the correct version, include +// libunwind.h first then this header. + +class UnwindMap : public BacktraceMap { +public: + UnwindMap(pid_t pid); + virtual ~UnwindMap(); + + virtual bool Build(); + + unw_map_cursor_t* GetMapCursor() { return &map_cursor_; } + +private: + unw_map_cursor_t map_cursor_; +}; + +#endif // _LIBBACKTRACE_UNWIND_MAP_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index e45c5f8..732dae8 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -25,6 +25,7 @@ #include #include +#include "UnwindMap.h" #include "UnwindPtrace.h" UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) { @@ -36,6 +37,10 @@ UnwindPtrace::~UnwindPtrace() { upt_info_ = NULL; } if (addr_space_) { + // Remove the map from the address space before destroying it. + // It will be freed in the UnwindMap destructor. + unw_map_set(addr_space_, NULL); + unw_destroy_addr_space(addr_space_); addr_space_ = NULL; } @@ -48,7 +53,10 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames) { return false; } - upt_info_ = reinterpret_cast(_UPT_create(backtrace_obj_->Tid())); + UnwindMap* map = static_cast(GetMap()); + unw_map_set(addr_space_, map->GetMapCursor()); + + upt_info_ = reinterpret_cast(_UPT_create(Tid())); if (!upt_info_) { BACK_LOGW("Failed to create upt info."); return false; @@ -91,9 +99,9 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames) { prev->stack_size = frame->sp - prev->sp; } - frame->func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset); + frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); - frame->map = backtrace_obj_->FindMap(frame->pc); + frame->map = FindMap(frame->pc); num_frames++; } else { diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h index 05375dd..1e82117 100644 --- a/libbacktrace/UnwindPtrace.h +++ b/libbacktrace/UnwindPtrace.h @@ -19,7 +19,7 @@ #include -#include "Backtrace.h" +#include "BacktraceImpl.h" #include @@ -32,8 +32,6 @@ public: virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new BacktraceMap(pid); } - private: unw_addr_space_t addr_space_; struct UPT_info* upt_info_; diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 0ff7897..23eaf92 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -247,7 +247,7 @@ TEST(libbacktrace, local_max_trace) { ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0); } -void VerifyProcTest(pid_t pid, pid_t tid, +void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, bool (*ReadyFunc)(Backtrace*), void (*VerifyFunc)(Backtrace*)) { pid_t ptrace_tid; @@ -264,7 +264,11 @@ void VerifyProcTest(pid_t pid, pid_t tid, // Wait for the process to get to a stopping point. WaitForStop(ptrace_tid); - UniquePtr backtrace(Backtrace::Create(pid, tid)); + UniquePtr map; + if (share_map) { + map.reset(BacktraceMap::Create(pid)); + } + UniquePtr backtrace(Backtrace::Create(pid, tid, map.get())); ASSERT_TRUE(backtrace->Unwind(0)); ASSERT_TRUE(backtrace.get() != NULL); if (ReadyFunc(backtrace.get())) { @@ -285,7 +289,21 @@ TEST(libbacktrace, ptrace_trace) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); exit(1); } - VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump); + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +TEST(libbacktrace, ptrace_trace_shared_map) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump); kill(pid, SIGKILL); int status; @@ -298,7 +316,7 @@ TEST(libbacktrace, ptrace_max_trace) { ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); exit(1); } - VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump); + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); kill(pid, SIGKILL); int status; @@ -323,7 +341,7 @@ TEST(libbacktrace, ptrace_ignore_frames) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); exit(1); } - VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); kill(pid, SIGKILL); int status; @@ -387,7 +405,7 @@ TEST(libbacktrace, ptrace_threads) { if (pid == *it) { continue; } - VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump); + VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump); } ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); @@ -586,6 +604,29 @@ TEST(libbacktrace, thread_multiple_dump) { } } +// This test is for UnwindMaps that should share the same map cursor when +// multiple maps are created for the current process at the same time. +TEST(libbacktrace, simultaneous_maps) { + BacktraceMap* map1 = BacktraceMap::Create(getpid()); + BacktraceMap* map2 = BacktraceMap::Create(getpid()); + BacktraceMap* map3 = BacktraceMap::Create(getpid()); + + Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1); + EXPECT_TRUE(back1->Unwind(0)); + delete back1; + delete map1; + + Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2); + EXPECT_TRUE(back2->Unwind(0)); + delete back2; + delete map2; + + Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3); + EXPECT_TRUE(back3->Unwind(0)); + delete back3; + delete map3; +} + TEST(libbacktrace, format_test) { UniquePtr backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD)); ASSERT_TRUE(backtrace.get() != NULL); -- cgit v1.1