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 auto AlwaysInstrument = Extractor.getU8(&OffsetPtr);
166 Entry.AlwaysInstrument = AlwaysInstrument != 0;
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.
173 CurFn = Entry.Function;
174 InstrMap[FuncId] = Entry.Function;
175 FunctionIds[Entry.Function] = FuncId;
177 if (Entry.Function != CurFn) {
179 CurFn = Entry.Function;
180 InstrMap[FuncId] = Entry.Function;
181 FunctionIds[Entry.Function] = FuncId;
184 OutputSleds = std::move(Sleds);
185 return llvm::Error::success();
190 InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
193 ErrorAsOutParameter ErrAsOutputParam(&EC);
195 case InputFormats::ELF: {
197 LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds),
198 [](std::unique_ptr<ErrorInfoBase> E) {
200 make_error<StringError>(
201 Twine("Cannot extract instrumentation map from '") +
203 std::make_error_code(std::errc::executable_format_error)),
209 llvm_unreachable("Input format type not supported yet.");
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});
226 static CommandRegistration Unused(&Extract, []() -> Error {
227 Error Err = Error::success();
228 xray::InstrumentationMapExtractor Extractor(
229 ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err);
234 raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text);
236 return make_error<StringError>(
237 Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
238 Extractor.exportAsYAML(OS);
239 return Error::success();