1 //===- xray-extract.cc - XRay Instrumentation Map Extraction --------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Implementation of the xray-extract.h interface.
12 // FIXME: Support other XRay-instrumented binary formats other than ELF.
14 //===----------------------------------------------------------------------===//
16 #include <type_traits>
19 #include "xray-extract.h"
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"
35 using namespace llvm::xray;
36 using namespace llvm::yaml;
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,
44 static cl::opt<std::string>
45 ExtractOutput("output", cl::value_desc("output file"), cl::init("-"),
46 cl::desc("output file; use '-' for stdout"),
48 static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput),
49 cl::desc("Alias for -output"),
52 struct YAMLXRaySledEntry {
56 SledEntry::FunctionKinds Kind;
57 bool AlwaysInstrument;
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);
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);
80 static constexpr bool flow = true;
85 LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry)
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);
96 return ObjectFile.takeError();
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));
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) {
113 if (Section.getName(Name))
115 return Name == "xray_instr_map";
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));
126 // Copy the instrumentation map data into the Sleds data structure.
127 auto C = Contents.bytes_begin();
128 static constexpr size_t ELF64SledEntrySize = 32;
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));
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,
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);
151 Entry.Kind = SledEntry::FunctionKinds::ENTRY;
154 Entry.Kind = SledEntry::FunctionKinds::EXIT;
157 Entry.Kind = SledEntry::FunctionKinds::TAIL;
160 return make_error<StringError>(
161 Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) +
163 std::make_error_code(std::errc::executable_format_error));
165 Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
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.
172 CurFn = Entry.Function;
173 InstrMap[FuncId] = Entry.Function;
174 FunctionIds[Entry.Function] = FuncId;
176 if (Entry.Function != CurFn) {
178 CurFn = Entry.Function;
179 InstrMap[FuncId] = Entry.Function;
180 FunctionIds[Entry.Function] = FuncId;
183 OutputSleds = std::move(Sleds);
184 return llvm::Error::success();
187 Error LoadYAMLInstrMap(
188 StringRef Filename, std::deque<SledEntry> &Sleds,
189 InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
190 InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
192 if (auto EC = sys::fs::openFileForRead(Filename, Fd))
193 return make_error<StringError>(
194 Twine("Failed opening file '") + Filename + "' for reading.", EC);
197 if (auto EC = sys::fs::file_size(Filename, FileSize))
198 return make_error<StringError>(
199 Twine("Failed getting size of file '") + Filename + "'.", EC);
202 sys::fs::mapped_file_region MappedFile(
203 Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
205 return make_error<StringError>(
206 Twine("Failed memory-mapping file '") + Filename + "'.", EC);
208 std::vector<YAMLXRaySledEntry> YAMLSleds;
209 Input In(StringRef(MappedFile.data(), MappedFile.size()));
212 return make_error<StringError>(
213 Twine("Failed loading YAML document from '") + Filename + "'.",
216 for (const auto &Y : YAMLSleds) {
217 InstrMap[Y.FuncId] = Y.Function;
218 FunctionIds[Y.Function] = Y.FuncId;
220 SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument});
222 return Error::success();
227 InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
230 ErrorAsOutParameter ErrAsOutputParam(&EC);
231 if (Filename.empty()) {
232 EC = Error::success();
236 case InputFormats::ELF: {
238 LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds),
239 [&](std::unique_ptr<ErrorInfoBase> E) {
241 make_error<StringError>(
242 Twine("Cannot extract instrumentation map from '") +
244 std::make_error_code(std::errc::executable_format_error)),
249 case InputFormats::YAML: {
251 LoadYAMLInstrMap(Filename, Sleds, FunctionAddresses, FunctionIds),
252 [&](std::unique_ptr<ErrorInfoBase> E) {
254 make_error<StringError>(
255 Twine("Cannot load YAML instrumentation map from '") +
257 std::make_error_code(std::errc::executable_format_error)),
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});
273 Output Out(OS, nullptr, 0);
277 static CommandRegistration Unused(&Extract, []() -> Error {
278 Error Err = Error::success();
279 xray::InstrumentationMapExtractor Extractor(
280 ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err);
285 raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text);
287 return make_error<StringError>(
288 Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
289 Extractor.exportAsYAML(OS);
290 return Error::success();