Path: blob/main/contrib/llvm-project/llvm/lib/Remarks/BitstreamRemarkParser.cpp
35262 views
//===- BitstreamRemarkParser.cpp ------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file provides utility methods used by clients that want to use the9// parser for remark diagnostics in LLVM.10//11//===----------------------------------------------------------------------===//1213#include "llvm/Remarks/BitstreamRemarkParser.h"14#include "BitstreamRemarkParser.h"15#include "llvm/Remarks/Remark.h"16#include "llvm/Support/MemoryBuffer.h"17#include "llvm/Support/Path.h"18#include <optional>1920using namespace llvm;21using namespace llvm::remarks;2223static Error unknownRecord(const char *BlockName, unsigned RecordID) {24return createStringError(25std::make_error_code(std::errc::illegal_byte_sequence),26"Error while parsing %s: unknown record entry (%lu).", BlockName,27RecordID);28}2930static Error malformedRecord(const char *BlockName, const char *RecordName) {31return createStringError(32std::make_error_code(std::errc::illegal_byte_sequence),33"Error while parsing %s: malformed record entry (%s).", BlockName,34RecordName);35}3637BitstreamMetaParserHelper::BitstreamMetaParserHelper(38BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo)39: Stream(Stream), BlockInfo(BlockInfo) {}4041/// Parse a record and fill in the fields in the parser.42static Error parseRecord(BitstreamMetaParserHelper &Parser, unsigned Code) {43BitstreamCursor &Stream = Parser.Stream;44// Note: 2 is used here because it's the max number of fields we have per45// record.46SmallVector<uint64_t, 2> Record;47StringRef Blob;48Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob);49if (!RecordID)50return RecordID.takeError();5152switch (*RecordID) {53case RECORD_META_CONTAINER_INFO: {54if (Record.size() != 2)55return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO");56Parser.ContainerVersion = Record[0];57Parser.ContainerType = Record[1];58break;59}60case RECORD_META_REMARK_VERSION: {61if (Record.size() != 1)62return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION");63Parser.RemarkVersion = Record[0];64break;65}66case RECORD_META_STRTAB: {67if (Record.size() != 0)68return malformedRecord("BLOCK_META", "RECORD_META_STRTAB");69Parser.StrTabBuf = Blob;70break;71}72case RECORD_META_EXTERNAL_FILE: {73if (Record.size() != 0)74return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE");75Parser.ExternalFilePath = Blob;76break;77}78default:79return unknownRecord("BLOCK_META", *RecordID);80}81return Error::success();82}8384BitstreamRemarkParserHelper::BitstreamRemarkParserHelper(85BitstreamCursor &Stream)86: Stream(Stream) {}8788/// Parse a record and fill in the fields in the parser.89static Error parseRecord(BitstreamRemarkParserHelper &Parser, unsigned Code) {90BitstreamCursor &Stream = Parser.Stream;91// Note: 5 is used here because it's the max number of fields we have per92// record.93SmallVector<uint64_t, 5> Record;94StringRef Blob;95Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob);96if (!RecordID)97return RecordID.takeError();9899switch (*RecordID) {100case RECORD_REMARK_HEADER: {101if (Record.size() != 4)102return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER");103Parser.Type = Record[0];104Parser.RemarkNameIdx = Record[1];105Parser.PassNameIdx = Record[2];106Parser.FunctionNameIdx = Record[3];107break;108}109case RECORD_REMARK_DEBUG_LOC: {110if (Record.size() != 3)111return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC");112Parser.SourceFileNameIdx = Record[0];113Parser.SourceLine = Record[1];114Parser.SourceColumn = Record[2];115break;116}117case RECORD_REMARK_HOTNESS: {118if (Record.size() != 1)119return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS");120Parser.Hotness = Record[0];121break;122}123case RECORD_REMARK_ARG_WITH_DEBUGLOC: {124if (Record.size() != 5)125return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC");126// Create a temporary argument. Use that as a valid memory location for this127// argument entry.128Parser.TmpArgs.emplace_back();129Parser.TmpArgs.back().KeyIdx = Record[0];130Parser.TmpArgs.back().ValueIdx = Record[1];131Parser.TmpArgs.back().SourceFileNameIdx = Record[2];132Parser.TmpArgs.back().SourceLine = Record[3];133Parser.TmpArgs.back().SourceColumn = Record[4];134Parser.Args =135ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs);136break;137}138case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC: {139if (Record.size() != 2)140return malformedRecord("BLOCK_REMARK",141"RECORD_REMARK_ARG_WITHOUT_DEBUGLOC");142// Create a temporary argument. Use that as a valid memory location for this143// argument entry.144Parser.TmpArgs.emplace_back();145Parser.TmpArgs.back().KeyIdx = Record[0];146Parser.TmpArgs.back().ValueIdx = Record[1];147Parser.Args =148ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs);149break;150}151default:152return unknownRecord("BLOCK_REMARK", *RecordID);153}154return Error::success();155}156157template <typename T>158static Error parseBlock(T &ParserHelper, unsigned BlockID,159const char *BlockName) {160BitstreamCursor &Stream = ParserHelper.Stream;161Expected<BitstreamEntry> Next = Stream.advance();162if (!Next)163return Next.takeError();164if (Next->Kind != BitstreamEntry::SubBlock || Next->ID != BlockID)165return createStringError(166std::make_error_code(std::errc::illegal_byte_sequence),167"Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].",168BlockName, BlockName);169if (Stream.EnterSubBlock(BlockID))170return createStringError(171std::make_error_code(std::errc::illegal_byte_sequence),172"Error while entering %s.", BlockName);173174// Stop when there is nothing to read anymore or when we encounter an175// END_BLOCK.176while (!Stream.AtEndOfStream()) {177Next = Stream.advance();178if (!Next)179return Next.takeError();180switch (Next->Kind) {181case BitstreamEntry::EndBlock:182return Error::success();183case BitstreamEntry::Error:184case BitstreamEntry::SubBlock:185return createStringError(186std::make_error_code(std::errc::illegal_byte_sequence),187"Error while parsing %s: expecting records.", BlockName);188case BitstreamEntry::Record:189if (Error E = parseRecord(ParserHelper, Next->ID))190return E;191continue;192}193}194// If we're here, it means we didn't get an END_BLOCK yet, but we're at the195// end of the stream. In this case, error.196return createStringError(197std::make_error_code(std::errc::illegal_byte_sequence),198"Error while parsing %s: unterminated block.", BlockName);199}200201Error BitstreamMetaParserHelper::parse() {202return parseBlock(*this, META_BLOCK_ID, "META_BLOCK");203}204205Error BitstreamRemarkParserHelper::parse() {206return parseBlock(*this, REMARK_BLOCK_ID, "REMARK_BLOCK");207}208209BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer)210: Stream(Buffer) {}211212Expected<std::array<char, 4>> BitstreamParserHelper::parseMagic() {213std::array<char, 4> Result;214for (unsigned i = 0; i < 4; ++i)215if (Expected<unsigned> R = Stream.Read(8))216Result[i] = *R;217else218return R.takeError();219return Result;220}221222Error BitstreamParserHelper::parseBlockInfoBlock() {223Expected<BitstreamEntry> Next = Stream.advance();224if (!Next)225return Next.takeError();226if (Next->Kind != BitstreamEntry::SubBlock ||227Next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID)228return createStringError(229std::make_error_code(std::errc::illegal_byte_sequence),230"Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, "231"BLOCKINFO_BLOCK, ...].");232233Expected<std::optional<BitstreamBlockInfo>> MaybeBlockInfo =234Stream.ReadBlockInfoBlock();235if (!MaybeBlockInfo)236return MaybeBlockInfo.takeError();237238if (!*MaybeBlockInfo)239return createStringError(240std::make_error_code(std::errc::illegal_byte_sequence),241"Error while parsing BLOCKINFO_BLOCK.");242243BlockInfo = **MaybeBlockInfo;244245Stream.setBlockInfo(&BlockInfo);246return Error::success();247}248249static Expected<bool> isBlock(BitstreamCursor &Stream, unsigned BlockID) {250bool Result = false;251uint64_t PreviousBitNo = Stream.GetCurrentBitNo();252Expected<BitstreamEntry> Next = Stream.advance();253if (!Next)254return Next.takeError();255switch (Next->Kind) {256case BitstreamEntry::SubBlock:257// Check for the block id.258Result = Next->ID == BlockID;259break;260case BitstreamEntry::Error:261return createStringError(262std::make_error_code(std::errc::illegal_byte_sequence),263"Unexpected error while parsing bitstream.");264default:265Result = false;266break;267}268if (Error E = Stream.JumpToBit(PreviousBitNo))269return std::move(E);270return Result;271}272273Expected<bool> BitstreamParserHelper::isMetaBlock() {274return isBlock(Stream, META_BLOCK_ID);275}276277Expected<bool> BitstreamParserHelper::isRemarkBlock() {278return isBlock(Stream, META_BLOCK_ID);279}280281static Error validateMagicNumber(StringRef MagicNumber) {282if (MagicNumber != remarks::ContainerMagic)283return createStringError(std::make_error_code(std::errc::invalid_argument),284"Unknown magic number: expecting %s, got %.4s.",285remarks::ContainerMagic.data(), MagicNumber.data());286return Error::success();287}288289static Error advanceToMetaBlock(BitstreamParserHelper &Helper) {290Expected<std::array<char, 4>> MagicNumber = Helper.parseMagic();291if (!MagicNumber)292return MagicNumber.takeError();293if (Error E = validateMagicNumber(294StringRef(MagicNumber->data(), MagicNumber->size())))295return E;296if (Error E = Helper.parseBlockInfoBlock())297return E;298Expected<bool> isMetaBlock = Helper.isMetaBlock();299if (!isMetaBlock)300return isMetaBlock.takeError();301if (!*isMetaBlock)302return createStringError(303std::make_error_code(std::errc::illegal_byte_sequence),304"Expecting META_BLOCK after the BLOCKINFO_BLOCK.");305return Error::success();306}307308Expected<std::unique_ptr<BitstreamRemarkParser>>309remarks::createBitstreamParserFromMeta(310StringRef Buf, std::optional<ParsedStringTable> StrTab,311std::optional<StringRef> ExternalFilePrependPath) {312BitstreamParserHelper Helper(Buf);313Expected<std::array<char, 4>> MagicNumber = Helper.parseMagic();314if (!MagicNumber)315return MagicNumber.takeError();316317if (Error E = validateMagicNumber(318StringRef(MagicNumber->data(), MagicNumber->size())))319return std::move(E);320321auto Parser =322StrTab ? std::make_unique<BitstreamRemarkParser>(Buf, std::move(*StrTab))323: std::make_unique<BitstreamRemarkParser>(Buf);324325if (ExternalFilePrependPath)326Parser->ExternalFilePrependPath = std::string(*ExternalFilePrependPath);327328return std::move(Parser);329}330331Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() {332if (ParserHelper.atEndOfStream())333return make_error<EndOfFileError>();334335if (!ReadyToParseRemarks) {336if (Error E = parseMeta())337return std::move(E);338ReadyToParseRemarks = true;339}340341return parseRemark();342}343344Error BitstreamRemarkParser::parseMeta() {345// Advance and to the meta block.346if (Error E = advanceToMetaBlock(ParserHelper))347return E;348349BitstreamMetaParserHelper MetaHelper(ParserHelper.Stream,350ParserHelper.BlockInfo);351if (Error E = MetaHelper.parse())352return E;353354if (Error E = processCommonMeta(MetaHelper))355return E;356357switch (ContainerType) {358case BitstreamRemarkContainerType::Standalone:359return processStandaloneMeta(MetaHelper);360case BitstreamRemarkContainerType::SeparateRemarksFile:361return processSeparateRemarksFileMeta(MetaHelper);362case BitstreamRemarkContainerType::SeparateRemarksMeta:363return processSeparateRemarksMetaMeta(MetaHelper);364}365llvm_unreachable("Unknown BitstreamRemarkContainerType enum");366}367368Error BitstreamRemarkParser::processCommonMeta(369BitstreamMetaParserHelper &Helper) {370if (std::optional<uint64_t> Version = Helper.ContainerVersion)371ContainerVersion = *Version;372else373return createStringError(374std::make_error_code(std::errc::illegal_byte_sequence),375"Error while parsing BLOCK_META: missing container version.");376377if (std::optional<uint8_t> Type = Helper.ContainerType) {378// Always >= BitstreamRemarkContainerType::First since it's unsigned.379if (*Type > static_cast<uint8_t>(BitstreamRemarkContainerType::Last))380return createStringError(381std::make_error_code(std::errc::illegal_byte_sequence),382"Error while parsing BLOCK_META: invalid container type.");383384ContainerType = static_cast<BitstreamRemarkContainerType>(*Type);385} else386return createStringError(387std::make_error_code(std::errc::illegal_byte_sequence),388"Error while parsing BLOCK_META: missing container type.");389390return Error::success();391}392393static Error processStrTab(BitstreamRemarkParser &P,394std::optional<StringRef> StrTabBuf) {395if (!StrTabBuf)396return createStringError(397std::make_error_code(std::errc::illegal_byte_sequence),398"Error while parsing BLOCK_META: missing string table.");399// Parse and assign the string table.400P.StrTab.emplace(*StrTabBuf);401return Error::success();402}403404static Error processRemarkVersion(BitstreamRemarkParser &P,405std::optional<uint64_t> RemarkVersion) {406if (!RemarkVersion)407return createStringError(408std::make_error_code(std::errc::illegal_byte_sequence),409"Error while parsing BLOCK_META: missing remark version.");410P.RemarkVersion = *RemarkVersion;411return Error::success();412}413414Error BitstreamRemarkParser::processExternalFilePath(415std::optional<StringRef> ExternalFilePath) {416if (!ExternalFilePath)417return createStringError(418std::make_error_code(std::errc::illegal_byte_sequence),419"Error while parsing BLOCK_META: missing external file path.");420421SmallString<80> FullPath(ExternalFilePrependPath);422sys::path::append(FullPath, *ExternalFilePath);423424// External file: open the external file, parse it, check if its metadata425// matches the one from the separate metadata, then replace the current parser426// with the one parsing the remarks.427ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =428MemoryBuffer::getFile(FullPath);429if (std::error_code EC = BufferOrErr.getError())430return createFileError(FullPath, EC);431432TmpRemarkBuffer = std::move(*BufferOrErr);433434// Don't try to parse the file if it's empty.435if (TmpRemarkBuffer->getBufferSize() == 0)436return make_error<EndOfFileError>();437438// Create a separate parser used for parsing the separate file.439ParserHelper = BitstreamParserHelper(TmpRemarkBuffer->getBuffer());440// Advance and check until we can parse the meta block.441if (Error E = advanceToMetaBlock(ParserHelper))442return E;443// Parse the meta from the separate file.444// Note: here we overwrite the BlockInfo with the one from the file. This will445// be used to parse the rest of the file.446BitstreamMetaParserHelper SeparateMetaHelper(ParserHelper.Stream,447ParserHelper.BlockInfo);448if (Error E = SeparateMetaHelper.parse())449return E;450451uint64_t PreviousContainerVersion = ContainerVersion;452if (Error E = processCommonMeta(SeparateMetaHelper))453return E;454455if (ContainerType != BitstreamRemarkContainerType::SeparateRemarksFile)456return createStringError(457std::make_error_code(std::errc::illegal_byte_sequence),458"Error while parsing external file's BLOCK_META: wrong container "459"type.");460461if (PreviousContainerVersion != ContainerVersion)462return createStringError(463std::make_error_code(std::errc::illegal_byte_sequence),464"Error while parsing external file's BLOCK_META: mismatching versions: "465"original meta: %lu, external file meta: %lu.",466PreviousContainerVersion, ContainerVersion);467468// Process the meta from the separate file.469return processSeparateRemarksFileMeta(SeparateMetaHelper);470}471472Error BitstreamRemarkParser::processStandaloneMeta(473BitstreamMetaParserHelper &Helper) {474if (Error E = processStrTab(*this, Helper.StrTabBuf))475return E;476return processRemarkVersion(*this, Helper.RemarkVersion);477}478479Error BitstreamRemarkParser::processSeparateRemarksFileMeta(480BitstreamMetaParserHelper &Helper) {481return processRemarkVersion(*this, Helper.RemarkVersion);482}483484Error BitstreamRemarkParser::processSeparateRemarksMetaMeta(485BitstreamMetaParserHelper &Helper) {486if (Error E = processStrTab(*this, Helper.StrTabBuf))487return E;488return processExternalFilePath(Helper.ExternalFilePath);489}490491Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::parseRemark() {492BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream);493if (Error E = RemarkHelper.parse())494return std::move(E);495496return processRemark(RemarkHelper);497}498499Expected<std::unique_ptr<Remark>>500BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) {501std::unique_ptr<Remark> Result = std::make_unique<Remark>();502Remark &R = *Result;503504if (StrTab == std::nullopt)505return createStringError(506std::make_error_code(std::errc::invalid_argument),507"Error while parsing BLOCK_REMARK: missing string table.");508509if (!Helper.Type)510return createStringError(511std::make_error_code(std::errc::illegal_byte_sequence),512"Error while parsing BLOCK_REMARK: missing remark type.");513514// Always >= Type::First since it's unsigned.515if (*Helper.Type > static_cast<uint8_t>(Type::Last))516return createStringError(517std::make_error_code(std::errc::illegal_byte_sequence),518"Error while parsing BLOCK_REMARK: unknown remark type.");519520R.RemarkType = static_cast<Type>(*Helper.Type);521522if (!Helper.RemarkNameIdx)523return createStringError(524std::make_error_code(std::errc::illegal_byte_sequence),525"Error while parsing BLOCK_REMARK: missing remark name.");526527if (Expected<StringRef> RemarkName = (*StrTab)[*Helper.RemarkNameIdx])528R.RemarkName = *RemarkName;529else530return RemarkName.takeError();531532if (!Helper.PassNameIdx)533return createStringError(534std::make_error_code(std::errc::illegal_byte_sequence),535"Error while parsing BLOCK_REMARK: missing remark pass.");536537if (Expected<StringRef> PassName = (*StrTab)[*Helper.PassNameIdx])538R.PassName = *PassName;539else540return PassName.takeError();541542if (!Helper.FunctionNameIdx)543return createStringError(544std::make_error_code(std::errc::illegal_byte_sequence),545"Error while parsing BLOCK_REMARK: missing remark function name.");546if (Expected<StringRef> FunctionName = (*StrTab)[*Helper.FunctionNameIdx])547R.FunctionName = *FunctionName;548else549return FunctionName.takeError();550551if (Helper.SourceFileNameIdx && Helper.SourceLine && Helper.SourceColumn) {552Expected<StringRef> SourceFileName = (*StrTab)[*Helper.SourceFileNameIdx];553if (!SourceFileName)554return SourceFileName.takeError();555R.Loc.emplace();556R.Loc->SourceFilePath = *SourceFileName;557R.Loc->SourceLine = *Helper.SourceLine;558R.Loc->SourceColumn = *Helper.SourceColumn;559}560561if (Helper.Hotness)562R.Hotness = *Helper.Hotness;563564if (!Helper.Args)565return std::move(Result);566567for (const BitstreamRemarkParserHelper::Argument &Arg : *Helper.Args) {568if (!Arg.KeyIdx)569return createStringError(570std::make_error_code(std::errc::illegal_byte_sequence),571"Error while parsing BLOCK_REMARK: missing key in remark argument.");572if (!Arg.ValueIdx)573return createStringError(574std::make_error_code(std::errc::illegal_byte_sequence),575"Error while parsing BLOCK_REMARK: missing value in remark "576"argument.");577578// We have at least a key and a value, create an entry.579R.Args.emplace_back();580581if (Expected<StringRef> Key = (*StrTab)[*Arg.KeyIdx])582R.Args.back().Key = *Key;583else584return Key.takeError();585586if (Expected<StringRef> Value = (*StrTab)[*Arg.ValueIdx])587R.Args.back().Val = *Value;588else589return Value.takeError();590591if (Arg.SourceFileNameIdx && Arg.SourceLine && Arg.SourceColumn) {592if (Expected<StringRef> SourceFileName =593(*StrTab)[*Arg.SourceFileNameIdx]) {594R.Args.back().Loc.emplace();595R.Args.back().Loc->SourceFilePath = *SourceFileName;596R.Args.back().Loc->SourceLine = *Arg.SourceLine;597R.Args.back().Loc->SourceColumn = *Arg.SourceColumn;598} else599return SourceFileName.takeError();600}601}602603return std::move(Result);604}605606607