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