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