|  | // Copyright 2017 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 <link.h> | 
|  | #include <stddef.h> | 
|  | #include <string.h> | 
|  | #include <threads.h> | 
|  | #include <unwind.h> | 
|  | #include <zircon/crashlogger.h> | 
|  | #include <zircon/process.h> | 
|  | #include <zircon/syscalls.h> | 
|  | #include <zircon/syscalls/definitions.h> | 
|  | #include <zircon/syscalls/port.h> | 
|  | #include <zircon/types.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iomanip> | 
|  | #include <iostream> | 
|  |  | 
|  | #include "base/logging.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace debug { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kProcessNamePrefix[] = "app:"; | 
|  | const size_t kProcessNamePrefixLen = arraysize(kProcessNamePrefix) - 1; | 
|  |  | 
|  | struct BacktraceData { | 
|  | void** trace_array; | 
|  | size_t* count; | 
|  | size_t max; | 
|  | }; | 
|  |  | 
|  | _Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context, | 
|  | void* user_data) { | 
|  | BacktraceData* data = reinterpret_cast<BacktraceData*>(user_data); | 
|  | uintptr_t pc = _Unwind_GetIP(context); | 
|  | data->trace_array[*data->count] = reinterpret_cast<void*>(pc); | 
|  | *data->count += 1; | 
|  | if (*data->count == data->max) | 
|  | return _URC_END_OF_STACK; | 
|  | return _URC_NO_REASON; | 
|  | } | 
|  |  | 
|  | // Stores and queries debugging symbol map info for the current process. | 
|  | class SymbolMap { | 
|  | public: | 
|  | struct Entry { | 
|  | void* addr; | 
|  | char name[ZX_MAX_NAME_LEN + kProcessNamePrefixLen]; | 
|  | }; | 
|  |  | 
|  | SymbolMap(); | 
|  | ~SymbolMap() = default; | 
|  |  | 
|  | // Gets the symbol map entry for |address|. Returns null if no entry could be | 
|  | // found for the address, or if the symbol map could not be queried. | 
|  | Entry* GetForAddress(void* address); | 
|  |  | 
|  | private: | 
|  | // Component builds of Chrome pull about 250 shared libraries (on Linux), so | 
|  | // 512 entries should be enough in most cases. | 
|  | static const size_t kMaxMapEntries = 512; | 
|  |  | 
|  | void Populate(); | 
|  |  | 
|  | // Sorted in descending order by address, for lookup purposes. | 
|  | Entry entries_[kMaxMapEntries]; | 
|  |  | 
|  | size_t count_ = 0; | 
|  | bool valid_ = false; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(SymbolMap); | 
|  | }; | 
|  |  | 
|  | SymbolMap::SymbolMap() { | 
|  | 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() { | 
|  | zx_handle_t process = zx_process_self(); | 
|  |  | 
|  | // Try to fetch the name of the process' main executable, which was set as the | 
|  | // name of the |process| kernel object. | 
|  | // 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] | 
|  | // through to here instead, e.g. using CommandLine::GetProgramName(). | 
|  | char app_name[arraysize(SymbolMap::Entry::name)]; | 
|  | strcpy(app_name, kProcessNamePrefix); | 
|  | zx_status_t status = zx_object_get_property( | 
|  | process, ZX_PROP_NAME, app_name + kProcessNamePrefixLen, | 
|  | sizeof(app_name) - kProcessNamePrefixLen); | 
|  | if (status != ZX_OK) { | 
|  | DPLOG(WARNING) | 
|  | << "Couldn't get name, falling back to 'app' for program name: " | 
|  | << status; | 
|  | strlcat(app_name, "app", sizeof(app_name)); | 
|  | } | 
|  |  | 
|  | // Retrieve the debug info struct. | 
|  | uintptr_t debug_addr; | 
|  | status = zx_object_get_property(process, ZX_PROP_PROCESS_DEBUG_ADDR, | 
|  | &debug_addr, sizeof(debug_addr)); | 
|  | if (status != ZX_OK) { | 
|  | DPLOG(ERROR) << "Couldn't get symbol map for process: " << status; | 
|  | return; | 
|  | } | 
|  | r_debug* debug_info = reinterpret_cast<r_debug*>(debug_addr); | 
|  |  | 
|  | // Get the link map from the debug info struct. | 
|  | link_map* lmap = reinterpret_cast<link_map*>(debug_info->r_map); | 
|  | if (!lmap) { | 
|  | DPLOG(ERROR) << "Null link_map for process."; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Copy the contents of the link map linked list to |entries_|. | 
|  | while (lmap != nullptr) { | 
|  | if (count_ >= arraysize(entries_)) { | 
|  | break; | 
|  | } | 
|  | SymbolMap::Entry* next_entry = &entries_[count_]; | 
|  | count_++; | 
|  |  | 
|  | next_entry->addr = reinterpret_cast<void*>(lmap->l_addr); | 
|  | char* name_to_use = lmap->l_name[0] ? lmap->l_name : app_name; | 
|  | strlcpy(next_entry->name, name_to_use, sizeof(next_entry->name)); | 
|  | lmap = lmap->l_next; | 
|  | } | 
|  |  | 
|  | std::sort( | 
|  | &entries_[0], &entries_[count_ - 1], | 
|  | [](const Entry& a, const Entry& b) -> bool { return a.addr >= b.addr; }); | 
|  |  | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | bool EnableInProcessStackDumping() { | 
|  | // StackTrace works to capture the current stack (e.g. for diagnostics added | 
|  | // to code), but for local capture and print of backtraces, we just let the | 
|  | // system crashlogger take over. It handles printing out a nicely formatted | 
|  | // backtrace with dso information, relative offsets, etc. that we can then | 
|  | // filter with addr2line in the run script to get file/line info. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | StackTrace::StackTrace(size_t count) : count_(0) { | 
|  | BacktraceData data = {&trace_[0], &count_, | 
|  | std::min(count, static_cast<size_t>(kMaxTraces))}; | 
|  | _Unwind_Backtrace(&UnwindStore, &data); | 
|  | } | 
|  |  | 
|  | void StackTrace::Print() const { | 
|  | OutputToStream(&std::cerr); | 
|  | } | 
|  |  | 
|  | // Sample stack trace output is designed to be similar to Fuchsia's crashlogger: | 
|  | // bt#00: pc 0x1527a058aa00 (app:/system/base_unittests,0x18bda00) | 
|  | // 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::OutputToStream(std::ostream* os) const { | 
|  | SymbolMap map; | 
|  |  | 
|  | size_t i = 0; | 
|  | for (; (i < count_) && os->good(); ++i) { | 
|  | SymbolMap::Entry* entry = map.GetForAddress(trace_[i]); | 
|  | if (entry) { | 
|  | size_t offset = reinterpret_cast<uintptr_t>(trace_[i]) - | 
|  | reinterpret_cast<uintptr_t>(entry->addr); | 
|  | *os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0) | 
|  | << ": pc " << trace_[i] << " (" << entry->name << ",0x" << std::hex | 
|  | << offset << std::dec << std::setw(0) << ")\n"; | 
|  | } else { | 
|  | // Fallback if the DSO map isn't available. | 
|  | // Logged PC values are absolute memory addresses, and the shared object | 
|  | // name is not emitted. | 
|  | *os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0) | 
|  | << ": pc " << trace_[i] << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | (*os) << "bt#" << std::setw(2) << i << ": end\n"; | 
|  | } | 
|  |  | 
|  | }  // namespace debug | 
|  | }  // namespace base |