From 635af78dedb0c1afec559c8a91eef702afef20e2 Mon Sep 17 00:00:00 2001 From: Pallab Bhattacharya Date: Thu, 7 Jan 2016 20:45:32 -0800 Subject: [PATCH] folly::Symbolizer can be enhanced for dumpStackTrace Summary: added functionality in Symbolizer.cpp to handle multi-segment binary and to allow symbol lookup via /proc/self/exe when some or whole text is relocated to ANON pages such as during hugification. An example of multi-segment binary : P56071432 cc markw65 - a part of the change include logic pulled from hphp/runtime/base/stack-logger.cpp:symbolize_huge_text so that hugified consumers of dumpStackTrace need not fork off. Reviewed By: edwardc Differential Revision: D2802837 fb-gh-sync-id: 577ab1b4ef8f22059894bfdd9c0526a22ee89ca8 --- folly/experimental/symbolizer/Symbolizer.cpp | 62 ++++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/folly/experimental/symbolizer/Symbolizer.cpp b/folly/experimental/symbolizer/Symbolizer.cpp index 41627761..a41c4d36 100644 --- a/folly/experimental/symbolizer/Symbolizer.cpp +++ b/folly/experimental/symbolizer/Symbolizer.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef __GNUC__ #include @@ -86,8 +87,12 @@ void skipWS(StringPiece& sp) { * Parse a line from /proc/self/maps */ bool parseProcMapsLine(StringPiece line, - uintptr_t& from, uintptr_t& to, + uintptr_t& from, + uintptr_t& to, + uintptr_t& fileOff, + bool& isSelf, StringPiece& fileName) { + isSelf = false; // from to perm offset dev inode path // 00400000-00405000 r-xp 00000000 08:03 35291182 /bin/cat if (line.empty()) { @@ -125,9 +130,15 @@ bool parseProcMapsLine(StringPiece line, return false; } line.pop_front(); - if (fileOffset != 0) { - return false; // main mapping starts at 0 - } + // main mapping starts at 0 but there can be multi-segment binary + // such as + // from to perm offset dev inode path + // 00400000-00405000 r-xp 00000000 08:03 54011424 /bin/foo + // 00600000-00605000 r-xp 00020000 08:03 54011424 /bin/foo + // 00800000-00805000 r-xp 00040000 08:03 54011424 /bin/foo + // if the offset > 0, this indicates to the caller that the baseAddress + // need to be used for undo relocation step. + fileOff = fileOffset; // dev skipNS(line); @@ -142,8 +153,14 @@ bool parseProcMapsLine(StringPiece line, return false; } + // if inode is 0, such as in case of ANON pages, there should be atleast + // one white space before EOL skipWS(line); if (line.empty()) { + // There will be no fileName for ANON text pages + // if the parsing came this far without a fileName, then from/to address + // may contain text in ANON pages. + isSelf = true; fileName.clear(); return true; } @@ -203,6 +220,14 @@ void Symbolizer::symbolize(const uintptr_t* addresses, return; } + char selfFile[PATH_MAX + 8]; + ssize_t selfSize; + if ((selfSize = readlink("/proc/self/exe", selfFile, PATH_MAX + 1)) == -1) { + // something terribly wrong + return; + } + selfFile[selfSize] = '\0'; + char buf[PATH_MAX + 100]; // Long enough for any line LineReader reader(fd, buf, sizeof(buf)); @@ -215,14 +240,33 @@ void Symbolizer::symbolize(const uintptr_t* addresses, // Parse line uintptr_t from; uintptr_t to; + uintptr_t fileOff; + uintptr_t base; + bool isSelf = false; // fileName can potentially be '/proc/self/exe' StringPiece fileName; - if (!parseProcMapsLine(line, from, to, fileName)) { + if (!parseProcMapsLine(line, from, to, fileOff, isSelf, fileName)) { continue; } + base = from; bool first = true; std::shared_ptr elfFile; + // case of text on ANON? + // Recompute from/to/base from the executable + if (isSelf && fileName.empty()) { + elfFile = cache_->getFile(selfFile); + + if (elfFile != nullptr) { + auto textSection = elfFile->getSectionByName(".text"); + base = elfFile->getBaseAddress(); + from = textSection->sh_addr; + to = from + textSection->sh_size; + fileName = selfFile; + first = false; // no need to get this file again from the cache + } + } + // See if any addresses are here for (size_t i = 0; i < addressCount; ++i) { auto& frame = frames[i]; @@ -244,6 +288,12 @@ void Symbolizer::symbolize(const uintptr_t* addresses, if (first) { first = false; elfFile = cache_->getFile(fileName); + + // Need to get the correct base address as from + // when fileOff > 0 + if (fileOff && elfFile != nullptr) { + base = elfFile->getBaseAddress(); + } } if (!elfFile) { @@ -251,7 +301,7 @@ void Symbolizer::symbolize(const uintptr_t* addresses, } // Undo relocation - frame.set(elfFile, address - from); + frame.set(elfFile, address - base); } } -- 2.34.1