]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-xray/xray-extract.cc
Import mandoc snapshot 2017-06-08
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / llvm-xray / xray-extract.cc
1 //===- xray-extract.cc - XRay Instrumentation Map Extraction --------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Implementation of the xray-extract.h interface.
11 //
12 // FIXME: Support other XRay-instrumented binary formats other than ELF.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #include <type_traits>
17 #include <utility>
18
19 #include "xray-extract.h"
20
21 #include "xray-registry.h"
22 #include "xray-sleds.h"
23 #include "llvm/Object/ELF.h"
24 #include "llvm/Object/ObjectFile.h"
25 #include "llvm/Support/CommandLine.h"
26 #include "llvm/Support/DataExtractor.h"
27 #include "llvm/Support/ELF.h"
28 #include "llvm/Support/Error.h"
29 #include "llvm/Support/FileSystem.h"
30 #include "llvm/Support/Format.h"
31 #include "llvm/Support/YAMLTraits.h"
32 #include "llvm/Support/raw_ostream.h"
33
34 using namespace llvm;
35 using namespace llvm::xray;
36 using namespace llvm::yaml;
37
38 // llvm-xray extract
39 // ----------------------------------------------------------------------------
40 static cl::SubCommand Extract("extract", "Extract instrumentation maps");
41 static cl::opt<std::string> ExtractInput(cl::Positional,
42                                          cl::desc("<input file>"), cl::Required,
43                                          cl::sub(Extract));
44 static cl::opt<std::string>
45     ExtractOutput("output", cl::value_desc("output file"), cl::init("-"),
46                   cl::desc("output file; use '-' for stdout"),
47                   cl::sub(Extract));
48 static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput),
49                                 cl::desc("Alias for -output"),
50                                 cl::sub(Extract));
51
52 struct YAMLXRaySledEntry {
53   int32_t FuncId;
54   Hex64 Address;
55   Hex64 Function;
56   SledEntry::FunctionKinds Kind;
57   bool AlwaysInstrument;
58 };
59
60 namespace llvm {
61 namespace yaml {
62
63 template <> struct ScalarEnumerationTraits<SledEntry::FunctionKinds> {
64   static void enumeration(IO &IO, SledEntry::FunctionKinds &Kind) {
65     IO.enumCase(Kind, "function-enter", SledEntry::FunctionKinds::ENTRY);
66     IO.enumCase(Kind, "function-exit", SledEntry::FunctionKinds::EXIT);
67     IO.enumCase(Kind, "tail-exit", SledEntry::FunctionKinds::TAIL);
68   }
69 };
70
71 template <> struct MappingTraits<YAMLXRaySledEntry> {
72   static void mapping(IO &IO, YAMLXRaySledEntry &Entry) {
73     IO.mapRequired("id", Entry.FuncId);
74     IO.mapRequired("address", Entry.Address);
75     IO.mapRequired("function", Entry.Function);
76     IO.mapRequired("kind", Entry.Kind);
77     IO.mapRequired("always-instrument", Entry.AlwaysInstrument);
78   }
79
80   static constexpr bool flow = true;
81 };
82 }
83 }
84
85 LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry)
86
87 namespace {
88
89 llvm::Error LoadBinaryInstrELF(
90     StringRef Filename, std::deque<SledEntry> &OutputSleds,
91     InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
92     InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
93   auto ObjectFile = object::ObjectFile::createObjectFile(Filename);
94
95   if (!ObjectFile)
96     return ObjectFile.takeError();
97
98   // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only.
99   if (!ObjectFile->getBinary()->isELF())
100     return make_error<StringError>(
101         "File format not supported (only does ELF).",
102         std::make_error_code(std::errc::not_supported));
103   if (ObjectFile->getBinary()->getArch() != Triple::x86_64)
104     return make_error<StringError>(
105         "File format not supported (only does ELF little endian 64-bit).",
106         std::make_error_code(std::errc::not_supported));
107
108   // Find the section named "xray_instr_map".
109   StringRef Contents = "";
110   const auto &Sections = ObjectFile->getBinary()->sections();
111   auto I = find_if(Sections, [&](object::SectionRef Section) {
112     StringRef Name = "";
113     if (Section.getName(Name))
114       return false;
115     return Name == "xray_instr_map";
116   });
117   if (I == Sections.end())
118     return make_error<StringError>(
119         "Failed to find XRay instrumentation map.",
120         std::make_error_code(std::errc::not_supported));
121   if (I->getContents(Contents))
122     return make_error<StringError>(
123         "Failed to get contents of 'xray_instr_map' section.",
124         std::make_error_code(std::errc::executable_format_error));
125
126   // Copy the instrumentation map data into the Sleds data structure.
127   auto C = Contents.bytes_begin();
128   static constexpr size_t ELF64SledEntrySize = 32;
129
130   if ((C - Contents.bytes_end()) % ELF64SledEntrySize != 0)
131     return make_error<StringError>(
132         "Instrumentation map entries not evenly divisible by size of an XRay "
133         "sled entry in ELF64.",
134         std::make_error_code(std::errc::executable_format_error));
135
136   int32_t FuncId = 1;
137   uint64_t CurFn = 0;
138   std::deque<SledEntry> Sleds;
139   for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) {
140     DataExtractor Extractor(
141         StringRef(reinterpret_cast<const char *>(C), ELF64SledEntrySize), true,
142         8);
143     Sleds.push_back({});
144     auto &Entry = Sleds.back();
145     uint32_t OffsetPtr = 0;
146     Entry.Address = Extractor.getU64(&OffsetPtr);
147     Entry.Function = Extractor.getU64(&OffsetPtr);
148     auto Kind = Extractor.getU8(&OffsetPtr);
149     switch (Kind) {
150     case 0: // ENTRY
151       Entry.Kind = SledEntry::FunctionKinds::ENTRY;
152       break;
153     case 1: // EXIT
154       Entry.Kind = SledEntry::FunctionKinds::EXIT;
155       break;
156     case 2: // TAIL
157       Entry.Kind = SledEntry::FunctionKinds::TAIL;
158       break;
159     default:
160       return make_error<StringError>(
161           Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) +
162               "'.",
163           std::make_error_code(std::errc::executable_format_error));
164     }
165     Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
166
167     // We replicate the function id generation scheme implemented in the runtime
168     // here. Ideally we should be able to break it out, or output this map from
169     // the runtime, but that's a design point we can discuss later on. For now,
170     // we replicate the logic and move on.
171     if (CurFn == 0) {
172       CurFn = Entry.Function;
173       InstrMap[FuncId] = Entry.Function;
174       FunctionIds[Entry.Function] = FuncId;
175     }
176     if (Entry.Function != CurFn) {
177       ++FuncId;
178       CurFn = Entry.Function;
179       InstrMap[FuncId] = Entry.Function;
180       FunctionIds[Entry.Function] = FuncId;
181     }
182   }
183   OutputSleds = std::move(Sleds);
184   return llvm::Error::success();
185 }
186
187 Error LoadYAMLInstrMap(
188     StringRef Filename, std::deque<SledEntry> &Sleds,
189     InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
190     InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
191   int Fd;
192   if (auto EC = sys::fs::openFileForRead(Filename, Fd))
193     return make_error<StringError>(
194         Twine("Failed opening file '") + Filename + "' for reading.", EC);
195
196   uint64_t FileSize;
197   if (auto EC = sys::fs::file_size(Filename, FileSize))
198     return make_error<StringError>(
199         Twine("Failed getting size of file '") + Filename + "'.", EC);
200
201   std::error_code EC;
202   sys::fs::mapped_file_region MappedFile(
203       Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
204   if (EC)
205     return make_error<StringError>(
206         Twine("Failed memory-mapping file '") + Filename + "'.", EC);
207
208   std::vector<YAMLXRaySledEntry> YAMLSleds;
209   Input In(StringRef(MappedFile.data(), MappedFile.size()));
210   In >> YAMLSleds;
211   if (In.error())
212     return make_error<StringError>(
213         Twine("Failed loading YAML document from '") + Filename + "'.",
214         In.error());
215
216   for (const auto &Y : YAMLSleds) {
217     InstrMap[Y.FuncId] = Y.Function;
218     FunctionIds[Y.Function] = Y.FuncId;
219     Sleds.push_back(
220         SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument});
221   }
222   return Error::success();
223 }
224
225 } // namespace
226
227 InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
228                                                          InputFormats Format,
229                                                          Error &EC) {
230   ErrorAsOutParameter ErrAsOutputParam(&EC);
231   if (Filename.empty()) {
232     EC = Error::success();
233     return;
234   }
235   switch (Format) {
236   case InputFormats::ELF: {
237     EC = handleErrors(
238         LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds),
239         [&](std::unique_ptr<ErrorInfoBase> E) {
240           return joinErrors(
241               make_error<StringError>(
242                   Twine("Cannot extract instrumentation map from '") +
243                       Filename + "'.",
244                   std::make_error_code(std::errc::executable_format_error)),
245               std::move(E));
246         });
247     break;
248   }
249   case InputFormats::YAML: {
250     EC = handleErrors(
251         LoadYAMLInstrMap(Filename, Sleds, FunctionAddresses, FunctionIds),
252         [&](std::unique_ptr<ErrorInfoBase> E) {
253           return joinErrors(
254               make_error<StringError>(
255                   Twine("Cannot load YAML instrumentation map from '") +
256                       Filename + "'.",
257                   std::make_error_code(std::errc::executable_format_error)),
258               std::move(E));
259         });
260     break;
261   }
262   }
263 }
264
265 void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) {
266   // First we translate the sleds into the YAMLXRaySledEntry objects in a deque.
267   std::vector<YAMLXRaySledEntry> YAMLSleds;
268   YAMLSleds.reserve(Sleds.size());
269   for (const auto &Sled : Sleds) {
270     YAMLSleds.push_back({FunctionIds[Sled.Function], Sled.Address,
271                          Sled.Function, Sled.Kind, Sled.AlwaysInstrument});
272   }
273   Output Out(OS, nullptr, 0);
274   Out << YAMLSleds;
275 }
276
277 static CommandRegistration Unused(&Extract, []() -> Error {
278   Error Err = Error::success();
279   xray::InstrumentationMapExtractor Extractor(
280       ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err);
281   if (Err)
282     return Err;
283
284   std::error_code EC;
285   raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text);
286   if (EC)
287     return make_error<StringError>(
288         Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
289   Extractor.exportAsYAML(OS);
290   return Error::success();
291 });