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 #include "folly/experimental/symbolizer/Symbolizer.h"
21 #include "folly/Conv.h"
22 #include "folly/FileUtil.h"
23 #include "folly/String.h"
25 #include "folly/experimental/symbolizer/Elf.h"
26 #include "folly/experimental/symbolizer/Dwarf.h"
27 #include "folly/experimental/symbolizer/LineReader.h"
30 namespace symbolizer {
37 uintptr_t readHex(StringPiece& sp) {
39 const char* p = sp.begin();
40 for (; p != sp.end(); ++p) {
42 if (*p >= '0' && *p <= '9') {
44 } else if (*p >= 'a' && *p <= 'f') {
46 } else if (*p >= 'A' && *p <= 'F') {
53 sp.assign(p, sp.end());
58 * Skip over non-space characters.
60 void skipNS(StringPiece& sp) {
61 const char* p = sp.begin();
62 for (; p != sp.end() && (*p != ' ' && *p != '\t'); ++p) { }
63 sp.assign(p, sp.end());
67 * Skip over space and tab characters.
69 void skipWS(StringPiece& sp) {
70 const char* p = sp.begin();
71 for (; p != sp.end() && (*p == ' ' || *p == '\t'); ++p) { }
72 sp.assign(p, sp.end());
76 * Parse a line from /proc/self/maps
78 bool parseProcMapsLine(StringPiece line,
79 uintptr_t& from, uintptr_t& to,
80 StringPiece& fileName) {
81 // from to perm offset dev inode path
82 // 00400000-00405000 r-xp 00000000 08:03 35291182 /bin/cat
87 // Remove trailing newline, if any
88 if (line.back() == '\n') {
94 if (line.empty() || line.front() != '-') {
101 if (line.empty() || line.front() != ' ') {
108 if (line.empty() || line.front() != ' ') {
113 uintptr_t fileOffset = readHex(line);
114 if (line.empty() || line.front() != ' ') {
118 if (fileOffset != 0) {
119 return false; // main mapping starts at 0
124 if (line.empty() || line.front() != ' ') {
131 if (line.empty() || line.front() != ' ') {
147 void Symbolizer::symbolize(const uintptr_t* addresses,
148 SymbolizedFrame* frames,
149 size_t addressCount) {
150 size_t remaining = 0;
151 for (size_t i = 0; i < addressCount; ++i) {
152 auto& frame = frames[i];
156 frame.location = Dwarf::LocationInfo();
160 if (remaining == 0) { // we're done
164 int fd = openNoInt("/proc/self/maps", O_RDONLY);
169 char buf[PATH_MAX + 100]; // Long enough for any line
170 LineReader reader(fd, buf, sizeof(buf));
172 char fileNameBuf[PATH_MAX];
174 while (remaining != 0) {
176 if (reader.readLine(line) != LineReader::kReading) {
183 StringPiece fileName;
184 if (!parseProcMapsLine(line, from, to, fileName)) {
189 ElfFile* elfFile = nullptr;
191 // See if any addresses are here
192 for (size_t i = 0; i < addressCount; ++i) {
193 auto& frame = frames[i];
198 uintptr_t address = addresses[i];
200 if (from > address || address >= to) {
208 // Open the file on first use
211 if (fileCount_ < kMaxFiles &&
213 fileName.size() < sizeof(fileNameBuf)) {
214 memcpy(fileNameBuf, fileName.data(), fileName.size());
215 fileNameBuf[fileName.size()] = '\0';
216 auto& f = files_[fileCount_++];
217 if (f.openNoThrow(fileNameBuf) != -1) {
228 uintptr_t fileAddress = address - from + elfFile->getBaseAddress();
229 auto sym = elfFile->getDefinitionByAddress(fileAddress);
233 auto name = elfFile->getSymbolName(sym);
238 Dwarf(elfFile).findAddress(fileAddress, frame.location);
246 const char kHexChars[] = "0123456789abcdef";
249 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
250 if (options_ & TERSE) {
251 printTerse(address, frame);
255 // Can't use sprintf, not async-signal-safe
256 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
257 char buf[] = " @ 0000000000000000";
258 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
259 const char padBuf[] = " ";
260 folly::StringPiece pad(padBuf,
261 sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
264 while (address != 0) {
265 *p-- = kHexChars[address & 0xf];
268 doPrint(folly::StringPiece(buf, end));
270 char mangledBuf[1024];
272 doPrint(" (not found)");
276 if (frame.name.empty()) {
277 doPrint(" (unknown)");
278 } else if (frame.name.size() >= sizeof(mangledBuf)) {
282 memcpy(mangledBuf, frame.name.data(), frame.name.size());
283 mangledBuf[frame.name.size()] = '\0';
285 char demangledBuf[1024];
286 demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
288 doPrint(demangledBuf);
291 if (!(options_ & NO_FILE_AND_LINE)) {
292 char fileBuf[PATH_MAX];
294 if (frame.location.hasFileAndLine) {
295 frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
301 uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
303 doPrint(StringPiece(buf, n));
306 if (frame.location.hasMainFile) {
307 char mainFileBuf[PATH_MAX];
308 mainFileBuf[0] = '\0';
309 frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
310 if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
314 doPrint(mainFileBuf);
320 void SymbolizePrinter::println(uintptr_t address,
321 const SymbolizedFrame& frame) {
322 print(address, frame);
326 void SymbolizePrinter::printTerse(uintptr_t address,
327 const SymbolizedFrame& frame) {
329 char mangledBuf[1024];
330 memcpy(mangledBuf, frame.name.data(), frame.name.size());
331 mangledBuf[frame.name.size()] = '\0';
333 char demangledBuf[1024];
334 demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
335 doPrint(demangledBuf);
337 // Can't use sprintf, not async-signal-safe
338 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
339 char buf[] = "0x0000000000000000";
340 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
343 while (address != 0) {
344 *p-- = kHexChars[address & 0xf];
347 doPrint(StringPiece(buf, end));
351 void SymbolizePrinter::println(const uintptr_t* addresses,
352 const SymbolizedFrame* frames,
354 for (size_t i = 0; i < frameCount; ++i) {
355 println(addresses[i], frames[i]);
359 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
363 void FDSymbolizePrinter::doPrint(StringPiece sp) {
364 writeFull(fd_, sp.data(), sp.size());
367 void FILESymbolizePrinter::doPrint(StringPiece sp) {
368 fwrite(sp.data(), 1, sp.size(), file_);
371 void StringSymbolizePrinter::doPrint(StringPiece sp) {
372 buf_.append(sp.data(), sp.size());
375 } // namespace symbolizer