//===- 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 "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 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)); struct YAMLXRaySledEntry { int32_t FuncId; Hex64 Address; Hex64 Function; SledEntry::FunctionKinds Kind; bool AlwaysInstrument; }; namespace llvm { namespace yaml { template <> struct ScalarEnumerationTraits { 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 { 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 &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( "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( "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( "Failed to find XRay instrumentation map.", std::make_error_code(std::errc::not_supported)); if (I->getContents(Contents)) return make_error( "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( "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 Sleds; for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) { DataExtractor Extractor( StringRef(reinterpret_cast(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( Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) + "'.", std::make_error_code(std::errc::executable_format_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 E) { return joinErrors( make_error( Twine("Cannot extract instrumentation map from '") + ExtractInput + "'.", std::make_error_code(std::errc::executable_format_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 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 { 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( Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); Extractor.exportAsYAML(OS); return Error::success(); });