]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-xray/xray-extract.cc
Update lld to trunk r290819 and resolve conflicts.
[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     auto AlwaysInstrument = Extractor.getU8(&OffsetPtr);
166     Entry.AlwaysInstrument = AlwaysInstrument != 0;
167
168     // We replicate the function id generation scheme implemented in the runtime
169     // here. Ideally we should be able to break it out, or output this map from
170     // the runtime, but that's a design point we can discuss later on. For now,
171     // we replicate the logic and move on.
172     if (CurFn == 0) {
173       CurFn = Entry.Function;
174       InstrMap[FuncId] = Entry.Function;
175       FunctionIds[Entry.Function] = FuncId;
176     }
177     if (Entry.Function != CurFn) {
178       ++FuncId;
179       CurFn = Entry.Function;
180       InstrMap[FuncId] = Entry.Function;
181       FunctionIds[Entry.Function] = FuncId;
182     }
183   }
184   OutputSleds = std::move(Sleds);
185   return llvm::Error::success();
186 }
187
188 } // namespace
189
190 InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
191                                                          InputFormats Format,
192                                                          Error &EC) {
193   ErrorAsOutParameter ErrAsOutputParam(&EC);
194   switch (Format) {
195   case InputFormats::ELF: {
196     EC = handleErrors(
197         LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds),
198         [](std::unique_ptr<ErrorInfoBase> E) {
199           return joinErrors(
200               make_error<StringError>(
201                   Twine("Cannot extract instrumentation map from '") +
202                       ExtractInput + "'.",
203                   std::make_error_code(std::errc::executable_format_error)),
204               std::move(E));
205         });
206     break;
207   }
208   default:
209     llvm_unreachable("Input format type not supported yet.");
210     break;
211   }
212 }
213
214 void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) {
215   // First we translate the sleds into the YAMLXRaySledEntry objects in a deque.
216   std::vector<YAMLXRaySledEntry> YAMLSleds;
217   YAMLSleds.reserve(Sleds.size());
218   for (const auto &Sled : Sleds) {
219     YAMLSleds.push_back({FunctionIds[Sled.Function], Sled.Address,
220                          Sled.Function, Sled.Kind, Sled.AlwaysInstrument});
221   }
222   Output Out(OS);
223   Out << YAMLSleds;
224 }
225
226 static CommandRegistration Unused(&Extract, []() -> Error {
227   Error Err = Error::success();
228   xray::InstrumentationMapExtractor Extractor(
229       ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err);
230   if (Err)
231     return Err;
232
233   std::error_code EC;
234   raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text);
235   if (EC)
236     return make_error<StringError>(
237         Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
238   Extractor.exportAsYAML(OS);
239   return Error::success();
240 });