2 * Copyright 2014 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "folly/experimental/symbolizer/Symbolizer.h"
25 #include <ext/stdio_filebuf.h>
26 #include <ext/stdio_sync_filebuf.h>
29 #include "folly/Conv.h"
30 #include "folly/FileUtil.h"
31 #include "folly/ScopeGuard.h"
32 #include "folly/String.h"
34 #include "folly/experimental/symbolizer/Elf.h"
35 #include "folly/experimental/symbolizer/Dwarf.h"
36 #include "folly/experimental/symbolizer/LineReader.h"
40 namespace symbolizer {
47 uintptr_t readHex(StringPiece& sp) {
49 const char* p = sp.begin();
50 for (; p != sp.end(); ++p) {
52 if (*p >= '0' && *p <= '9') {
54 } else if (*p >= 'a' && *p <= 'f') {
56 } else if (*p >= 'A' && *p <= 'F') {
63 sp.assign(p, sp.end());
68 * Skip over non-space characters.
70 void skipNS(StringPiece& sp) {
71 const char* p = sp.begin();
72 for (; p != sp.end() && (*p != ' ' && *p != '\t'); ++p) { }
73 sp.assign(p, sp.end());
77 * Skip over space and tab characters.
79 void skipWS(StringPiece& sp) {
80 const char* p = sp.begin();
81 for (; p != sp.end() && (*p == ' ' || *p == '\t'); ++p) { }
82 sp.assign(p, sp.end());
86 * Parse a line from /proc/self/maps
88 bool parseProcMapsLine(StringPiece line,
89 uintptr_t& from, uintptr_t& to,
90 StringPiece& fileName) {
91 // from to perm offset dev inode path
92 // 00400000-00405000 r-xp 00000000 08:03 35291182 /bin/cat
97 // Remove trailing newline, if any
98 if (line.back() == '\n') {
103 from = readHex(line);
104 if (line.empty() || line.front() != '-') {
111 if (line.empty() || line.front() != ' ') {
118 if (line.empty() || line.front() != ' ') {
123 uintptr_t fileOffset = readHex(line);
124 if (line.empty() || line.front() != ' ') {
128 if (fileOffset != 0) {
129 return false; // main mapping starts at 0
134 if (line.empty() || line.front() != ' ') {
141 if (line.empty() || line.front() != ' ') {
157 void Symbolizer::symbolize(const uintptr_t* addresses,
158 SymbolizedFrame* frames,
159 size_t addressCount) {
160 size_t remaining = 0;
161 for (size_t i = 0; i < addressCount; ++i) {
162 auto& frame = frames[i];
166 frame.location = Dwarf::LocationInfo();
170 if (remaining == 0) { // we're done
174 int fd = openNoInt("/proc/self/maps", O_RDONLY);
179 char buf[PATH_MAX + 100]; // Long enough for any line
180 LineReader reader(fd, buf, sizeof(buf));
182 char fileNameBuf[PATH_MAX];
184 while (remaining != 0) {
186 if (reader.readLine(line) != LineReader::kReading) {
193 StringPiece fileName;
194 if (!parseProcMapsLine(line, from, to, fileName)) {
199 ElfFile* elfFile = nullptr;
201 // See if any addresses are here
202 for (size_t i = 0; i < addressCount; ++i) {
203 auto& frame = frames[i];
208 uintptr_t address = addresses[i];
210 if (from > address || address >= to) {
218 // Open the file on first use
221 if (fileCount_ < kMaxFiles &&
223 fileName.size() < sizeof(fileNameBuf)) {
224 memcpy(fileNameBuf, fileName.data(), fileName.size());
225 fileNameBuf[fileName.size()] = '\0';
226 auto& f = files_[fileCount_++];
227 if (f.openNoThrow(fileNameBuf) != -1) {
238 uintptr_t fileAddress = address - from + elfFile->getBaseAddress();
239 auto sym = elfFile->getDefinitionByAddress(fileAddress);
243 auto name = elfFile->getSymbolName(sym);
248 Dwarf(elfFile).findAddress(fileAddress, frame.location);
256 const char kHexChars[] = "0123456789abcdef";
257 const SymbolizePrinter::Color kAddressColor = SymbolizePrinter::Color::BLUE;
258 const SymbolizePrinter::Color kFunctionColor = SymbolizePrinter::Color::PURPLE;
259 const SymbolizePrinter::Color kFileColor = SymbolizePrinter::Color::DEFAULT;
262 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
263 if (options_ & TERSE) {
264 printTerse(address, frame);
268 SCOPE_EXIT { color(Color::DEFAULT); };
270 color(kAddressColor);
271 // Can't use sprintf, not async-signal-safe
272 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
273 char buf[] = " @ 0000000000000000";
274 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
275 const char padBuf[] = " ";
276 folly::StringPiece pad(padBuf,
277 sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
280 while (address != 0) {
281 *p-- = kHexChars[address & 0xf];
284 doPrint(folly::StringPiece(buf, end));
286 color(kFunctionColor);
287 char mangledBuf[1024];
289 doPrint(" (not found)");
293 if (frame.name.empty()) {
294 doPrint(" (unknown)");
295 } else if (frame.name.size() >= sizeof(mangledBuf)) {
299 memcpy(mangledBuf, frame.name.data(), frame.name.size());
300 mangledBuf[frame.name.size()] = '\0';
302 char demangledBuf[1024];
303 demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
305 doPrint(demangledBuf);
308 if (!(options_ & NO_FILE_AND_LINE)) {
310 char fileBuf[PATH_MAX];
312 if (frame.location.hasFileAndLine) {
313 frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
319 uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
321 doPrint(StringPiece(buf, n));
324 if (frame.location.hasMainFile) {
325 char mainFileBuf[PATH_MAX];
326 mainFileBuf[0] = '\0';
327 frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
328 if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
332 doPrint(mainFileBuf);
340 const std::map<SymbolizePrinter::Color, std::string> kColorMap = {
341 { SymbolizePrinter::Color::DEFAULT, "\x1B[0m" },
342 { SymbolizePrinter::Color::RED, "\x1B[31m" },
343 { SymbolizePrinter::Color::GREEN, "\x1B[32m" },
344 { SymbolizePrinter::Color::YELLOW, "\x1B[33m" },
345 { SymbolizePrinter::Color::BLUE, "\x1B[34m" },
346 { SymbolizePrinter::Color::CYAN, "\x1B[36m" },
347 { SymbolizePrinter::Color::WHITE, "\x1B[37m" },
348 { SymbolizePrinter::Color::PURPLE, "\x1B[35m" },
353 void SymbolizePrinter::color(SymbolizePrinter::Color color) {
354 if ((options_ & COLOR) == 0 &&
355 ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
358 auto it = kColorMap.find(color);
359 if (it == kColorMap.end()) {
365 void SymbolizePrinter::println(uintptr_t address,
366 const SymbolizedFrame& frame) {
367 print(address, frame);
371 void SymbolizePrinter::printTerse(uintptr_t address,
372 const SymbolizedFrame& frame) {
374 char mangledBuf[1024];
375 memcpy(mangledBuf, frame.name.data(), frame.name.size());
376 mangledBuf[frame.name.size()] = '\0';
378 char demangledBuf[1024] = {0};
379 demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
380 doPrint(strlen(demangledBuf) == 0 ? "(unknown)" : demangledBuf);
382 // Can't use sprintf, not async-signal-safe
383 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
384 char buf[] = "0x0000000000000000";
385 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
388 while (address != 0) {
389 *p-- = kHexChars[address & 0xf];
392 doPrint(StringPiece(buf, end));
396 void SymbolizePrinter::println(const uintptr_t* addresses,
397 const SymbolizedFrame* frames,
399 for (size_t i = 0; i < frameCount; ++i) {
400 println(addresses[i], frames[i]);
406 int getFD(const std::ios& stream) {
408 std::streambuf* buf = stream.rdbuf();
409 using namespace __gnu_cxx;
412 auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
414 return fileno(sbuf->file());
418 auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
427 bool isTty(int options, int fd) {
428 return ((options & SymbolizePrinter::TERSE) == 0 &&
429 (options & SymbolizePrinter::COLOR_IF_TTY) != 0 &&
430 fd >= 0 && ::isatty(fd));
433 } // anonymous namespace
435 OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
436 : SymbolizePrinter(options, isTty(options, getFD(out))),
440 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
444 FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options)
445 : SymbolizePrinter(options, isTty(options, fd)),
449 void FDSymbolizePrinter::doPrint(StringPiece sp) {
450 writeFull(fd_, sp.data(), sp.size());
453 FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
454 : SymbolizePrinter(options, isTty(options, fileno(file))),
458 void FILESymbolizePrinter::doPrint(StringPiece sp) {
459 fwrite(sp.data(), 1, sp.size(), file_);
462 void StringSymbolizePrinter::doPrint(StringPiece sp) {
463 buf_.append(sp.data(), sp.size());
466 } // namespace symbolizer