diff --git a/llvm/include/llvm/CodeGen/MIPReader.h b/llvm/include/llvm/CodeGen/MIPReader.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MIPReader.h @@ -0,0 +1,59 @@ +//===---- MIPReader.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_MIP_READER_H +#define LLVM_CODEGEN_MIP_READER_H + +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/MIP/MIP.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { +namespace MachineProfile { + +class MIPReader { +public: + static ErrorOr> read(const Twine &Filename); + +private: + static std::error_code readData(std::unique_ptr &Buffer, + std::unique_ptr &MIP); + static MFProfile readNextProfile(const char *&Data); +}; + +class MIPMapReader { +public: + static ErrorOr> read(const Twine &Filename); + +private: + static std::error_code readData(std::unique_ptr &Buffer, + std::unique_ptr &MIP); + static ErrorOr> + readNextProfile(const char *&Data, uint64_t CurrentOffset, + const MIPHeader &Header); +}; + +class MIPRawReader { +public: + static ErrorOr> + read(const Twine &Filename, const std::unique_ptr &MIP); + +private: + static std::error_code readData(std::unique_ptr &Buffer, + std::unique_ptr &RawMIP, + const std::unique_ptr &MIP); +}; + +} // namespace MachineProfile +} // namespace llvm + +#endif // LLVM_CODEGEN_MIP_READER_H diff --git a/llvm/include/llvm/CodeGen/MIPWriter.h b/llvm/include/llvm/CodeGen/MIPWriter.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MIPWriter.h @@ -0,0 +1,45 @@ +//===---- MIPWriter.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_MIP_WRITER_H +#define LLVM_CODEGEN_MIP_WRITER_H + +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/MIP/MIP.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { +namespace MachineProfile { + +class MIPWriter { +public: + static ErrorOr> create(StringRef Filename); + + void write(const std::vector &Profiles, uint16_t Version, + uint32_t ProfileType, uint32_t ModuleHash); + +private: + std::unique_ptr OutputStream; + + MIPWriter(std::unique_ptr &OS) : OutputStream(std::move(OS)) {} + + static std::unique_ptr create(std::unique_ptr &OS) { + std::unique_ptr Writer; + Writer.reset(new MIPWriter(OS)); + return Writer; + } +}; + +} // namespace MachineProfile +} // namespace llvm + +#endif // LLVM_CODEGEN_MIP_WRITER_H diff --git a/llvm/include/llvm/CodeGen/MIPYaml.h b/llvm/include/llvm/CodeGen/MIPYaml.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MIPYaml.h @@ -0,0 +1,126 @@ +//===---- MIPYaml.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_MIP_YAML_H +#define LLVM_CODEGEN_MIP_YAML_H + +#include "llvm/MIP/MIP.h" +#include "llvm/Support/YAMLTraits.h" + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachineProfile::MFProfile) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachineProfile::MBBProfile) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachineProfile::CallEdge) + +namespace llvm { +namespace yaml { + +// Map this value as a hex value. +template +static inline void mapRequiredAsHex(IO &io, const char *Key, IntType &Value) { + auto HexValue = static_cast(Value); + io.mapRequired(Key, HexValue); + Value = static_cast(HexValue); +} + +template <> struct ScalarBitSetTraits { + static void bitset(IO &io, llvm::MachineProfile::MIPFileType &FileType) { + io.bitSetCase(FileType, ".mipraw", llvm::MachineProfile::MIP_FILE_TYPE_RAW); + io.bitSetCase(FileType, ".mipret", + llvm::MachineProfile::MIP_FILE_TYPE_CALL_EDGE_SAMPLES); + io.bitSetCase(FileType, ".mipmap", llvm::MachineProfile::MIP_FILE_TYPE_MAP); + io.bitSetCase(FileType, ".mip", + llvm::MachineProfile::MIP_FILE_TYPE_PROFILE); + io.bitSetCase(FileType, "64 bit", + llvm::MachineProfile::MIP_FILE_TYPE_64_BIT); + io.bitSetCase(FileType, "32 bit", + llvm::MachineProfile::MIP_FILE_TYPE_32_BIT); + } +}; + +template <> struct ScalarBitSetTraits { + static void bitset(IO &io, + llvm::MachineProfile::MIPProfileType &ProfileType) { + io.bitSetCase(ProfileType, "Function Coverage", + llvm::MachineProfile::MIP_PROFILE_TYPE_FUNCTION_COVERAGE); + io.bitSetCase(ProfileType, "Block Coverage", + llvm::MachineProfile::MIP_PROFILE_TYPE_BLOCK_COVERAGE); + io.bitSetCase(ProfileType, "Function Timestamp", + llvm::MachineProfile::MIP_PROFILE_TYPE_FUNCTION_TIMESTAMP); + io.bitSetCase(ProfileType, "Function Call Count", + llvm::MachineProfile::MIP_PROFILE_TYPE_FUNCTION_CALL_COUNT); + io.bitSetCase(ProfileType, "Return Address", + llvm::MachineProfile::MIP_PROFILE_TYPE_RETURN_ADDRESS); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, llvm::MachineProfile::CallEdge &Edge) { + mapRequiredAsHex(io, "Section Relative Source Address", + Edge.SectionRelativeSourceAddress); + io.mapRequired("Weight", Edge.Weight); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, llvm::MachineProfile::MBBProfile &Profile) { + mapRequiredAsHex(io, "Offset", Profile.Offset); + io.mapRequired("Covered", Profile.IsCovered); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, llvm::MachineProfile::MIPHeader &Header) { + auto FileType = + static_cast(Header.FileType); + auto ProfileType = + static_cast(Header.ProfileType); + io.mapRequired("File Type", FileType); + io.mapRequired("Profile Type", ProfileType); + Header.FileType = FileType; + Header.ProfileType = ProfileType; + + mapRequiredAsHex(io, "Module Hash", Header.ModuleHash); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, llvm::MachineProfile::MFProfile &Profile) { + io.mapRequired("Function Name", Profile.FunctionName); + mapRequiredAsHex(io, "Function Signature", + Profile.FunctionSignature); + mapRequiredAsHex(io, "Raw Profile Data Address", + Profile.RawProfileDataAddress); + mapRequiredAsHex(io, "Encoded Function Address", + Profile.EncodedFunctionAddress); + mapRequiredAsHex(io, "Function Size", Profile.FunctionSize); + mapRequiredAsHex(io, "Control Flow Graph Signature", + Profile.ControlFlowGraphSignature); + io.mapRequired("Raw Profile Count", Profile.RawProfileCount); + io.mapRequired("Function Call Count", Profile.FunctionCallCount); + io.mapRequired("Function Order Sum", Profile.FunctionOrderSum); + io.mapRequired("Basic Block Profiles", Profile.BasicBlockProfiles); + io.mapRequired("Call Edges", Profile.CallEdges); + } +}; + +template <> +struct MappingTraits> { + static void mapping(IO &io, + std::unique_ptr &MIP) { + io.mapRequired("Header", MIP->Header); + io.mapRequired("Profiles", MIP->Profiles); + } +}; + +} // namespace yaml +} // namespace llvm + +#endif // LLVM_CODEGEN_MIP_YAML_H diff --git a/llvm/include/llvm/MIP/MIP.h b/llvm/include/llvm/MIP/MIP.h --- a/llvm/include/llvm/MIP/MIP.h +++ b/llvm/include/llvm/MIP/MIP.h @@ -9,11 +9,93 @@ #ifndef LLVM_MIP_MIP_H #define LLVM_MIP_MIP_H +#include "llvm/ADT/SmallVector.h" +#include +#include + namespace llvm { namespace MachineProfile { #include "llvm/MIP/MIPData.inc" +// Profile of a function call to a particular machine function +struct CallEdge { + // The section-relative address of the callsite + uint32_t SectionRelativeSourceAddress; + // The weight associated with this call edge + uint32_t Weight; +}; + +// Machine IR profile data of a machine basic block. +struct MBBProfile { + // Function-relative machine basic block offset + uint32_t Offset; + // True if this block was executed + bool IsCovered; + + MBBProfile() : Offset(0), IsCovered(false) {} + MBBProfile(uint32_t Offset) : Offset(Offset), IsCovered(false) {} +}; + +// Machine IR profile data of a machine function. +struct MFProfile { + std::string FunctionName; + // MD5 hash of the function name + uint64_t FunctionSignature; + // Section-relative raw profile address + uint64_t RawProfileDataAddress; + // Function address offset to raw section + int64_t EncodedFunctionAddress; + // Function size + uint32_t FunctionSize; + // MD5 hash of the control flow graph of the function + uint32_t ControlFlowGraphSignature; + // The number of raw profiles accumulated into this profile + uint32_t RawProfileCount; + // The number of times this function was called. + uint64_t FunctionCallCount; + // Accumulation over all raw profiles of the order index of this function + uint64_t FunctionOrderSum; + // Profiles of machine basic blocks + SmallVector BasicBlockProfiles; + // Profiles of incoming machine function calls + SmallVector CallEdges; +}; + +// Machine IR profile data of a particular module. +struct MIRProfile { +public: + MIPHeader Header; + std::vector Profiles; + + void getOrderedProfiles(std::vector &OrderedProfiles) const; +}; + +// Machine IR raw profile data of a machine function. +struct MFRawProfile { + // Section-relative raw profile address + uint64_t RawProfileDataAddress = 0; + + // MIPProfileType::FUNCTION_COVERAGE + bool IsFunctionCovered = 0; + + // MIPProfileType::BLOCK_COVERAGE + SmallVector BasicBlockCoverage; + + // MIPProfileType::FUNCTION_CALL_COUNT + uint32_t FunctionCallCount = 0; + + // MIPProfileType::FUNCTION_TIMESTAMP + uint32_t FunctionTimestamp = 0; +}; + +// Machine IR raw profile data of a module. +struct MIRRawProfile { + MIPHeader Header; + std::vector RawProfiles; + std::vector RawCallEdges; +}; + } // namespace MachineProfile } // namespace llvm diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -147,7 +147,10 @@ MIRNamerPass.cpp MIRCanonicalizerPass.cpp MIRInstrumentationPass.cpp + MIPReader.cpp MIPSectionEmitter.cpp + MIPWriter.cpp + MIP.cpp RegisterUsageInfo.cpp RegUsageInfoCollector.cpp RegUsageInfoPropagate.cpp diff --git a/llvm/lib/CodeGen/MIP.cpp b/llvm/lib/CodeGen/MIP.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MIP.cpp @@ -0,0 +1,34 @@ +//===- MIP.cpp - Machine IR Profile ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/MIP/MIP.h" +#include "llvm/ADT/Twine.h" + +namespace llvm { +namespace MachineProfile { + +void MIRProfile::getOrderedProfiles( + std::vector &OrderedProfiles) const { + for (auto &Profile : Profiles) { + if (Profile.RawProfileCount > 0) { + // Only consider functions that have been profiled. + OrderedProfiles.push_back(Profile); + } + } + std::stable_sort(OrderedProfiles.begin(), OrderedProfiles.end(), + [](auto &A, auto &B) { + // NOTE: The equation was modified to avoid division. + // A.OrderSum / A.ProfileCount < B.OrderSum / + // B.ProfileCount + return A.FunctionOrderSum * B.RawProfileCount < + B.FunctionOrderSum * A.RawProfileCount; + }); +} + +} // namespace MachineProfile +} // namespace llvm diff --git a/llvm/lib/CodeGen/MIPReader.cpp b/llvm/lib/CodeGen/MIPReader.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MIPReader.cpp @@ -0,0 +1,315 @@ +//===---- MIPReader.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MIPReader.h" +#include "llvm/Support/MD5.h" +#include +#include + +using namespace llvm; +using namespace llvm::support; + +namespace llvm { +namespace MachineProfile { + +ErrorOr> +readMIPHeader(const Twine &Filename, MIPFileType FileType, MIPHeader &Header) { + auto BufferOrErr = MemoryBuffer::getFile(Filename); + if (auto EC = BufferOrErr.getError()) { + WithColor::error() << "Unable to open " << Filename << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } + + auto &Buffer = BufferOrErr.get(); + + if (Buffer->getBufferSize() < sizeof(MIPHeader)) { + WithColor::error() << Filename << ": too small\n"; + WithColor::error() << "Expected: >= " << sizeof(MIPHeader) << "\n"; + WithColor::error() << " Got: " << Buffer->getBufferSize() << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } + + const char *Data = Buffer->getBufferStart(); + + Header.Magic = endian::readNext(Data); + Header.Version = endian::readNext(Data); + Header.FileType = endian::readNext(Data); + Header.ProfileType = endian::readNext(Data); + Header.ModuleHash = endian::readNext(Data); + if (Header.FileType & MIP_FILE_TYPE_32_BIT) { + Header.RawSectionOffset = + endian::readNext(Data); + (void)endian::readNext(Data); + } else { + Header.RawSectionOffset = + endian::readNext(Data); + } + Header.Reserved = endian::readNext(Data); + Header.OffsetToData = endian::readNext(Data); + + if (Header.Magic != MIP_MAGIC_VALUE) { + WithColor::error() << Filename << ": Invalid MIPMagic value\n" + << "Expected: " << Twine::utohexstr(MIP_MAGIC_VALUE) + << "\n" + << " Got: " << Twine::utohexstr(Header.Magic) + << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } else if (Header.Version > MIP_VERSION) { + WithColor::error() << Filename << ": Invalid MIP version\n" + << "Expected: <=" << MIP_VERSION << "\n" + << " Got: " << Header.Version << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } else if ((Header.FileType & 0b1111) != FileType) { + WithColor::error() << Filename << ": Invalid file type\n" + << "Expected: " << Twine::utohexstr(FileType) << "\n" + << " Got: " << Twine::utohexstr(Header.FileType) + << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } else if ((Header.FileType & MIP_FILE_TYPE_64_BIT) && + (Header.FileType & MIP_FILE_TYPE_32_BIT)) { + WithColor::error() << Filename + << ": File type cannot be both 64 bit and 32 bit\n" + << " Got: " << Twine::utohexstr(Header.FileType) + << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } else if (Header.ProfileType & ~(MIP_PROFILE_TYPE_FUNCTION_COVERAGE | + MIP_PROFILE_TYPE_BLOCK_COVERAGE)) { + WithColor::error() << Filename << ": Invalid profile type\n" + << "Got: 0x" << Twine::utohexstr(Header.ProfileType) + << " \n"; + return std::make_error_code(std::errc::invalid_argument); + } else { + return std::move(Buffer); + } +} + +ErrorOr> MIPReader::read(const Twine &Filename) { + std::unique_ptr MIP(new MIRProfile()); + auto BufferOrErr = + readMIPHeader(Filename, MIP_FILE_TYPE_PROFILE, MIP->Header); + if (auto EC = BufferOrErr.getError()) + return EC; + + if (auto EC = readData(BufferOrErr.get(), MIP)) + return EC; + + return MIP; +} + +std::error_code MIPReader::readData(std::unique_ptr &Buffer, + std::unique_ptr &MIP) { + const char *Data = Buffer->getBufferStart() + MIP->Header.OffsetToData; + + std::map> SignatureToProfileIndices; + + auto NumProfiles = endian::readNext(Data); + for (uint64_t i = 0; i < NumProfiles; i++) { + auto Profile = readNextProfile(Data); + size_t Index = MIP->Profiles.size(); + if (SignatureToProfileIndices.count(Profile.FunctionSignature)) { + SignatureToProfileIndices[Profile.FunctionSignature].push_back(Index); + } else { + SignatureToProfileIndices[Profile.FunctionSignature] = {Index}; + } + MIP->Profiles.push_back(Profile); + } + + std::set FunctionNames; + auto NumNames = endian::readNext(Data); + for (uint64_t i = 0; i < NumNames; i++) { + std::string Name(Data); + Data += Name.size() + 1; + FunctionNames.insert(Name); + } + + for (auto &Name : FunctionNames) { + for (auto Index : SignatureToProfileIndices[MD5Hash(Name)]) { + MIP->Profiles[Index].FunctionName = Name; + } + } + + return {}; +} + +MFProfile MIPReader::readNextProfile(const char *&Data) { + MFProfile Profile; + Profile.FunctionSignature = + endian::readNext(Data); + Profile.RawProfileDataAddress = + endian::readNext(Data); + Profile.EncodedFunctionAddress = + endian::readNext(Data); + Profile.FunctionSize = endian::readNext(Data); + Profile.ControlFlowGraphSignature = + endian::readNext(Data); + uint32_t NonEntryBasicBlockCount = + endian::readNext(Data); + Profile.RawProfileCount = endian::readNext(Data); + Profile.FunctionCallCount = + endian::readNext(Data); + Profile.FunctionOrderSum = + endian::readNext(Data); + + // NOTE: The file format does not include the entry basic block in the block + // profile list. + MBBProfile EntryBlockProfile(0); + EntryBlockProfile.IsCovered = + Profile.FunctionCallCount > 0 || Profile.RawProfileCount > 0; + Profile.BasicBlockProfiles.push_back(EntryBlockProfile); + for (uint32_t j = 0; j < NonEntryBasicBlockCount; j++) { + MBBProfile BlockProfile; + BlockProfile.Offset = endian::readNext(Data); + BlockProfile.IsCovered = endian::readNext(Data); + Profile.BasicBlockProfiles.push_back(BlockProfile); + } + + uint32_t CallEdgeCount = endian::readNext(Data); + for (uint32_t j = 0; j < CallEdgeCount; j++) { + llvm_unreachable("Not implemented yet"); + } + return Profile; +} + +ErrorOr> MIPMapReader::read(const Twine &Filename) { + std::unique_ptr MIP(new MIRProfile()); + auto BufferOrErr = readMIPHeader(Filename, MIP_FILE_TYPE_MAP, MIP->Header); + if (auto EC = BufferOrErr.getError()) + return EC; + + if (auto EC = readData(BufferOrErr.get(), MIP)) + return EC; + + return MIP; +} + +std::error_code MIPMapReader::readData(std::unique_ptr &Buffer, + std::unique_ptr &MIP) { + const char *Data = Buffer->getBufferStart() + MIP->Header.OffsetToData; + + while (Data < Buffer->getBufferEnd()) { + uint64_t CurrentOffset = Data - Buffer->getBufferStart(); + const auto &Profile = readNextProfile(Data, CurrentOffset, MIP->Header); + MIP->Profiles.push_back(*Profile->get()); + } + + return {}; +} + +ErrorOr> +MIPMapReader::readNextProfile(const char *&Data, uint64_t CurrentOffset, + const MIPHeader &Header) { + std::unique_ptr Profile(new MFProfile()); + bool IsArch64Bit = Header.FileType & MIP_FILE_TYPE_64_BIT; + int64_t RelativeRawSectionStart = Header.RawSectionOffset - CurrentOffset; + int64_t RelativeRawProfileAddress = + IsArch64Bit ? endian::readNext(Data) + : endian::readNext(Data); + Profile->RawProfileDataAddress = + RelativeRawProfileAddress - RelativeRawSectionStart; + int64_t RelativeFunctionAddress = + IsArch64Bit ? endian::readNext(Data) + : endian::readNext(Data); + Profile->EncodedFunctionAddress = + RelativeFunctionAddress - RelativeRawSectionStart; + Profile->FunctionSize = endian::readNext(Data); + Profile->ControlFlowGraphSignature = + endian::readNext(Data); + uint32_t NonEntryBasicBlockCount = + endian::readNext(Data); + + // Create the entry block profile. + Profile->BasicBlockProfiles.push_back(MBBProfile()); + for (size_t i = 0; i < NonEntryBasicBlockCount; i++) { + auto Offset = endian::readNext(Data); + Profile->BasicBlockProfiles.push_back(MBBProfile(Offset)); + } + uint32_t FunctionNameSize = + endian::readNext(Data); + Profile->FunctionName = std::string(Data, FunctionNameSize); + if (IsArch64Bit) { + // Align to 64 bits. + Data = reinterpret_cast( + alignTo<8>(reinterpret_cast(Data) + FunctionNameSize)); + } else { + // Align to 32 bits. + Data = reinterpret_cast( + alignTo<4>(reinterpret_cast(Data) + FunctionNameSize)); + } + + Profile->FunctionSignature = MD5Hash(Profile->FunctionName); + Profile->RawProfileCount = 0; + Profile->FunctionCallCount = 0; + Profile->FunctionOrderSum = 0; + + return std::move(Profile); +} + +ErrorOr> +MIPRawReader::read(const Twine &Filename, + const std::unique_ptr &MIP) { + std::unique_ptr RawMIP(new MIRRawProfile()); + auto BufferOrErr = readMIPHeader(Filename, MIP_FILE_TYPE_RAW, RawMIP->Header); + if (auto EC = BufferOrErr.getError()) + return EC; + + if (MIP->Header.ModuleHash != RawMIP->Header.ModuleHash) { + WithColor::error() << Filename << ": Invalid module hash\n"; + WithColor::error() << "Expected: 0x" + << Twine::utohexstr(MIP->Header.ModuleHash) << "\n"; + WithColor::error() << " Got: 0x" + << Twine::utohexstr(RawMIP->Header.ModuleHash) << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } + + if (auto EC = readData(BufferOrErr.get(), RawMIP, MIP)) + return EC; + + return RawMIP; +} + +std::error_code MIPRawReader::readData(std::unique_ptr &Buffer, + std::unique_ptr &RawMIP, + const std::unique_ptr &MIP) { + for (auto &Profile : MIP->Profiles) { + if (Profile.RawProfileDataAddress >= Buffer->getBufferSize()) { + WithColor::error() << "Raw profile offset too large\n"; + WithColor::error() << "Expected: < " + << Twine::utohexstr(Buffer->getBufferSize()) << "\n"; + WithColor::error() << " Got: " + << Twine::utohexstr(Profile.RawProfileDataAddress) + << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } + const char *Data = Buffer->getBufferStart() + Profile.RawProfileDataAddress; + MFRawProfile RawProfile; + RawProfile.RawProfileDataAddress = Profile.RawProfileDataAddress; + if (RawMIP->Header.ProfileType & MIP_PROFILE_TYPE_FUNCTION_COVERAGE) { + RawProfile.IsFunctionCovered = + (endian::readNext(Data) == 0x00); + } + + if (RawMIP->Header.ProfileType & MIP_PROFILE_TYPE_BLOCK_COVERAGE) { + // NOTE: The entry basic block profile is not in the raw file format. + RawProfile.BasicBlockCoverage.push_back(RawProfile.IsFunctionCovered); + for (size_t i = 1; i < Profile.BasicBlockProfiles.size(); i++) { + bool IsBlockCovered = + (endian::readNext(Data) == 0x00); + RawProfile.BasicBlockCoverage.push_back(IsBlockCovered); + } + } + + RawMIP->RawProfiles.push_back(RawProfile); + } + return {}; +} + +} // namespace MachineProfile +} // namespace llvm diff --git a/llvm/lib/CodeGen/MIPWriter.cpp b/llvm/lib/CodeGen/MIPWriter.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MIPWriter.cpp @@ -0,0 +1,94 @@ +//===---- MIPWriter.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MIPWriter.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MD5.h" +#include +#include + +using namespace llvm; + +namespace llvm { +namespace MachineProfile { + +void writeMIPHeader(raw_ostream &OS, uint16_t Version, uint16_t FileType, + uint32_t ProfileType, uint32_t ModuleHash) { + support::endian::Writer Writer(OS, support::little); + Writer.write(MIP_MAGIC_VALUE); + Writer.write(Version); + Writer.write(FileType); + Writer.write(ProfileType); + Writer.write(ModuleHash); + Writer.write(/*Reserved=*/0); + Writer.write(/*Reserved=*/0); + Writer.write(sizeof(MIPHeader)); +} + +ErrorOr> MIPWriter::create(StringRef Filename) { + std::error_code EC; + std::unique_ptr OS; + OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None)); + if (EC) { + WithColor::error() << "Unable to open " << Filename << "\n"; + return EC; + } + + return MIPWriter::create(OS); +} + +void MIPWriter::write(const std::vector &Profiles, uint16_t Version, + uint32_t ProfileType, uint32_t ModuleHash) { + support::endian::Writer Writer(*OutputStream, support::little); + + writeMIPHeader(Writer.OS, Version, MIP_FILE_TYPE_PROFILE, ProfileType, + ModuleHash); + + Writer.write(Profiles.size()); + for (auto &Profile : Profiles) { + Writer.write(Profile.FunctionSignature); + Writer.write(Profile.RawProfileDataAddress); + Writer.write(Profile.EncodedFunctionAddress); + Writer.write(Profile.FunctionSize); + Writer.write(Profile.ControlFlowGraphSignature); + Writer.write(Profile.BasicBlockProfiles.size() - 1); + Writer.write(Profile.RawProfileCount); + Writer.write(Profile.FunctionCallCount); + Writer.write(Profile.FunctionOrderSum); + + // NOTE: We do not include the entry basic block. + for (auto it = Profile.BasicBlockProfiles.begin() + 1; + it != Profile.BasicBlockProfiles.end(); it++) { + const auto &BlockProfile = *it; + Writer.write(BlockProfile.Offset); + Writer.write(BlockProfile.IsCovered); + } + + Writer.write(Profile.CallEdges.size()); + for (const auto &CallEdge : Profile.CallEdges) { + (void)CallEdge; + llvm_unreachable("Not implemented"); + } + } + + std::set FunctionNames; + for (auto &Profile : Profiles) { + FunctionNames.insert(Profile.FunctionName); + } + + Writer.write(FunctionNames.size()); + for (auto &FunctionName : FunctionNames) { + Writer.OS << FunctionName << '\0'; + } +} + +} // namespace MachineProfile +} // namespace llvm diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -98,6 +98,7 @@ llvm-nm llvm-objcopy llvm-objdump + llvm-mipdata llvm-opt-fuzzer llvm-opt-report llvm-otool diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -160,7 +160,7 @@ 'llvm-dwarfdump', 'llvm-dlltool', 'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs', 'llvm-install-name-tool', 'llvm-jitlink', 'llvm-opt-fuzzer', 'llvm-lib', - 'llvm-link', 'llvm-lto', 'llvm-lto2', 'llvm-mc', 'llvm-mca', + 'llvm-link', 'llvm-lto', 'llvm-lto2', 'llvm-mc', 'llvm-mca', 'llvm-mipdata', 'llvm-modextract', 'llvm-nm', 'llvm-objcopy', 'llvm-objdump', 'llvm-otool', 'llvm-pdbutil', 'llvm-profdata', 'llvm-profgen', 'llvm-ranlib', 'llvm-rc', 'llvm-readelf', 'llvm-readobj', 'llvm-rtdyld', 'llvm-size', 'llvm-split', 'llvm-strings', diff --git a/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.mipmap.hex b/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.mipmap.hex new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.mipmap.hex @@ -0,0 +1,7 @@ +0000000: FB4D 4950 0800 1400 0100 0000 EFBE ADDE // Magic, Version=8, FileType=Map, ProfileType=FunctionCoverage, ModuleHash=0xdeadbeef +0000010: a0FE FFFF FFFF FFFF 0000 0000 2000 0000 // RelRawSectionStart=0xfffffffffffffea0, Reserved=0, OffsetToData=0x20 +0000020: a0FE FFFF FFFF FFFF 80FF FFFF FFFF FFFF // RelRawProfileAddress=0xfffffffffffffea0, RelFunctionAddress=0xffffffffffffff80 +0000030: 1000 0000 1577 0E00 0100 0000 0400 0000 // FunctionSize=0x10, CFG=0xe7715, NumBlocks=1, Offset1=4 +0000040: 0300 0000 666F 6F00 79FE FFFF FFFF FFFF // FunctionNameLength=3, Name=foo, RelRawProfileAddress=0xfffffffffffffe79 +0000050: 68FF FFFF FFFF FFFF 3000 0000 1111 0000 // RelFunctionAddress=0xffffffffffffff68, FunctionSize=0x30, CFG=0x1111 +0000060: 0000 0000 0400 0000 6D61 696E // NumBlocks=0, FunctionNameLength=4, Name=main diff --git a/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.mipraw.hex b/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.mipraw.hex new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.mipraw.hex @@ -0,0 +1,3 @@ +0000000: FB4D 4950 0800 1100 0100 0000 EFBE ADDE // Magic, Version=8, FileType=Raw, ProfileType=FunctionCoverage, ModuleHash=0xdeadbeef +0000010: 0000 0000 0000 0000 0000 0000 2000 0000 // Reserved=0, OffsetToData=0x20 +0000020: 00FF // foo=Covered, main=NotCovered diff --git a/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.yaml b/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-mipdata/Inputs/function-coverage.yaml @@ -0,0 +1,35 @@ +--- +Header: + File Type: [ .mip ] + Profile Type: [ Function Coverage ] + Module Hash: 0xDEADBEEF +Profiles: + - Function Name: foo + Function Signature: 0x5CF8C24CDB18BDAC + Raw Profile Data Address: 0x20 + Encoded Function Address: 0x100 + Function Size: 0x10 + Control Flow Graph Signature: 0xE7715 + Raw Profile Count: 1 + Function Call Count: 1 + Function Order Sum: 0 + Basic Block Profiles: + - Offset: 0x0 + Covered: true + - Offset: 0x4 + Covered: false + Call Edges: [] + - Function Name: main + Function Signature: 0xDB956436E78DD5FA + Raw Profile Data Address: 0x21 + Encoded Function Address: 0x110 + Function Size: 0x30 + Control Flow Graph Signature: 0x1111 + Raw Profile Count: 0 + Function Call Count: 0 + Function Order Sum: 0 + Basic Block Profiles: + - Offset: 0x0 + Covered: false + Call Edges: [] +... diff --git a/llvm/test/tools/llvm-mipdata/function-coverage.test b/llvm/test/tools/llvm-mipdata/function-coverage.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-mipdata/function-coverage.test @@ -0,0 +1,6 @@ +; RUN: xxd -r %p/Inputs/function-coverage.mipmap.hex > %t.mipmap +; RUN: xxd -r %p/Inputs/function-coverage.mipraw.hex > %t.mipraw +; RUN: llvm-mipdata create -p %t.mip %t.mipmap +; RUN: llvm-mipdata merge -p %t.mip %t.mipraw +; RUN: llvm-mipdata mip2yaml -p %t.mip -o %t.yaml +; RUN: diff %p/Inputs/function-coverage.yaml %t.yaml diff --git a/llvm/test/tools/llvm-mipdata/yaml.test b/llvm/test/tools/llvm-mipdata/yaml.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-mipdata/yaml.test @@ -0,0 +1,3 @@ +; RUN: llvm-mipdata yaml2mip -p %t.mip %p/Inputs/function-coverage.yaml +; RUN: llvm-mipdata mip2yaml -p %t.mip -o %t.yaml +; RUN: diff %p/Inputs/function-coverage.yaml %t.yaml diff --git a/llvm/tools/llvm-mipdata/CMakeLists.txt b/llvm/tools/llvm-mipdata/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-mipdata/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS + CodeGen + Core + Object + Support + Symbolize + ) + +add_llvm_tool(llvm-mipdata + llvm-mipdata.cpp + SymReader.cpp + DEPENDS + intrinsics_gen + ) diff --git a/llvm/tools/llvm-mipdata/LLVMBuild.txt b/llvm/tools/llvm-mipdata/LLVMBuild.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-mipdata/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-mipdata/LLVMBuild.txt ------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-mipdata +parent = Tools +required_libraries = MIP Support diff --git a/llvm/tools/llvm-mipdata/SymReader.h b/llvm/tools/llvm-mipdata/SymReader.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-mipdata/SymReader.h @@ -0,0 +1,48 @@ +//===- SymReader.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_SYMREADER_H +#define LLVM_TOOLS_LLVM_SYMREADER_H + +#include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace MachineProfile { + +class SymReader { + uint64_t MIPRawSectionBeginAddress; + + std::unique_ptr Symbolizer; + std::string SymbolFilePath; + + static ErrorOr> + expandBundle(const std::string &InputPath); + + static uint32_t getCPUType(const object::MachOObjectFile &MachO); + static bool filterArch(const object::ObjectFile &Obj); + +public: + SymReader(object::ObjectFile *Obj, bool Demangle); + virtual ~SymReader() = default; + + SymReader(SymReader &) = delete; + SymReader &operator=(SymReader &) = delete; + + static ErrorOr> create(const Twine &Filename, + bool Demangle = true); + + Expected getDIInliningInfo(int64_t MIPRawOffset); +}; + +} // namespace MachineProfile +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_SYMREADER_H diff --git a/llvm/tools/llvm-mipdata/SymReader.cpp b/llvm/tools/llvm-mipdata/SymReader.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-mipdata/SymReader.cpp @@ -0,0 +1,170 @@ +//===- SymReader.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Symbol Reader to correlate address to source info from symbol files. +// +//===----------------------------------------------------------------------===// + +#include "SymReader.h" +#include "llvm/MIP/MIP.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::MachineProfile; + +/// @} +/// Command line options. +/// @{ + +namespace { + +static cl::opt + Arch("arch", cl::init("arm64"), + cl::desc("Dump debug information for the specified CPU " + "architecture only. Architectures may be specified by " + "name or by number.")); + +} // namespace +/// @} +//===----------------------------------------------------------------------===// + +uint32_t SymReader::getCPUType(const MachOObjectFile &MachO) { + if (MachO.is64Bit()) + return MachO.getHeader64().cputype; + else + return MachO.getHeader().cputype; +} + +/// Return true if the object file has not been filtered by an --arch option. +bool SymReader::filterArch(const ObjectFile &Obj) { + if (Arch.empty()) + return true; + + if (auto *MachO = dyn_cast(&Obj)) { + unsigned Value; + if (!StringRef(Arch).getAsInteger(0, Value)) + if (Value == getCPUType(*MachO)) + return true; + + // Match as name. + if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName()) + return true; + } + + return false; +} + +/// If the input path is a .dSYM bundle (as created by the dsymutil tool), +/// replace it with individual entries for each of the object files inside the +/// bundle otherwise return the input path. +ErrorOr> +SymReader::expandBundle(const std::string &InputPath) { + std::vector BundlePaths; + SmallString<256> BundlePath(InputPath); + // Normalize input path. This is necessary to accept `bundle.dSYM/`. + sys::path::remove_dots(BundlePath); + // Manually open up the bundle to avoid introducing additional dependencies. + if (sys::fs::is_directory(BundlePath) && + sys::path::extension(BundlePath) == ".dSYM") { + std::error_code EC; + sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); + for (sys::fs::directory_iterator Dir(BundlePath, EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + const std::string &Path = Dir->path(); + sys::fs::file_status Status; + if (auto EC = sys::fs::status(Path, Status)) + return EC; + switch (Status.type()) { + case sys::fs::file_type::regular_file: + case sys::fs::file_type::symlink_file: + case sys::fs::file_type::type_unknown: + BundlePaths.push_back(Path); + break; + default: /*ignore*/; + } + } + if (EC) + return EC; + } + if (!BundlePaths.size()) + BundlePaths.push_back(InputPath); + return BundlePaths; +} + +SymReader::SymReader(ObjectFile *Obj, bool Demangle) { + SymbolFilePath = Obj->getFileName().str(); + + for (auto &Section : Obj->sections()) { + if (auto SectionName = Section.getName()) { + if (SectionName.get() == MIP_RAW_SECTION_NAME) { + MIPRawSectionBeginAddress = Section.getAddress(); + break; + } + } + } + + symbolize::LLVMSymbolizer::Options SymbolizerOpts; + SymbolizerOpts.PrintFunctions = + DILineInfoSpecifier::FunctionNameKind::LinkageName; + SymbolizerOpts.Demangle = Demangle; + SymbolizerOpts.DefaultArch = Arch; + SymbolizerOpts.UseSymbolTable = false; + SymbolizerOpts.RelativeAddresses = false; + Symbolizer = std::make_unique(SymbolizerOpts); +} + +Expected SymReader::getDIInliningInfo(int64_t MIPRawOffset) { + auto Addr = object::SectionedAddress{MIPRawSectionBeginAddress + MIPRawOffset, + object::SectionedAddress::UndefSection}; + return Symbolizer->symbolizeInlinedCode(SymbolFilePath, Addr); +} + +ErrorOr> SymReader::create(const Twine &InputPath, + bool Demangle) { + auto ObjsOrErr = expandBundle(InputPath.str()); + if (auto EC = ObjsOrErr.getError()) + return EC; + auto Objs = ObjsOrErr.get(); + if (Objs.size() != 1) { + WithColor::error() << "Cannot handle " << Objs.size() << " objects in " + << InputPath << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } + auto Filename = Objs[0]; + + auto BuffOrErr = MemoryBuffer::getFile(Filename); + if (auto EC = BuffOrErr.getError()) + return EC; + + auto Buffer = std::move(BuffOrErr.get()); + auto BinOrErr = createBinary(*Buffer); + if (auto EC = errorToErrorCode(BinOrErr.takeError())) + return EC; + + if (auto *Obj = dyn_cast(BinOrErr->get())) { + return std::make_unique(Obj, Demangle); + } else if (auto *Fat = dyn_cast(BinOrErr->get())) { + for (auto &ObjForArch : Fat->objects()) { + if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { + auto &MachO = **MachOOrErr; + if (filterArch(MachO)) { + // Consider object file that is matched only. + return std::make_unique(&MachO, Demangle); + } + } + } + } + + WithColor::error() << Filename << " has unhandled type\n"; + return std::make_error_code(std::errc::invalid_argument); +} diff --git a/llvm/tools/llvm-mipdata/llvm-mipdata.cpp b/llvm/tools/llvm-mipdata/llvm-mipdata.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-mipdata/llvm-mipdata.cpp @@ -0,0 +1,444 @@ +//===- llvm-mipdata.cpp - LLVM profile data tool --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymReader.h" +#include "llvm/CodeGen/MIPReader.h" +#include "llvm/CodeGen/MIPWriter.h" +#include "llvm/CodeGen/MIPYaml.h" +#include "llvm/MIP/MIP.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::MachineProfile; + +cl::SubCommand CreateSubCommand("create", + "Create an empty profile from a map file."); +cl::SubCommand MergeSubCommand("merge", + "Merge raw data into the specified profile."); +cl::SubCommand + OrderSubCommand("order", + "List functions in the optimal order for binary layout."); +cl::SubCommand ShowSubCommand("show", + "Show profile data in a human-readable format."); +cl::SubCommand CoveredSubCommand("covered", + "List functions that have been executed."); +cl::SubCommand MIP2YamlSubCommand("mip2yaml", + "Output the profile in YAML format."); +cl::SubCommand Yaml2MIPSubCommand("yaml2mip", + "Produce a profile from a YAML file."); +cl::SubCommand InfoSubCommand("info", + "Report statistics about the specified profile."); + +cl::opt + ProfileFilename("profile", cl::value_desc("profile"), cl::Required, + cl::sub(CreateSubCommand), cl::sub(MergeSubCommand), + cl::sub(OrderSubCommand), cl::sub(ShowSubCommand), + cl::sub(CoveredSubCommand), cl::sub(MIP2YamlSubCommand), + cl::sub(Yaml2MIPSubCommand), cl::sub(InfoSubCommand), + cl::desc("The profile (.mip) to use.")); +cl::alias ProfileFilenameA("p", cl::desc("Alias for --profile"), + cl::aliasopt(ProfileFilename)); + +cl::opt MapFilename(cl::Positional, cl::Required, + cl::sub(CreateSubCommand), + cl::desc("<.mipmap>")); + +cl::list RawFilenames(cl::Positional, cl::OneOrMore, + cl::sub(MergeSubCommand), + cl::desc("<.mipraw ...>")); + +cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::sub(ShowSubCommand), + cl::sub(CoveredSubCommand), + cl::sub(MIP2YamlSubCommand), + cl::sub(InfoSubCommand), + cl::desc("The output text file.")); +cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + +cl::opt + RegexOption("regex", cl::value_desc("regex"), cl::init(".*"), + cl::sub(ShowSubCommand), cl::sub(OrderSubCommand), + cl::sub(CoveredSubCommand), + cl::desc("Only process function names that match .")); +cl::alias RegexOptionA("r", cl::desc("Alias for --regex"), + cl::aliasopt(RegexOption)); + +cl::opt + StrictOption("strict", cl::sub(MergeSubCommand), + cl::desc("Enable strict mode. Fail on corrupt raw profiles.")); + +cl::opt DebugInfoFilename( + "debug", cl::value_desc("debug info"), cl::sub(ShowSubCommand), + cl::sub(CoveredSubCommand), + cl::desc("Use to include source info in the profile data.")); + +cl::opt YamlFilename(cl::Positional, cl::init("-"), + cl::sub(Yaml2MIPSubCommand), + cl::desc("<.yaml>")); + +cl::opt ShowLinesOption( + "lines", cl::sub(CoveredSubCommand), + cl::desc("List line numbers that are covered (requires --debug).")); +cl::opt ShowPathsOption( + "paths", cl::sub(CoveredSubCommand), + cl::desc("List paths that are covered (requires --debug).")); + +std::error_code createMain() { + auto MIPMapOrErr = MIPMapReader::read(MapFilename); + if (auto EC = MIPMapOrErr.getError()) + return EC; + + const auto &MIPMap = MIPMapOrErr.get(); + const auto &Header = MIPMap->Header; + + auto WriterOrErr = MIPWriter::create(ProfileFilename); + if (auto EC = WriterOrErr.getError()) + return EC; + + auto &Writer = WriterOrErr.get(); + Writer->write(MIPMap->Profiles, MIP_VERSION, Header.ProfileType, + Header.ModuleHash); + + WithColor::remark() << "Wrote empty profile to " << ProfileFilename << "\n"; + return std::error_code(); +} + +std::error_code mergeMain() { + auto MIPOrErr = MIPReader::read(ProfileFilename); + if (auto EC = MIPOrErr.getError()) + return EC; + + auto &MIP = MIPOrErr.get(); + const auto &Header = MIP->Header; + + unsigned MergedRawProfileCount = 0; + for (const auto &RawFilename : RawFilenames) { + const auto RawMIPOrErr = MIPRawReader::read(RawFilename, MIP); + if (auto EC = RawMIPOrErr.getError()) { + if (StrictOption) { + return EC; + } else { + continue; + } + } + + const auto &RawMIP = RawMIPOrErr.get(); + const auto &RawHeader = RawMIP->Header; + + std::map ProfileMap; + for (size_t i = 0; i < MIP->Profiles.size(); i++) { + ProfileMap[MIP->Profiles[i].RawProfileDataAddress] = i; + } + + for (auto &RawProfile : RawMIP->RawProfiles) { + auto &Profile = + MIP->Profiles[ProfileMap[RawProfile.RawProfileDataAddress]]; + + if (RawHeader.ProfileType & MIP_PROFILE_TYPE_FUNCTION_COVERAGE) { + if (RawProfile.IsFunctionCovered) { + Profile.RawProfileCount++; + Profile.FunctionCallCount = + std::max(Profile.FunctionCallCount, 1); + } + } + + if (RawHeader.ProfileType & MIP_PROFILE_TYPE_BLOCK_COVERAGE) { + assert(RawProfile.BasicBlockCoverage.size() == + Profile.BasicBlockProfiles.size()); + for (unsigned BlockID = 0; + BlockID < RawProfile.BasicBlockCoverage.size(); BlockID++) { + Profile.BasicBlockProfiles[BlockID].IsCovered = + RawProfile.BasicBlockCoverage[BlockID]; + } + } + } + MergedRawProfileCount++; + } + + auto WriterOrErr = MIPWriter::create(ProfileFilename); + if (auto EC = WriterOrErr.getError()) + return EC; + + auto &Writer = WriterOrErr.get(); + Writer->write(MIP->Profiles, MIP_VERSION, Header.ProfileType, + Header.ModuleHash); + + WithColor::remark() << "Merged " << MergedRawProfileCount + << " raw profiles into " << ProfileFilename << "\n"; + return std::error_code(); +} + +std::error_code showMain() { + std::error_code EC; + raw_fd_ostream OS(OutputFilename, EC); + if (EC) + return EC; + + const auto MIPOrErr = MIPReader::read(ProfileFilename); + if (auto EC = MIPOrErr.getError()) + return EC; + + std::unique_ptr SymReader; + if (!DebugInfoFilename.empty()) { + auto SymReaderOrErr = SymReader::create(DebugInfoFilename); + if (auto EC = SymReaderOrErr.getError()) + return EC; + SymReader = std::move(SymReaderOrErr.get()); + } + + const Regex RE(RegexOption); + for (const auto &Profile : MIPOrErr.get()->Profiles) { + if (!RE.match(Profile.FunctionName)) + continue; + + DILineInfo SourceInfo; + if (SymReader) { + auto InliningInfo = + SymReader->getDIInliningInfo(Profile.EncodedFunctionAddress); + if (InliningInfo && InliningInfo.get().getNumberOfFrames()) + SourceInfo = InliningInfo.get().getFrame(0); + } + + OS << Profile.FunctionName << "\n"; + if (SourceInfo) + OS << " Source Info: " << SourceInfo.FileName << ":" << SourceInfo.Line + << "\n"; + OS << " Call Count: " << Profile.FunctionCallCount << "\n"; + if (Profile.BasicBlockProfiles.size() > 1) { + OS << " Block Coverage:"; + for (unsigned I = 0; I < Profile.BasicBlockProfiles.size(); I++) { + const auto &BlockProfile = Profile.BasicBlockProfiles[I]; + if (I % 8 == 0) { + OS << "\n "; + } + if (BlockProfile.IsCovered) { + WithColor(OS, raw_ostream::RED) << " HOT "; + } else { + WithColor(OS, raw_ostream::CYAN) << " COLD"; + } + } + OS << "\n"; + } + OS << "\n"; + } + + return std::error_code(); +} + +std::error_code orderMain() { + std::error_code EC; + raw_fd_ostream OS(OutputFilename, EC); + if (EC) + return EC; + + const auto MIPOrErr = MIPReader::read(ProfileFilename); + if (auto EC = MIPOrErr.getError()) + return EC; + + std::vector OrderedProfiles; + MIPOrErr.get()->getOrderedProfiles(OrderedProfiles); + for (const auto &Profile : OrderedProfiles) { + OS << Profile.FunctionName << "\n"; + } + + WithColor::remark() << "Ordered " << OrderedProfiles.size() << " functions\n"; + return std::error_code(); +} + +std::error_code coveredMain() { + std::error_code EC; + raw_fd_ostream OS(OutputFilename, EC); + if (EC) + return EC; + + const auto MIPOrErr = MIPReader::read(ProfileFilename); + if (auto EC = MIPOrErr.getError()) + return EC; + + std::unique_ptr SymReader; + if (!DebugInfoFilename.empty()) { + auto SymReaderOrErr = SymReader::create(DebugInfoFilename); + if (auto EC = SymReaderOrErr.getError()) + return EC; + SymReader = std::move(SymReaderOrErr.get()); + } + + if ((ShowPathsOption || ShowLinesOption) && DebugInfoFilename.empty()) { + WithColor::error() << "--" << ShowPathsOption.ArgStr << " and --" + << ShowLinesOption.ArgStr << " require <" + << DebugInfoFilename.ValueStr << ">\n"; + return std::make_error_code(std::errc::invalid_argument); + } else if (ShowPathsOption && ShowLinesOption) { + WithColor::error() << "--" << ShowPathsOption.ArgStr << " and --" + << ShowLinesOption.ArgStr << " cannot both be set\n"; + return std::make_error_code(std::errc::invalid_argument); + } + + unsigned NumCoveredProfiles = 0; + const Regex RE(RegexOption); + for (const auto &Profile : MIPOrErr.get()->Profiles) { + if (Profile.RawProfileCount) { + if (RE.match(Profile.FunctionName)) { + if (SymReader) { + auto SourceInfoOrErr = + SymReader->getDIInliningInfo(Profile.EncodedFunctionAddress); + if (!SourceInfoOrErr || !SourceInfoOrErr.get().getNumberOfFrames()) { + WithColor::warning() + << "No debug info found for " << Profile.FunctionName << "\n"; + } else { + auto SourceInfo = SourceInfoOrErr.get(); + for (unsigned I = 0; I < SourceInfo.getNumberOfFrames(); I++) { + if (ShowPathsOption) { + OS << SourceInfo.getFrame(I).FileName << "\n"; + } else if (ShowLinesOption) { + OS << SourceInfo.getFrame(I).FileName << ":" + << SourceInfo.getFrame(I).Line << "\n"; + } else { + OS << SourceInfo.getFrame(I).FunctionName << "\n"; + } + } + } + } else { // No SymReader + OS << Profile.FunctionName << "\n"; + } + } + NumCoveredProfiles++; + } + } + + WithColor::remark() << NumCoveredProfiles << " total functions are covered\n"; + return std::error_code(); +} + +std::error_code mip2yamlMain() { + std::error_code EC; + raw_fd_ostream OS(OutputFilename, EC); + if (EC) + return EC; + + auto MIPOrErr = MIPReader::read(ProfileFilename); + if (auto EC = MIPOrErr.getError()) + return EC; + + auto MIP = std::move(MIPOrErr.get()); + + yaml::Output yamlOS(OS); + yamlOS << MIP; + + return std::error_code(); +} + +std::error_code yaml2mipMain() { + auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(YamlFilename); + if (auto EC = BufferOrErr.getError()) { + WithColor::error() << "Unable to open " << YamlFilename << "\n"; + return std::make_error_code(std::errc::invalid_argument); + } + auto &Buffer = BufferOrErr.get(); + + yaml::Input yamlOS(Buffer->getMemBufferRef()); + std::unique_ptr MIP(new MIRProfile()); + yamlOS >> MIP; + + if (yamlOS.error()) { + WithColor::error() << "Unable to read " << YamlFilename + << " as a profile\n"; + return std::make_error_code(std::errc::invalid_argument); + } + + auto WriterOrErr = MIPWriter::create(ProfileFilename); + if (auto EC = WriterOrErr.getError()) + return EC; + + auto &Writer = WriterOrErr.get(); + Writer->write(MIP->Profiles, MIP_VERSION, MIP->Header.ProfileType, + MIP->Header.ModuleHash); + + WithColor::remark() << "Wrote empty profile to " << ProfileFilename << "\n"; + return std::error_code(); +} + +std::error_code infoMain() { + std::error_code EC; + raw_fd_ostream OS(OutputFilename, EC); + if (EC) + return EC; + + auto MIPOrErr = MIPReader::read(ProfileFilename); + if (auto EC = MIPOrErr.getError()) + return EC; + + auto MIP = std::move(MIPOrErr.get()); + + unsigned TotalFunctions = MIP->Profiles.size(); + unsigned ProfiledFunctions = std::count_if( + MIP->Profiles.begin(), MIP->Profiles.end(), + [](const auto &Profile) { return Profile.RawProfileCount > 0; }); + unsigned TotalRawProfiles = 0, TotalBlocks = 0, CoveredBlocks = 0, + TotalCallEdges = 0; + for (const auto &Profile : MIP->Profiles) { + TotalRawProfiles = std::max(Profile.RawProfileCount, TotalRawProfiles); + TotalBlocks += Profile.BasicBlockProfiles.size(); + CoveredBlocks += std::count_if( + Profile.BasicBlockProfiles.begin(), Profile.BasicBlockProfiles.end(), + [](const auto &BlockProfile) { return BlockProfile.IsCovered; }); + TotalCallEdges += Profile.CallEdges.size(); + } + + OS << "Total Raw Profile Count: " << TotalRawProfiles << "\n"; + OS << "Total Machine Functions: " << TotalFunctions << "\n"; + OS << "Profiled Machine Functions: " << ProfiledFunctions << "\n"; + OS << "Total Machine Basic Blocks: " << TotalBlocks << "\n"; + OS << "Covered Machine Basic Blocks: " << CoveredBlocks << "\n"; + OS << "Total Call Edges: " << TotalCallEdges << "\n"; + + return std::error_code(); +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + cl::SetVersionPrinter( + [](raw_ostream &OS) { OS << "MIP Version " << MIP_VERSION << "\n"; }); + cl::ParseCommandLineOptions(argc, argv, + "A tool to create, populate, and read machine " + "instrumentation profiles (MIP)."); + + std::error_code EC; + if (CreateSubCommand) { + EC = createMain(); + } else if (MergeSubCommand) { + EC = mergeMain(); + } else if (ShowSubCommand) { + EC = showMain(); + } else if (OrderSubCommand) { + EC = orderMain(); + } else if (CoveredSubCommand) { + EC = coveredMain(); + } else if (MIP2YamlSubCommand) { + EC = mip2yamlMain(); + } else if (Yaml2MIPSubCommand) { + EC = yaml2mipMain(); + } else if (InfoSubCommand) { + EC = infoMain(); + } else { + cl::PrintHelpMessage(); + } + + if (EC) { + WithColor::error() << EC.message() << "\n"; + return 1; + } + + return 0; +}