402 lines · cpp
1//===- unittests/Support/BitstreamRemarksParsingTest.cpp - Parsing tests --===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm-c/Remarks.h"10#include "llvm/Remarks/Remark.h"11#include "llvm/Remarks/RemarkParser.h"12#include "llvm/Remarks/RemarkSerializer.h"13#include "gtest/gtest.h"14 15using namespace llvm;16 17template <size_t N> static void parseGood(const char (&Buf)[N]) {18 // 1. Parse the YAML remark -> FromYAMLRemark19 // 2. Serialize it to bitstream -> BSStream20 // 3. Parse it back -> FromBSRemark21 // 4. Compare the remark objects22 //23 // This testing methodology has the drawback of relying on both the YAML24 // remark parser and the bitstream remark serializer. It does simplify25 // testing a lot, since working directly with bitstream is not that easy.26 27 // 1.28 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =29 remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});30 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));31 EXPECT_TRUE(*MaybeParser != nullptr);32 33 std::unique_ptr<remarks::Remark> FromYAMLRemark = nullptr;34 remarks::RemarkParser &Parser = **MaybeParser;35 Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();36 EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.37 EXPECT_TRUE(*Remark != nullptr); // At least one remark.38 // Keep the previous remark around.39 FromYAMLRemark = std::move(*Remark);40 Remark = Parser.next();41 Error E = Remark.takeError();42 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());43 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.44 45 // 2.46 remarks::StringTable BSStrTab;47 BSStrTab.internalize(*FromYAMLRemark);48 std::string BSBuf;49 raw_string_ostream BSStream(BSBuf);50 Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =51 remarks::createRemarkSerializer(remarks::Format::Bitstream, BSStream,52 std::move(BSStrTab));53 EXPECT_FALSE(errorToBool(BSSerializer.takeError()));54 (*BSSerializer)->emit(*FromYAMLRemark);55 (*BSSerializer)->finalize();56 57 // 3.58 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =59 remarks::createRemarkParser(remarks::Format::Bitstream, BSStream.str());60 EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));61 EXPECT_TRUE(*MaybeBSParser != nullptr);62 63 std::unique_ptr<remarks::Remark> FromBSRemark = nullptr;64 remarks::RemarkParser &BSParser = **MaybeBSParser;65 Expected<std::unique_ptr<remarks::Remark>> BSRemark = BSParser.next();66 EXPECT_FALSE(errorToBool(BSRemark.takeError())); // Check for parsing errors.67 EXPECT_TRUE(*BSRemark != nullptr); // At least one remark.68 // Keep the previous remark around.69 FromBSRemark = std::move(*BSRemark);70 BSRemark = BSParser.next();71 Error BSE = BSRemark.takeError();72 EXPECT_TRUE(BSE.isA<remarks::EndOfFileError>());73 EXPECT_TRUE(errorToBool(std::move(BSE))); // Check for parsing errors.74 75 EXPECT_EQ(*FromYAMLRemark, *FromBSRemark);76}77 78TEST(BitstreamRemarks, ParsingGood) {79 parseGood("\n"80 "--- !Missed\n"81 "Pass: inline\n"82 "Name: NoDefinition\n"83 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"84 "Function: foo\n"85 "Args:\n"86 " - Callee: bar\n"87 " - String: ' will not be inlined into '\n"88 " - Caller: foo\n"89 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"90 " - String: ' because its definition is unavailable'\n"91 "");92 93 // No debug loc should also pass.94 parseGood("\n"95 "--- !Missed\n"96 "Pass: inline\n"97 "Name: NoDefinition\n"98 "Function: foo\n"99 "Args:\n"100 " - Callee: bar\n"101 " - String: ' will not be inlined into '\n"102 " - Caller: foo\n"103 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"104 " - String: ' because its definition is unavailable'\n"105 "");106 107 // No args is also ok.108 parseGood("\n"109 "--- !Missed\n"110 "Pass: inline\n"111 "Name: NoDefinition\n"112 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"113 "Function: foo\n"114 "");115}116 117// Mandatory common part of a remark.118#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"119// Test all the types.120TEST(BitstreamRemarks, ParsingTypes) {121 // Type: Passed122 parseGood("--- !Passed" COMMON_REMARK);123 // Type: Missed124 parseGood("--- !Missed" COMMON_REMARK);125 // Type: Analysis126 parseGood("--- !Analysis" COMMON_REMARK);127 // Type: AnalysisFPCommute128 parseGood("--- !AnalysisFPCommute" COMMON_REMARK);129 // Type: AnalysisAliasing130 parseGood("--- !AnalysisAliasing" COMMON_REMARK);131 // Type: Failure132 parseGood("--- !Failure" COMMON_REMARK);133}134#undef COMMON_REMARK135 136static inline StringRef checkStr(StringRef Str, unsigned ExpectedLen) {137 const char *StrData = Str.data();138 unsigned StrLen = Str.size();139 EXPECT_EQ(StrLen, ExpectedLen);140 return StringRef(StrData, StrLen);141}142 143TEST(BitstreamRemarks, Contents) {144 StringRef Buf = "\n"145 "--- !Missed\n"146 "Pass: inline\n"147 "Name: NoDefinition\n"148 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"149 "Function: foo\n"150 "Hotness: 4\n"151 "Args:\n"152 " - Callee: bar\n"153 " - String: ' will not be inlined into '\n"154 " - Caller: foo\n"155 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"156 " - String: ' because its definition is unavailable'\n"157 "\n";158 159 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =160 remarks::createRemarkParser(remarks::Format::YAML, Buf);161 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));162 EXPECT_TRUE(*MaybeParser != nullptr);163 164 remarks::RemarkParser &Parser = **MaybeParser;165 Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();166 EXPECT_FALSE(167 errorToBool(MaybeRemark.takeError())); // Check for parsing errors.168 EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.169 170 const remarks::Remark &Remark = **MaybeRemark;171 EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);172 EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");173 EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");174 EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");175 EXPECT_TRUE(Remark.Loc);176 const remarks::RemarkLocation &RL = *Remark.Loc;177 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");178 EXPECT_EQ(RL.SourceLine, 3U);179 EXPECT_EQ(RL.SourceColumn, 12U);180 EXPECT_TRUE(Remark.Hotness);181 EXPECT_EQ(*Remark.Hotness, 4U);182 EXPECT_EQ(Remark.Args.size(), 4U);183 184 unsigned ArgID = 0;185 for (const remarks::Argument &Arg : Remark.Args) {186 switch (ArgID) {187 case 0:188 EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");189 EXPECT_EQ(checkStr(Arg.Val, 3), "bar");190 EXPECT_FALSE(Arg.Loc);191 break;192 case 1:193 EXPECT_EQ(checkStr(Arg.Key, 6), "String");194 EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");195 EXPECT_FALSE(Arg.Loc);196 break;197 case 2: {198 EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");199 EXPECT_EQ(checkStr(Arg.Val, 3), "foo");200 EXPECT_TRUE(Arg.Loc);201 const remarks::RemarkLocation &RL = *Arg.Loc;202 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");203 EXPECT_EQ(RL.SourceLine, 2U);204 EXPECT_EQ(RL.SourceColumn, 0U);205 break;206 }207 case 3:208 EXPECT_EQ(checkStr(Arg.Key, 6), "String");209 EXPECT_EQ(checkStr(Arg.Val, 38),210 " because its definition is unavailable");211 EXPECT_FALSE(Arg.Loc);212 break;213 default:214 break;215 }216 ++ArgID;217 }218 219 MaybeRemark = Parser.next();220 Error E = MaybeRemark.takeError();221 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());222 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.223}224 225static inline StringRef checkStr(LLVMRemarkStringRef Str,226 unsigned ExpectedLen) {227 const char *StrData = LLVMRemarkStringGetData(Str);228 unsigned StrLen = LLVMRemarkStringGetLen(Str);229 EXPECT_EQ(StrLen, ExpectedLen);230 return StringRef(StrData, StrLen);231}232 233TEST(BitstreamRemarks, ContentsCAPI) {234 remarks::StringTable BSStrTab;235 remarks::Remark ToSerializeRemark;236 ToSerializeRemark.RemarkType = remarks::Type::Missed;237 ToSerializeRemark.PassName = "inline";238 ToSerializeRemark.RemarkName = "NoDefinition";239 ToSerializeRemark.FunctionName = "foo";240 ToSerializeRemark.Loc = remarks::RemarkLocation{"file.c", 3, 12};241 ToSerializeRemark.Hotness = 0;242 ToSerializeRemark.Args.emplace_back();243 ToSerializeRemark.Args.back().Key = "Callee";244 ToSerializeRemark.Args.back().Val = "bar";245 ToSerializeRemark.Args.emplace_back();246 ToSerializeRemark.Args.back().Key = "String";247 ToSerializeRemark.Args.back().Val = " will not be inlined into ";248 ToSerializeRemark.Args.emplace_back();249 ToSerializeRemark.Args.back().Key = "Caller";250 ToSerializeRemark.Args.back().Val = "foo";251 ToSerializeRemark.Args.back().Loc = remarks::RemarkLocation{"file.c", 2, 0};252 ToSerializeRemark.Args.emplace_back();253 ToSerializeRemark.Args.back().Key = "String";254 ToSerializeRemark.Args.back().Val = " because its definition is unavailable";255 BSStrTab.internalize(ToSerializeRemark);256 std::string BSBuf;257 raw_string_ostream BSStream(BSBuf);258 Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =259 remarks::createRemarkSerializer(remarks::Format::Bitstream, BSStream,260 std::move(BSStrTab));261 EXPECT_FALSE(errorToBool(BSSerializer.takeError()));262 (*BSSerializer)->emit(ToSerializeRemark);263 (*BSSerializer)->finalize();264 265 StringRef Buf = BSStream.str();266 LLVMRemarkParserRef Parser =267 LLVMRemarkParserCreateBitstream(Buf.data(), Buf.size());268 LLVMRemarkEntryRef Remark = LLVMRemarkParserGetNext(Parser);269 EXPECT_FALSE(Remark == nullptr);270 EXPECT_EQ(LLVMRemarkEntryGetType(Remark), LLVMRemarkTypeMissed);271 EXPECT_EQ(checkStr(LLVMRemarkEntryGetPassName(Remark), 6), "inline");272 EXPECT_EQ(checkStr(LLVMRemarkEntryGetRemarkName(Remark), 12), "NoDefinition");273 EXPECT_EQ(checkStr(LLVMRemarkEntryGetFunctionName(Remark), 3), "foo");274 LLVMRemarkDebugLocRef DL = LLVMRemarkEntryGetDebugLoc(Remark);275 EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");276 EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 3U);277 EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 12U);278 EXPECT_EQ(LLVMRemarkEntryGetHotness(Remark), 0U);279 EXPECT_EQ(LLVMRemarkEntryGetNumArgs(Remark), 4U);280 281 unsigned ArgID = 0;282 LLVMRemarkArgRef Arg = LLVMRemarkEntryGetFirstArg(Remark);283 do {284 switch (ArgID) {285 case 0:286 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Callee");287 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "bar");288 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);289 break;290 case 1:291 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");292 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 26),293 " will not be inlined into ");294 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);295 break;296 case 2: {297 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Caller");298 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "foo");299 LLVMRemarkDebugLocRef DL = LLVMRemarkArgGetDebugLoc(Arg);300 EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");301 EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 2U);302 EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 0U);303 break;304 }305 case 3:306 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");307 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 38),308 " because its definition is unavailable");309 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);310 break;311 default:312 break;313 }314 ++ArgID;315 } while ((Arg = LLVMRemarkEntryGetNextArg(Arg, Remark)));316 317 LLVMRemarkEntryDispose(Remark);318 319 EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);320 321 EXPECT_FALSE(LLVMRemarkParserHasError(Parser));322 LLVMRemarkParserDispose(Parser);323}324 325static void parseBad(StringRef Input, const char *ErrorMsg) {326 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =327 remarks::createRemarkParser(remarks::Format::Bitstream, Input);328 EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));329 EXPECT_TRUE(*MaybeBSParser != nullptr);330 331 remarks::RemarkParser &BSParser = **MaybeBSParser;332 Expected<std::unique_ptr<remarks::Remark>> BSRemark = BSParser.next();333 EXPECT_EQ(ErrorMsg, toString(BSRemark.takeError())); // Expect an error.334}335 336TEST(BitstreamRemarks, ParsingEmpty) {337 parseBad(StringRef(), "End of file reached.");338}339 340TEST(BitstreamRemarks, ParsingBadMagic) {341 parseBad("KRMR", "Unknown magic number: expecting RMRK, got KRMR.");342}343 344// Testing malformed bitstream is not easy. We would need to replace bytes in345// the stream to create malformed and unknown records and blocks. There is no346// textual format for bitstream that can be decoded, modified and encoded347// back.348 349// FIXME: Add tests for the following error messages:350// * Error while parsing META_BLOCK: malformed record entry351// (RECORD_META_CONTAINER_INFO).352// * Error while parsing META_BLOCK: malformed record entry353// (RECORD_META_REMARK_VERSION).354// * Error while parsing META_BLOCK: malformed record entry355// (RECORD_META_STRTAB).356// * Error while parsing META_BLOCK: malformed record entry357// (RECORD_META_EXTERNAL_FILE).358// * Error while parsing META_BLOCK: unknown record entry (NUM).359// * Error while parsing REMARK_BLOCK: malformed record entry360// (RECORD_REMARK_HEADER).361// * Error while parsing REMARK_BLOCK: malformed record entry362// (RECORD_REMARK_DEBUG_LOC).363// * Error while parsing REMARK_BLOCK: malformed record entry364// (RECORD_REMARK_HOTNESS).365// * Error while parsing REMARK_BLOCK: malformed record entry366// (RECORD_REMARK_ARG_WITH_DEBUGLOC).367// * Error while parsing REMARK_BLOCK: malformed record entry368// (RECORD_REMARK_ARG_WITHOUT_DEBUGLOC).369// * Error while parsing REMARK_BLOCK: unknown record entry (NUM).370// * Error while parsing META_BLOCK: expecting [ENTER_SUBBLOCO, META_BLOCK,371// ...].372// * Error while entering META_BLOCK.373// * Error while parsing META_BLOCK: expecting records.374// * Error while parsing META_BLOCK: unterminated block.375// * Error while parsing REMARK_BLOCK: expecting [ENTER_SUBBLOCO, REMARK_BLOCK,376// ...].377// * Error while entering REMARK_BLOCK.378// * Error while parsing REMARK_BLOCK: expecting records.379// * Error while parsing REMARK_BLOCK: unterminated block.380// * Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK,381// BLOCKINFO_BLOCK, ...].382// * Error while parsing BLOCKINFO_BLOCK.383// * Unexpected error while parsing bitstream.384// * Expecting META_BLOCK after the BLOCKINFO_BLOCK.385// * Error while parsing BLOCK_META: missing container version.386// * Error while parsing BLOCK_META: invalid container type.387// * Error while parsing BLOCK_META: missing container type.388// * Error while parsing BLOCK_META: missing string table.389// * Error while parsing BLOCK_META: missing remark version.390// * Error while parsing BLOCK_META: missing external file path.391// * Error while parsing external file's BLOCK_META: wrong container type.392// * Error while parsing external file's BLOCK_META: mismatching versions:393// original meta: NUM, external file meta: NUM.394// * Error while parsing BLOCK_REMARK: missing string table.395// * Error while parsing BLOCK_REMARK: missing remark type.396// * Error while parsing BLOCK_REMARK: unknown remark type.397// * Error while parsing BLOCK_REMARK: missing remark name.398// * Error while parsing BLOCK_REMARK: missing remark pass.399// * Error while parsing BLOCK_REMARK: missing remark function name.400// * Error while parsing BLOCK_REMARK: missing key in remark argument.401// * Error while parsing BLOCK_REMARK: missing value in remark argument.402