2 * Copyright 2013 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 // Must be first to ensure that UNW_LOCAL_ONLY is defined
18 #define UNW_LOCAL_ONLY 1
19 #include <libunwind.h>
21 #include "folly/experimental/symbolizer/Symbolizer.h"
25 #include "folly/Conv.h"
26 #include "folly/FileUtil.h"
27 #include "folly/String.h"
29 #include "folly/experimental/symbolizer/Elf.h"
30 #include "folly/experimental/symbolizer/Dwarf.h"
31 #include "folly/experimental/symbolizer/LineReader.h"
34 namespace symbolizer {
41 uintptr_t readHex(StringPiece& sp) {
43 const char* p = sp.begin();
44 for (; p != sp.end(); ++p) {
46 if (*p >= '0' && *p <= '9') {
48 } else if (*p >= 'a' && *p <= 'f') {
50 } else if (*p >= 'A' && *p <= 'F') {
57 sp.assign(p, sp.end());
62 * Skip over non-space characters.
64 void skipNS(StringPiece& sp) {
65 const char* p = sp.begin();
66 for (; p != sp.end() && (*p != ' ' && *p != '\t'); ++p) { }
67 sp.assign(p, sp.end());
71 * Skip over space and tab characters.
73 void skipWS(StringPiece& sp) {
74 const char* p = sp.begin();
75 for (; p != sp.end() && (*p == ' ' || *p == '\t'); ++p) { }
76 sp.assign(p, sp.end());
80 * Parse a line from /proc/self/maps
82 bool parseProcMapsLine(StringPiece line,
83 uintptr_t& from, uintptr_t& to,
84 StringPiece& fileName) {
85 // from to perm offset dev inode path
86 // 00400000-00405000 r-xp 00000000 08:03 35291182 /bin/cat
91 // Remove trailing newline, if any
92 if (line.back() == '\n') {
98 if (line.empty() || line.front() != '-') {
105 if (line.empty() || line.front() != ' ') {
112 if (line.empty() || line.front() != ' ') {
117 uintptr_t fileOffset = readHex(line);
118 if (line.empty() || line.front() != ' ') {
122 if (fileOffset != 0) {
123 return false; // main mapping starts at 0
128 if (line.empty() || line.front() != ' ') {
135 if (line.empty() || line.front() != ' ') {
151 ssize_t getStackTrace(FrameInfo* addresses,
155 int r = unw_getcontext(&uctx);
166 r = unw_init_local(&cursor, &uctx);
168 r = unw_step(&cursor);
182 if (idx < maxAddresses) {
184 int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip);
189 // If error, assume not a signal frame
190 rr = unw_is_signal_frame(&cursor);
191 addresses[idx] = FrameInfo(ip, (rr > 0));
199 void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) {
200 size_t remaining = 0;
201 for (size_t i = 0; i < addressCount; ++i) {
202 auto& ainfo = addresses[i];
206 ainfo.location = Dwarf::LocationInfo();
210 if (remaining == 0) { // we're done
214 int fd = openNoInt("/proc/self/maps", O_RDONLY);
219 char buf[PATH_MAX + 100]; // Long enough for any line
220 LineReader reader(fd, buf, sizeof(buf));
222 char fileNameBuf[PATH_MAX];
224 while (remaining != 0) {
226 if (reader.readLine(line) != LineReader::kReading) {
233 StringPiece fileName;
234 if (!parseProcMapsLine(line, from, to, fileName)) {
239 ElfFile* elfFile = nullptr;
241 // See if any addresses are here
242 for (size_t i = 0; i < addressCount; ++i) {
243 auto& ainfo = addresses[i];
248 uintptr_t address = ainfo.address;
250 // If the next address (closer to the top of the stack) was a signal
251 // frame, then this is the *resume* address, which is the address
252 // after the location where the signal was caught. This might be in
253 // the next function, so subtract 1 before symbolizing.
254 if (i != 0 && addresses[i-1].isSignalFrame) {
258 if (from > address || address >= to) {
266 // Open the file on first use
269 if (fileCount_ < kMaxFiles &&
271 fileName.size() < sizeof(fileNameBuf)) {
272 memcpy(fileNameBuf, fileName.data(), fileName.size());
273 fileNameBuf[fileName.size()] = '\0';
274 auto& f = files_[fileCount_++];
275 if (f.openNoThrow(fileNameBuf) != -1) {
286 uintptr_t fileAddress = address - from + elfFile->getBaseAddress();
287 auto sym = elfFile->getDefinitionByAddress(fileAddress);
291 auto name = elfFile->getSymbolName(sym);
296 Dwarf(elfFile).findAddress(fileAddress, ainfo.location);
304 const char kHexChars[] = "0123456789abcdef";
307 void SymbolizePrinter::print(const FrameInfo& ainfo) {
308 uintptr_t address = ainfo.address;
309 // Can't use sprintf, not async-signal-safe
310 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
311 char buf[] = " @ 0000000000000000";
312 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
313 const char padBuf[] = " ";
314 folly::StringPiece pad(padBuf,
315 sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
318 while (address != 0) {
319 *p-- = kHexChars[address & 0xf];
322 doPrint(folly::StringPiece(buf, end));
324 char mangledBuf[1024];
326 doPrint(" (not found)\n");
330 if (ainfo.name.empty()) {
331 doPrint(" (unknown)\n");
332 } else if (ainfo.name.size() >= sizeof(mangledBuf)) {
337 memcpy(mangledBuf, ainfo.name.data(), ainfo.name.size());
338 mangledBuf[ainfo.name.size()] = '\0';
340 char demangledBuf[1024];
341 demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
343 doPrint(demangledBuf);
347 char fileBuf[PATH_MAX];
349 if (ainfo.location.hasFileAndLine) {
350 ainfo.location.file.toBuffer(fileBuf, sizeof(fileBuf));
355 uint32_t n = uint64ToBufferUnsafe(ainfo.location.line, buf);
357 doPrint(StringPiece(buf, n));
361 if (ainfo.location.hasMainFile) {
362 char mainFileBuf[PATH_MAX];
363 mainFileBuf[0] = '\0';
364 ainfo.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
365 if (!ainfo.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
368 doPrint(mainFileBuf);
374 void SymbolizePrinter::print(const FrameInfo* addresses,
375 size_t addressesSize,
377 for (size_t i = 0; i < std::min(addressesSize, frameCount); ++i) {
378 auto& ainfo = addresses[i];
382 // Indicate the number of frames that we couldn't log due to space
383 if (frameCount > addressesSize) {
385 uint32_t n = uint64ToBufferUnsafe(frameCount - addressesSize, buf);
387 doPrint(StringPiece(buf, n));
388 doPrint(" omitted, max buffer size reached)\n");
392 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
396 void FDSymbolizePrinter::doPrint(StringPiece sp) {
397 writeFull(fd_, sp.data(), sp.size());
400 std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo) {
401 OStreamSymbolizePrinter osp(out);
406 } // namespace symbolizer