Move exception tracer library to folly/experimental
[folly.git] / folly / experimental / symbolizer / Symbolizer.cpp
1 /*
2  * Copyright 2012 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
18 #include "folly/experimental/symbolizer/Symbolizer.h"
19
20 #include <boost/regex.hpp>
21
22 #include "folly/experimental/symbolizer/Elf.h"
23 #include "folly/experimental/symbolizer/Dwarf.h"
24 #include "glog/logging.h"
25 #include "folly/Range.h"
26 #include "folly/FBString.h"
27 #include "folly/String.h"
28 #include "folly/experimental/io/Stream.h"
29
30 namespace facebook {
31 namespace symbolizer {
32
33 namespace {
34 folly::StringPiece sp(const boost::csub_match& m) {
35   return folly::StringPiece(m.first, m.second);
36 }
37
38 uint64_t fromHex(folly::StringPiece s) {
39   // Make a copy; we need a null-terminated string for strtoull
40   folly::fbstring str(s.data(), s.size());
41   const char* p = str.c_str();
42   char* end;
43   uint64_t val = strtoull(p, &end, 16);
44   CHECK(*p != '\0' && *end == '\0');
45   return val;
46 }
47
48 struct MappedFile {
49   uintptr_t begin;
50   uintptr_t end;
51   std::string name;
52 };
53
54 }  // namespace
55
56 bool Symbolizer::symbolize(uintptr_t address, folly::StringPiece& symbolName,
57                            Dwarf::LocationInfo& location) {
58   symbolName.clear();
59   location = Dwarf::LocationInfo();
60
61   // Entry in /proc/self/maps
62   static const boost::regex mapLineRegex(
63     "([[:xdigit:]]+)-([[:xdigit:]]+)"  // from-to
64     "\\s+"
65     "[\\w-]+"  // permissions
66     "\\s+"
67     "([[:xdigit:]]+)"  // offset
68     "\\s+"
69     "[[:xdigit:]]+:[[:xdigit:]]+"  // device, minor:major
70     "\\s+"
71     "\\d+"  // inode
72     "\\s*"
73     "(.*)");  // file name
74
75   boost::cmatch match;
76
77   MappedFile foundFile;
78   bool found = false;
79   for (auto& byteLine : folly::byLine("/proc/self/maps")) {
80     folly::StringPiece line(byteLine);
81     CHECK(boost::regex_match(line.begin(), line.end(), match, mapLineRegex));
82     uint64_t begin = fromHex(sp(match[1]));
83     uint64_t end = fromHex(sp(match[2]));
84     uint64_t fileOffset = fromHex(sp(match[3]));
85     if (fileOffset != 0) {
86       continue;  // main mapping starts at 0
87     }
88
89     if (begin <= address && address < end) {
90       found = true;
91       foundFile.begin = begin;
92       foundFile.end = end;
93       foundFile.name.assign(match[4].first, match[4].second);
94       break;
95     }
96   }
97
98   if (!found) {
99     return false;
100   }
101
102   auto& elfFile = getFile(foundFile.name);
103   // Undo relocation
104   uintptr_t origAddress = address - foundFile.begin + elfFile.getBaseAddress();
105
106   auto sym = elfFile.getDefinitionByAddress(origAddress);
107   if (!sym.first) {
108     return false;
109   }
110
111   auto name = elfFile.getSymbolName(sym);
112   if (name) {
113     symbolName = name;
114   }
115
116   Dwarf(&elfFile).findAddress(origAddress, location);
117   return true;
118 }
119
120 ElfFile& Symbolizer::getFile(const std::string& name) {
121   auto pos = elfFiles_.find(name);
122   if (pos != elfFiles_.end()) {
123     return pos->second;
124   }
125
126   return elfFiles_.insert(
127       std::make_pair(name, ElfFile(name.c_str()))).first->second;
128 }
129
130 void Symbolizer::write(std::ostream& out, uintptr_t address,
131                        folly::StringPiece symbolName,
132                        const Dwarf::LocationInfo& location) {
133   char buf[20];
134   sprintf(buf, "%#18jx", address);
135   out << "    @ " << buf;
136
137   if (!symbolName.empty()) {
138     out << " " << folly::demangle(symbolName.toString().c_str());
139
140     std::string file;
141     if (location.hasFileAndLine) {
142       file = location.file.toString();
143       out << "   " << file << ":" << location.line;
144     }
145
146     std::string mainFile;
147     if (location.hasMainFile) {
148       mainFile = location.mainFile.toString();
149       if (!location.hasFileAndLine || file != mainFile) {
150         out << "\n                         (compiling "
151             << location.mainFile << ")";
152       }
153     }
154   } else {
155     out << " (unknown)";
156   }
157   out << "\n";
158 }
159
160 }  // namespace symbolizer
161 }  // namespace facebook