summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHidehiko Abe <hidehiko@google.com>2018-03-15 00:16:10 +0900
committerHidehiko Abe <hidehiko@google.com>2018-03-15 11:26:46 +0900
commitd7e7f68f7b706658ab8c251f5dbcf352c1755330 (patch)
tree0425e2523ce7f86610ca9b3f09e9c8f735886984
parentb912d92fdb4d105106904cbff5424e129e5398e3 (diff)
downloadlibchrome-d7e7f68f7b706658ab8c251f5dbcf352c1755330.tar.gz
Introduce stack_trace_android.cc.
This CL moves stack_trace_android.cc from libmojo. The file is identical with what is in chrome repository at r462023. Along with the change, this CL removes UCLIBC macro definition. It looks workaround to deal with stack trace. Bug: 73270448, 73606903 Test: Ran on Treehugger. Change-Id: I6b12b767c64c81e07b7a2a29f5073d9aebc47afa
-rw-r--r--Android.bp6
-rw-r--r--base/debug/proc_maps_linux.cc169
-rw-r--r--base/debug/stack_trace_android.cc134
-rw-r--r--base/debug/stack_trace_posix.cc11
-rw-r--r--build/build_config.h3
-rw-r--r--libchrome_tools/patch/build_config.patch7
-rw-r--r--libchrome_tools/patch/stack_trace_posix.patch45
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",
+ &region.start, &region.end, permissions, &region.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, &regions)) {
+ __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)
-