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