Use more libstdc++ specific macro
[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(const std::shared_ptr<ElfFile>& file,
64                           uintptr_t address,
65                           Dwarf::LocationInfoMode mode) {
66   clear();
67   found = true;
68
69   address += file->getBaseAddress();
70   auto sym = file->getDefinitionByAddress(address);
71   if (!sym.first) {
72     return;
73   }
74
75   file_ = file;
76   name = file->getSymbolName(sym);
77
78   Dwarf(file.get()).findAddress(address, location, mode);
79 }
80
81 Symbolizer::Symbolizer(ElfCacheBase* cache, Dwarf::LocationInfoMode mode)
82   : cache_(cache ? cache : defaultElfCache()), mode_(mode) {
83 }
84
85 void Symbolizer::symbolize(const uintptr_t* addresses,
86                            SymbolizedFrame* frames,
87                            size_t addrCount) {
88   size_t remaining = 0;
89   for (size_t i = 0; i < addrCount; ++i) {
90     auto& frame = frames[i];
91     if (!frame.found) {
92       ++remaining;
93       frame.clear();
94     }
95   }
96
97   if (remaining == 0) {  // we're done
98     return;
99   }
100
101   if (_r_debug.r_version != 1) {
102     return;
103   }
104
105   char selfPath[PATH_MAX + 8];
106   ssize_t selfSize;
107   if ((selfSize = readlink("/proc/self/exe", selfPath, PATH_MAX + 1)) == -1) {
108     // Something has gone terribly wrong.
109     return;
110   }
111   selfPath[selfSize] = '\0';
112
113   for (auto lmap = _r_debug.r_map;
114        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 = lmap->l_addr != 0
133       ? lmap->l_addr
134       : elfFile->getBaseAddress();
135
136     for (size_t i = 0; i < addrCount && remaining != 0; ++i) {
137       auto& frame = frames[i];
138       if (frame.found) {
139         continue;
140       }
141
142       auto const addr = addresses[i];
143       // Get the unrelocated, ELF-relative address.
144       auto const adjusted = addr - base;
145
146       if (elfFile->getSectionContainingAddress(adjusted)) {
147         frame.set(elfFile, adjusted, mode_);
148         --remaining;
149       }
150     }
151   }
152 }
153
154 namespace {
155 constexpr char kHexChars[] = "0123456789abcdef";
156 constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
157 constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
158 constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
159 }  // namespace
160
161 constexpr char AddressFormatter::bufTemplate[];
162 constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
163     SymbolizePrinter::kColorMap;
164
165 AddressFormatter::AddressFormatter() {
166   memcpy(buf_, bufTemplate, sizeof(buf_));
167 }
168
169 folly::StringPiece AddressFormatter::format(uintptr_t address) {
170   // Can't use sprintf, not async-signal-safe
171   static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
172   char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));
173   char* p = end;
174   *p-- = '\0';
175   while (address != 0) {
176     *p-- = kHexChars[address & 0xf];
177     address >>= 4;
178   }
179
180   return folly::StringPiece(buf_, end);
181 }
182
183 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
184   if (options_ & TERSE) {
185     printTerse(address, frame);
186     return;
187   }
188
189   SCOPE_EXIT { color(Color::DEFAULT); };
190
191   if (!(options_ & NO_FRAME_ADDRESS)) {
192     color(kAddressColor);
193
194     AddressFormatter formatter;
195     doPrint(formatter.format(address));
196   }
197
198   const char padBuf[] = "                       ";
199   folly::StringPiece pad(padBuf,
200                          sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
201
202   color(kFunctionColor);
203   if (!frame.found) {
204     doPrint(" (not found)");
205     return;
206   }
207
208   if (!frame.name || frame.name[0] == '\0') {
209     doPrint(" (unknown)");
210   } else {
211     char demangledBuf[2048];
212     demangle(frame.name, demangledBuf, sizeof(demangledBuf));
213     doPrint(" ");
214     doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
215   }
216
217   if (!(options_ & NO_FILE_AND_LINE)) {
218     color(kFileColor);
219     char fileBuf[PATH_MAX];
220     fileBuf[0] = '\0';
221     if (frame.location.hasFileAndLine) {
222       frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
223       doPrint("\n");
224       doPrint(pad);
225       doPrint(fileBuf);
226
227       char buf[22];
228       uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
229       doPrint(":");
230       doPrint(StringPiece(buf, n));
231     }
232
233     if (frame.location.hasMainFile) {
234       char mainFileBuf[PATH_MAX];
235       mainFileBuf[0] = '\0';
236       frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
237       if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
238         doPrint("\n");
239         doPrint(pad);
240         doPrint("-> ");
241         doPrint(mainFileBuf);
242       }
243     }
244   }
245 }
246
247 void SymbolizePrinter::color(SymbolizePrinter::Color color) {
248   if ((options_ & COLOR) == 0 &&
249       ((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(uintptr_t address,
259                                const SymbolizedFrame& frame) {
260   print(address, frame);
261   doPrint("\n");
262 }
263
264 void SymbolizePrinter::printTerse(uintptr_t address,
265                                   const SymbolizedFrame& frame) {
266   if (frame.found && frame.name && frame.name[0] != '\0') {
267     char demangledBuf[2048] = {0};
268     demangle(frame.name, demangledBuf, sizeof(demangledBuf));
269     doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
270   } else {
271     // Can't use sprintf, not async-signal-safe
272     static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
273     char buf[] = "0x0000000000000000";
274     char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
275     char* p = end;
276     *p-- = '\0';
277     while (address != 0) {
278       *p-- = kHexChars[address & 0xf];
279       address >>= 4;
280     }
281     doPrint(StringPiece(buf, end));
282   }
283 }
284
285 void SymbolizePrinter::println(const uintptr_t* addresses,
286                                const SymbolizedFrame* frames,
287                                size_t frameCount) {
288   for (size_t i = 0; i < frameCount; ++i) {
289     println(addresses[i], frames[i]);
290   }
291 }
292
293 namespace {
294
295 int getFD(const std::ios& stream) {
296 #ifdef __GNUC__
297   std::streambuf* buf = stream.rdbuf();
298   using namespace __gnu_cxx;
299
300   {
301     auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
302     if (sbuf) {
303       return fileno(sbuf->file());
304     }
305   }
306   {
307     auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
308     if (sbuf) {
309       return sbuf->fd();
310     }
311   }
312 #endif  // __GNUC__
313   return -1;
314 }
315
316 bool isColorfulTty(int options, int fd) {
317   if ((options & SymbolizePrinter::TERSE) != 0 ||
318       (options & SymbolizePrinter::COLOR_IF_TTY) == 0 ||
319       fd < 0 || !::isatty(fd)) {
320     return false;
321   }
322   auto term = ::getenv("TERM");
323   return !(term == nullptr || term[0] == '\0' || strcmp(term, "dumb") == 0);
324 }
325
326 }  // anonymous namespace
327
328 OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
329   : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),
330     out_(out) {
331 }
332
333 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
334   out_ << sp;
335 }
336
337 FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
338   : SymbolizePrinter(options, isColorfulTty(options, fd)),
339     fd_(fd),
340     buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {
341 }
342
343 FDSymbolizePrinter::~FDSymbolizePrinter() {
344   flush();
345 }
346
347 void FDSymbolizePrinter::doPrint(StringPiece sp) {
348   if (buffer_) {
349     if (sp.size() > buffer_->tailroom()) {
350       flush();
351       writeFull(fd_, sp.data(), sp.size());
352     } else {
353       memcpy(buffer_->writableTail(), sp.data(), sp.size());
354       buffer_->append(sp.size());
355     }
356   } else {
357     writeFull(fd_, sp.data(), sp.size());
358   }
359 }
360
361 void FDSymbolizePrinter::flush() {
362   if (buffer_ && !buffer_->empty()) {
363     writeFull(fd_, buffer_->data(), buffer_->length());
364     buffer_->clear();
365   }
366 }
367
368 FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
369   : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),
370     file_(file) {
371 }
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