Commit c2e5f30c authored by Kevin Marshall's avatar Kevin Marshall Committed by Commit Bot

[Fuchsia] Output new-style Fuchsia stack traces.

Migrate base::debug::StackTrace to emit stack traces in the new
format documented at
https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md

Bug: 777252
Change-Id: If42d62af4d358efc3ba61af2b6118026eec9a15f
Reviewed-on: https://chromium-review.googlesource.com/c/1474678
Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636114}
parent b2c2f313
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
#include "base/debug/stack_trace.h" #include "base/debug/stack_trace.h"
#include <elf.h>
#include <link.h> #include <link.h>
#include <stddef.h> #include <stddef.h>
#include <string.h>
#include <threads.h> #include <threads.h>
#include <unwind.h> #include <unwind.h>
#include <zircon/process.h> #include <zircon/process.h>
...@@ -21,7 +21,10 @@ ...@@ -21,7 +21,10 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include "base/atomic_sequence_num.h"
#include "base/debug/elf_reader.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/no_destructor.h"
#include "base/stl_util.h" #include "base/stl_util.h"
namespace base { namespace base {
...@@ -30,7 +33,7 @@ namespace debug { ...@@ -30,7 +33,7 @@ namespace debug {
namespace { namespace {
const char kProcessNamePrefix[] = "app:"; const char kProcessNamePrefix[] = "app:";
const size_t kProcessNamePrefixLen = base::size(kProcessNamePrefix) - 1; const size_t kProcessNamePrefixLength = base::size(kProcessNamePrefix) - 1;
struct BacktraceData { struct BacktraceData {
void** trace_array; void** trace_array;
...@@ -49,20 +52,53 @@ _Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context, ...@@ -49,20 +52,53 @@ _Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context,
return _URC_NO_REASON; return _URC_NO_REASON;
} }
// Build a "rwx" C string-based representation of the permission bits.
// The output buffer is reused across calls, and should not be retained across
// consecutive invocations of this function.
const char* PermissionFlagsToString(int flags, char permission_buf[4]) {
char* permission = permission_buf;
if (flags & PF_R)
(*permission++) = 'r';
if (flags & PF_W)
(*permission++) = 'w';
if (flags & PF_X)
(*permission++) = 'x';
*permission = '\0';
return permission_buf;
}
// Stores and queries debugging symbol map info for the current process. // Stores and queries debugging symbol map info for the current process.
class SymbolMap { class SymbolMap {
public: public:
struct Entry { struct Segment {
void* addr; const void* addr = nullptr;
char name[ZX_MAX_NAME_LEN + kProcessNamePrefixLen]; size_t relative_addr = 0;
int permission_flags = 0;
size_t size = 0;
};
struct Module {
// Maximum number of PT_LOAD segments to process per ELF binary. Most
// binaries have only 2-3 such segments.
static constexpr size_t kMaxSegmentCount = 8;
const void* addr = nullptr;
std::array<Segment, kMaxSegmentCount> segments;
size_t segment_count = 0;
char name[ZX_MAX_NAME_LEN + kProcessNamePrefixLength + 1] = {0};
char build_id[kMaxBuildIdStringLength + 1] = {0};
}; };
SymbolMap(); SymbolMap();
~SymbolMap() = default; ~SymbolMap() = default;
// Gets the symbol map entry for |address|. Returns null if no entry could be // Gets all entries for the symbol map.
// found for the address, or if the symbol map could not be queried. span<Module> GetModules() { return {modules_.data(), count_}; }
Entry* GetForAddress(void* address);
private: private:
// Component builds of Chrome pull about 250 shared libraries (on Linux), so // Component builds of Chrome pull about 250 shared libraries (on Linux), so
...@@ -72,7 +108,7 @@ class SymbolMap { ...@@ -72,7 +108,7 @@ class SymbolMap {
void Populate(); void Populate();
// Sorted in descending order by address, for lookup purposes. // Sorted in descending order by address, for lookup purposes.
std::array<Entry, kMaxMapEntries> entries_; std::array<Module, kMaxMapEntries> modules_;
size_t count_ = 0; size_t count_ = 0;
bool valid_ = false; bool valid_ = false;
...@@ -84,21 +120,6 @@ SymbolMap::SymbolMap() { ...@@ -84,21 +120,6 @@ SymbolMap::SymbolMap() {
Populate(); Populate();
} }
SymbolMap::Entry* SymbolMap::GetForAddress(void* address) {
if (!valid_) {
return nullptr;
}
// Working backwards in the address space, return the first map entry whose
// address comes before |address| (thereby enclosing it.)
for (size_t i = 0; i < count_; ++i) {
if (address >= entries_[i].addr) {
return &entries_[i];
}
}
return nullptr;
}
void SymbolMap::Populate() { void SymbolMap::Populate() {
zx_handle_t process = zx_process_self(); zx_handle_t process = zx_process_self();
...@@ -107,11 +128,11 @@ void SymbolMap::Populate() { ...@@ -107,11 +128,11 @@ void SymbolMap::Populate() {
// TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so // TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so
// if we keep hitting problems with truncation, find a way to plumb argv[0] // if we keep hitting problems with truncation, find a way to plumb argv[0]
// through to here instead, e.g. using CommandLine::GetProgramName(). // through to here instead, e.g. using CommandLine::GetProgramName().
char app_name[std::extent<decltype(SymbolMap::Entry::name)>()]; char app_name[std::extent<decltype(SymbolMap::Module::name)>()];
strcpy(app_name, kProcessNamePrefix); strncpy(app_name, kProcessNamePrefix, sizeof(kProcessNamePrefix));
zx_status_t status = zx_object_get_property( zx_status_t status = zx_object_get_property(
process, ZX_PROP_NAME, app_name + kProcessNamePrefixLen, process, ZX_PROP_NAME, app_name + kProcessNamePrefixLength,
sizeof(app_name) - kProcessNamePrefixLen); sizeof(app_name) - kProcessNamePrefixLength);
if (status != ZX_OK) { if (status != ZX_OK) {
DPLOG(WARNING) DPLOG(WARNING)
<< "Couldn't get name, falling back to 'app' for program name: " << "Couldn't get name, falling back to 'app' for program name: "
...@@ -136,23 +157,67 @@ void SymbolMap::Populate() { ...@@ -136,23 +157,67 @@ void SymbolMap::Populate() {
return; return;
} }
// Copy the contents of the link map linked list to |entries_|. // Populate ELF binary metadata into |modules_|.
while (lmap != nullptr) { while (lmap != nullptr) {
if (count_ >= entries_.size()) { if (count_ >= kMaxMapEntries)
break;
SymbolMap::Module& next_entry = modules_[count_];
++count_;
next_entry.addr = reinterpret_cast<void*>(lmap->l_addr);
// Create Segment sub-entries for all PT_LOAD headers.
// Each Segment corresponds to a "mmap" line in the output.
next_entry.segment_count = 0;
for (const Elf64_Phdr& phdr : GetElfProgramHeaders(next_entry.addr)) {
if (phdr.p_type != PT_LOAD)
continue;
if (next_entry.segment_count > Module::kMaxSegmentCount) {
LOG(WARNING) << "Exceeded the maximum number of segments.";
break; break;
} }
SymbolMap::Entry* next_entry = &entries_[count_];
count_++;
next_entry->addr = reinterpret_cast<void*>(lmap->l_addr); Segment segment;
char* name_to_use = lmap->l_name[0] ? lmap->l_name : app_name; segment.addr =
strlcpy(next_entry->name, name_to_use, sizeof(next_entry->name)); reinterpret_cast<const char*>(next_entry.addr) + phdr.p_vaddr;
lmap = lmap->l_next; segment.relative_addr = phdr.p_vaddr;
segment.size = phdr.p_memsz;
segment.permission_flags = phdr.p_flags;
next_entry.segments[next_entry.segment_count] = std::move(segment);
++next_entry.segment_count;
}
// Get the human-readable library name from the ELF header, falling back on
// using names from the link map for binaries that aren't shared libraries.
Optional<StringPiece> elf_library_name =
ReadElfLibraryName(next_entry.addr);
if (elf_library_name) {
strlcpy(next_entry.name, elf_library_name->data(),
elf_library_name->size() + 1);
} else {
StringPiece link_map_name(lmap->l_name[0] ? lmap->l_name : app_name);
// The "module" stack trace annotation doesn't allow for strings which
// resemble paths, so extract the filename portion from |link_map_name|.
size_t directory_prefix_idx = link_map_name.find_last_of("/");
if (directory_prefix_idx != StringPiece::npos) {
link_map_name = link_map_name.substr(
directory_prefix_idx + 1,
link_map_name.size() - directory_prefix_idx - 1);
}
strlcpy(next_entry.name, link_map_name.data(), link_map_name.size() + 1);
}
if (!ReadElfBuildId(next_entry.addr, false, next_entry.build_id)) {
LOG(WARNING) << "Couldn't read build ID.";
continue;
} }
std::sort( lmap = lmap->l_next;
entries_.begin(), entries_.begin() + count_, }
[](const Entry& a, const Entry& b) -> bool { return a.addr > b.addr; });
valid_ = true; valid_ = true;
} }
...@@ -180,39 +245,35 @@ void StackTrace::PrintWithPrefix(const char* prefix_string) const { ...@@ -180,39 +245,35 @@ void StackTrace::PrintWithPrefix(const char* prefix_string) const {
OutputToStreamWithPrefix(&std::cerr, prefix_string); OutputToStreamWithPrefix(&std::cerr, prefix_string);
} }
// Sample stack trace output is designed to be similar to Fuchsia's crashlogger: // Emits stack trace data using the symbolizer markup format specified at:
// bt#00: pc 0x1527a058aa00 (app:/system/base_unittests,0x18bda00) // https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
// bt#01: pc 0x1527a0254b5c (app:/system/base_unittests,0x1587b5c)
// bt#02: pc 0x15279f446ece (app:/system/base_unittests,0x779ece)
// ...
// bt#21: pc 0x1527a05b51b4 (app:/system/base_unittests,0x18e81b4)
// bt#22: pc 0x54fdbf3593de (libc.so,0x1c3de)
// bt#23: end
void StackTrace::OutputToStreamWithPrefix(std::ostream* os, void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
const char* prefix_string) const { const char* prefix_string) const {
SymbolMap map; SymbolMap map;
size_t i = 0; int module_id = 0;
for (; (i < count_) && os->good(); ++i) { *os << "{{{reset}}}\n";
SymbolMap::Entry* entry = map.GetForAddress(trace_[i]); for (const SymbolMap::Module& entry : map.GetModules()) {
if (prefix_string) *os << "{{{module:" << module_id << ":" << entry.name
*os << prefix_string; << ":elf:" << entry.build_id << "}}}\n";
if (entry) {
size_t offset = reinterpret_cast<uintptr_t>(trace_[i]) - for (size_t i = 0; i < entry.segment_count; ++i) {
reinterpret_cast<uintptr_t>(entry->addr); const SymbolMap::Segment& segment = entry.segments[i];
*os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0)
<< ": pc " << trace_[i] << " (" << entry->name << ",0x" << std::hex char permission_string[4] = {};
<< offset << std::dec << std::setw(0) << ")\n"; *os << "{{{mmap:" << segment.addr << ":0x" << std::hex << segment.size
} else { << std::dec << ":load:" << module_id << ":"
// Fallback if the DSO map isn't available. << PermissionFlagsToString(segment.permission_flags,
// Logged PC values are absolute memory addresses, and the shared object permission_string)
// name is not emitted. << ":"
*os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0) << "0x" << std::hex << segment.relative_addr << std::dec << "}}}\n";
<< ": pc " << trace_[i] << "\n";
} }
++module_id;
} }
(*os) << "bt#" << std::setw(2) << i << ": end\n"; for (size_t i = 0; i < count_; ++i)
*os << "{{{bt:" << i << ":" << trace_[i] << "}}}\n";
} }
} // namespace debug } // namespace debug
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment