d0f0c1650435f52c02de280b945a754735777c01
[folly.git] / folly / experimental / symbolizer / Symbolizer.cpp
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "folly/experimental/symbolizer/Symbolizer.h"
18
19 #include <limits.h>
20
21 #include "folly/Conv.h"
22 #include "folly/FileUtil.h"
23 #include "folly/String.h"
24
25 #include "folly/experimental/symbolizer/Elf.h"
26 #include "folly/experimental/symbolizer/Dwarf.h"
27 #include "folly/experimental/symbolizer/LineReader.h"
28
29 namespace folly {
30 namespace symbolizer {
31
32 namespace {
33
34 /**
35  * Read a hex value.
36  */
37 uintptr_t readHex(StringPiece& sp) {
38   uintptr_t val = 0;
39   const char* p = sp.begin();
40   for (; p != sp.end(); ++p) {
41     unsigned int v;
42     if (*p >= '0' && *p <= '9') {
43       v = (*p - '0');
44     } else if (*p >= 'a' && *p <= 'f') {
45       v = (*p - 'a') + 10;
46     } else if (*p >= 'A' && *p <= 'F') {
47       v = (*p - 'A') + 10;
48     } else {
49       break;
50     }
51     val = (val << 4) + v;
52   }
53   sp.assign(p, sp.end());
54   return val;
55 }
56
57 /**
58  * Skip over non-space characters.
59  */
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());
64 }
65
66 /**
67  * Skip over space and tab characters.
68  */
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());
73 }
74
75 /**
76  * Parse a line from /proc/self/maps
77  */
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
83   if (line.empty()) {
84     return false;
85   }
86
87   // Remove trailing newline, if any
88   if (line.back() == '\n') {
89     line.pop_back();
90   }
91
92   // from
93   from = readHex(line);
94   if (line.empty() || line.front() != '-') {
95     return false;
96   }
97   line.pop_front();
98
99   // to
100   to = readHex(line);
101   if (line.empty() || line.front() != ' ') {
102     return false;
103   }
104   line.pop_front();
105
106   // perms
107   skipNS(line);
108   if (line.empty() || line.front() != ' ') {
109     return false;
110   }
111   line.pop_front();
112
113   uintptr_t fileOffset = readHex(line);
114   if (line.empty() || line.front() != ' ') {
115     return false;
116   }
117   line.pop_front();
118   if (fileOffset != 0) {
119     return false;  // main mapping starts at 0
120   }
121
122   // dev
123   skipNS(line);
124   if (line.empty() || line.front() != ' ') {
125     return false;
126   }
127   line.pop_front();
128
129   // inode
130   skipNS(line);
131   if (line.empty() || line.front() != ' ') {
132     return false;
133   }
134
135   skipWS(line);
136   if (line.empty()) {
137     fileName.clear();
138     return true;
139   }
140
141   fileName = line;
142   return true;
143 }
144
145 }  // namespace
146
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];
153     if (!frame.found) {
154       ++remaining;
155       frame.name.clear();
156       frame.location = Dwarf::LocationInfo();
157     }
158   }
159
160   if (remaining == 0) {  // we're done
161     return;
162   }
163
164   int fd = openNoInt("/proc/self/maps", O_RDONLY);
165   if (fd == -1) {
166     return;
167   }
168
169   char buf[PATH_MAX + 100];  // Long enough for any line
170   LineReader reader(fd, buf, sizeof(buf));
171
172   char fileNameBuf[PATH_MAX];
173
174   while (remaining != 0) {
175     StringPiece line;
176     if (reader.readLine(line) != LineReader::kReading) {
177       break;
178     }
179
180     // Parse line
181     uintptr_t from;
182     uintptr_t to;
183     StringPiece fileName;
184     if (!parseProcMapsLine(line, from, to, fileName)) {
185       continue;
186     }
187
188     bool first = true;
189     ElfFile* elfFile = nullptr;
190
191     // See if any addresses are here
192     for (size_t i = 0; i < addressCount; ++i) {
193       auto& frame = frames[i];
194       if (frame.found) {
195         continue;
196       }
197
198       uintptr_t address = addresses[i];
199
200       if (from > address || address >= to) {
201         continue;
202       }
203
204       // Found
205       frame.found = true;
206       --remaining;
207
208       // Open the file on first use
209       if (first) {
210         first = false;
211         if (fileCount_ < kMaxFiles &&
212             !fileName.empty() &&
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) {
218             elfFile = &f;
219           }
220         }
221       }
222
223       if (!elfFile) {
224         continue;
225       }
226
227       // Undo relocation
228       uintptr_t fileAddress = address - from + elfFile->getBaseAddress();
229       auto sym = elfFile->getDefinitionByAddress(fileAddress);
230       if (!sym.first) {
231         continue;
232       }
233       auto name = elfFile->getSymbolName(sym);
234       if (name) {
235         frame.name = name;
236       }
237
238       Dwarf(elfFile).findAddress(fileAddress, frame.location);
239     }
240   }
241
242   closeNoInt(fd);
243 }
244
245 namespace {
246 const char kHexChars[] = "0123456789abcdef";
247 }  // namespace
248
249 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
250   if (options_ & TERSE) {
251     printTerse(address, frame);
252     return;
253   }
254
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)));
262   char* p = end;
263   *p-- = '\0';
264   while (address != 0) {
265     *p-- = kHexChars[address & 0xf];
266     address >>= 4;
267   }
268   doPrint(folly::StringPiece(buf, end));
269
270   char mangledBuf[1024];
271   if (!frame.found) {
272     doPrint(" (not found)");
273     return;
274   }
275
276   if (frame.name.empty()) {
277     doPrint(" (unknown)");
278   } else if (frame.name.size() >= sizeof(mangledBuf)) {
279     doPrint(" ");
280     doPrint(frame.name);
281   } else {
282     memcpy(mangledBuf, frame.name.data(), frame.name.size());
283     mangledBuf[frame.name.size()] = '\0';
284
285     char demangledBuf[1024];
286     demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
287     doPrint(" ");
288     doPrint(demangledBuf);
289   }
290
291   if (!(options_ & NO_FILE_AND_LINE)) {
292     char fileBuf[PATH_MAX];
293     fileBuf[0] = '\0';
294     if (frame.location.hasFileAndLine) {
295       frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
296       doPrint("\n");
297       doPrint(pad);
298       doPrint(fileBuf);
299
300       char buf[22];
301       uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
302       doPrint(":");
303       doPrint(StringPiece(buf, n));
304     }
305
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)) {
311         doPrint("\n");
312         doPrint(pad);
313         doPrint("-> ");
314         doPrint(mainFileBuf);
315       }
316     }
317   }
318 }
319
320 void SymbolizePrinter::println(uintptr_t address,
321                                const SymbolizedFrame& frame) {
322   print(address, frame);
323   doPrint("\n");
324 }
325
326 void SymbolizePrinter::printTerse(uintptr_t address,
327                                   const SymbolizedFrame& frame) {
328   if (frame.found) {
329     char mangledBuf[1024];
330     memcpy(mangledBuf, frame.name.data(), frame.name.size());
331     mangledBuf[frame.name.size()] = '\0';
332
333     char demangledBuf[1024];
334     demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
335     doPrint(demangledBuf);
336   } else {
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));
341     char* p = end;
342     *p-- = '\0';
343     while (address != 0) {
344       *p-- = kHexChars[address & 0xf];
345       address >>= 4;
346     }
347     doPrint(StringPiece(buf, end));
348   }
349 }
350
351 void SymbolizePrinter::println(const uintptr_t* addresses,
352                                const SymbolizedFrame* frames,
353                                size_t frameCount) {
354   for (size_t i = 0; i < frameCount; ++i) {
355     println(addresses[i], frames[i]);
356   }
357 }
358
359 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
360   out_ << sp;
361 }
362
363 void FDSymbolizePrinter::doPrint(StringPiece sp) {
364   writeFull(fd_, sp.data(), sp.size());
365 }
366
367 void FILESymbolizePrinter::doPrint(StringPiece sp) {
368   fwrite(sp.data(), 1, sp.size(), file_);
369 }
370
371 void StringSymbolizePrinter::doPrint(StringPiece sp) {
372   buf_.append(sp.data(), sp.size());
373 }
374
375 }  // namespace symbolizer
376 }  // namespace folly