Refactor the code.

The object hierarchy was confusing and convoluted. This removes
a lot of unnecessary code, and consolidates the BacktraceCurrent
and BacktraceThread code into BacktraceCurrent.

Change-Id: I01c8407d493712a48169df49dd3ff46db4a7c3ae
This commit is contained in:
Christopher Ferris 2015-03-26 19:18:36 -07:00
parent e29744d94d
commit 2c43cff01d
23 changed files with 773 additions and 773 deletions

View File

@ -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

View File

@ -19,28 +19,34 @@ 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)) \
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)) \

23
libbacktrace/Android.mk Executable file → Normal file
View File

@ -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 \
build_host := false
@ -37,20 +37,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 \
@ -86,6 +87,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
@ -124,6 +126,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

138
libbacktrace/Backtrace.cpp Normal file
View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

0
libbacktrace/BacktraceLog.h Executable file → Normal file
View File

View File

@ -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;
}

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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

116
libbacktrace/UnwindCurrent.cpp Executable file → Normal file
View File

@ -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;
}
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;
}
else {
GetUnwContextFromUcontext(ucontext);
}
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;
}
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_);
if (ret < 0) {
if (!within_handler) {
BACK_LOGW("unw_init_local failed %d", ret);
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;
}
delete cursor;
} else {
GetUnwContextFromUcontext(ucontext);
}
// The cursor structure is pretty large, do not put it on the stack.
std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
int ret = unw_init_local(cursor.get(), &context_);
if (ret < 0) {
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);
}

View File

@ -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();
UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
virtual ~UnwindCurrent() {}
virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
private:
void GetUnwContextFromUcontext(const ucontext_t* ucontext);
bool UnwindFromContext(size_t num_ignore_frames, bool within_handler);
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
void GetUnwContextFromUcontext(const ucontext_t* context);
protected:
unw_context_t context_;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -17,20 +17,26 @@
#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
#define _LIBBACKTRACE_UNWIND_PTRACE_H
#include <stdint.h>
#include <sys/types.h>
#include <string>
#include "BacktraceImpl.h"
#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_;

View File

@ -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);