diff options
Diffstat (limited to 'libbacktrace')
-rwxr-xr-x | libbacktrace/Android.mk | 33 | ||||
-rw-r--r-- | libbacktrace/BacktraceImpl.cpp | 1 | ||||
-rwxr-xr-x | libbacktrace/BacktraceImpl.h | 5 | ||||
-rwxr-xr-x | libbacktrace/BacktraceLog.h | 28 | ||||
-rw-r--r-- | libbacktrace/BacktraceMap.cpp | 4 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.cpp | 7 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.h | 9 | ||||
-rw-r--r-- | libbacktrace/Corkscrew.cpp | 251 | ||||
-rw-r--r-- | libbacktrace/Corkscrew.h | 82 | ||||
-rw-r--r-- | libbacktrace/GetPss.cpp | 85 | ||||
-rw-r--r-- | libbacktrace/GetPss.h | 22 | ||||
-rwxr-xr-x | libbacktrace/UnwindCurrent.cpp | 23 | ||||
-rw-r--r-- | libbacktrace/UnwindCurrent.h | 2 | ||||
-rw-r--r-- | libbacktrace/UnwindMap.cpp | 122 | ||||
-rw-r--r-- | libbacktrace/UnwindMap.h | 19 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.cpp | 3 | ||||
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 160 |
17 files changed, 417 insertions, 439 deletions
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index 2e56756..a7305da 100755 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -46,10 +46,6 @@ libbacktrace_shared_libraries_target := \ libcutils \ libgccdemangle \ -# To enable using libunwind on each arch, add it to this list. -libunwind_architectures := arm arm64 mips x86 x86_64 - -ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures))) libbacktrace_src_files += \ UnwindCurrent.cpp \ UnwindMap.cpp \ @@ -68,24 +64,6 @@ libbacktrace_shared_libraries_host := \ libbacktrace_static_libraries_host := \ libcutils \ -else -libbacktrace_src_files += \ - Corkscrew.cpp \ - -libbacktrace_c_includes := \ - system/core/libcorkscrew \ - -libbacktrace_shared_libraries := \ - libcorkscrew \ - -libbacktrace_shared_libraries_target += \ - libdl \ - -libbacktrace_ldlibs_host := \ - -ldl \ - -endif - module := libbacktrace module_tag := optional build_type := target @@ -118,20 +96,13 @@ backtrace_test_cflags := \ -fno-builtin \ -O0 \ -g \ - -DGTEST_HAS_STD_STRING \ - -ifneq ($(TARGET_ARCH),arm64) -backtrace_test_cflags += -fstack-protector-all -else - $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain) - common_cflags += -fno-stack-protector -endif # arm64 backtrace_test_cflags_target := \ - -DGTEST_OS_LINUX_ANDROID \ + -DENABLE_PSS_TESTS \ backtrace_test_src_files := \ backtrace_test.cpp \ + GetPss.cpp \ thread_utils.c \ backtrace_test_ldlibs := \ diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp index 855810e..05007d9 100644 --- a/libbacktrace/BacktraceImpl.cpp +++ b/libbacktrace/BacktraceImpl.cpp @@ -27,6 +27,7 @@ #include <backtrace/BacktraceMap.h> #include "BacktraceImpl.h" +#include "BacktraceLog.h" #include "thread_utils.h" //------------------------------------------------------------------------- diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h index 48dd11c..7b31c38 100755 --- a/libbacktrace/BacktraceImpl.h +++ b/libbacktrace/BacktraceImpl.h @@ -21,11 +21,6 @@ #include <backtrace/BacktraceMap.h> #include <sys/types.h> -#include <log/log.h> - -// Macro to log the function name along with the warning message. -#define BACK_LOGW(format, ...) \ - ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) class BacktraceImpl { public: diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h new file mode 100755 index 0000000..1632ec2 --- /dev/null +++ b/libbacktrace/BacktraceLog.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H +#define _LIBBACKTRACE_BACKTRACE_LOG_H + +#define LOG_TAG "libbacktrace" + +#include <log/log.h> + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +#endif // _LIBBACKTRACE_BACKTRACE_LOG_H diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp index 6eb290d..0056f4b 100644 --- a/libbacktrace/BacktraceMap.cpp +++ b/libbacktrace/BacktraceMap.cpp @@ -108,11 +108,11 @@ bool BacktraceMap::Build() { #if defined(__APPLE__) // cmd is guaranteed to always be big enough to hold this string. - sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); FILE* fp = popen(cmd, "r"); #else // path is guaranteed to always be big enough to hold this string. - sprintf(path, "/proc/%d/maps", pid_); + snprintf(path, sizeof(path), "/proc/%d/maps", pid_); FILE* fp = fopen(path, "r"); #endif if (fp == NULL) { diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp index 5ffe516..e0bab24 100644 --- a/libbacktrace/BacktraceThread.cpp +++ b/libbacktrace/BacktraceThread.cpp @@ -23,6 +23,7 @@ #include <cutils/atomic.h> +#include "BacktraceLog.h" #include "BacktraceThread.h" #include "thread_utils.h" @@ -135,7 +136,7 @@ void BacktraceThread::FinishUnwind() { bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { entry->state = STATE_WAITING; - if (tgkill(Pid(), Tid(), SIGURG) != 0) { + if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { BACK_LOGW("tgkill failed %s", strerror(errno)); return false; } @@ -195,9 +196,9 @@ bool BacktraceThread::Unwind(size_t num_ignore_frames) { act.sa_sigaction = SignalHandler; act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; sigemptyset(&act.sa_mask); - if (sigaction(SIGURG, &act, &oldact) == 0) { + if (sigaction(THREAD_SIGNAL, &act, &oldact) == 0) { retval = TriggerUnwindOnThread(entry); - sigaction(SIGURG, &oldact, NULL); + sigaction(THREAD_SIGNAL, &oldact, NULL); } else { BACK_LOGW("sigaction failed %s", strerror(errno)); } diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h index 3412d58..9310a44 100644 --- a/libbacktrace/BacktraceThread.h +++ b/libbacktrace/BacktraceThread.h @@ -18,6 +18,7 @@ #define _LIBBACKTRACE_BACKTRACE_THREAD_H #include <inttypes.h> +#include <signal.h> #include <sys/types.h> #include "BacktraceImpl.h" @@ -29,6 +30,14 @@ enum state_e { STATE_CANCEL, }; +// 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 BacktraceThreadInterface; struct ThreadEntry { diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp deleted file mode 100644 index efeee2e..0000000 --- a/libbacktrace/Corkscrew.cpp +++ /dev/null @@ -1,251 +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. - */ - -#define LOG_TAG "libbacktrace" - -#include <backtrace/Backtrace.h> - -#include <string.h> - -#include <backtrace-arch.h> -#include <corkscrew/backtrace.h> - -#ifndef __USE_GNU -#define __USE_GNU -#endif -#include <dlfcn.h> - -#include "Corkscrew.h" - -//------------------------------------------------------------------------- -// CorkscrewMap functions. -//------------------------------------------------------------------------- -CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) { -} - -CorkscrewMap::~CorkscrewMap() { - if (map_info_) { - free_map_info_list(map_info_); - map_info_ = NULL; - } -} - -bool CorkscrewMap::Build() { - map_info_ = load_map_info_list(pid_); - - // Use the information in map_info_ to construct the BacktraceMap data - // rather than reparsing /proc/self/maps. - map_info_t* cur_map = map_info_; - while (cur_map) { - backtrace_map_t map; - map.start = cur_map->start; - map.end = cur_map->end; - map.flags = 0; - if (cur_map->is_readable) { - map.flags |= PROT_READ; - } - if (cur_map->is_writable) { - map.flags |= PROT_WRITE; - } - if (cur_map->is_executable) { - map.flags |= PROT_EXEC; - } - map.name = cur_map->name; - - // The maps are in descending order, but we want them in ascending order. - maps_.push_front(map); - - cur_map = cur_map->next; - } - return map_info_ != NULL; -} - -//------------------------------------------------------------------------- -// CorkscrewCommon functions. -//------------------------------------------------------------------------- -bool CorkscrewCommon::GenerateFrameData( - backtrace_frame_t* cork_frames, ssize_t num_frames) { - if (num_frames < 0) { - BACK_LOGW("libcorkscrew unwind failed."); - return false; - } - - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->func_offset = 0; - - it->map = FindMap(it->pc); - it->func_name = GetFunctionName(it->pc, &it->func_offset); - } - return true; -} - -//------------------------------------------------------------------------- -// CorkscrewCurrent functions. -//------------------------------------------------------------------------- -CorkscrewCurrent::CorkscrewCurrent() { -} - -CorkscrewCurrent::~CorkscrewCurrent() { -} - -bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) { - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - *offset = 0; - - Dl_info info; - const backtrace_map_t* map = FindMap(pc); - if (map) { - if (dladdr((const void*)pc, &info)) { - if (info.dli_sname) { - *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase; - return info.dli_sname; - } - } else { - // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... - symbol_table_t* symbol_table = load_symbol_table(map->name.c_str()); - if (symbol_table) { - // First check if we can find the symbol using a relative pc. - std::string name; - const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start); - if (elf_symbol) { - name = elf_symbol->name; - *offset = pc - map->start - elf_symbol->start; - } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) { - // Found the symbol using the absolute pc. - name = elf_symbol->name; - *offset = pc - elf_symbol->start; - } - free_symbol_table(symbol_table); - return name; - } - } - } - return ""; -} - -//------------------------------------------------------------------------- -// CorkscrewThread functions. -//------------------------------------------------------------------------- -CorkscrewThread::CorkscrewThread() { -} - -CorkscrewThread::~CorkscrewThread() { -} - -void CorkscrewThread::ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES]; - CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap()); - ssize_t num_frames = unwind_backtrace_signal_arch( - siginfo, sigcontext, map->GetMapInfo(), cork_frames, - num_ignore_frames, MAX_BACKTRACE_FRAMES); - if (num_frames > 0) { - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->map = NULL; - it->func_offset = 0; - } - } -} - -//------------------------------------------------------------------------- -// CorkscrewPtrace functions. -//------------------------------------------------------------------------- -CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) { -} - -CorkscrewPtrace::~CorkscrewPtrace() { - if (ptrace_context_) { - free_ptrace_context(ptrace_context_); - ptrace_context_ = NULL; - } -} - -bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { - ptrace_context_ = load_ptrace_context(Tid()); - - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace_ptrace( - Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - // Get information about a different process. - const map_info_t* map_info; - const symbol_t* symbol; - find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol); - char* symbol_name = NULL; - if (symbol) { - if (map_info) { - *offset = pc - map_info->start - symbol->start; - } - symbol_name = symbol->name; - return symbol_name; - } - - return ""; -} - -//------------------------------------------------------------------------- -// C++ object creation functions. -//------------------------------------------------------------------------- -Backtrace* CreateCurrentObj(BacktraceMap* map) { - return new BacktraceCurrent(new CorkscrewCurrent(), map); -} - -Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { - return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map); -} - -Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - CorkscrewThread* thread_obj = new CorkscrewThread(); - return new BacktraceThread(thread_obj, thread_obj, tid, map); -} - -//------------------------------------------------------------------------- -// BacktraceMap create function. -//------------------------------------------------------------------------- -BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new CorkscrewMap(pid); - if (!map->Build()) { - delete map; - return NULL; - } - return map; -} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h deleted file mode 100644 index 1633398..0000000 --- a/libbacktrace/Corkscrew.h +++ /dev/null @@ -1,82 +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_CORKSCREW_H -#define _LIBBACKTRACE_CORKSCREW_H - -#include <inttypes.h> - -#include <string> - -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -#include <corkscrew/backtrace.h> - -#include "BacktraceImpl.h" -#include "BacktraceThread.h" - -class CorkscrewMap : public BacktraceMap { -public: - CorkscrewMap(pid_t pid); - virtual ~CorkscrewMap(); - - virtual bool Build(); - - map_info_t* GetMapInfo() { return map_info_; } - -private: - map_info_t* map_info_; -}; - -class CorkscrewCommon : public BacktraceImpl { -public: - bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); -}; - -class CorkscrewCurrent : public CorkscrewCommon { -public: - CorkscrewCurrent(); - virtual ~CorkscrewCurrent(); - - virtual bool Unwind(size_t num_ignore_threads); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); -}; - -class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { -public: - CorkscrewThread(); - virtual ~CorkscrewThread(); - - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); -}; - -class CorkscrewPtrace : public CorkscrewCommon { -public: - CorkscrewPtrace(); - virtual ~CorkscrewPtrace(); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - - virtual bool Unwind(size_t num_ignore_threads); - -private: - ptrace_context_t* ptrace_context_; -}; - -#endif // _LIBBACKTRACE_CORKSCREW_H diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp new file mode 100644 index 0000000..442383b --- /dev/null +++ b/libbacktrace/GetPss.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> + +// This is an extremely simplified version of libpagemap. + +#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1)) + +#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1)) +#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1)) +#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6)) +#define PAGEMAP_PFN(x) (_BITS(x, 0, 55)) +#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50)) +#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5)) + +static bool ReadData(int fd, unsigned long place, uint64_t *data) { + if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) { + return false; + } + if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) { + return false; + } + return true; +} + +size_t GetPssBytes() { + FILE* maps = fopen("/proc/self/maps", "r"); + assert(maps != NULL); + + int pagecount_fd = open("/proc/kpagecount", O_RDONLY); + assert(pagecount_fd >= 0); + + int pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + assert(pagemap_fd >= 0); + + char line[4096]; + size_t total_pss = 0; + int pagesize = getpagesize(); + while (fgets(line, sizeof(line), maps)) { + uintptr_t start, end; + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) { + total_pss = 0; + break; + } + for (size_t page = start/pagesize; page < end/pagesize; page++) { + uint64_t data; + if (ReadData(pagemap_fd, page, &data)) { + if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) { + uint64_t count; + if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) { + total_pss += (count >= 1) ? pagesize / count : 0; + } + } + } + } + } + + fclose(maps); + + close(pagecount_fd); + close(pagemap_fd); + + return total_pss; +} diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h new file mode 100644 index 0000000..787c33d --- /dev/null +++ b/libbacktrace/GetPss.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBBACKTRACE_GET_PSS_H +#define _LIBBACKTRACE_GET_PSS_H + +size_t GetPssBytes(); + +#endif // _LIBBACKTRACE_GET_PSS_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 81e69bb..67d372a 100755 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <sys/ucontext.h> #include <sys/types.h> @@ -25,6 +23,7 @@ #define UNW_LOCAL_ONLY #include <libunwind.h> +#include "BacktraceLog.h" #include "UnwindCurrent.h" #include "UnwindMap.h" @@ -43,7 +42,7 @@ bool UnwindCurrent::Unwind(size_t num_ignore_frames) { BACK_LOGW("unw_getcontext failed %d", ret); return false; } - return UnwindFromContext(num_ignore_frames, true); + return UnwindFromContext(num_ignore_frames, false); } std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { @@ -58,12 +57,14 @@ std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { return ""; } -bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { +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) { - BACK_LOGW("unw_init_local failed %d", ret); + if (!within_handler) { + BACK_LOGW("unw_init_local failed %d", ret); + } delete cursor; return false; } @@ -75,13 +76,17 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { unw_word_t pc; ret = unw_get_reg(cursor, UNW_REG_IP, &pc); if (ret < 0) { - BACK_LOGW("Failed to read IP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read IP %d", ret); + } break; } unw_word_t sp; ret = unw_get_reg(cursor, UNW_REG_SP, &sp); if (ret < 0) { - BACK_LOGW("Failed to read SP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read SP %d", ret); + } break; } @@ -99,7 +104,7 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { prev->stack_size = frame->sp - prev->sp; } - if (resolve) { + if (!within_handler) { frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); frame->map = FindMap(frame->pc); } else { @@ -155,7 +160,7 @@ UnwindThread::~UnwindThread() { void UnwindThread::ThreadUnwind( siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { ExtractContext(sigcontext); - UnwindFromContext(num_ignore_frames, false); + UnwindFromContext(num_ignore_frames, true); } //------------------------------------------------------------------------- diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h index acce110..41080c7 100644 --- a/libbacktrace/UnwindCurrent.h +++ b/libbacktrace/UnwindCurrent.h @@ -34,7 +34,7 @@ public: virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - bool UnwindFromContext(size_t num_ignore_frames, bool resolve); + bool UnwindFromContext(size_t num_ignore_frames, bool within_handler); void ExtractContext(void* sigcontext); diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index 8268db6..1615518 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <pthread.h> #include <sys/types.h> #include <unistd.h> @@ -24,6 +22,7 @@ #include <libunwind.h> +#include "BacktraceLog.h" #include "UnwindMap.h" //------------------------------------------------------------------------- @@ -32,57 +31,21 @@ // only update the local address space once, and keep a reference count // of maps using the same map cursor. //------------------------------------------------------------------------- -static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER; -static unw_map_cursor_t g_map_cursor; -static int g_map_references = 0; - UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) { - map_cursor_.map_list = NULL; } UnwindMap::~UnwindMap() { - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (--g_map_references == 0) { - // Clear the local address space map. - unw_map_local_set(NULL); - unw_map_cursor_destroy(&map_cursor_); - } - pthread_mutex_unlock(&g_map_mutex); - } else { - unw_map_cursor_destroy(&map_cursor_); - } + unw_map_cursor_destroy(&map_cursor_); + unw_map_cursor_clear(&map_cursor_); } -bool UnwindMap::Build() { - bool return_value = true; - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (g_map_references == 0) { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - if (return_value) { - // Set the local address space map to our new map. - unw_map_local_set(&map_cursor_); - g_map_references = 1; - g_map_cursor = map_cursor_; - } - } else { - g_map_references++; - map_cursor_ = g_map_cursor; - } - pthread_mutex_unlock(&g_map_mutex); - } else { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - } - - if (!return_value) - return false; - +bool UnwindMap::GenerateMap() { // Use the map_cursor information to construct the BacktraceMap data // rather than reparsing /proc/self/maps. unw_map_cursor_reset(&map_cursor_); + unw_map_t unw_map; - while (unw_map_cursor_get(&map_cursor_, &unw_map)) { + while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) { backtrace_map_t map; map.start = unw_map.start; @@ -97,11 +60,82 @@ bool UnwindMap::Build() { return true; } +bool UnwindMap::Build() { + return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap(); +} + +UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) { +} + +UnwindMapLocal::~UnwindMapLocal() { + if (map_created_) { + unw_map_local_destroy(); + unw_map_cursor_clear(&map_cursor_); + } +} + +bool UnwindMapLocal::GenerateMap() { + // It's possible for the map to be regenerated while this loop is occurring. + // If that happens, get the map again, but only try at most three times + // before giving up. + for (int i = 0; i < 3; i++) { + maps_.clear(); + + unw_map_local_cursor_get(&map_cursor_); + + unw_map_t unw_map; + int ret; + while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) { + backtrace_map_t map; + + map.start = unw_map.start; + map.end = unw_map.end; + map.flags = unw_map.flags; + map.name = unw_map.path; + + free(unw_map.path); + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + } + // Check to see if the map changed while getting the data. + if (ret != -UNW_EINVAL) { + return true; + } + } + + BACK_LOGW("Unable to generate the map."); + return false; +} + +bool UnwindMapLocal::Build() { + return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();; +} + +const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) { + const backtrace_map_t* map = BacktraceMap::Find(addr); + if (!map) { + // Check to see if the underlying map changed and regenerate the map + // if it did. + if (unw_map_local_cursor_valid(&map_cursor_) < 0) { + if (GenerateMap()) { + map = BacktraceMap::Find(addr); + } + } + } + return map; +} + //------------------------------------------------------------------------- // BacktraceMap create function. //------------------------------------------------------------------------- BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new UnwindMap(pid); + BacktraceMap* map; + if (pid == getpid()) { + map = new UnwindMapLocal(); + } else { + map = new UnwindMap(pid); + } if (!map->Build()) { delete map; return NULL; diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h index 5a874e8..2fdb29f 100644 --- a/libbacktrace/UnwindMap.h +++ b/libbacktrace/UnwindMap.h @@ -32,8 +32,25 @@ public: unw_map_cursor_t* GetMapCursor() { return &map_cursor_; } -private: +protected: + virtual bool GenerateMap(); + unw_map_cursor_t map_cursor_; }; +class UnwindMapLocal : public UnwindMap { +public: + UnwindMapLocal(); + virtual ~UnwindMapLocal(); + + virtual bool Build(); + + virtual const backtrace_map_t* Find(uintptr_t addr); + +protected: + virtual bool GenerateMap(); + + bool map_created_; +}; + #endif // _LIBBACKTRACE_UNWIND_MAP_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index 732dae8..5ca7e60 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> @@ -25,6 +23,7 @@ #include <libunwind.h> #include <libunwind-ptrace.h> +#include "BacktraceLog.h" #include "UnwindMap.h" #include "UnwindPtrace.h" diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 23eaf92..9744922 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -16,9 +16,10 @@ #include <dirent.h> #include <errno.h> +#include <inttypes.h> #include <pthread.h> #include <signal.h> -#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,9 +33,13 @@ #include <backtrace/BacktraceMap.h> #include <UniquePtr.h> +// For the THREAD_SIGNAL definition. +#include "BacktraceThread.h" + #include <cutils/atomic.h> #include <gtest/gtest.h> +#include <algorithm> #include <vector> #include "thread_utils.h" @@ -287,7 +292,7 @@ TEST(libbacktrace, ptrace_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); @@ -300,7 +305,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump); @@ -314,7 +319,7 @@ TEST(libbacktrace, ptrace_max_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); @@ -339,7 +344,7 @@ TEST(libbacktrace, ptrace_ignore_frames) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); @@ -384,7 +389,7 @@ TEST(libbacktrace, ptrace_threads) { ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); } ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } // Check to see that all of the threads are running before unwinding. @@ -458,9 +463,15 @@ TEST(libbacktrace, thread_level_trace) { // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + // Make sure that the thread signal used is not visible when compiled for + // the target. +#if !defined(__GLIBC__) + ASSERT_LT(THREAD_SIGNAL, SIGRTMIN); +#endif + // Save the current signal action and make sure it is restored afterwards. struct sigaction cur_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0); UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); ASSERT_TRUE(backtrace.get() != NULL); @@ -473,7 +484,7 @@ TEST(libbacktrace, thread_level_trace) { // Verify that the old action was restored. struct sigaction new_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0); EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); } @@ -693,3 +704,136 @@ TEST(libbacktrace, format_test) { #endif backtrace->FormatFrameData(&frame)); } + +struct map_test_t { + uintptr_t start; + uintptr_t end; +}; + +bool map_sort(map_test_t i, map_test_t j) { + return i.start < j.start; +} + +static void VerifyMap(pid_t pid) { + char buffer[4096]; + snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid); + + FILE* map_file = fopen(buffer, "r"); + ASSERT_TRUE(map_file != NULL); + std::vector<map_test_t> test_maps; + while (fgets(buffer, sizeof(buffer), map_file)) { + map_test_t map; + ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end)); + test_maps.push_back(map); + } + fclose(map_file); + std::sort(test_maps.begin(), test_maps.end(), map_sort); + + UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); + + // Basic test that verifies that the map is in the expected order. + std::vector<map_test_t>::const_iterator test_it = test_maps.begin(); + for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { + ASSERT_TRUE(test_it != test_maps.end()); + ASSERT_EQ(test_it->start, it->start); + ASSERT_EQ(test_it->end, it->end); + ++test_it; + } + ASSERT_TRUE(test_it == test_maps.end()); +} + +TEST(libbacktrace, verify_map_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + // The maps should match exactly since the forked process has been paused. + VerifyMap(pid); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} + +#if defined(ENABLE_PSS_TESTS) +#include "GetPss.h" + +#define MAX_LEAK_BYTES 32*1024UL + +static void CheckForLeak(pid_t pid, pid_t tid) { + // Do a few runs to get the PSS stable. + for (size_t i = 0; i < 100; i++) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + ASSERT_TRUE(backtrace != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + delete backtrace; + } + size_t stable_pss = GetPssBytes(); + + // Loop enough that even a small leak should be detectable. + for (size_t i = 0; i < 4096; i++) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + ASSERT_TRUE(backtrace != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + delete backtrace; + } + size_t new_pss = GetPssBytes(); + 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); +} + +TEST(libbacktrace, check_for_leak_local) { + CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD); +} + +TEST(libbacktrace, check_for_leak_local_thread) { + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); + + ASSERT_TRUE(pthread_join(thread, NULL) == 0); +} + +TEST(libbacktrace, check_for_leak_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + CheckForLeak(pid, BACKTRACE_CURRENT_THREAD); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} +#endif |