//===- 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/Remark.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include 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 Record; StringRef Blob; Expected 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 Record; StringRef Blob; Expected 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(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(Parser.TmpArgs); break; } default: return unknownRecord("BLOCK_REMARK", *RecordID); } return Error::success(); } template static Error parseBlock(T &ParserHelper, unsigned BlockID, const char *BlockName) { BitstreamCursor &Stream = ParserHelper.Stream; Expected 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()) { 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> BitstreamParserHelper::parseMagic() { std::array Result; for (unsigned i = 0; i < 4; ++i) if (Expected R = Stream.Read(8)) Result[i] = *R; else return R.takeError(); return Result; } Error BitstreamParserHelper::parseBlockInfoBlock() { Expected 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> 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 isBlock(BitstreamCursor &Stream, unsigned BlockID) { bool Result = false; uint64_t PreviousBitNo = Stream.GetCurrentBitNo(); Expected 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 BitstreamParserHelper::isMetaBlock() { return isBlock(Stream, META_BLOCK_ID); } Expected BitstreamParserHelper::isRemarkBlock() { return isBlock(Stream, META_BLOCK_ID); } static Error validateMagicNumber(StringRef MagicNumber) { if (MagicNumber != remarks::ContainerMagic) return createStringError(std::make_error_code(std::errc::invalid_argument), "Unknown magic number: expecting %s, got %.4s.", remarks::ContainerMagic.data(), MagicNumber.data()); return Error::success(); } static Error advanceToMetaBlock(BitstreamParserHelper &Helper) { Expected> MagicNumber = Helper.parseMagic(); if (!MagicNumber) return MagicNumber.takeError(); if (Error E = validateMagicNumber( StringRef(MagicNumber->data(), MagicNumber->size()))) return E; if (Error E = Helper.parseBlockInfoBlock()) return E; Expected 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> remarks::createBitstreamParserFromMeta( StringRef Buf, std::optional StrTab, std::optional ExternalFilePrependPath) { BitstreamParserHelper Helper(Buf); Expected> MagicNumber = Helper.parseMagic(); if (!MagicNumber) return MagicNumber.takeError(); if (Error E = validateMagicNumber( StringRef(MagicNumber->data(), MagicNumber->size()))) return std::move(E); auto Parser = StrTab ? std::make_unique(Buf, std::move(*StrTab)) : std::make_unique(Buf); if (ExternalFilePrependPath) Parser->ExternalFilePrependPath = std::string(*ExternalFilePrependPath); return std::move(Parser); } Expected> BitstreamRemarkParser::next() { if (ParserHelper.atEndOfStream()) return make_error(); 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 &Helper) { if (std::optional Version = Helper.ContainerVersion) ContainerVersion = *Version; else return createStringError( std::make_error_code(std::errc::illegal_byte_sequence), "Error while parsing BLOCK_META: missing container version."); if (std::optional Type = Helper.ContainerType) { // Always >= BitstreamRemarkContainerType::First since it's unsigned. if (*Type > static_cast(BitstreamRemarkContainerType::Last)) return createStringError( std::make_error_code(std::errc::illegal_byte_sequence), "Error while parsing BLOCK_META: invalid container type."); ContainerType = static_cast(*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, std::optional 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, std::optional 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( std::optional 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> BufferOrErr = MemoryBuffer::getFile(FullPath); if (std::error_code EC = BufferOrErr.getError()) return createFileError(FullPath, EC); TmpRemarkBuffer = std::move(*BufferOrErr); // Don't try to parse the file if it's empty. if (TmpRemarkBuffer->getBufferSize() == 0) return make_error(); // 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> BitstreamRemarkParser::parseRemark() { BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream); if (Error E = RemarkHelper.parse()) return std::move(E); return processRemark(RemarkHelper); } Expected> BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) { std::unique_ptr Result = std::make_unique(); Remark &R = *Result; if (StrTab == std::nullopt) 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(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(*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 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 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 FunctionName = (*StrTab)[*Helper.FunctionNameIdx]) R.FunctionName = *FunctionName; else return FunctionName.takeError(); if (Helper.SourceFileNameIdx && Helper.SourceLine && Helper.SourceColumn) { Expected 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 Key = (*StrTab)[*Arg.KeyIdx]) R.Args.back().Key = *Key; else return Key.takeError(); if (Expected Value = (*StrTab)[*Arg.ValueIdx]) R.Args.back().Val = *Value; else return Value.takeError(); if (Arg.SourceFileNameIdx && Arg.SourceLine && Arg.SourceColumn) { if (Expected 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); }