diff options
author | Christopher Ferris <cferris@google.com> | 2015-03-31 23:01:28 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-03-31 23:01:28 +0000 |
commit | 7d4347389c0585e556e059594ae01266535b1511 (patch) | |
tree | eb8eb4ac2bf5f4d0126f4347c74ec4a65166a4cc | |
parent | 6f8edc29c7ee194110864ad2c3a1f882c40125c0 (diff) | |
parent | 22790dfcfceebc537ffd3eb0a15fc0512f036091 (diff) | |
download | system_core-7d4347389c0585e556e059594ae01266535b1511.zip system_core-7d4347389c0585e556e059594ae01266535b1511.tar.gz system_core-7d4347389c0585e556e059594ae01266535b1511.tar.bz2 |
am 22790dfc: am 246e9d50: am 9dc41d5d: Merge "Refactor the code."
* commit '22790dfcfceebc537ffd3eb0a15fc0512f036091':
Refactor the code.
23 files changed, 770 insertions, 770 deletions
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h index 8c39acb..290682a 100644 --- a/include/backtrace/Backtrace.h +++ b/include/backtrace/Backtrace.h @@ -44,9 +44,6 @@ struct backtrace_frame_data_t { uintptr_t func_offset; // pc relative to the start of the function, only valid if func_name is not NULL. }; -// Forward declarations. -class BacktraceImpl; - #if defined(__APPLE__) struct __darwin_ucontext; typedef __darwin_ucontext ucontext_t; @@ -72,7 +69,7 @@ public: virtual ~Backtrace(); // Get the current stack trace and store in the backtrace_ structure. - virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL); + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0; // Get the function name and offset into the function given the pc. // If the string is empty, then no valid function name was found. @@ -95,9 +92,9 @@ public: virtual std::string FormatFrameData(size_t frame_num); virtual std::string FormatFrameData(const backtrace_frame_data_t* frame); - pid_t Pid() { return pid_; } - pid_t Tid() { return tid_; } - size_t NumFrames() { return frames_.size(); } + pid_t Pid() const { return pid_; } + pid_t Tid() const { return tid_; } + size_t NumFrames() const { return frames_.size(); } const backtrace_frame_data_t* GetFrame(size_t frame_num) { if (frame_num >= frames_.size()) { @@ -117,7 +114,11 @@ public: BacktraceMap* GetMap() { return map_; } protected: - Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map); + Backtrace(pid_t pid, pid_t tid, BacktraceMap* map); + + // The name returned is not demangled, GetFunctionName() takes care of + // demangling the name. + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0; virtual bool VerifyReadWordArgs(uintptr_t ptr, word_t* out_value); @@ -130,10 +131,6 @@ protected: bool map_shared_; std::vector<backtrace_frame_data_t> frames_; - - BacktraceImpl* impl_; - - friend class BacktraceImpl; }; #endif // _BACKTRACE_BACKTRACE_H diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk index ec3a71b..35fed6d 100644 --- a/libbacktrace/Android.build.mk +++ b/libbacktrace/Android.build.mk @@ -19,13 +19,19 @@ include $(CLEAR_VARS) LOCAL_MODULE := $(module) LOCAL_MODULE_TAGS := $(module_tag) LOCAL_MULTILIB := $($(module)_multilib) +ifeq ($(LOCAL_MULTILIB),both) +ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRRARY)) + LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 + LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +endif +endif LOCAL_ADDITIONAL_DEPENDENCIES := \ $(LOCAL_PATH)/Android.mk \ $(LOCAL_PATH)/Android.build.mk \ LOCAL_CFLAGS := \ - $(common_cflags) \ + $(libbacktrace_common_cflags) \ $($(module)_cflags) \ $($(module)_cflags_$(build_type)) \ @@ -33,17 +39,17 @@ LOCAL_CLANG_CFLAGS += \ $(libbacktrace_common_clang_cflags) \ LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ + $(libbacktrace_common_conlyflags) \ $($(module)_conlyflags) \ $($(module)_conlyflags_$(build_type)) \ LOCAL_CPPFLAGS += \ - $(common_cppflags) \ + $(libbacktrace_common_cppflags) \ $($(module)_cppflags) \ $($(module)_cppflags_$(build_type)) \ LOCAL_C_INCLUDES := \ - $(common_c_includes) \ + $(libbacktrace_common_c_includes) \ $($(module)_c_includes) \ $($(module)_c_includes_$(build_type)) \ diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index f72d667..b875efd 100755..100644 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -16,14 +16,14 @@ LOCAL_PATH:= $(call my-dir) -common_cflags := \ +libbacktrace_common_cflags := \ -Wall \ -Werror \ -common_conlyflags := \ +libbacktrace_common_conlyflags := \ -std=gnu99 \ -common_cppflags := \ +libbacktrace_common_cppflags := \ -std=gnu++11 \ # The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists. @@ -41,20 +41,21 @@ endif # The libbacktrace library. #------------------------------------------------------------------------- libbacktrace_src_files := \ - BacktraceImpl.cpp \ + Backtrace.cpp \ + BacktraceCurrent.cpp \ BacktraceMap.cpp \ - BacktraceThread.cpp \ + BacktracePtrace.cpp \ thread_utils.c \ - -libbacktrace_shared_libraries_target := \ - libcutils \ - -libbacktrace_src_files += \ + ThreadEntry.cpp \ UnwindCurrent.cpp \ UnwindMap.cpp \ UnwindPtrace.cpp \ +libbacktrace_shared_libraries_target := \ + libcutils \ + libbacktrace_shared_libraries := \ + libbase \ libunwind \ libunwind-ptrace \ @@ -90,6 +91,7 @@ module := libbacktrace_test module_tag := debug build_type := target build_target := SHARED_LIBRARY +libbacktrace_test_multilib := both include $(LOCAL_PATH)/Android.build.mk build_type := host include $(LOCAL_PATH)/Android.build.mk @@ -128,6 +130,7 @@ module := backtrace_test module_tag := debug build_type := target build_target := NATIVE_TEST +backtrace_test_multilib := both include $(LOCAL_PATH)/Android.build.mk build_type := host include $(LOCAL_PATH)/Android.build.mk diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp new file mode 100644 index 0000000..91ca8b7 --- /dev/null +++ b/libbacktrace/Backtrace.cpp @@ -0,0 +1,138 @@ +/* + * 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 <inttypes.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <ucontext.h> + +#include <string> + +#include <base/stringprintf.h> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include "BacktraceLog.h" +#include "thread_utils.h" +#include "UnwindCurrent.h" +#include "UnwindPtrace.h" + +using android::base::StringPrintf; + +//------------------------------------------------------------------------- +// Backtrace functions. +//------------------------------------------------------------------------- +Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map) + : pid_(pid), tid_(tid), map_(map), map_shared_(true) { + if (map_ == nullptr) { + map_ = BacktraceMap::Create(pid); + map_shared_ = false; + } +} + +Backtrace::~Backtrace() { + if (map_ && !map_shared_) { + delete map_; + map_ = nullptr; + } +} + +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 = 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, word_t* out_value) { + if (ptr & (sizeof(word_t)-1)) { + BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr)); + *out_value = static_cast<word_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 (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) { + map_name = frame->map.name.c_str(); + } else { + map_name = "<unknown>"; + } + + uintptr_t relative_pc; + if (BacktraceMap::IsValid(frame->map)) { + relative_pc = frame->pc - frame->map.start; + } else { + relative_pc = frame->pc; + } + + std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name)); + if (!frame->func_name.empty()) { + line += " (" + frame->func_name; + if (frame->func_offset) { + line += StringPrintf("+%" PRIuPTR, frame->func_offset); + } + line += ')'; + } + + return line; +} + +void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) { + map_->FillIn(pc, map); +} + +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(); + } + } else if (tid == BACKTRACE_CURRENT_THREAD) { + tid = pid; + } + + if (pid == getpid()) { + return new UnwindCurrent(pid, tid, map); + } else { + return new UnwindPtrace(pid, tid, map); + } +} diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp new file mode 100644 index 0000000..b7190e2 --- /dev/null +++ b/libbacktrace/BacktraceCurrent.cpp @@ -0,0 +1,144 @@ +/* + * 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 <errno.h> +#include <stdint.h> +#include <string.h> +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <ucontext.h> +#include <unistd.h> + +#include <string> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include "BacktraceCurrent.h" +#include "BacktraceLog.h" +#include "ThreadEntry.h" +#include "thread_utils.h" + +bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + + backtrace_map_t map; + FillInMap(ptr, &map); + if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) { + *out_value = *reinterpret_cast<word_t*>(ptr); + return true; + } else { + BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr)); + *out_value = static_cast<word_t>(-1); + return false; + } +} + +size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { + backtrace_map_t map; + FillInMap(addr, &map); + if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) { + return 0; + } + bytes = MIN(map.end - addr, bytes); + memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes); + return bytes; +} + +bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext) { + return UnwindFromContext(num_ignore_frames, ucontext); + } + + if (Tid() != gettid()) { + return UnwindThread(num_ignore_frames); + } + + return UnwindFromContext(num_ignore_frames, nullptr); +} + +static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void SignalHandler(int, siginfo_t*, void* sigcontext) { + ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false); + if (!entry) { + BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid()); + return; + } + + entry->CopyUcontextFromSigcontext(sigcontext); + + // Indicate the ucontext is now valid. + entry->Wake(); + + // Pause the thread until the unwind is complete. This avoids having + // the thread run ahead causing problems. + // The number indicates that we are waiting for the second Wake() call + // overall which is made by the thread requesting an unwind. + entry->Wait(2); + + ThreadEntry::Remove(entry); +} + +bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) { + // Prevent multiple threads trying to set the trigger action on different + // threads at the same time. + pthread_mutex_lock(&g_sigaction_mutex); + + ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid()); + entry->Lock(); + + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalHandler; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&act.sa_mask); + if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) { + BACK_LOGW("sigaction failed %s", strerror(errno)); + entry->Unlock(); + ThreadEntry::Remove(entry); + pthread_mutex_unlock(&g_sigaction_mutex); + return false; + } + + if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { + BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno)); + sigaction(THREAD_SIGNAL, &oldact, nullptr); + entry->Unlock(); + ThreadEntry::Remove(entry); + pthread_mutex_unlock(&g_sigaction_mutex); + return false; + } + + // Wait for the thread to get the ucontext. The number indicates + // that we are waiting for the first Wake() call made by the thread. + entry->Wait(1); + + // After the thread has received the signal, allow other unwinders to + // continue. + sigaction(THREAD_SIGNAL, &oldact, nullptr); + pthread_mutex_unlock(&g_sigaction_mutex); + + bool unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext()); + + // Tell the signal handler to exit and release the entry. + entry->Wake(); + + return unwind_done; +} diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h new file mode 100644 index 0000000..81ea81d --- /dev/null +++ b/libbacktrace/BacktraceCurrent.h @@ -0,0 +1,55 @@ +/* + * 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_CURRENT_H +#define _LIBBACKTRACE_BACKTRACE_CURRENT_H + +#include <stdint.h> +#include <sys/types.h> +#include <ucontext.h> + +#include <backtrace/Backtrace.h> + +// The signal used to cause a thread to dump the stack. +#if defined(__GLIBC__) +// In order to run the backtrace_tests on the host, we can't use +// the internal real time signals used by GLIBC. To avoid this, +// use SIGRTMIN for the signal to dump the stack. +#define THREAD_SIGNAL SIGRTMIN +#else +#define THREAD_SIGNAL (__SIGRTMIN+1) +#endif + +class BacktraceMap; + +class BacktraceCurrent : public Backtrace { +public: + BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {} + virtual ~BacktraceCurrent() {} + + size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override; + + bool ReadWord(uintptr_t ptr, word_t* out_value) override; + + bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override; + +private: + bool UnwindThread(size_t num_ignore_frames); + + virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0; +}; + +#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp deleted file mode 100644 index 4650b6a..0000000 --- a/libbacktrace/BacktraceImpl.cpp +++ /dev/null @@ -1,283 +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 <errno.h> -#include <stdlib.h> -#include <string.h> -#include <sys/param.h> -#include <sys/ptrace.h> -#include <sys/types.h> -#include <ucontext.h> -#include <unistd.h> - -#include <string> - -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -#include "BacktraceImpl.h" -#include "BacktraceLog.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, ucontext_t* ucontext) { - return impl_->Unwind(num_ignore_frames, ucontext); -} - -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, word_t* out_value) { - if (ptr & (sizeof(word_t)-1)) { - BACK_LOGW("invalid pointer %p", (void*)ptr); - *out_value = (word_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 (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) { - map_name = frame->map.name.c_str(); - } else { - map_name = "<unknown>"; - } - - uintptr_t relative_pc; - if (BacktraceMap::IsValid(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; -} - -void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) { - map_->FillIn(pc, map); -} - -//------------------------------------------------------------------------- -// BacktraceCurrent functions. -//------------------------------------------------------------------------- -BacktraceCurrent::BacktraceCurrent( - BacktraceImpl* impl, BacktraceMap* map) : Backtrace(impl, getpid(), map) { -} - -BacktraceCurrent::~BacktraceCurrent() { -} - -bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) { - if (!VerifyReadWordArgs(ptr, out_value)) { - return false; - } - - backtrace_map_t map; - FillInMap(ptr, &map); - if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) { - *out_value = *reinterpret_cast<word_t*>(ptr); - return true; - } else { - BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr)); - *out_value = static_cast<word_t>(-1); - return false; - } -} - -size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { - backtrace_map_t map; - FillInMap(addr, &map); - if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) { - return 0; - } - bytes = MIN(map.end - addr, bytes); - memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes); - return bytes; -} - -//------------------------------------------------------------------------- -// BacktracePtrace functions. -//------------------------------------------------------------------------- -BacktracePtrace::BacktracePtrace( - BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map) - : Backtrace(impl, pid, map) { - tid_ = tid; -} - -BacktracePtrace::~BacktracePtrace() { -} - -#if !defined(__APPLE__) -static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) { - // 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<void*>(addr), NULL); - if (*out_value == static_cast<word_t>(-1) && errno) { - BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", - reinterpret_cast<void*>(addr), tid, strerror(errno)); - return false; - } - return true; -} -#endif - -bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) { -#if defined(__APPLE__) - BACK_LOGW("MacOS does not support reading from another pid."); - return false; -#else - if (!VerifyReadWordArgs(ptr, out_value)) { - return false; - } - - backtrace_map_t map; - FillInMap(ptr, &map); - if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) { - return false; - } - - return PtraceRead(Tid(), ptr, out_value); -#endif -} - -size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { -#if defined(__APPLE__) - BACK_LOGW("MacOS does not support reading from another pid."); - return 0; -#else - backtrace_map_t map; - FillInMap(addr, &map); - if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) { - return 0; - } - - bytes = MIN(map.end - addr, bytes); - size_t bytes_read = 0; - word_t data_word; - size_t align_bytes = addr & (sizeof(word_t) - 1); - if (align_bytes != 0) { - if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) { - return 0; - } - align_bytes = sizeof(word_t) - align_bytes; - memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + sizeof(word_t) - align_bytes, - align_bytes); - addr += align_bytes; - buffer += align_bytes; - bytes -= align_bytes; - bytes_read += align_bytes; - } - - size_t num_words = bytes / sizeof(word_t); - for (size_t i = 0; i < num_words; i++) { - if (!PtraceRead(Tid(), addr, &data_word)) { - return bytes_read; - } - memcpy(buffer, &data_word, sizeof(word_t)); - buffer += sizeof(word_t); - addr += sizeof(word_t); - bytes_read += sizeof(word_t); - } - - size_t left_over = bytes & (sizeof(word_t) - 1); - if (left_over) { - if (!PtraceRead(Tid(), addr, &data_word)) { - return bytes_read; - } - memcpy(buffer, &data_word, left_over); - bytes_read += left_over; - } - return bytes_read; -#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 deleted file mode 100755 index 18c3cb5..0000000 --- a/libbacktrace/BacktraceImpl.h +++ /dev/null @@ -1,78 +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_IMPL_H -#define _LIBBACKTRACE_BACKTRACE_IMPL_H - -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -#include <sys/types.h> - -class BacktraceImpl { -public: - virtual ~BacktraceImpl() { } - - virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) = 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 void FillInMap(uintptr_t addr, backtrace_map_t* map) { - backtrace_obj_->FillInMap(addr, map); - } - 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<backtrace_frame_data_t>* GetFrames() { return &backtrace_obj_->frames_; } - - Backtrace* backtrace_obj_; -}; - -class BacktraceCurrent : public Backtrace { -public: - BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map); - virtual ~BacktraceCurrent(); - - size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes); - - bool ReadWord(uintptr_t ptr, word_t* out_value); -}; - -class BacktracePtrace : public Backtrace { -public: - BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map); - virtual ~BacktracePtrace(); - - size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes); - - bool ReadWord(uintptr_t ptr, word_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/BacktraceLog.h b/libbacktrace/BacktraceLog.h index 1632ec2..1632ec2 100755..100644 --- a/libbacktrace/BacktraceLog.h +++ b/libbacktrace/BacktraceLog.h diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp index 82a4085..b0ada46 100644 --- a/libbacktrace/BacktraceMap.cpp +++ b/libbacktrace/BacktraceMap.cpp @@ -15,18 +15,15 @@ */ #include <ctype.h> +#include <stdint.h> #include <sys/types.h> #include <unistd.h> -#include <string> -#include <vector> - #include <backtrace/backtrace_constants.h> #include <backtrace/BacktraceMap.h> #include <log/log.h> #include "thread_utils.h" -#include "BacktraceImpl.h" BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) { if (pid_ < 0) { @@ -116,7 +113,7 @@ bool BacktraceMap::Build() { snprintf(path, sizeof(path), "/proc/%d/maps", pid_); FILE* fp = fopen(path, "r"); #endif - if (fp == NULL) { + if (fp == nullptr) { return false; } @@ -142,7 +139,7 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) { BacktraceMap* map = new BacktraceMap(pid); if (!map->Build()) { delete map; - return NULL; + return nullptr; } return map; } diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp new file mode 100644 index 0000000..6134438 --- /dev/null +++ b/libbacktrace/BacktracePtrace.cpp @@ -0,0 +1,116 @@ +/* + * 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 <errno.h> +#include <stdint.h> +#include <string.h> +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <ucontext.h> +#include <unistd.h> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include "BacktraceLog.h" +#include "BacktracePtrace.h" +#include "thread_utils.h" + +#if !defined(__APPLE__) +static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) { + // 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<void*>(addr), nullptr); + if (*out_value == static_cast<word_t>(-1) && errno) { + BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", + reinterpret_cast<void*>(addr), tid, strerror(errno)); + return false; + } + return true; +} +#endif + +bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) { +#if defined(__APPLE__) + BACK_LOGW("MacOS does not support reading from another pid."); + return false; +#else + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + + backtrace_map_t map; + FillInMap(ptr, &map); + if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) { + return false; + } + + return PtraceRead(Tid(), ptr, out_value); +#endif +} + +size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { +#if defined(__APPLE__) + BACK_LOGW("MacOS does not support reading from another pid."); + return 0; +#else + backtrace_map_t map; + FillInMap(addr, &map); + if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) { + return 0; + } + + bytes = MIN(map.end - addr, bytes); + size_t bytes_read = 0; + word_t data_word; + size_t align_bytes = addr & (sizeof(word_t) - 1); + if (align_bytes != 0) { + if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) { + return 0; + } + align_bytes = sizeof(word_t) - align_bytes; + memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + sizeof(word_t) - align_bytes, + align_bytes); + addr += align_bytes; + buffer += align_bytes; + bytes -= align_bytes; + bytes_read += align_bytes; + } + + size_t num_words = bytes / sizeof(word_t); + for (size_t i = 0; i < num_words; i++) { + if (!PtraceRead(Tid(), addr, &data_word)) { + return bytes_read; + } + memcpy(buffer, &data_word, sizeof(word_t)); + buffer += sizeof(word_t); + addr += sizeof(word_t); + bytes_read += sizeof(word_t); + } + + size_t left_over = bytes & (sizeof(word_t) - 1); + if (left_over) { + if (!PtraceRead(Tid(), addr, &data_word)) { + return bytes_read; + } + memcpy(buffer, &data_word, left_over); + bytes_read += left_over; + } + return bytes_read; +#endif +} diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h new file mode 100644 index 0000000..1d49811 --- /dev/null +++ b/libbacktrace/BacktracePtrace.h @@ -0,0 +1,37 @@ +/* + * 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_PTRACE_H +#define _LIBBACKTRACE_BACKTRACE_PTRACE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <backtrace/Backtrace.h> + +class BacktraceMap; + +class BacktracePtrace : public Backtrace { +public: + BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {} + virtual ~BacktracePtrace() {} + + size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes); + + bool ReadWord(uintptr_t ptr, word_t* out_value); +}; + +#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp deleted file mode 100644 index 439cc3b..0000000 --- a/libbacktrace/BacktraceThread.cpp +++ /dev/null @@ -1,227 +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 <errno.h> -#include <inttypes.h> -#include <limits.h> -#include <pthread.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <sys/types.h> -#include <ucontext.h> -#include <unistd.h> - -#include <cutils/atomic.h> - -#include "BacktraceLog.h" -#include "BacktraceThread.h" -#include "thread_utils.h" - -//------------------------------------------------------------------------- -// ThreadEntry implementation. -//------------------------------------------------------------------------- -ThreadEntry* ThreadEntry::list_ = NULL; -pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER; - -// Assumes that ThreadEntry::list_mutex_ has already been locked before -// creating a ThreadEntry object. -ThreadEntry::ThreadEntry(pid_t pid, pid_t tid) - : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), - wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0), - next_(ThreadEntry::list_), prev_(NULL) { - pthread_condattr_t attr; - pthread_condattr_init(&attr); - pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); - pthread_cond_init(&wait_cond_, &attr); - - // Add ourselves to the list. - if (ThreadEntry::list_) { - ThreadEntry::list_->prev_ = this; - } - ThreadEntry::list_ = this; -} - -ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) { - pthread_mutex_lock(&ThreadEntry::list_mutex_); - ThreadEntry* entry = list_; - while (entry != NULL) { - if (entry->Match(pid, tid)) { - break; - } - entry = entry->next_; - } - - if (!entry) { - if (create) { - entry = new ThreadEntry(pid, tid); - } - } else { - entry->ref_count_++; - } - pthread_mutex_unlock(&ThreadEntry::list_mutex_); - - return entry; -} - -void ThreadEntry::Remove(ThreadEntry* entry) { - pthread_mutex_unlock(&entry->mutex_); - - pthread_mutex_lock(&ThreadEntry::list_mutex_); - if (--entry->ref_count_ == 0) { - delete entry; - } - pthread_mutex_unlock(&ThreadEntry::list_mutex_); -} - -// Assumes that ThreadEntry::list_mutex_ has already been locked before -// deleting a ThreadEntry object. -ThreadEntry::~ThreadEntry() { - if (list_ == this) { - list_ = next_; - } else { - if (next_) { - next_->prev_ = prev_; - } - prev_->next_ = next_; - } - - next_ = NULL; - prev_ = NULL; - - pthread_cond_destroy(&wait_cond_); -} - -void ThreadEntry::Wait(int value) { - timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { - BACK_LOGW("clock_gettime failed: %s", strerror(errno)); - abort(); - } - ts.tv_sec += 10; - - pthread_mutex_lock(&wait_mutex_); - while (wait_value_ != value) { - int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts); - if (ret != 0) { - BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret)); - break; - } - } - pthread_mutex_unlock(&wait_mutex_); -} - -void ThreadEntry::Wake() { - pthread_mutex_lock(&wait_mutex_); - wait_value_++; - pthread_mutex_unlock(&wait_mutex_); - - pthread_cond_signal(&wait_cond_); -} - -void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) { - ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext); - // The only thing the unwinder cares about is the mcontext data. - memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext)); -} - -//------------------------------------------------------------------------- -// BacktraceThread functions. -//------------------------------------------------------------------------- -static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void SignalHandler(int, siginfo_t*, void* sigcontext) { - ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false); - if (!entry) { - BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid()); - return; - } - - entry->CopyUcontextFromSigcontext(sigcontext); - - // Indicate the ucontext is now valid. - entry->Wake(); - - // Pause the thread until the unwind is complete. This avoids having - // the thread run ahead causing problems. - entry->Wait(2); - - ThreadEntry::Remove(entry); -} - -BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map) - : BacktraceCurrent(impl, map) { - tid_ = tid; -} - -BacktraceThread::~BacktraceThread() { -} - -bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { - if (ucontext) { - // Unwind using an already existing ucontext. - return impl_->Unwind(num_ignore_frames, ucontext); - } - - // Prevent multiple threads trying to set the trigger action on different - // threads at the same time. - if (pthread_mutex_lock(&g_sigaction_mutex) < 0) { - BACK_LOGW("sigaction failed: %s", strerror(errno)); - return false; - } - - ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid()); - entry->Lock(); - - struct sigaction act, oldact; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = SignalHandler; - act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&act.sa_mask); - if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) { - BACK_LOGW("sigaction failed %s", strerror(errno)); - entry->Unlock(); - ThreadEntry::Remove(entry); - pthread_mutex_unlock(&g_sigaction_mutex); - return false; - } - - if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { - BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno)); - sigaction(THREAD_SIGNAL, &oldact, NULL); - entry->Unlock(); - ThreadEntry::Remove(entry); - pthread_mutex_unlock(&g_sigaction_mutex); - return false; - } - - // Wait for the thread to get the ucontext. - entry->Wait(1); - - // After the thread has received the signal, allow other unwinders to - // continue. - sigaction(THREAD_SIGNAL, &oldact, NULL); - pthread_mutex_unlock(&g_sigaction_mutex); - - bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext()); - - // Tell the signal handler to exit and release the entry. - entry->Wake(); - - return unwind_done; -} diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp index 442383b..09a721d 100644 --- a/libbacktrace/GetPss.cpp +++ b/libbacktrace/GetPss.cpp @@ -14,11 +14,10 @@ * limitations under the License. */ -#include <assert.h> #include <inttypes.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <stdint.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> @@ -46,13 +45,22 @@ static bool ReadData(int fd, unsigned long place, uint64_t *data) { size_t GetPssBytes() { FILE* maps = fopen("/proc/self/maps", "r"); - assert(maps != NULL); + if (maps == nullptr) { + return 0; + } int pagecount_fd = open("/proc/kpagecount", O_RDONLY); - assert(pagecount_fd >= 0); + if (pagecount_fd == -1) { + fclose(maps); + return 0; + } int pagemap_fd = open("/proc/self/pagemap", O_RDONLY); - assert(pagemap_fd >= 0); + if (pagemap_fd == -1) { + fclose(maps); + close(pagecount_fd); + return 0; + } char line[4096]; size_t total_pss = 0; diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp new file mode 100644 index 0000000..e8b60c8 --- /dev/null +++ b/libbacktrace/ThreadEntry.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 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 <pthread.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <time.h> +#include <ucontext.h> + +#include "BacktraceLog.h" +#include "ThreadEntry.h" + +// Initialize static member variables. +ThreadEntry* ThreadEntry::list_ = nullptr; +pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER; + +// Assumes that ThreadEntry::list_mutex_ has already been locked before +// creating a ThreadEntry object. +ThreadEntry::ThreadEntry(pid_t pid, pid_t tid) + : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), + wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0), + next_(ThreadEntry::list_), prev_(nullptr) { + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + pthread_cond_init(&wait_cond_, &attr); + + // Add ourselves to the list. + if (ThreadEntry::list_) { + ThreadEntry::list_->prev_ = this; + } + ThreadEntry::list_ = this; +} + +ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) { + pthread_mutex_lock(&ThreadEntry::list_mutex_); + ThreadEntry* entry = list_; + while (entry != nullptr) { + if (entry->Match(pid, tid)) { + break; + } + entry = entry->next_; + } + + if (!entry) { + if (create) { + entry = new ThreadEntry(pid, tid); + } + } else { + entry->ref_count_++; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); + + return entry; +} + +void ThreadEntry::Remove(ThreadEntry* entry) { + pthread_mutex_unlock(&entry->mutex_); + + pthread_mutex_lock(&ThreadEntry::list_mutex_); + if (--entry->ref_count_ == 0) { + delete entry; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); +} + +// Assumes that ThreadEntry::list_mutex_ has already been locked before +// deleting a ThreadEntry object. +ThreadEntry::~ThreadEntry() { + if (list_ == this) { + list_ = next_; + } else { + if (next_) { + next_->prev_ = prev_; + } + prev_->next_ = next_; + } + + next_ = nullptr; + prev_ = nullptr; + + pthread_cond_destroy(&wait_cond_); +} + +void ThreadEntry::Wait(int value) { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + ts.tv_sec += 10; + + pthread_mutex_lock(&wait_mutex_); + while (wait_value_ != value) { + int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts); + if (ret != 0) { + BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret)); + break; + } + } + pthread_mutex_unlock(&wait_mutex_); +} + +void ThreadEntry::Wake() { + pthread_mutex_lock(&wait_mutex_); + wait_value_++; + pthread_mutex_unlock(&wait_mutex_); + + pthread_cond_signal(&wait_cond_); +} + +void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) { + ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext); + // The only thing the unwinder cares about is the mcontext data. + memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext)); +} diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/ThreadEntry.h index 99a8638..94becf2 100644 --- a/libbacktrace/BacktraceThread.h +++ b/libbacktrace/ThreadEntry.h @@ -14,26 +14,13 @@ * limitations under the License. */ -#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H -#define _LIBBACKTRACE_BACKTRACE_THREAD_H +#ifndef _LIBBACKTRACE_THREAD_ENTRY_H +#define _LIBBACKTRACE_THREAD_ENTRY_H -#include <inttypes.h> #include <pthread.h> -#include <signal.h> -#include <string.h> #include <sys/types.h> #include <ucontext.h> -#include "BacktraceImpl.h" - -// The signal used to cause a thread to dump the stack. -#if defined(__GLIBC__) -// GLIBC reserves __SIGRTMIN signals, so use SIGRTMIN to avoid errors. -#define THREAD_SIGNAL SIGRTMIN -#else -#define THREAD_SIGNAL (__SIGRTMIN+1) -#endif - class ThreadEntry { public: static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true); @@ -81,12 +68,4 @@ private: static pthread_mutex_t list_mutex_; }; -class BacktraceThread : public BacktraceCurrent { -public: - BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map); - virtual ~BacktraceThread(); - - virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); -}; - -#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H +#endif // _LIBBACKTRACE_THREAD_ENTRY_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 372555b..12e2890 100755..100644 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -14,41 +14,30 @@ * limitations under the License. */ -#include <sys/types.h> +#include <stdint.h> #include <ucontext.h> -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> +#include <memory> +#include <string> #define UNW_LOCAL_ONLY #include <libunwind.h> +#include <backtrace/Backtrace.h> + #include "BacktraceLog.h" -#include "BacktraceThread.h" #include "UnwindCurrent.h" -#include "UnwindMap.h" - -//------------------------------------------------------------------------- -// UnwindCurrent functions. -//------------------------------------------------------------------------- -UnwindCurrent::UnwindCurrent() { -} - -UnwindCurrent::~UnwindCurrent() { -} -bool UnwindCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { - if (!ucontext) { - int ret = unw_getcontext(&context_); - if (ret < 0) { - BACK_LOGW("unw_getcontext failed %d", ret); - return false; - } - } - else { - GetUnwContextFromUcontext(ucontext); +std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + char buf[512]; + unw_word_t value; + if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf), + &value, &context_) >= 0 && buf[0] != '\0') { + *offset = static_cast<uintptr_t>(value); + return buf; } - return UnwindFromContext(num_ignore_frames, false); + return ""; } void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) { @@ -76,54 +65,43 @@ void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) { #endif } -std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - *offset = 0; - char buf[512]; - unw_word_t value; - if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf), - &value, &context_) >= 0 && buf[0] != '\0') { - *offset = static_cast<uintptr_t>(value); - return buf; +bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext == nullptr) { + int ret = unw_getcontext(&context_); + if (ret < 0) { + BACK_LOGW("unw_getcontext failed %d", ret); + return false; + } + } else { + GetUnwContextFromUcontext(ucontext); } - return ""; -} -bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_handler) { // The cursor structure is pretty large, do not put it on the stack. - unw_cursor_t* cursor = new unw_cursor_t; - int ret = unw_init_local(cursor, &context_); + std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t); + int ret = unw_init_local(cursor.get(), &context_); if (ret < 0) { - if (!within_handler) { - BACK_LOGW("unw_init_local failed %d", ret); - } - delete cursor; + BACK_LOGW("unw_init_local failed %d", ret); return false; } - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->reserve(MAX_BACKTRACE_FRAMES); size_t num_frames = 0; do { unw_word_t pc; - ret = unw_get_reg(cursor, UNW_REG_IP, &pc); + ret = unw_get_reg(cursor.get(), UNW_REG_IP, &pc); if (ret < 0) { - if (!within_handler) { - BACK_LOGW("Failed to read IP %d", ret); - } + BACK_LOGW("Failed to read IP %d", ret); break; } unw_word_t sp; - ret = unw_get_reg(cursor, UNW_REG_SP, &sp); + ret = unw_get_reg(cursor.get(), UNW_REG_SP, &sp); if (ret < 0) { - if (!within_handler) { - BACK_LOGW("Failed to read SP %d", ret); - } + BACK_LOGW("Failed to read SP %d", ret); break; } if (num_ignore_frames == 0) { - frames->resize(num_frames+1); - backtrace_frame_data_t* frame = &frames->at(num_frames); + frames_.resize(num_frames+1); + backtrace_frame_data_t* frame = &frames_.at(num_frames); frame->num = num_frames; frame->pc = static_cast<uintptr_t>(pc); frame->sp = static_cast<uintptr_t>(sp); @@ -131,34 +109,18 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_hand if (num_frames > 0) { // Set the stack size for the previous frame. - backtrace_frame_data_t* prev = &frames->at(num_frames-1); + backtrace_frame_data_t* prev = &frames_.at(num_frames-1); prev->stack_size = frame->sp - prev->sp; } - if (!within_handler) { - frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); - FillInMap(frame->pc, &frame->map); - } else { - frame->func_offset = 0; - } + frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); + FillInMap(frame->pc, &frame->map); num_frames++; } else { num_ignore_frames--; } - ret = unw_step (cursor); + ret = unw_step (cursor.get()); } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES); - delete cursor; return true; } - -//------------------------------------------------------------------------- -// C++ object creation function. -//------------------------------------------------------------------------- -Backtrace* CreateCurrentObj(BacktraceMap* map) { - return new BacktraceCurrent(new UnwindCurrent(), map); -} - -Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - return new BacktraceThread(new UnwindCurrent(), tid, map); -} diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h index 2375e6e..3023996 100644 --- a/libbacktrace/UnwindCurrent.h +++ b/libbacktrace/UnwindCurrent.h @@ -17,27 +17,32 @@ #ifndef _LIBBACKTRACE_UNWIND_CURRENT_H #define _LIBBACKTRACE_UNWIND_CURRENT_H +#include <stdint.h> +#include <sys/types.h> +#include <ucontext.h> + #include <string> -#include "BacktraceImpl.h" +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include "BacktraceCurrent.h" #define UNW_LOCAL_ONLY #include <libunwind.h> -class UnwindCurrent : public BacktraceImpl { +class UnwindCurrent : public BacktraceCurrent { public: - UnwindCurrent(); - virtual ~UnwindCurrent(); - - virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); + UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {} + virtual ~UnwindCurrent() {} - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override; - bool UnwindFromContext(size_t num_ignore_frames, bool within_handler); +private: + void GetUnwContextFromUcontext(const ucontext_t* ucontext); - void GetUnwContextFromUcontext(const ucontext_t* context); + bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override; -protected: unw_context_t context_; }; diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index 284a561..fa59d07 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <pthread.h> +#include <stdint.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> @@ -142,7 +142,7 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) { } if (!map->Build()) { delete map; - return NULL; + return nullptr; } return map; } diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h index be8855e..e292016 100644 --- a/libbacktrace/UnwindMap.h +++ b/libbacktrace/UnwindMap.h @@ -17,6 +17,9 @@ #ifndef _LIBBACKTRACE_UNWIND_MAP_H #define _LIBBACKTRACE_UNWIND_MAP_H +#include <stdint.h> +#include <sys/types.h> + #include <backtrace/BacktraceMap.h> // The unw_map_cursor_t structure is different depending on whether it is diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index efe758b..a7c3de5 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -14,35 +14,36 @@ * limitations under the License. */ -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - +#include <stdint.h> #include <sys/types.h> -#include <string.h> #include <ucontext.h> #include <libunwind.h> #include <libunwind-ptrace.h> +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + #include "BacktraceLog.h" #include "UnwindMap.h" #include "UnwindPtrace.h" -UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) { +UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map) + : BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) { } UnwindPtrace::~UnwindPtrace() { if (upt_info_) { _UPT_destroy(upt_info_); - upt_info_ = NULL; + upt_info_ = nullptr; } 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_map_set(addr_space_, nullptr); unw_destroy_addr_space(addr_space_); - addr_space_ = NULL; + addr_space_ = nullptr; } } @@ -74,8 +75,6 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { return false; } - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->reserve(MAX_BACKTRACE_FRAMES); size_t num_frames = 0; do { unw_word_t pc; @@ -92,15 +91,15 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { } if (num_ignore_frames == 0) { - frames->resize(num_frames+1); - backtrace_frame_data_t* frame = &frames->at(num_frames); + frames_.resize(num_frames+1); + backtrace_frame_data_t* frame = &frames_.at(num_frames); frame->num = num_frames; frame->pc = static_cast<uintptr_t>(pc); frame->sp = static_cast<uintptr_t>(sp); frame->stack_size = 0; if (num_frames > 0) { - backtrace_frame_data_t* prev = &frames->at(num_frames-1); + backtrace_frame_data_t* prev = &frames_.at(num_frames-1); prev->stack_size = frame->sp - prev->sp; } @@ -129,10 +128,3 @@ std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { } return ""; } - -//------------------------------------------------------------------------- -// C++ object creation function. -//------------------------------------------------------------------------- -Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { - return new BacktracePtrace(new UnwindPtrace(), pid, tid, map); -} diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h index 2fb7967..ab04abf 100644 --- a/libbacktrace/UnwindPtrace.h +++ b/libbacktrace/UnwindPtrace.h @@ -17,20 +17,26 @@ #ifndef _LIBBACKTRACE_UNWIND_PTRACE_H #define _LIBBACKTRACE_UNWIND_PTRACE_H -#include <string> +#include <stdint.h> +#include <sys/types.h> -#include "BacktraceImpl.h" +#include <string> +#ifdef UNW_LOCAL_ONLY +#undef UNW_LOCAL_ONLY +#endif #include <libunwind.h> -class UnwindPtrace : public BacktraceImpl { +#include "BacktracePtrace.h" + +class UnwindPtrace : public BacktracePtrace { public: - UnwindPtrace(); + UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map); virtual ~UnwindPtrace(); - virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); + bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override; - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override; private: unw_addr_space_t addr_space_; diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index b1e34bd..d408856 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -33,13 +33,14 @@ #include <backtrace/BacktraceMap.h> // For the THREAD_SIGNAL definition. -#include "BacktraceThread.h" +#include "BacktraceCurrent.h" #include <cutils/atomic.h> #include <gtest/gtest.h> #include <algorithm> #include <memory> +#include <string> #include <vector> #include "thread_utils.h" @@ -83,15 +84,16 @@ uint64_t NanoTime() { return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec); } -void DumpFrames(Backtrace* backtrace) { +std::string DumpFrames(Backtrace* backtrace) { if (backtrace->NumFrames() == 0) { - printf(" No frames to dump\n"); - return; + return " No frames to dump\n"; } + std::string frame; for (size_t i = 0; i < backtrace->NumFrames(); i++) { - printf(" %s\n", backtrace->FormatFrameData(i).c_str()); + frame += " " + backtrace->FormatFrameData(i) + '\n'; } + return frame; } void WaitForStop(pid_t pid) { @@ -133,8 +135,8 @@ void VerifyLevelDump(Backtrace* backtrace) { break; } } - ASSERT_LT(static_cast<size_t>(0), frame_num); - ASSERT_LE(static_cast<size_t>(3), frame_num); + ASSERT_LT(static_cast<size_t>(0), frame_num) << DumpFrames(backtrace); + ASSERT_LE(static_cast<size_t>(3), frame_num) << DumpFrames(backtrace); ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one"); ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two"); @@ -490,9 +492,13 @@ TEST(libbacktrace, thread_level_trace) { // The SA_RESTORER flag gets set behind our back, so a direct comparison // doesn't work unless we mask the value off. Mips doesn't have this // flag, so skip this on that platform. -#ifdef SA_RESTORER +#if defined(SA_RESTORER) cur_action.sa_flags &= ~SA_RESTORER; new_action.sa_flags &= ~SA_RESTORER; +#elif defined(__GLIBC__) + // Our host compiler doesn't appear to define this flag for some reason. + cur_action.sa_flags &= ~0x04000000; + new_action.sa_flags &= ~0x04000000; #endif EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); } @@ -858,10 +864,15 @@ void* ThreadReadTest(void* data) { // Tell the caller it's okay to start reading memory. android_atomic_acquire_store(1, &thread_data->state); - // Loop waiting for everything + // Loop waiting for the caller to finish reading the memory. while (thread_data->state) { } + // Re-enable read-write on the page so that we don't crash if we try + // and access data on this page when freeing the memory. + if (mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) != 0) { + return reinterpret_cast<void*>(-1); + } free(memory); android_atomic_acquire_store(1, &thread_data->state); @@ -1005,6 +1016,7 @@ void CheckForLeak(pid_t pid, pid_t tid) { delete backtrace; } size_t stable_pss = GetPssBytes(); + ASSERT_TRUE(stable_pss != 0); // Loop enough that even a small leak should be detectable. for (size_t i = 0; i < 4096; i++) { @@ -1014,6 +1026,7 @@ void CheckForLeak(pid_t pid, pid_t tid) { delete backtrace; } size_t new_pss = GetPssBytes(); + ASSERT_TRUE(new_pss != 0); size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss; // As long as the new pss is within a certain amount, consider everything okay. ASSERT_LE(abs_diff, MAX_LEAK_BYTES); |