--- /dev/null
+//===- xray-extract.cc - XRay Instrumentation Map Extraction --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the xray-extract.h interface.
+//
+// FIXME: Support other XRay-instrumented binary formats other than ELF.
+//
+//===----------------------------------------------------------------------===//
+
+#include <type_traits>
+#include <unistd.h>
+#include <utility>
+
+#include "xray-extract.h"
+
+#include "xray-registry.h"
+#include "xray-sleds.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/ELF.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+using namespace llvm::yaml;
+
+// llvm-xray extract
+// ----------------------------------------------------------------------------
+static cl::SubCommand Extract("extract", "Extract instrumentation maps");
+static cl::opt<std::string> ExtractInput(cl::Positional,
+ cl::desc("<input file>"), cl::Required,
+ cl::sub(Extract));
+static cl::opt<std::string>
+ ExtractOutput("output", cl::value_desc("output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"),
+ cl::sub(Extract));
+static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput),
+ cl::desc("Alias for -output"),
+ cl::sub(Extract));
+
+struct YAMLXRaySledEntry {
+ int32_t FuncId;
+ Hex64 Address;
+ Hex64 Function;
+ SledEntry::FunctionKinds Kind;
+ bool AlwaysInstrument;
+};
+
+template <> struct ScalarEnumerationTraits<SledEntry::FunctionKinds> {
+ static void enumeration(IO &IO, SledEntry::FunctionKinds &Kind) {
+ IO.enumCase(Kind, "function-enter", SledEntry::FunctionKinds::ENTRY);
+ IO.enumCase(Kind, "function-exit", SledEntry::FunctionKinds::EXIT);
+ IO.enumCase(Kind, "tail-exit", SledEntry::FunctionKinds::TAIL);
+ }
+};
+
+template <> struct MappingTraits<YAMLXRaySledEntry> {
+ static void mapping(IO &IO, YAMLXRaySledEntry &Entry) {
+ IO.mapRequired("id", Entry.FuncId);
+ IO.mapRequired("address", Entry.Address);
+ IO.mapRequired("function", Entry.Function);
+ IO.mapRequired("kind", Entry.Kind);
+ IO.mapRequired("always-instrument", Entry.AlwaysInstrument);
+ }
+
+ static constexpr bool flow = true;
+};
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry);
+
+namespace {
+
+llvm::Error LoadBinaryInstrELF(
+ StringRef Filename, std::deque<SledEntry> &OutputSleds,
+ InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
+ InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
+ auto ObjectFile = object::ObjectFile::createObjectFile(Filename);
+
+ if (!ObjectFile)
+ return ObjectFile.takeError();
+
+ // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only.
+ if (!ObjectFile->getBinary()->isELF())
+ return make_error<StringError>(
+ "File format not supported (only does ELF).",
+ std::make_error_code(std::errc::not_supported));
+ if (ObjectFile->getBinary()->getArch() != Triple::x86_64)
+ return make_error<StringError>(
+ "File format not supported (only does ELF little endian 64-bit).",
+ std::make_error_code(std::errc::not_supported));
+
+ // Find the section named "xray_instr_map".
+ StringRef Contents = "";
+ const auto &Sections = ObjectFile->getBinary()->sections();
+ auto I = find_if(Sections, [&](object::SectionRef Section) {
+ StringRef Name = "";
+ if (Section.getName(Name))
+ return false;
+ return Name == "xray_instr_map";
+ });
+ if (I == Sections.end())
+ return make_error<StringError>(
+ "Failed to find XRay instrumentation map.",
+ std::make_error_code(std::errc::not_supported));
+ if (I->getContents(Contents))
+ return make_error<StringError>(
+ "Failed to get contents of 'xray_instr_map' section.",
+ std::make_error_code(std::errc::executable_format_error));
+
+ // Copy the instrumentation map data into the Sleds data structure.
+ auto C = Contents.bytes_begin();
+ static constexpr size_t ELF64SledEntrySize = 32;
+
+ if ((C - Contents.bytes_end()) % ELF64SledEntrySize != 0)
+ return make_error<StringError>(
+ "Instrumentation map entries not evenly divisible by size of an XRay "
+ "sled entry in ELF64.",
+ std::make_error_code(std::errc::executable_format_error));
+
+ int32_t FuncId = 1;
+ uint64_t CurFn = 0;
+ std::deque<SledEntry> Sleds;
+ for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) {
+ DataExtractor Extractor(
+ StringRef(reinterpret_cast<const char *>(C), ELF64SledEntrySize), true,
+ 8);
+ Sleds.push_back({});
+ auto &Entry = Sleds.back();
+ uint32_t OffsetPtr = 0;
+ Entry.Address = Extractor.getU64(&OffsetPtr);
+ Entry.Function = Extractor.getU64(&OffsetPtr);
+ auto Kind = Extractor.getU8(&OffsetPtr);
+ switch (Kind) {
+ case 0: // ENTRY
+ Entry.Kind = SledEntry::FunctionKinds::ENTRY;
+ break;
+ case 1: // EXIT
+ Entry.Kind = SledEntry::FunctionKinds::EXIT;
+ break;
+ case 2: // TAIL
+ Entry.Kind = SledEntry::FunctionKinds::TAIL;
+ break;
+ default:
+ return make_error<StringError>(
+ Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) +
+ "'.",
+ std::make_error_code(std::errc::protocol_error));
+ }
+ auto AlwaysInstrument = Extractor.getU8(&OffsetPtr);
+ Entry.AlwaysInstrument = AlwaysInstrument != 0;
+
+ // We replicate the function id generation scheme implemented in the runtime
+ // here. Ideally we should be able to break it out, or output this map from
+ // the runtime, but that's a design point we can discuss later on. For now,
+ // we replicate the logic and move on.
+ if (CurFn == 0) {
+ CurFn = Entry.Function;
+ InstrMap[FuncId] = Entry.Function;
+ FunctionIds[Entry.Function] = FuncId;
+ }
+ if (Entry.Function != CurFn) {
+ ++FuncId;
+ CurFn = Entry.Function;
+ InstrMap[FuncId] = Entry.Function;
+ FunctionIds[Entry.Function] = FuncId;
+ }
+ }
+ OutputSleds = std::move(Sleds);
+ return llvm::Error::success();
+}
+
+} // namespace
+
+InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
+ InputFormats Format,
+ Error &EC) {
+ ErrorAsOutParameter ErrAsOutputParam(&EC);
+ switch (Format) {
+ case InputFormats::ELF: {
+ EC = handleErrors(
+ LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds),
+ [](std::unique_ptr<ErrorInfoBase> E) {
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Cannot extract instrumentation map from '") +
+ ExtractInput + "'.",
+ std::make_error_code(std::errc::protocol_error)),
+ std::move(E));
+ });
+ break;
+ }
+ default:
+ llvm_unreachable("Input format type not supported yet.");
+ break;
+ }
+}
+
+void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) {
+ // First we translate the sleds into the YAMLXRaySledEntry objects in a deque.
+ std::vector<YAMLXRaySledEntry> YAMLSleds;
+ YAMLSleds.reserve(Sleds.size());
+ for (const auto &Sled : Sleds) {
+ YAMLSleds.push_back({FunctionIds[Sled.Function], Sled.Address,
+ Sled.Function, Sled.Kind, Sled.AlwaysInstrument});
+ }
+ Output Out(OS);
+ Out << YAMLSleds;
+}
+
+static CommandRegistration Unused(&Extract, [] {
+ Error Err;
+ xray::InstrumentationMapExtractor Extractor(
+ ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err);
+ if (Err)
+ return Err;
+
+ std::error_code EC;
+ raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text);
+ if (EC)
+ return make_error<StringError>(
+ Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
+ Extractor.exportAsYAML(OS);
+ return Error::success();
+});