diff options
author | Hidehiko Abe <hidehiko@google.com> | 2018-03-20 05:07:43 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-03-20 05:07:43 +0000 |
commit | 026f42b9b0be91cab925c28b01b5271e17d1e7f5 (patch) | |
tree | 0425e2523ce7f86610ca9b3f09e9c8f735886984 | |
parent | f6c5bec42b570e697e4aa475904edeaac24af5ad (diff) | |
parent | d7e7f68f7b706658ab8c251f5dbcf352c1755330 (diff) | |
download | libchrome-026f42b9b0be91cab925c28b01b5271e17d1e7f5.tar.gz |
Introduce stack_trace_android.cc.
am: d7e7f68f7b
Change-Id: Ic1bf9135ec0cfe6bd40e64e972abc6506f59ff68
-rw-r--r-- | Android.bp | 6 | ||||
-rw-r--r-- | base/debug/proc_maps_linux.cc | 169 | ||||
-rw-r--r-- | base/debug/stack_trace_android.cc | 134 | ||||
-rw-r--r-- | base/debug/stack_trace_posix.cc | 11 | ||||
-rw-r--r-- | build/build_config.h | 3 | ||||
-rw-r--r-- | libchrome_tools/patch/build_config.patch | 7 | ||||
-rw-r--r-- | libchrome_tools/patch/stack_trace_posix.patch | 45 |
7 files changed, 315 insertions, 60 deletions
diff --git a/Android.bp b/Android.bp index ac00435be8..c2a3ec056c 100644 --- a/Android.bp +++ b/Android.bp @@ -92,9 +92,9 @@ libchromeCommonSrc = [ "base/debug/debugger.cc", "base/debug/debugger_posix.cc", "base/debug/dump_without_crashing.cc", + "base/debug/proc_maps_linux.cc", "base/debug/profiler.cc", "base/debug/stack_trace.cc", - "base/debug/stack_trace_posix.cc", "base/debug/task_annotator.cc", "base/environment.cc", "base/feature_list.cc", @@ -296,10 +296,12 @@ libchromeLinuxSrc = [ libchromeLinuxGlibcSrc = [ "base/allocator/allocator_shim.cc", - "base/allocator/allocator_shim_default_dispatch_to_glibc.cc" + "base/allocator/allocator_shim_default_dispatch_to_glibc.cc", + "base/debug/stack_trace_posix.cc", ] libchromeAndroidSrc = [ + "base/debug/stack_trace_android.cc", "base/memory/shared_memory_android.cc", "base/sys_info_chromeos.cc", ] diff --git a/base/debug/proc_maps_linux.cc b/base/debug/proc_maps_linux.cc new file mode 100644 index 0000000000..0bb44b45ac --- /dev/null +++ b/base/debug/proc_maps_linux.cc @@ -0,0 +1,169 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/proc_maps_linux.h" + +#include <fcntl.h> +#include <stddef.h> + +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/strings/string_split.h" +#include "build/build_config.h" + +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include <inttypes.h> +#endif + +#if defined(OS_ANDROID) && !defined(__LP64__) +// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an +// unsigned long int, which is incompatible with Bionic's stdint.h +// defining uintptr_t as an unsigned int: +// https://code.google.com/p/android/issues/detail?id=57218 +#undef SCNxPTR +#define SCNxPTR "x" +#endif + +namespace base { +namespace debug { + +// Scans |proc_maps| starting from |pos| returning true if the gate VMA was +// found, otherwise returns false. +static bool ContainsGateVMA(std::string* proc_maps, size_t pos) { +#if defined(ARCH_CPU_ARM_FAMILY) + // The gate VMA on ARM kernels is the interrupt vectors page. + return proc_maps->find(" [vectors]\n", pos) != std::string::npos; +#elif defined(ARCH_CPU_X86_64) + // The gate VMA on x86 64-bit kernels is the virtual system call page. + return proc_maps->find(" [vsyscall]\n", pos) != std::string::npos; +#else + // Otherwise assume there is no gate VMA in which case we shouldn't + // get duplicate entires. + return false; +#endif +} + +bool ReadProcMaps(std::string* proc_maps) { + // seq_file only writes out a page-sized amount on each call. Refer to header + // file for details. + const long kReadSize = sysconf(_SC_PAGESIZE); + + base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY))); + if (!fd.is_valid()) { + DPLOG(ERROR) << "Couldn't open /proc/self/maps"; + return false; + } + proc_maps->clear(); + + while (true) { + // To avoid a copy, resize |proc_maps| so read() can write directly into it. + // Compute |buffer| afterwards since resize() may reallocate. + size_t pos = proc_maps->size(); + proc_maps->resize(pos + kReadSize); + void* buffer = &(*proc_maps)[pos]; + + ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, kReadSize)); + if (bytes_read < 0) { + DPLOG(ERROR) << "Couldn't read /proc/self/maps"; + proc_maps->clear(); + return false; + } + + // ... and don't forget to trim off excess bytes. + proc_maps->resize(pos + bytes_read); + + if (bytes_read == 0) + break; + + // The gate VMA is handled as a special case after seq_file has finished + // iterating through all entries in the virtual memory table. + // + // Unfortunately, if additional entries are added at this point in time + // seq_file gets confused and the next call to read() will return duplicate + // entries including the gate VMA again. + // + // Avoid this by searching for the gate VMA and breaking early. + if (ContainsGateVMA(proc_maps, pos)) + break; + } + + return true; +} + +bool ParseProcMaps(const std::string& input, + std::vector<MappedMemoryRegion>* regions_out) { + CHECK(regions_out); + std::vector<MappedMemoryRegion> regions; + + // This isn't async safe nor terribly efficient, but it doesn't need to be at + // this point in time. + std::vector<std::string> lines = SplitString( + input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + + for (size_t i = 0; i < lines.size(); ++i) { + // Due to splitting on '\n' the last line should be empty. + if (i == lines.size() - 1) { + if (!lines[i].empty()) { + DLOG(WARNING) << "Last line not empty"; + return false; + } + break; + } + + MappedMemoryRegion region; + const char* line = lines[i].c_str(); + char permissions[5] = {'\0'}; // Ensure NUL-terminated string. + uint8_t dev_major = 0; + uint8_t dev_minor = 0; + long inode = 0; + int path_index = 0; + + // Sample format from man 5 proc: + // + // address perms offset dev inode pathname + // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm + // + // The final %n term captures the offset in the input string, which is used + // to determine the path name. It *does not* increment the return value. + // Refer to man 3 sscanf for details. + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n", + ®ion.start, ®ion.end, permissions, ®ion.offset, + &dev_major, &dev_minor, &inode, &path_index) < 7) { + DPLOG(WARNING) << "sscanf failed for line: " << line; + return false; + } + + region.permissions = 0; + + if (permissions[0] == 'r') + region.permissions |= MappedMemoryRegion::READ; + else if (permissions[0] != '-') + return false; + + if (permissions[1] == 'w') + region.permissions |= MappedMemoryRegion::WRITE; + else if (permissions[1] != '-') + return false; + + if (permissions[2] == 'x') + region.permissions |= MappedMemoryRegion::EXECUTE; + else if (permissions[2] != '-') + return false; + + if (permissions[3] == 'p') + region.permissions |= MappedMemoryRegion::PRIVATE; + else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory. + return false; + + // Pushing then assigning saves us a string copy. + regions.push_back(region); + regions.back().path.assign(line + path_index); + } + + regions_out->swap(regions); + return true; +} + +} // namespace debug +} // namespace base diff --git a/base/debug/stack_trace_android.cc b/base/debug/stack_trace_android.cc new file mode 100644 index 0000000000..329204c8c6 --- /dev/null +++ b/base/debug/stack_trace_android.cc @@ -0,0 +1,134 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/stack_trace.h" + +#include <android/log.h> +#include <stddef.h> +#include <unwind.h> + +#include <algorithm> +#include <ostream> + +#include "base/debug/proc_maps_linux.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_restrictions.h" + +#ifdef __LP64__ +#define FMT_ADDR "0x%016lx" +#else +#define FMT_ADDR "0x%08x" +#endif + +namespace { + +struct StackCrawlState { + StackCrawlState(uintptr_t* frames, size_t max_depth) + : frames(frames), + frame_count(0), + max_depth(max_depth), + have_skipped_self(false) {} + + uintptr_t* frames; + size_t frame_count; + size_t max_depth; + bool have_skipped_self; +}; + +_Unwind_Reason_Code TraceStackFrame(_Unwind_Context* context, void* arg) { + StackCrawlState* state = static_cast<StackCrawlState*>(arg); + uintptr_t ip = _Unwind_GetIP(context); + + // The first stack frame is this function itself. Skip it. + if (ip != 0 && !state->have_skipped_self) { + state->have_skipped_self = true; + return _URC_NO_REASON; + } + + state->frames[state->frame_count++] = ip; + if (state->frame_count >= state->max_depth) + return _URC_END_OF_STACK; + return _URC_NO_REASON; +} + +} // namespace + +namespace base { +namespace debug { + +bool EnableInProcessStackDumping() { + // When running in an application, our code typically expects SIGPIPE + // to be ignored. Therefore, when testing that same code, it should run + // with SIGPIPE ignored as well. + // TODO(phajdan.jr): De-duplicate this SIGPIPE code. + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_IGN; + sigemptyset(&action.sa_mask); + return (sigaction(SIGPIPE, &action, NULL) == 0); +} + +StackTrace::StackTrace(size_t count) { + count = std::min(arraysize(trace_), count); + + StackCrawlState state(reinterpret_cast<uintptr_t*>(trace_), count); + _Unwind_Backtrace(&TraceStackFrame, &state); + count_ = state.frame_count; +} + +void StackTrace::Print() const { + std::string backtrace = ToString(); + __android_log_write(ANDROID_LOG_ERROR, "chromium", backtrace.c_str()); +} + +// NOTE: Native libraries in APKs are stripped before installing. Print out the +// relocatable address and library names so host computers can use tools to +// symbolize and demangle (e.g., addr2line, c++filt). +void StackTrace::OutputToStream(std::ostream* os) const { + std::string proc_maps; + std::vector<MappedMemoryRegion> regions; + // Allow IO to read /proc/self/maps. Reading this file doesn't hit the disk + // since it lives in procfs, and this is currently used to print a stack trace + // on fatal log messages in debug builds only. If the restriction is enabled + // then it will recursively trigger fatal failures when this enters on the + // UI thread. + base::ThreadRestrictions::ScopedAllowIO allow_io; + if (!ReadProcMaps(&proc_maps)) { + __android_log_write( + ANDROID_LOG_ERROR, "chromium", "Failed to read /proc/self/maps"); + } else if (!ParseProcMaps(proc_maps, ®ions)) { + __android_log_write( + ANDROID_LOG_ERROR, "chromium", "Failed to parse /proc/self/maps"); + } + + for (size_t i = 0; i < count_; ++i) { + // Subtract one as return address of function may be in the next + // function when a function is annotated as noreturn. + uintptr_t address = reinterpret_cast<uintptr_t>(trace_[i]) - 1; + + std::vector<MappedMemoryRegion>::iterator iter = regions.begin(); + while (iter != regions.end()) { + if (address >= iter->start && address < iter->end && + !iter->path.empty()) { + break; + } + ++iter; + } + + *os << base::StringPrintf("#%02zd " FMT_ADDR " ", i, address); + + if (iter != regions.end()) { + uintptr_t rel_pc = address - iter->start + iter->offset; + const char* path = iter->path.c_str(); + *os << base::StringPrintf("%s+" FMT_ADDR, path, rel_pc); + } else { + *os << "<unknown>"; + } + + *os << "\n"; + } +} + +} // namespace debug +} // namespace base diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc index ab4c34b021..4a55f64cc6 100644 --- a/base/debug/stack_trace_posix.cc +++ b/base/debug/stack_trace_posix.cc @@ -59,7 +59,7 @@ namespace { volatile sig_atomic_t in_signal_handler = 0; -#if !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__) +#if !defined(USE_SYMBOLIZE) // The prefix used for mangled symbols, per the Itanium C++ ABI: // http://www.codesourcery.com/cxx-abi/abi.html#mangling const char kMangledSymbolPrefix[] = "_Z"; @@ -68,9 +68,9 @@ const char kMangledSymbolPrefix[] = "_Z"; // (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join const char kSymbolCharacters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; -#endif // !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__) +#endif // !defined(USE_SYMBOLIZE) -#if !defined(USE_SYMBOLIZE) && !defined(__UCLIBC__) +#if !defined(USE_SYMBOLIZE) // Demangles C++ symbols in the given text. Example: // // "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]" @@ -79,7 +79,8 @@ const char kSymbolCharacters[] = void DemangleSymbols(std::string* text) { // Note: code in this function is NOT async-signal safe (std::string uses // malloc internally). -#if defined(__GLIBCXX__) && !defined(__UCLIBC__) + +#if !defined(__UCLIBC__) std::string::size_type search_from = 0; while (search_from < text->size()) { @@ -115,7 +116,7 @@ void DemangleSymbols(std::string* text) { search_from = mangled_start + 2; } } -#endif // defined(__GLIBCXX__) && !defined(__UCLIBC__) +#endif // !defined(__UCLIBC__) } #endif // !defined(USE_SYMBOLIZE) diff --git a/build/build_config.h b/build/build_config.h index 219b0e2ad9..5ee96b18a1 100644 --- a/build/build_config.h +++ b/build/build_config.h @@ -40,9 +40,6 @@ #if defined(__ANDROID__) // Android targets #define __linux__ 1 -#if defined(__BIONIC__) -#define __UCLIBC__ 1 -#endif // defined(__BIONIC__) #elif !defined(__ANDROID_HOST__) // Chrome OS diff --git a/libchrome_tools/patch/build_config.patch b/libchrome_tools/patch/build_config.patch index d22e935925..daf5fb27ef 100644 --- a/libchrome_tools/patch/build_config.patch +++ b/libchrome_tools/patch/build_config.patch @@ -1,6 +1,6 @@ --- a/build/build_config.h +++ b/build/build_config.h -@@ -16,6 +16,43 @@ +@@ -16,6 +16,40 @@ #ifndef BUILD_BUILD_CONFIG_H_ #define BUILD_BUILD_CONFIG_H_ @@ -28,9 +28,6 @@ +#if defined(__ANDROID__) // Android targets + +#define __linux__ 1 -+#if defined(__BIONIC__) -+#define __UCLIBC__ 1 -+#endif // defined(__BIONIC__) + +#elif !defined(__ANDROID_HOST__) // Chrome OS + @@ -44,7 +41,7 @@ // A set of macros to use for platform detection. #if defined(__native_client__) // __native_client__ must be first, so that other OS_ defines are not set. -@@ -28,8 +65,7 @@ +@@ -28,8 +62,7 @@ #else #define OS_NACL_SFI #endif diff --git a/libchrome_tools/patch/stack_trace_posix.patch b/libchrome_tools/patch/stack_trace_posix.patch deleted file mode 100644 index 3e2c18818e..0000000000 --- a/libchrome_tools/patch/stack_trace_posix.patch +++ /dev/null @@ -1,45 +0,0 @@ -# stack_trace_posix.cc depends on glibc, but Android uses bionic. -# Exclude glibc specific part. - ---- a/base/debug/stack_trace_posix.cc -+++ b/base/debug/stack_trace_posix.cc -@@ -59,7 +59,7 @@ namespace { - - volatile sig_atomic_t in_signal_handler = 0; - --#if !defined(USE_SYMBOLIZE) -+#if !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__) - // The prefix used for mangled symbols, per the Itanium C++ ABI: - // http://www.codesourcery.com/cxx-abi/abi.html#mangling - const char kMangledSymbolPrefix[] = "_Z"; -@@ -68,9 +68,9 @@ const char kMangledSymbolPrefix[] = "_Z" - // (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join - const char kSymbolCharacters[] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; --#endif // !defined(USE_SYMBOLIZE) -+#endif // !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__) - --#if !defined(USE_SYMBOLIZE) -+#if !defined(USE_SYMBOLIZE) && !defined(__UCLIBC__) - // Demangles C++ symbols in the given text. Example: - // - // "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]" -@@ -79,8 +79,7 @@ const char kSymbolCharacters[] = - void DemangleSymbols(std::string* text) { - // Note: code in this function is NOT async-signal safe (std::string uses - // malloc internally). -- --#if !defined(__UCLIBC__) -+#if defined(__GLIBCXX__) && !defined(__UCLIBC__) - - std::string::size_type search_from = 0; - while (search_from < text->size()) { -@@ -116,7 +115,7 @@ void DemangleSymbols(std::string* text) - search_from = mangled_start + 2; - } - } --#endif // !defined(__UCLIBC__) -+#endif // defined(__GLIBCXX__) && !defined(__UCLIBC__) - } - #endif // !defined(USE_SYMBOLIZE) - |