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