Index: test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml =================================================================== --- /dev/null +++ test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml @@ -0,0 +1,10 @@ +# This is a simple instrumentation map with bogus addresses and offsets, but +# follow the recommended format. +--- +- { id: 1, address: 0x1, function: 0x1, kind: function-enter, always-instrument: true} +- { id: 1, address: 0x2, function: 0x1, kind: function-exit, always-instrument: true} +- { id: 2, address: 0x2, function: 0x2, kind: function-enter, always-instrument: true} +- { id: 2, address: 0x3, function: 0x2, kind: function-exit, always-instrument: true} +- { id: 3, address: 0x3, function: 0x3, kind: function-enter, always-instrument: true} +- { id: 3, address: 0x4, function: 0x3, kind: function-exit, always-instrument: true} +... Index: test/tools/llvm-xray/X86/extract-instrmap.ll =================================================================== --- /dev/null +++ test/tools/llvm-xray/X86/extract-instrmap.ll @@ -0,0 +1,34 @@ +; This test makes sure we can extract the instrumentation map from an +; XRay-instrumented object file. +; +; RUN: llc -filetype=obj -o %t -mtriple=x86_64-unknown-linux-gnu < %s +; RUN: llvm-xray extract -format=json %t | FileCheck %s --check-prefix=CHECK-JSON +; RUN: llvm-xray extract -format=yaml %t | FileCheck %s --check-prefix=CHECK-YAML + +define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" { + ret i32 0 +} + +; CHECK-JSON: [ +; CHECK-JSON-NEXT: { +; CHECK-JSON-NEXT: "funcId": 1, +; CHECK-JSON-NEXT: "funcAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledKind": "FunctionEnter", +; CHECK-JSON-NEXT: "alwaysInstrument": true +; CHECK-JSON-NEXT: }, +; CHECK-JSON-NEXT: { +; CHECK-JSON-NEXT: "funcId": 1, +; CHECK-JSON-NEXT: "funcAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledAddr": {{.*}}, +; CHECK-JSON-NEXT: "sledKind": "FunctionExit", +; CHECK-JSON-NEXT: "alwaysInstrument": true +; CHECK-JSON-NEXT: } +; CHECK-JSON-NEXT: ] + +; CHECK-YAML: --- +; CHECK-YAML-NEXT: - { id: 1, address: 0x{{[0-9A-F]+}}, function: 0x{{[0-9A-F]+}}, kind: function-enter, +; CHECK-YAML-NEXT: always-instrument: true } +; CHECK-YAML-NEXT: - { id: 1, address: 0x{{[0-9A-F]+}}, function: 0x{{[0-9A-F]+}}, kind: function-exit, +; CHECK-YAML-NEXT: always-instrument: true } +; CHECK-YAML-NEXT: ... Index: test/tools/llvm-xray/X86/lit.local.cfg =================================================================== --- /dev/null +++ test/tools/llvm-xray/X86/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.yaml', '.ll'] Index: tools/llvm-xray/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-xray/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Support + Object) + +set(LLVM_XRAY_TOOLS + xray-extract.cc + xray-registry.cc) + +add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) Index: tools/llvm-xray/llvm-xray.cc =================================================================== --- /dev/null +++ tools/llvm-xray/llvm-xray.cc @@ -0,0 +1,45 @@ +//===- llvm-xray.cc - XRay Tool Main Program ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the main entry point for the suite of XRay tools. All +// additional functionality are implemented as subcommands. +// +//===----------------------------------------------------------------------===// +// +// Basic usage: +// +// llvm-xray [options] [subcommand-specific options] +// + +#include "xray-registry.h" +#include "xray-extract.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; +using namespace llvm::xray; + +int main(int argc, char *argv[]) { + cl::ParseCommandLineOptions(argc, argv, + "XRay Tools\n\n" + " This program consolidates multiple XRay trace " + "processing tools for convenient access.\n"); + for (auto *SC : cl::getRegisteredSubcommands()) { + if (*SC) { + auto C = dispatch(SC); + if (C) { + return C(); + } + } + } + + cl::PrintHelpMessage(false, true); +} Index: tools/llvm-xray/xray-extract.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-extract.h @@ -0,0 +1,68 @@ +//===- xray-extract.h - 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. +// +//===----------------------------------------------------------------------===// +// +// Defines the interface for extracting the instrumentation map from an +// XRay-instrumented binary. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_XRAY_EXTRACT_H +#define LLVM_TOOLS_XRAY_EXTRACT_H + +#include +#include +#include +#include + +#include "xray-sleds.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace xray { + +class InstrumentationMapExtractor { +public: + typedef std::unordered_map FunctionAddressMap; + typedef std::unordered_map FunctionAddressReverseMap; + + enum class InputFormats { ELF, YAML }; + +private: + std::string Filename; + std::deque Sleds; + FunctionAddressMap FunctionAddresses; + FunctionAddressReverseMap FunctionIds; + +public: + /// Loads the instrumentation map from |Filename|. Updates |EC| in case there + /// were errors encountered opening the file. |Format| defines what the input + /// instrumentation map is in. + explicit InstrumentationMapExtractor(std::string Filename, + InputFormats Format, + std::error_code &EC); + + const FunctionAddressMap &getFunctionAddresses() { return FunctionAddresses; } + + /// Exports the loaded function address map as JSON through |OS|. + void exportAsJSON(raw_ostream &OS); + + /// Exports the loaded function address map as YAML through |OS|. + void exportAsYAML(raw_ostream &OS); +}; + +enum class ExtractOutputFormats { JSON, YAML }; + +int extract(StringRef Input, + InstrumentationMapExtractor::InputFormats InputFormat, + StringRef Output, ExtractOutputFormats OutputFormat); + +} // namespace xray +} // namespace llvm + +#endif // LLVM_TOOLS_XRAY_EXTRACT_H Index: tools/llvm-xray/xray-extract.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-extract.cc @@ -0,0 +1,331 @@ +//===- 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 +#include +#include + +#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/ELF.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; + +// llvm-xray extract +// ---------------------------------------------------------------------------- +static cl::SubCommand Extract("extract", "Extract instrumentation maps"); +static cl::opt ExtractInput(cl::Positional, + cl::desc(""), cl::Required, + cl::sub(Extract)); +static cl::opt + 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)); + +static cl::opt + ExtractFormat("format", cl::desc("output format"), + cl::values(clEnumValN(ExtractOutputFormats::JSON, "json", + "instrumentation map in json"), + clEnumValN(ExtractOutputFormats::YAML, "yaml", + "output instrumentation map in csv"), + clEnumValEnd), + cl::init(ExtractOutputFormats::YAML), cl::sub(Extract)); +static cl::alias ExtractFormat2("f", cl::desc("Alias for -format"), + cl::aliasopt(ExtractFormat), cl::sub(Extract)); + +static cl::opt InstrMapFormat( + "instr-map-format", cl::desc("format of instrumentation map"), + cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf", + "instrumentation map in an ELF header"), + clEnumValN(InstrumentationMapExtractor::InputFormats::YAML, + "yaml", "instrumentation map in YAML"), + clEnumValEnd), + cl::sub(Extract), cl::init(InstrumentationMapExtractor::InputFormats::ELF)); +static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat), + cl::desc("Alias for -instr-map-format"), + cl::sub(Extract)); + +using ::llvm::yaml::MappingTraits; +using ::llvm::yaml::ScalarEnumerationTraits; +using ::llvm::yaml::IO; +using ::llvm::yaml::Hex64; +using ::llvm::yaml::Output; +using ::llvm::yaml::Input; + +enum class FunctionKinds { ENTRY, EXIT }; + +struct YAMLXRaySledEntry { + int32_t FuncId; + Hex64 Address; + Hex64 Function; + FunctionKinds Kind; + bool AlwaysWinstrument; +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FunctionKinds &Kind) { + IO.enumCase(Kind, "function-enter", FunctionKinds::ENTRY); + IO.enumCase(Kind, "function-exit", FunctionKinds::EXIT); + } +}; + +template <> struct MappingTraits { + 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.AlwaysWinstrument); + } + + static constexpr bool flow = true; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry); + +namespace { + +// Returns {"message", false} on errors; {"", true} otherwise. +std::pair LoadBinaryInstrELF( + StringRef Filename, std::deque &Sleds, + InstrumentationMapExtractor::FunctionAddressMap &InstrMap, + InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { + if (Filename.empty()) + llvm_unreachable("Provided Filename is empty."); + + auto ObjectFile = object::ObjectFile::createObjectFile(Filename); + + // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only. + if (!ObjectFile || !ObjectFile->getBinary()->isELF()) + return std::make_pair("File format not supported (only does ELF).", + 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 std::make_pair("Failed to find XRay instrumentation map.", + std::make_error_code(std::errc::not_supported)); + if (I->getContents(Contents)) + return std::make_pair( + "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(); + if (std::distance(C, Contents.bytes_end()) % sizeof(XRaySledEntry) != 0) + return std::make_pair( + "Instrumentation map entries not evenly divisible by " + "size of XRaySledEntry", + std::make_error_code(std::errc::executable_format_error)); + while (C != Contents.bytes_end()) { + // Because we have a 'const char*' we need to assume that it is not aligned. + // This means we're going to have to memcpy it into properly-aligned memory + // first, then copy that into the Sleds data structure. + std::aligned_storage::type AlignedSled; + memcpy(&AlignedSled, C, sizeof(XRaySledEntry)); + Sleds.push_back(*reinterpret_cast(&AlignedSled)); + C += sizeof(XRaySledEntry); + } + + // 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. + int32_t FuncId = 1; + uint64_t CurFn = 0; + for (const auto &Sled : Sleds) { + auto F = Sled.Function; + if (CurFn == 0) { + CurFn = F; + InstrMap[FuncId] = F; + FunctionIds[F] = FuncId; + } + if (F != CurFn) { + ++FuncId; + CurFn = F; + InstrMap[FuncId] = F; + FunctionIds[F] = FuncId; + } + } + return std::make_pair("", std::error_code()); +} + +std::pair LoadYAMLInstrFile( + StringRef Filename, std::deque &Sleds, + InstrumentationMapExtractor::FunctionAddressMap &InstrMap, + InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { + int Fd; + auto EC = sys::fs::openFileForRead(Filename, Fd); + if (EC) + return {"Cannot open file for reading", EC}; + off_t FileSize; + while ((FileSize = lseek(Fd, 0, SEEK_END)) == -1) { + if (errno == EINTR) + continue; + } + assert(FileSize != -1 && "Error seeking to the end of file"); + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + if (EC) + return {"Cannot mmap file for read", EC}; + + std::vector YAMLSleds; + Input In(StringRef(MappedFile.data(), FileSize)); + In >> YAMLSleds; + if (In.error()) + return {"Error parsing YAML file", In.error()}; + + // Convert the sleds into proper XRaySledEntry objects. + for (const auto &Y : YAMLSleds) { + InstrMap[Y.FuncId] = Y.Function; + FunctionIds[Y.Function] = Y.FuncId; + Sleds.emplace_back( + XRaySledEntry{Y.Address, Y.Function, + Y.Kind == FunctionKinds::ENTRY ? uint8_t{0} : uint8_t{1}, + Y.AlwaysWinstrument, ""}); + } + return {"", std::error_code()}; +} + +} // namespace + +InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename, + InputFormats Format, + std::error_code &EC) + : Filename(std::move(Filename)) { + switch (Format) { + case InputFormats::ELF: + std::tie(std::ignore, EC) = LoadBinaryInstrELF( + this->Filename, Sleds, FunctionAddresses, FunctionIds); + break; + case InputFormats::YAML: + std::tie(std::ignore, EC) = LoadYAMLInstrFile( + this->Filename, Sleds, FunctionAddresses, FunctionIds); + break; + } +} + +void InstrumentationMapExtractor::exportAsJSON(raw_ostream &OS) { + // JSON Output for the instrumentation map should look like the following: + // + // [ + // , + // , + // ... + // + // ] + // + // Each of the elements turn into: + // + // { + // "funcId": , + // "funcAdd": , + // "sledKind": , + // "alwaysInstrument": + // } + OS << "[\n"; + auto Countdown = Sleds.size(); + for (const auto &Sled : Sleds) { + OS << " {\n \"funcId\": " << FunctionIds[Sled.Function] + << ",\n \"funcAddr\": \"" << format_hex(Sled.Function, 16) + << "\",\n \"sledAddr\": \"" << format_hex(Sled.Address, 16) + << "\",\n \"sledKind\": \""; + switch (Sled.Kind) { + case '\0': + OS << "FunctionEnter"; + break; + case '\1': + OS << "FunctionExit"; + break; + default: + OS << "Unknown"; + break; + } + OS << "\",\n \"alwaysInstrument\": " + << (Sled.AlwaysInstrument ? "true" : "false") << "\n }"; + if (--Countdown != 0) + OS << ","; + OS << "\n"; + } + OS << "]\n"; +} + +void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) { + // First we translate the sleds into the YAMLXRaySledEntry objects in a deque. + std::vector YAMLSleds; + YAMLSleds.reserve(Sleds.size()); + for (const auto &Sled : Sleds) { + YAMLSleds.push_back( + {FunctionIds[Sled.Function], Sled.Address, Sled.Function, + Sled.Kind == '\0' ? FunctionKinds::ENTRY : FunctionKinds::EXIT, + Sled.AlwaysInstrument ? true : false}); + } + Output Out(OS); + Out << YAMLSleds; +} + +namespace llvm { +namespace xray { + +static auto Unused = [] { + registerCommand(&Extract, [] { + std::error_code EC; + xray::InstrumentationMapExtractor Extractor(ExtractInput, InstrMapFormat, + EC); + if (EC) { + errs() << "Cannot extract instrumentation map from '" << ExtractInput + << "'\n"; + return 1; + } + raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) { + errs() << "Cannot open file '" << ExtractOutput << "' for writing.\n"; + return 1; + } + switch (ExtractFormat) { + case ExtractOutputFormats::JSON: + Extractor.exportAsJSON(OS); + break; + case ExtractOutputFormats::YAML: + Extractor.exportAsYAML(OS); + break; + } + return 0; + }); + return false; +}(); + +} // namespace xray +} // namespace llvm Index: tools/llvm-xray/xray-registry.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-registry.h @@ -0,0 +1,31 @@ +//===- xray-registry.h - Define registry mechanism for commands. ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implement a simple subcommand registry. +// +//===----------------------------------------------------------------------===// +#ifndef TOOLS_LLVM_XRAY_XRAY_REGISTRY_H +#define TOOLS_LLVM_XRAY_XRAY_REGISTRY_H + +#include "llvm/Support/CommandLine.h" + +namespace llvm { +namespace xray { + +// Requires that a command has not been registered to a given |SC| before. +void registerCommand(cl::SubCommand* SC, std::function Command); + +// Requires that |SC| is not nullptr. If there was no command associated with +// |SC| then returns an empty function. +std::function dispatch(cl::SubCommand* SC); + +} // namespace xray +} // namespace llvm + +#endif // TOOLS_LLVM_XRAY_XRAY_REGISTRY_H Index: tools/llvm-xray/xray-registry.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-registry.cc @@ -0,0 +1,38 @@ +//===- xray-registry.cc - Implement a command registry. -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implement a simple subcommand registry. +// +//===----------------------------------------------------------------------===// +#include "xray-registry.h" + +#include "llvm/Support/ManagedStatic.h" +#include + +namespace llvm { +namespace xray { + +ManagedStatic>> + Commands; + +void registerCommand(cl::SubCommand *SC, std::function Command) { + assert(Commands->count(SC) == 0 && + "Attempting to overwrite a command handler"); + (*Commands)[SC] = Command; +} + +std::function dispatch(cl::SubCommand *SC) { + auto It = Commands->find(SC); + if (It == Commands->end()) + return {}; + return It->second; +} + +} // namespace xray +} // namespace llvm Index: tools/llvm-xray/xray-sleds.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-sleds.h @@ -0,0 +1,31 @@ +//===- xray-sleds.h - XRay Sleds Data Structure ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the structure used to represent XRay instrumentation map entries. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H +#define LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H + +namespace llvm { +namespace xray { + +struct XRaySledEntry { + uint64_t Address; + uint64_t Function; + unsigned char Kind; + unsigned char AlwaysInstrument; + unsigned char Padding[14]; +}; + +} // namespace xray +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H