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:
parent
e29744d94d
commit
2c43cff01d
|
@ -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
|
||||
|
|
|
@ -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)) \
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue