diff options
Diffstat (limited to 'lib/Remarks/BitstreamRemarkParser.cpp')
-rw-r--r-- | lib/Remarks/BitstreamRemarkParser.cpp | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/lib/Remarks/BitstreamRemarkParser.cpp b/lib/Remarks/BitstreamRemarkParser.cpp new file mode 100644 index 000000000000..99a82e1ee3af --- /dev/null +++ b/lib/Remarks/BitstreamRemarkParser.cpp @@ -0,0 +1,597 @@ +//===- BitstreamRemarkParser.cpp ------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides utility methods used by clients that want to use the +// parser for remark diagnostics in LLVM. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Remarks/BitstreamRemarkParser.h" +#include "BitstreamRemarkParser.h" +#include "llvm/Remarks/BitstreamRemarkContainer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::remarks; + +static Error unknownRecord(const char *BlockName, unsigned RecordID) { + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing %s: unknown record entry (%lu).", BlockName, + RecordID); +} + +static Error malformedRecord(const char *BlockName, const char *RecordName) { + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing %s: malformed record entry (%s).", BlockName, + RecordName); +} + +BitstreamMetaParserHelper::BitstreamMetaParserHelper( + BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo) + : Stream(Stream), BlockInfo(BlockInfo) {} + +/// Parse a record and fill in the fields in the parser. +static Error parseRecord(BitstreamMetaParserHelper &Parser, unsigned Code) { + BitstreamCursor &Stream = Parser.Stream; + // Note: 2 is used here because it's the max number of fields we have per + // record. + SmallVector<uint64_t, 2> Record; + StringRef Blob; + Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob); + if (!RecordID) + return RecordID.takeError(); + + switch (*RecordID) { + case RECORD_META_CONTAINER_INFO: { + if (Record.size() != 2) + return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO"); + Parser.ContainerVersion = Record[0]; + Parser.ContainerType = Record[1]; + break; + } + case RECORD_META_REMARK_VERSION: { + if (Record.size() != 1) + return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION"); + Parser.RemarkVersion = Record[0]; + break; + } + case RECORD_META_STRTAB: { + if (Record.size() != 0) + return malformedRecord("BLOCK_META", "RECORD_META_STRTAB"); + Parser.StrTabBuf = Blob; + break; + } + case RECORD_META_EXTERNAL_FILE: { + if (Record.size() != 0) + return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE"); + Parser.ExternalFilePath = Blob; + break; + } + default: + return unknownRecord("BLOCK_META", *RecordID); + } + return Error::success(); +} + +BitstreamRemarkParserHelper::BitstreamRemarkParserHelper( + BitstreamCursor &Stream) + : Stream(Stream) {} + +/// Parse a record and fill in the fields in the parser. +static Error parseRecord(BitstreamRemarkParserHelper &Parser, unsigned Code) { + BitstreamCursor &Stream = Parser.Stream; + // Note: 5 is used here because it's the max number of fields we have per + // record. + SmallVector<uint64_t, 5> Record; + StringRef Blob; + Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob); + if (!RecordID) + return RecordID.takeError(); + + switch (*RecordID) { + case RECORD_REMARK_HEADER: { + if (Record.size() != 4) + return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER"); + Parser.Type = Record[0]; + Parser.RemarkNameIdx = Record[1]; + Parser.PassNameIdx = Record[2]; + Parser.FunctionNameIdx = Record[3]; + break; + } + case RECORD_REMARK_DEBUG_LOC: { + if (Record.size() != 3) + return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC"); + Parser.SourceFileNameIdx = Record[0]; + Parser.SourceLine = Record[1]; + Parser.SourceColumn = Record[2]; + break; + } + case RECORD_REMARK_HOTNESS: { + if (Record.size() != 1) + return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS"); + Parser.Hotness = Record[0]; + break; + } + case RECORD_REMARK_ARG_WITH_DEBUGLOC: { + if (Record.size() != 5) + return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC"); + // Create a temporary argument. Use that as a valid memory location for this + // argument entry. + Parser.TmpArgs.emplace_back(); + Parser.TmpArgs.back().KeyIdx = Record[0]; + Parser.TmpArgs.back().ValueIdx = Record[1]; + Parser.TmpArgs.back().SourceFileNameIdx = Record[2]; + Parser.TmpArgs.back().SourceLine = Record[3]; + Parser.TmpArgs.back().SourceColumn = Record[4]; + Parser.Args = + ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs); + break; + } + case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC: { + if (Record.size() != 2) + return malformedRecord("BLOCK_REMARK", + "RECORD_REMARK_ARG_WITHOUT_DEBUGLOC"); + // Create a temporary argument. Use that as a valid memory location for this + // argument entry. + Parser.TmpArgs.emplace_back(); + Parser.TmpArgs.back().KeyIdx = Record[0]; + Parser.TmpArgs.back().ValueIdx = Record[1]; + Parser.Args = + ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs); + break; + } + default: + return unknownRecord("BLOCK_REMARK", *RecordID); + } + return Error::success(); +} + +template <typename T> +static Error parseBlock(T &ParserHelper, unsigned BlockID, + const char *BlockName) { + BitstreamCursor &Stream = ParserHelper.Stream; + Expected<BitstreamEntry> Next = Stream.advance(); + if (!Next) + return Next.takeError(); + if (Next->Kind != BitstreamEntry::SubBlock || Next->ID != BlockID) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].", + BlockName, BlockName); + if (Stream.EnterSubBlock(BlockID)) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while entering %s.", BlockName); + + // Stop when there is nothing to read anymore or when we encounter an + // END_BLOCK. + while (!Stream.AtEndOfStream()) { + Expected<BitstreamEntry> Next = Stream.advance(); + if (!Next) + return Next.takeError(); + switch (Next->Kind) { + case BitstreamEntry::EndBlock: + return Error::success(); + case BitstreamEntry::Error: + case BitstreamEntry::SubBlock: + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing %s: expecting records.", BlockName); + case BitstreamEntry::Record: + if (Error E = parseRecord(ParserHelper, Next->ID)) + return E; + continue; + } + } + // If we're here, it means we didn't get an END_BLOCK yet, but we're at the + // end of the stream. In this case, error. + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing %s: unterminated block.", BlockName); +} + +Error BitstreamMetaParserHelper::parse() { + return parseBlock(*this, META_BLOCK_ID, "META_BLOCK"); +} + +Error BitstreamRemarkParserHelper::parse() { + return parseBlock(*this, REMARK_BLOCK_ID, "REMARK_BLOCK"); +} + +BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer) + : Stream(Buffer) {} + +Expected<std::array<char, 4>> BitstreamParserHelper::parseMagic() { + std::array<char, 4> Result; + for (unsigned i = 0; i < 4; ++i) + if (Expected<unsigned> R = Stream.Read(8)) + Result[i] = *R; + else + return R.takeError(); + return Result; +} + +Error BitstreamParserHelper::parseBlockInfoBlock() { + Expected<BitstreamEntry> Next = Stream.advance(); + if (!Next) + return Next.takeError(); + if (Next->Kind != BitstreamEntry::SubBlock || + Next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, " + "BLOCKINFO_BLOCK, ...]."); + + Expected<Optional<BitstreamBlockInfo>> MaybeBlockInfo = + Stream.ReadBlockInfoBlock(); + if (!MaybeBlockInfo) + return MaybeBlockInfo.takeError(); + + if (!*MaybeBlockInfo) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCKINFO_BLOCK."); + + BlockInfo = **MaybeBlockInfo; + + Stream.setBlockInfo(&BlockInfo); + return Error::success(); +} + +static Expected<bool> isBlock(BitstreamCursor &Stream, unsigned BlockID) { + bool Result = false; + uint64_t PreviousBitNo = Stream.GetCurrentBitNo(); + Expected<BitstreamEntry> Next = Stream.advance(); + if (!Next) + return Next.takeError(); + switch (Next->Kind) { + case BitstreamEntry::SubBlock: + // Check for the block id. + Result = Next->ID == BlockID; + break; + case BitstreamEntry::Error: + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Unexpected error while parsing bitstream."); + default: + Result = false; + break; + } + if (Error E = Stream.JumpToBit(PreviousBitNo)) + return std::move(E); + return Result; +} + +Expected<bool> BitstreamParserHelper::isMetaBlock() { + return isBlock(Stream, META_BLOCK_ID); +} + +Expected<bool> BitstreamParserHelper::isRemarkBlock() { + return isBlock(Stream, META_BLOCK_ID); +} + +static Error validateMagicNumber(StringRef Magic) { + if (Magic != remarks::ContainerMagic) + return createStringError(std::make_error_code(std::errc::invalid_argument), + "Unknown magic number: expecting %s, got %.4s.", + remarks::ContainerMagic.data(), Magic.data()); + return Error::success(); +} + +static Error advanceToMetaBlock(BitstreamParserHelper &Helper) { + Expected<std::array<char, 4>> Magic = Helper.parseMagic(); + if (!Magic) + return Magic.takeError(); + if (Error E = validateMagicNumber(StringRef(Magic->data(), Magic->size()))) + return E; + if (Error E = Helper.parseBlockInfoBlock()) + return E; + Expected<bool> isMetaBlock = Helper.isMetaBlock(); + if (!isMetaBlock) + return isMetaBlock.takeError(); + if (!*isMetaBlock) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Expecting META_BLOCK after the BLOCKINFO_BLOCK."); + return Error::success(); +} + +Expected<std::unique_ptr<BitstreamRemarkParser>> +remarks::createBitstreamParserFromMeta( + StringRef Buf, Optional<ParsedStringTable> StrTab, + Optional<StringRef> ExternalFilePrependPath) { + BitstreamParserHelper Helper(Buf); + Expected<std::array<char, 4>> Magic = Helper.parseMagic(); + if (!Magic) + return Magic.takeError(); + + if (Error E = validateMagicNumber(StringRef(Magic->data(), Magic->size()))) + return std::move(E); + + auto Parser = + StrTab ? std::make_unique<BitstreamRemarkParser>(Buf, std::move(*StrTab)) + : std::make_unique<BitstreamRemarkParser>(Buf); + + if (ExternalFilePrependPath) + Parser->ExternalFilePrependPath = *ExternalFilePrependPath; + + return std::move(Parser); +} + +Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() { + if (ParserHelper.atEndOfStream()) + return make_error<EndOfFileError>(); + + if (!ReadyToParseRemarks) { + if (Error E = parseMeta()) + return std::move(E); + ReadyToParseRemarks = true; + } + + return parseRemark(); +} + +Error BitstreamRemarkParser::parseMeta() { + // Advance and to the meta block. + if (Error E = advanceToMetaBlock(ParserHelper)) + return E; + + BitstreamMetaParserHelper MetaHelper(ParserHelper.Stream, + ParserHelper.BlockInfo); + if (Error E = MetaHelper.parse()) + return E; + + if (Error E = processCommonMeta(MetaHelper)) + return E; + + switch (ContainerType) { + case BitstreamRemarkContainerType::Standalone: + return processStandaloneMeta(MetaHelper); + case BitstreamRemarkContainerType::SeparateRemarksFile: + return processSeparateRemarksFileMeta(MetaHelper); + case BitstreamRemarkContainerType::SeparateRemarksMeta: + return processSeparateRemarksMetaMeta(MetaHelper); + } + llvm_unreachable("Unknown BitstreamRemarkContainerType enum"); +} + +Error BitstreamRemarkParser::processCommonMeta( + BitstreamMetaParserHelper &MetaHelper) { + if (Optional<uint64_t> Version = MetaHelper.ContainerVersion) + ContainerVersion = *Version; + else + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_META: missing container version."); + + if (Optional<uint8_t> Type = MetaHelper.ContainerType) { + // Always >= BitstreamRemarkContainerType::First since it's unsigned. + if (*Type > static_cast<uint8_t>(BitstreamRemarkContainerType::Last)) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_META: invalid container type."); + + ContainerType = static_cast<BitstreamRemarkContainerType>(*Type); + } else + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_META: missing container type."); + + return Error::success(); +} + +static Error processStrTab(BitstreamRemarkParser &P, + Optional<StringRef> StrTabBuf) { + if (!StrTabBuf) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_META: missing string table."); + // Parse and assign the string table. + P.StrTab.emplace(*StrTabBuf); + return Error::success(); +} + +static Error processRemarkVersion(BitstreamRemarkParser &P, + Optional<uint64_t> RemarkVersion) { + if (!RemarkVersion) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_META: missing remark version."); + P.RemarkVersion = *RemarkVersion; + return Error::success(); +} + +Error BitstreamRemarkParser::processExternalFilePath( + Optional<StringRef> ExternalFilePath) { + if (!ExternalFilePath) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_META: missing external file path."); + + SmallString<80> FullPath(ExternalFilePrependPath); + sys::path::append(FullPath, *ExternalFilePath); + + // External file: open the external file, parse it, check if its metadata + // matches the one from the separate metadata, then replace the current parser + // with the one parsing the remarks. + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFile(FullPath); + if (std::error_code EC = BufferOrErr.getError()) + return createFileError(FullPath, EC); + TmpRemarkBuffer = std::move(*BufferOrErr); + + // Create a separate parser used for parsing the separate file. + ParserHelper = BitstreamParserHelper(TmpRemarkBuffer->getBuffer()); + // Advance and check until we can parse the meta block. + if (Error E = advanceToMetaBlock(ParserHelper)) + return E; + // Parse the meta from the separate file. + // Note: here we overwrite the BlockInfo with the one from the file. This will + // be used to parse the rest of the file. + BitstreamMetaParserHelper SeparateMetaHelper(ParserHelper.Stream, + ParserHelper.BlockInfo); + if (Error E = SeparateMetaHelper.parse()) + return E; + + uint64_t PreviousContainerVersion = ContainerVersion; + if (Error E = processCommonMeta(SeparateMetaHelper)) + return E; + + if (ContainerType != BitstreamRemarkContainerType::SeparateRemarksFile) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing external file's BLOCK_META: wrong container " + "type."); + + if (PreviousContainerVersion != ContainerVersion) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing external file's BLOCK_META: mismatching versions: " + "original meta: %lu, external file meta: %lu.", + PreviousContainerVersion, ContainerVersion); + + // Process the meta from the separate file. + return processSeparateRemarksFileMeta(SeparateMetaHelper); +} + +Error BitstreamRemarkParser::processStandaloneMeta( + BitstreamMetaParserHelper &Helper) { + if (Error E = processStrTab(*this, Helper.StrTabBuf)) + return E; + return processRemarkVersion(*this, Helper.RemarkVersion); +} + +Error BitstreamRemarkParser::processSeparateRemarksFileMeta( + BitstreamMetaParserHelper &Helper) { + return processRemarkVersion(*this, Helper.RemarkVersion); +} + +Error BitstreamRemarkParser::processSeparateRemarksMetaMeta( + BitstreamMetaParserHelper &Helper) { + if (Error E = processStrTab(*this, Helper.StrTabBuf)) + return E; + return processExternalFilePath(Helper.ExternalFilePath); +} + +Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::parseRemark() { + BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream); + if (Error E = RemarkHelper.parse()) + return std::move(E); + + return processRemark(RemarkHelper); +} + +Expected<std::unique_ptr<Remark>> +BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) { + std::unique_ptr<Remark> Result = std::make_unique<Remark>(); + Remark &R = *Result; + + if (StrTab == None) + return createStringError( + std::make_error_code(std::errc::invalid_argument), + "Error while parsing BLOCK_REMARK: missing string table."); + + if (!Helper.Type) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: missing remark type."); + + // Always >= Type::First since it's unsigned. + if (*Helper.Type > static_cast<uint8_t>(Type::Last)) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: unknown remark type."); + + R.RemarkType = static_cast<Type>(*Helper.Type); + + if (!Helper.RemarkNameIdx) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: missing remark name."); + + if (Expected<StringRef> RemarkName = (*StrTab)[*Helper.RemarkNameIdx]) + R.RemarkName = *RemarkName; + else + return RemarkName.takeError(); + + if (!Helper.PassNameIdx) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: missing remark pass."); + + if (Expected<StringRef> PassName = (*StrTab)[*Helper.PassNameIdx]) + R.PassName = *PassName; + else + return PassName.takeError(); + + if (!Helper.FunctionNameIdx) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: missing remark function name."); + if (Expected<StringRef> FunctionName = (*StrTab)[*Helper.FunctionNameIdx]) + R.FunctionName = *FunctionName; + else + return FunctionName.takeError(); + + if (Helper.SourceFileNameIdx && Helper.SourceLine && Helper.SourceColumn) { + Expected<StringRef> SourceFileName = (*StrTab)[*Helper.SourceFileNameIdx]; + if (!SourceFileName) + return SourceFileName.takeError(); + R.Loc.emplace(); + R.Loc->SourceFilePath = *SourceFileName; + R.Loc->SourceLine = *Helper.SourceLine; + R.Loc->SourceColumn = *Helper.SourceColumn; + } + + if (Helper.Hotness) + R.Hotness = *Helper.Hotness; + + if (!Helper.Args) + return std::move(Result); + + for (const BitstreamRemarkParserHelper::Argument &Arg : *Helper.Args) { + if (!Arg.KeyIdx) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: missing key in remark argument."); + if (!Arg.ValueIdx) + return createStringError( + std::make_error_code(std::errc::illegal_byte_sequence), + "Error while parsing BLOCK_REMARK: missing value in remark " + "argument."); + + // We have at least a key and a value, create an entry. + R.Args.emplace_back(); + + if (Expected<StringRef> Key = (*StrTab)[*Arg.KeyIdx]) + R.Args.back().Key = *Key; + else + return Key.takeError(); + + if (Expected<StringRef> Value = (*StrTab)[*Arg.ValueIdx]) + R.Args.back().Val = *Value; + else + return Value.takeError(); + + if (Arg.SourceFileNameIdx && Arg.SourceLine && Arg.SourceColumn) { + if (Expected<StringRef> SourceFileName = + (*StrTab)[*Arg.SourceFileNameIdx]) { + R.Args.back().Loc.emplace(); + R.Args.back().Loc->SourceFilePath = *SourceFileName; + R.Args.back().Loc->SourceLine = *Arg.SourceLine; + R.Args.back().Loc->SourceColumn = *Arg.SourceColumn; + } else + return SourceFileName.takeError(); + } + } + + return std::move(Result); +} |