Consistency in namespace-closing comments
[folly.git] / folly / experimental / symbolizer / Symbolizer.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/experimental/symbolizer/Symbolizer.h>
18
19 #include <link.h>
20
21 #include <climits>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <iostream>
25
26 #ifdef __GLIBCXX__
27 #include <ext/stdio_filebuf.h>
28 #include <ext/stdio_sync_filebuf.h>
29 #endif
30
31 #include <folly/Conv.h>
32 #include <folly/FileUtil.h>
33 #include <folly/Memory.h>
34 #include <folly/ScopeGuard.h>
35 #include <folly/String.h>
36
37 #include <folly/experimental/symbolizer/Dwarf.h>
38 #include <folly/experimental/symbolizer/Elf.h>
39 #include <folly/experimental/symbolizer/LineReader.h>
40 #include <folly/portability/Unistd.h>
41
42 /*
43  * This is declared in `link.h' on Linux platforms, but apparently not on the
44  * Mac version of the file.  It's harmless to declare again, in any case.
45  *
46  * Note that declaring it with `extern "C"` results in linkage conflicts.
47  */
48 extern struct r_debug _r_debug;
49
50 namespace folly {
51 namespace symbolizer {
52
53 namespace {
54
55 ElfCache* defaultElfCache() {
56   static constexpr size_t defaultCapacity = 500;
57   static auto cache = new ElfCache(defaultCapacity);
58   return cache;
59 }
60
61 } // namespace
62
63 void SymbolizedFrame::set(
64     const std::shared_ptr<ElfFile>& file,
65     uintptr_t address,
66     Dwarf::LocationInfoMode mode) {
67   clear();
68   found = true;
69
70   address += file->getBaseAddress();
71   auto sym = file->getDefinitionByAddress(address);
72   if (!sym.first) {
73     return;
74   }
75
76   file_ = file;
77   name = file->getSymbolName(sym);
78
79   Dwarf(file.get()).findAddress(address, location, mode);
80 }
81
82 Symbolizer::Symbolizer(ElfCacheBase* cache, Dwarf::LocationInfoMode mode)
83     : cache_(cache ? cache : defaultElfCache()), mode_(mode) {}
84
85 void Symbolizer::symbolize(
86     const uintptr_t* addresses,
87     SymbolizedFrame* frames,
88     size_t addrCount) {
89   size_t remaining = 0;
90   for (size_t i = 0; i < addrCount; ++i) {
91     auto& frame = frames[i];
92     if (!frame.found) {
93       ++remaining;
94       frame.clear();
95     }
96   }
97
98   if (remaining == 0) { // we're done
99     return;
100   }
101
102   if (_r_debug.r_version != 1) {
103     return;
104   }
105
106   char selfPath[PATH_MAX + 8];
107   ssize_t selfSize;
108   if ((selfSize = readlink("/proc/self/exe", selfPath, PATH_MAX + 1)) == -1) {
109     // Something has gone terribly wrong.
110     return;
111   }
112   selfPath[selfSize] = '\0';
113
114   for (auto lmap = _r_debug.r_map; lmap != nullptr && remaining != 0;
115        lmap = lmap->l_next) {
116     // The empty string is used in place of the filename for the link_map
117     // corresponding to the running executable.  Additionally, the `l_addr' is
118     // 0 and the link_map appears to be first in the list---but none of this
119     // behavior appears to be documented, so checking for the empty string is
120     // as good as anything.
121     auto const objPath = lmap->l_name[0] != '\0' ? lmap->l_name : selfPath;
122
123     auto const elfFile = cache_->getFile(objPath);
124     if (!elfFile) {
125       continue;
126     }
127
128     // Get the address at which the object is loaded.  We have to use the ELF
129     // header for the running executable, since its `l_addr' is zero, but we
130     // should use `l_addr' for everything else---in particular, if the object
131     // is position-independent, getBaseAddress() (which is p_vaddr) will be 0.
132     auto const base =
133         lmap->l_addr != 0 ? lmap->l_addr : elfFile->getBaseAddress();
134
135     for (size_t i = 0; i < addrCount && remaining != 0; ++i) {
136       auto& frame = frames[i];
137       if (frame.found) {
138         continue;
139       }
140
141       auto const addr = addresses[i];
142       // Get the unrelocated, ELF-relative address.
143       auto const adjusted = addr - base;
144
145       if (elfFile->getSectionContainingAddress(adjusted)) {
146         frame.set(elfFile, adjusted, mode_);
147         --remaining;
148       }
149     }
150   }
151 }
152
153 namespace {
154 constexpr char kHexChars[] = "0123456789abcdef";
155 constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
156 constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
157 constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
158 } // namespace
159
160 constexpr char AddressFormatter::bufTemplate[];
161 constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
162     SymbolizePrinter::kColorMap;
163
164 AddressFormatter::AddressFormatter() {
165   memcpy(buf_, bufTemplate, sizeof(buf_));
166 }
167
168 folly::StringPiece AddressFormatter::format(uintptr_t address) {
169   // Can't use sprintf, not async-signal-safe
170   static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
171   char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));
172   char* p = end;
173   *p-- = '\0';
174   while (address != 0) {
175     *p-- = kHexChars[address & 0xf];
176     address >>= 4;
177   }
178
179   return folly::StringPiece(buf_, end);
180 }
181
182 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
183   if (options_ & TERSE) {
184     printTerse(address, frame);
185     return;
186   }
187
188   SCOPE_EXIT {
189     color(Color::DEFAULT);
190   };
191
192   if (!(options_ & NO_FRAME_ADDRESS)) {
193     color(kAddressColor);
194
195     AddressFormatter formatter;
196     doPrint(formatter.format(address));
197   }
198
199   const char padBuf[] = "                       ";
200   folly::StringPiece pad(
201       padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
202
203   color(kFunctionColor);
204   if (!frame.found) {
205     doPrint(" (not found)");
206     return;
207   }
208
209   if (!frame.name || frame.name[0] == '\0') {
210     doPrint(" (unknown)");
211   } else {
212     char demangledBuf[2048];
213     demangle(frame.name, demangledBuf, sizeof(demangledBuf));
214     doPrint(" ");
215     doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
216   }
217
218   if (!(options_ & NO_FILE_AND_LINE)) {
219     color(kFileColor);
220     char fileBuf[PATH_MAX];
221     fileBuf[0] = '\0';
222     if (frame.location.hasFileAndLine) {
223       frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
224       doPrint("\n");
225       doPrint(pad);
226       doPrint(fileBuf);
227
228       char buf[22];
229       uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
230       doPrint(":");
231       doPrint(StringPiece(buf, n));
232     }
233
234     if (frame.location.hasMainFile) {
235       char mainFileBuf[PATH_MAX];
236       mainFileBuf[0] = '\0';
237       frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
238       if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
239         doPrint("\n");
240         doPrint(pad);
241         doPrint("-> ");
242         doPrint(mainFileBuf);
243       }
244     }
245   }
246 }
247
248 void SymbolizePrinter::color(SymbolizePrinter::Color color) {
249   if ((options_ & COLOR) == 0 && ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
250     return;
251   }
252   if (color < 0 || color >= kColorMap.size()) {
253     return;
254   }
255   doPrint(kColorMap[color]);
256 }
257
258 void SymbolizePrinter::println(
259     uintptr_t address,
260     const SymbolizedFrame& frame) {
261   print(address, frame);
262   doPrint("\n");
263 }
264
265 void SymbolizePrinter::printTerse(
266     uintptr_t address,
267     const SymbolizedFrame& frame) {
268   if (frame.found && frame.name && frame.name[0] != '\0') {
269     char demangledBuf[2048] = {0};
270     demangle(frame.name, demangledBuf, sizeof(demangledBuf));
271     doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
272   } else {
273     // Can't use sprintf, not async-signal-safe
274     static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
275     char buf[] = "0x0000000000000000";
276     char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
277     char* p = end;
278     *p-- = '\0';
279     while (address != 0) {
280       *p-- = kHexChars[address & 0xf];
281       address >>= 4;
282     }
283     doPrint(StringPiece(buf, end));
284   }
285 }
286
287 void SymbolizePrinter::println(
288     const uintptr_t* addresses,
289     const SymbolizedFrame* frames,
290     size_t frameCount) {
291   for (size_t i = 0; i < frameCount; ++i) {
292     println(addresses[i], frames[i]);
293   }
294 }
295
296 namespace {
297
298 int getFD(const std::ios& stream) {
299 #ifdef __GNUC__
300   std::streambuf* buf = stream.rdbuf();
301   using namespace __gnu_cxx;
302
303   {
304     auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
305     if (sbuf) {
306       return fileno(sbuf->file());
307     }
308   }
309   {
310     auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
311     if (sbuf) {
312       return sbuf->fd();
313     }
314   }
315 #endif // __GNUC__
316   return -1;
317 }
318
319 bool isColorfulTty(int options, int fd) {
320   if ((options & SymbolizePrinter::TERSE) != 0 ||
321       (options & SymbolizePrinter::COLOR_IF_TTY) == 0 || fd < 0 ||
322       !::isatty(fd)) {
323     return false;
324   }
325   auto term = ::getenv("TERM");
326   return !(term == nullptr || term[0] == '\0' || strcmp(term, "dumb") == 0);
327 }
328
329 } // namespace
330
331 OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
332     : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),
333       out_(out) {}
334
335 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
336   out_ << sp;
337 }
338
339 FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
340     : SymbolizePrinter(options, isColorfulTty(options, fd)),
341       fd_(fd),
342       buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {}
343
344 FDSymbolizePrinter::~FDSymbolizePrinter() {
345   flush();
346 }
347
348 void FDSymbolizePrinter::doPrint(StringPiece sp) {
349   if (buffer_) {
350     if (sp.size() > buffer_->tailroom()) {
351       flush();
352       writeFull(fd_, sp.data(), sp.size());
353     } else {
354       memcpy(buffer_->writableTail(), sp.data(), sp.size());
355       buffer_->append(sp.size());
356     }
357   } else {
358     writeFull(fd_, sp.data(), sp.size());
359   }
360 }
361
362 void FDSymbolizePrinter::flush() {
363   if (buffer_ && !buffer_->empty()) {
364     writeFull(fd_, buffer_->data(), buffer_->length());
365     buffer_->clear();
366   }
367 }
368
369 FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
370     : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),
371       file_(file) {}
372
373 void FILESymbolizePrinter::doPrint(StringPiece sp) {
374   fwrite(sp.data(), 1, sp.size(), file_);
375 }
376
377 void StringSymbolizePrinter::doPrint(StringPiece sp) {
378   buf_.append(sp.data(), sp.size());
379 }
380
381 StackTracePrinter::StackTracePrinter(size_t minSignalSafeElfCacheSize, int fd)
382     : fd_(fd),
383       elfCache_(std::max(countLoadedElfFiles(), minSignalSafeElfCacheSize)),
384       printer_(
385           fd,
386           SymbolizePrinter::COLOR_IF_TTY,
387           size_t(64) << 10), // 64KiB
388       addresses_(std::make_unique<FrameArray<kMaxStackTraceDepth>>()) {}
389
390 void StackTracePrinter::flush() {
391   printer_.flush();
392   fsyncNoInt(fd_);
393 }
394
395 void StackTracePrinter::printStackTrace(bool symbolize) {
396   SCOPE_EXIT {
397     flush();
398   };
399
400   // Skip the getStackTrace frame
401   if (!getStackTraceSafe(*addresses_)) {
402     print("(error retrieving stack trace)\n");
403   } else if (symbolize) {
404     // Do our best to populate location info, process is going to terminate,
405     // so performance isn't critical.
406     Symbolizer symbolizer(&elfCache_, Dwarf::LocationInfoMode::FULL);
407     symbolizer.symbolize(*addresses_);
408
409     // Skip the top 2 frames:
410     // getStackTraceSafe
411     // StackTracePrinter::printStackTrace (here)
412     //
413     // Leaving signalHandler on the stack for clarity, I think.
414     printer_.println(*addresses_, 2);
415   } else {
416     print("(safe mode, symbolizer not available)\n");
417     AddressFormatter formatter;
418     for (size_t i = 0; i < addresses_->frameCount; ++i) {
419       print(formatter.format(addresses_->addresses[i]));
420       print("\n");
421     }
422   }
423 }
424
425 } // namespace symbolizer
426 } // namespace folly