blob: ecd535114a62a4a9f6950de90187c931be2b2a11 [file] [log] [blame]
//===- 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 <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;
};
namespace llvm {
namespace yaml {
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::executable_format_error));
}
Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 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();
}
Error LoadYAMLInstrMap(
StringRef Filename, std::deque<SledEntry> &Sleds,
InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
int Fd;
if (auto EC = sys::fs::openFileForRead(Filename, Fd))
return make_error<StringError>(
Twine("Failed opening file '") + Filename + "' for reading.", EC);
uint64_t FileSize;
if (auto EC = sys::fs::file_size(Filename, FileSize))
return make_error<StringError>(
Twine("Failed getting size of file '") + Filename + "'.", EC);
std::error_code EC;
sys::fs::mapped_file_region MappedFile(
Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
if (EC)
return make_error<StringError>(
Twine("Failed memory-mapping file '") + Filename + "'.", EC);
std::vector<YAMLXRaySledEntry> YAMLSleds;
Input In(StringRef(MappedFile.data(), MappedFile.size()));
In >> YAMLSleds;
if (In.error())
return make_error<StringError>(
Twine("Failed loading YAML document from '") + Filename + "'.",
In.error());
for (const auto &Y : YAMLSleds) {
InstrMap[Y.FuncId] = Y.Function;
FunctionIds[Y.Function] = Y.FuncId;
Sleds.push_back(
SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument});
}
return Error::success();
}
} // namespace
InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
InputFormats Format,
Error &EC) {
ErrorAsOutParameter ErrAsOutputParam(&EC);
if (Filename.empty()) {
EC = Error::success();
return;
}
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 '") +
Filename + "'.",
std::make_error_code(std::errc::executable_format_error)),
std::move(E));
});
break;
}
case InputFormats::YAML: {
EC = handleErrors(
LoadYAMLInstrMap(Filename, Sleds, FunctionAddresses, FunctionIds),
[&](std::unique_ptr<ErrorInfoBase> E) {
return joinErrors(
make_error<StringError>(
Twine("Cannot load YAML instrumentation map from '") +
Filename + "'.",
std::make_error_code(std::errc::executable_format_error)),
std::move(E));
});
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, nullptr, 0);
Out << YAMLSleds;
}
static CommandRegistration Unused(&Extract, []() -> Error {
Error Err = Error::success();
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();
});