2 * Copyright 2015 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>
24 #include <ext/stdio_filebuf.h>
25 #include <ext/stdio_sync_filebuf.h>
28 #include <folly/Conv.h>
29 #include <folly/FileUtil.h>
30 #include <folly/ScopeGuard.h>
31 #include <folly/String.h>
33 #include <folly/experimental/symbolizer/Elf.h>
34 #include <folly/experimental/symbolizer/Dwarf.h>
35 #include <folly/experimental/symbolizer/LineReader.h>
39 namespace symbolizer {
46 uintptr_t readHex(StringPiece& sp) {
48 const char* p = sp.begin();
49 for (; p != sp.end(); ++p) {
51 if (*p >= '0' && *p <= '9') {
53 } else if (*p >= 'a' && *p <= 'f') {
55 } else if (*p >= 'A' && *p <= 'F') {
62 sp.assign(p, sp.end());
67 * Skip over non-space characters.
69 void skipNS(StringPiece& sp) {
70 const char* p = sp.begin();
71 for (; p != sp.end() && (*p != ' ' && *p != '\t'); ++p) { }
72 sp.assign(p, sp.end());
76 * Skip over space and tab characters.
78 void skipWS(StringPiece& sp) {
79 const char* p = sp.begin();
80 for (; p != sp.end() && (*p == ' ' || *p == '\t'); ++p) { }
81 sp.assign(p, sp.end());
85 * Parse a line from /proc/self/maps
87 bool parseProcMapsLine(StringPiece line,
88 uintptr_t& from, uintptr_t& to,
89 StringPiece& fileName) {
90 // from to perm offset dev inode path
91 // 00400000-00405000 r-xp 00000000 08:03 35291182 /bin/cat
96 // Remove trailing newline, if any
97 if (line.back() == '\n') {
102 from = readHex(line);
103 if (line.empty() || line.front() != '-') {
110 if (line.empty() || line.front() != ' ') {
117 if (line.empty() || line.front() != ' ') {
122 uintptr_t fileOffset = readHex(line);
123 if (line.empty() || line.front() != ' ') {
127 if (fileOffset != 0) {
128 return false; // main mapping starts at 0
133 if (line.empty() || line.front() != ' ') {
140 if (line.empty() || line.front() != ' ') {
154 ElfCache* defaultElfCache() {
155 static constexpr size_t defaultCapacity = 500;
156 static ElfCache cache(defaultCapacity);
162 void SymbolizedFrame::set(const std::shared_ptr<ElfFile>& file,
167 address += file->getBaseAddress();
168 auto sym = file->getDefinitionByAddress(address);
174 name = file->getSymbolName(sym);
176 Dwarf(file.get()).findAddress(address, location);
180 Symbolizer::Symbolizer(ElfCacheBase* cache)
181 : cache_(cache ?: defaultElfCache()) {
184 void Symbolizer::symbolize(const uintptr_t* addresses,
185 SymbolizedFrame* frames,
186 size_t addressCount) {
187 size_t remaining = 0;
188 for (size_t i = 0; i < addressCount; ++i) {
189 auto& frame = frames[i];
196 if (remaining == 0) { // we're done
200 int fd = openNoInt("/proc/self/maps", O_RDONLY);
205 char buf[PATH_MAX + 100]; // Long enough for any line
206 LineReader reader(fd, buf, sizeof(buf));
208 while (remaining != 0) {
210 if (reader.readLine(line) != LineReader::kReading) {
217 StringPiece fileName;
218 if (!parseProcMapsLine(line, from, to, fileName)) {
223 std::shared_ptr<ElfFile> elfFile;
225 // See if any addresses are here
226 for (size_t i = 0; i < addressCount; ++i) {
227 auto& frame = frames[i];
232 uintptr_t address = addresses[i];
234 if (from > address || address >= to) {
242 // Open the file on first use
245 elfFile = cache_->getFile(fileName);
253 frame.set(elfFile, address - from);
261 constexpr char kHexChars[] = "0123456789abcdef";
262 constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
263 constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
264 constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
267 constexpr char AddressFormatter::bufTemplate[];
268 constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
269 SymbolizePrinter::kColorMap;
271 AddressFormatter::AddressFormatter() {
272 memcpy(buf_, bufTemplate, sizeof(buf_));
275 folly::StringPiece AddressFormatter::format(uintptr_t address) {
276 // Can't use sprintf, not async-signal-safe
277 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
278 char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));
281 while (address != 0) {
282 *p-- = kHexChars[address & 0xf];
286 return folly::StringPiece(buf_, end);
289 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
290 if (options_ & TERSE) {
291 printTerse(address, frame);
295 SCOPE_EXIT { color(Color::DEFAULT); };
297 color(kAddressColor);
299 AddressFormatter formatter;
300 doPrint(formatter.format(address));
302 const char padBuf[] = " ";
303 folly::StringPiece pad(padBuf,
304 sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
306 color(kFunctionColor);
308 doPrint(" (not found)");
312 if (!frame.name || frame.name[0] == '\0') {
313 doPrint(" (unknown)");
315 char demangledBuf[2048];
316 demangle(frame.name, demangledBuf, sizeof(demangledBuf));
318 doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
321 if (!(options_ & NO_FILE_AND_LINE)) {
323 char fileBuf[PATH_MAX];
325 if (frame.location.hasFileAndLine) {
326 frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
332 uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
334 doPrint(StringPiece(buf, n));
337 if (frame.location.hasMainFile) {
338 char mainFileBuf[PATH_MAX];
339 mainFileBuf[0] = '\0';
340 frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
341 if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
345 doPrint(mainFileBuf);
351 void SymbolizePrinter::color(SymbolizePrinter::Color color) {
352 if ((options_ & COLOR) == 0 &&
353 ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
356 if (color < 0 || color >= kColorMap.size()) {
359 doPrint(kColorMap[color]);
362 void SymbolizePrinter::println(uintptr_t address,
363 const SymbolizedFrame& frame) {
364 print(address, frame);
368 void SymbolizePrinter::printTerse(uintptr_t address,
369 const SymbolizedFrame& frame) {
370 if (frame.found && frame.name && frame.name[0] != '\0') {
371 char demangledBuf[2048] = {0};
372 demangle(frame.name, demangledBuf, sizeof(demangledBuf));
373 doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
375 // Can't use sprintf, not async-signal-safe
376 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
377 char buf[] = "0x0000000000000000";
378 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
381 while (address != 0) {
382 *p-- = kHexChars[address & 0xf];
385 doPrint(StringPiece(buf, end));
389 void SymbolizePrinter::println(const uintptr_t* addresses,
390 const SymbolizedFrame* frames,
392 for (size_t i = 0; i < frameCount; ++i) {
393 println(addresses[i], frames[i]);
399 int getFD(const std::ios& stream) {
401 std::streambuf* buf = stream.rdbuf();
402 using namespace __gnu_cxx;
405 auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
407 return fileno(sbuf->file());
411 auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
420 bool isTty(int options, int fd) {
421 return ((options & SymbolizePrinter::TERSE) == 0 &&
422 (options & SymbolizePrinter::COLOR_IF_TTY) != 0 &&
423 fd >= 0 && ::isatty(fd));
426 } // anonymous namespace
428 OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
429 : SymbolizePrinter(options, isTty(options, getFD(out))),
433 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
437 FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
438 : SymbolizePrinter(options, isTty(options, fd)),
440 buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {
443 FDSymbolizePrinter::~FDSymbolizePrinter() {
447 void FDSymbolizePrinter::doPrint(StringPiece sp) {
449 if (sp.size() > buffer_->tailroom()) {
451 writeFull(fd_, sp.data(), sp.size());
453 memcpy(buffer_->writableTail(), sp.data(), sp.size());
454 buffer_->append(sp.size());
457 writeFull(fd_, sp.data(), sp.size());
461 void FDSymbolizePrinter::flush() {
462 if (buffer_ && !buffer_->empty()) {
463 writeFull(fd_, buffer_->data(), buffer_->length());
468 FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
469 : SymbolizePrinter(options, isTty(options, fileno(file))),
473 void FILESymbolizePrinter::doPrint(StringPiece sp) {
474 fwrite(sp.data(), 1, sp.size(), file_);
477 void StringSymbolizePrinter::doPrint(StringPiece sp) {
478 buf_.append(sp.data(), sp.size());
481 } // namespace symbolizer