xref: /aosp_15_r20/external/cronet/base/debug/stack_trace_fuchsia.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/debug/stack_trace.h"
6 
7 #include <elf.h>
8 #include <link.h>
9 #include <stddef.h>
10 #include <threads.h>
11 #include <unwind.h>
12 #include <zircon/process.h>
13 #include <zircon/syscalls.h>
14 #include <zircon/syscalls/port.h>
15 #include <zircon/types.h>
16 
17 #include <algorithm>
18 #include <array>
19 #include <iomanip>
20 #include <iostream>
21 #include <string_view>
22 #include <type_traits>
23 
24 #include "base/atomic_sequence_num.h"
25 #include "base/debug/elf_reader.h"
26 #include "base/logging.h"
27 
28 namespace base {
29 namespace debug {
30 namespace {
31 
32 struct BacktraceData {
33   const void** trace_array;
34   size_t* count;
35   size_t max;
36 };
37 
UnwindStore(struct _Unwind_Context * context,void * user_data)38 _Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context,
39                                 void* user_data) {
40   BacktraceData* data = reinterpret_cast<BacktraceData*>(user_data);
41   uintptr_t pc = _Unwind_GetIP(context);
42   data->trace_array[*data->count] = reinterpret_cast<void*>(pc);
43   *data->count += 1;
44   if (*data->count == data->max)
45     return _URC_END_OF_STACK;
46   return _URC_NO_REASON;
47 }
48 
49 // Build a "rwx" C string-based representation of the permission bits.
50 // The output buffer is reused across calls, and should not be retained across
51 // consecutive invocations of this function.
PermissionFlagsToString(int flags,char permission_buf[4])52 const char* PermissionFlagsToString(int flags, char permission_buf[4]) {
53   char* permission = permission_buf;
54 
55   if (flags & PF_R)
56     (*permission++) = 'r';
57 
58   if (flags & PF_W)
59     (*permission++) = 'w';
60 
61   if (flags & PF_X)
62     (*permission++) = 'x';
63 
64   *permission = '\0';
65 
66   return permission_buf;
67 }
68 
69 // Stores and queries debugging symbol map info for the current process.
70 class SymbolMap {
71  public:
72   struct Segment {
73     const void* addr = nullptr;
74     size_t relative_addr = 0;
75     int permission_flags = 0;
76     size_t size = 0;
77   };
78 
79   struct Module {
80     // Maximum number of PT_LOAD segments to process per ELF binary. Most
81     // binaries have only 2-3 such segments.
82     static constexpr size_t kMaxSegmentCount = 8;
83 
84     const void* addr = nullptr;
85     std::array<Segment, kMaxSegmentCount> segments;
86     size_t segment_count = 0;
87     char name[ZX_MAX_NAME_LEN + 1] = {0};
88     char build_id[kMaxBuildIdStringLength + 1] = {0};
89   };
90 
91   SymbolMap();
92 
93   SymbolMap(const SymbolMap&) = delete;
94   SymbolMap& operator=(const SymbolMap&) = delete;
95 
96   ~SymbolMap() = default;
97 
98   // Gets all entries for the symbol map.
GetModules()99   span<Module> GetModules() { return {modules_.data(), count_}; }
100 
101  private:
102   // Component builds of Chrome pull about 250 shared libraries (on Linux), so
103   // 512 entries should be enough in most cases.
104   static const size_t kMaxMapEntries = 512;
105 
106   void Populate();
107 
108   // Sorted in descending order by address, for lookup purposes.
109   std::array<Module, kMaxMapEntries> modules_;
110 
111   size_t count_ = 0;
112   bool valid_ = false;
113 };
114 
SymbolMap()115 SymbolMap::SymbolMap() {
116   Populate();
117 }
118 
Populate()119 void SymbolMap::Populate() {
120   zx_handle_t process = zx_process_self();
121 
122   // Retrieve the debug info struct.
123   uintptr_t debug_addr;
124   zx_status_t status = zx_object_get_property(
125       process, ZX_PROP_PROCESS_DEBUG_ADDR, &debug_addr, sizeof(debug_addr));
126   if (status != ZX_OK) {
127     DPLOG(ERROR) << "Couldn't get symbol map for process: " << status;
128     return;
129   }
130   r_debug* debug_info = reinterpret_cast<r_debug*>(debug_addr);
131 
132   // Get the link map from the debug info struct.
133   link_map* lmap = reinterpret_cast<link_map*>(debug_info->r_map);
134   if (!lmap) {
135     DPLOG(ERROR) << "Null link_map for process.";
136     return;
137   }
138 
139   // Populate ELF binary metadata into |modules_|.
140   while (lmap != nullptr) {
141     if (count_ >= kMaxMapEntries)
142       break;
143 
144     SymbolMap::Module& next_entry = modules_[count_];
145     ++count_;
146 
147     next_entry.addr = reinterpret_cast<void*>(lmap->l_addr);
148 
149     // Create Segment sub-entries for all PT_LOAD headers.
150     // Each Segment corresponds to a "mmap" line in the output.
151     next_entry.segment_count = 0;
152     for (const Elf64_Phdr& phdr : GetElfProgramHeaders(next_entry.addr)) {
153       if (phdr.p_type != PT_LOAD)
154         continue;
155 
156       if (next_entry.segment_count > Module::kMaxSegmentCount) {
157         LOG(WARNING) << "Exceeded the maximum number of segments.";
158         break;
159       }
160 
161       Segment segment;
162       segment.addr =
163           reinterpret_cast<const char*>(next_entry.addr) + phdr.p_vaddr;
164       segment.relative_addr = phdr.p_vaddr;
165       segment.size = phdr.p_memsz;
166       segment.permission_flags = static_cast<int>(phdr.p_flags);
167 
168       next_entry.segments[next_entry.segment_count] = std::move(segment);
169       ++next_entry.segment_count;
170     }
171 
172     // Get the human-readable library name from the ELF header, falling back on
173     // using names from the link map for binaries that aren't shared libraries.
174     std::optional<std::string_view> elf_library_name =
175         ReadElfLibraryName(next_entry.addr);
176     if (elf_library_name) {
177       strlcpy(next_entry.name, elf_library_name->data(),
178               elf_library_name->size() + 1);
179     } else {
180       std::string_view link_map_name(lmap->l_name[0] ? lmap->l_name
181                                                      : "<executable>");
182 
183       // The "module" stack trace annotation doesn't allow for strings which
184       // resemble paths, so extract the filename portion from |link_map_name|.
185       size_t directory_prefix_idx = link_map_name.find_last_of("/");
186       if (directory_prefix_idx != StringPiece::npos) {
187         link_map_name = link_map_name.substr(
188             directory_prefix_idx + 1,
189             link_map_name.size() - directory_prefix_idx - 1);
190       }
191       strlcpy(next_entry.name, link_map_name.data(), link_map_name.size() + 1);
192     }
193 
194     if (!ReadElfBuildId(next_entry.addr, false, next_entry.build_id)) {
195       LOG(WARNING) << "Couldn't read build ID.";
196       continue;
197     }
198 
199     lmap = lmap->l_next;
200   }
201 
202   valid_ = true;
203 }
204 
205 // Returns true if |address| is contained by any of the memory regions
206 // mapped for |module_entry|.
ModuleContainsFrameAddress(const void * address,const SymbolMap::Module & module_entry)207 bool ModuleContainsFrameAddress(const void* address,
208                                 const SymbolMap::Module& module_entry) {
209   for (size_t i = 0; i < module_entry.segment_count; ++i) {
210     const SymbolMap::Segment& segment = module_entry.segments[i];
211     const void* segment_end = reinterpret_cast<const void*>(
212         reinterpret_cast<const char*>(segment.addr) + segment.size - 1);
213 
214     if (address >= segment.addr && address <= segment_end) {
215       return true;
216     }
217   }
218   return false;
219 }
220 
221 }  // namespace
222 
223 // static
EnableInProcessStackDumping()224 bool EnableInProcessStackDumping() {
225   // StackTrace works to capture the current stack (e.g. for diagnostics added
226   // to code), but for local capture and print of backtraces, we just let the
227   // system crashlogger take over. It handles printing out a nicely formatted
228   // backtrace with dso information, relative offsets, etc. that we can then
229   // filter with addr2line in the run script to get file/line info.
230   return true;
231 }
232 
CollectStackTrace(const void ** trace,size_t count)233 size_t CollectStackTrace(const void** trace, size_t count) {
234   size_t frame_count = 0;
235   BacktraceData data = {trace, &frame_count, count};
236   _Unwind_Backtrace(&UnwindStore, &data);
237   return frame_count;
238 }
239 
240 // static
PrintMessageWithPrefix(cstring_view prefix_string,cstring_view message)241 void StackTrace::PrintMessageWithPrefix(cstring_view prefix_string,
242                                         cstring_view message) {
243   std::cerr << prefix_string << message;
244 }
245 
PrintWithPrefixImpl(cstring_view prefix_string) const246 void StackTrace::PrintWithPrefixImpl(cstring_view prefix_string) const {
247   OutputToStreamWithPrefixImpl(&std::cerr, prefix_string);
248 }
249 
250 // Emits stack trace data using the symbolizer markup format specified at:
251 // https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
OutputToStreamWithPrefixImpl(std::ostream * os,cstring_view prefix_string) const252 void StackTrace::OutputToStreamWithPrefixImpl(
253     std::ostream* os,
254     cstring_view prefix_string) const {
255   SymbolMap map;
256 
257   int module_id = 0;
258   for (const SymbolMap::Module& module_entry : map.GetModules()) {
259     // Don't emit information on modules that aren't useful for the actual
260     // stack trace, so as to reduce the load on the symbolizer and syslog.
261     bool should_emit_module = false;
262     for (size_t i = 0; i < count_ && !should_emit_module; ++i) {
263       should_emit_module = ModuleContainsFrameAddress(trace_[i], module_entry);
264     }
265     if (!should_emit_module) {
266       continue;
267     }
268 
269     *os << "{{{module:" << module_id << ":" << module_entry.name
270         << ":elf:" << module_entry.build_id << "}}}\n";
271 
272     for (size_t i = 0; i < module_entry.segment_count; ++i) {
273       const SymbolMap::Segment& segment = module_entry.segments[i];
274 
275       char permission_string[4] = {};
276       *os << "{{{mmap:" << segment.addr << ":0x" << std::hex << segment.size
277           << std::dec << ":load:" << module_id << ":"
278           << PermissionFlagsToString(segment.permission_flags,
279                                      permission_string)
280           << ":"
281           << "0x" << std::hex << segment.relative_addr << std::dec << "}}}\n";
282     }
283 
284     ++module_id;
285   }
286 
287   for (size_t i = 0; i < count_; ++i)
288     *os << "{{{bt:" << i << ":" << trace_[i] << "}}}\n";
289 
290   *os << "{{{reset}}}\n";
291 }
292 
293 }  // namespace debug
294 }  // namespace base
295