1 //===- BitstreamRemarkParser.cpp ------------------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 // This file provides utility methods used by clients that want to use the
10 // parser for remark diagnostics in LLVM.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/Remarks/BitstreamRemarkParser.h"
15 #include "BitstreamRemarkParser.h"
16 #include "llvm/Support/MemoryBuffer.h"
17 #include "llvm/Support/Path.h"
20 using namespace llvm::remarks
;
22 static Error
unknownRecord(const char *BlockName
, unsigned RecordID
) {
23 return createStringError(
24 std::make_error_code(std::errc::illegal_byte_sequence
),
25 "Error while parsing %s: unknown record entry (%lu).", BlockName
,
29 static Error
malformedRecord(const char *BlockName
, const char *RecordName
) {
30 return createStringError(
31 std::make_error_code(std::errc::illegal_byte_sequence
),
32 "Error while parsing %s: malformed record entry (%s).", BlockName
,
36 BitstreamMetaParserHelper::BitstreamMetaParserHelper(
37 BitstreamCursor
&Stream
, BitstreamBlockInfo
&BlockInfo
)
38 : Stream(Stream
), BlockInfo(BlockInfo
) {}
40 /// Parse a record and fill in the fields in the parser.
41 static Error
parseRecord(BitstreamMetaParserHelper
&Parser
, unsigned Code
) {
42 BitstreamCursor
&Stream
= Parser
.Stream
;
43 // Note: 2 is used here because it's the max number of fields we have per
45 SmallVector
<uint64_t, 2> Record
;
47 Expected
<unsigned> RecordID
= Stream
.readRecord(Code
, Record
, &Blob
);
49 return RecordID
.takeError();
52 case RECORD_META_CONTAINER_INFO
: {
53 if (Record
.size() != 2)
54 return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO");
55 Parser
.ContainerVersion
= Record
[0];
56 Parser
.ContainerType
= Record
[1];
59 case RECORD_META_REMARK_VERSION
: {
60 if (Record
.size() != 1)
61 return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION");
62 Parser
.RemarkVersion
= Record
[0];
65 case RECORD_META_STRTAB
: {
66 if (Record
.size() != 0)
67 return malformedRecord("BLOCK_META", "RECORD_META_STRTAB");
68 Parser
.StrTabBuf
= Blob
;
71 case RECORD_META_EXTERNAL_FILE
: {
72 if (Record
.size() != 0)
73 return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE");
74 Parser
.ExternalFilePath
= Blob
;
78 return unknownRecord("BLOCK_META", *RecordID
);
80 return Error::success();
83 BitstreamRemarkParserHelper::BitstreamRemarkParserHelper(
84 BitstreamCursor
&Stream
)
87 /// Parse a record and fill in the fields in the parser.
88 static Error
parseRecord(BitstreamRemarkParserHelper
&Parser
, unsigned Code
) {
89 BitstreamCursor
&Stream
= Parser
.Stream
;
90 // Note: 5 is used here because it's the max number of fields we have per
92 SmallVector
<uint64_t, 5> Record
;
94 Expected
<unsigned> RecordID
= Stream
.readRecord(Code
, Record
, &Blob
);
96 return RecordID
.takeError();
99 case RECORD_REMARK_HEADER
: {
100 if (Record
.size() != 4)
101 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER");
102 Parser
.Type
= Record
[0];
103 Parser
.RemarkNameIdx
= Record
[1];
104 Parser
.PassNameIdx
= Record
[2];
105 Parser
.FunctionNameIdx
= Record
[3];
108 case RECORD_REMARK_DEBUG_LOC
: {
109 if (Record
.size() != 3)
110 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC");
111 Parser
.SourceFileNameIdx
= Record
[0];
112 Parser
.SourceLine
= Record
[1];
113 Parser
.SourceColumn
= Record
[2];
116 case RECORD_REMARK_HOTNESS
: {
117 if (Record
.size() != 1)
118 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS");
119 Parser
.Hotness
= Record
[0];
122 case RECORD_REMARK_ARG_WITH_DEBUGLOC
: {
123 if (Record
.size() != 5)
124 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC");
125 // Create a temporary argument. Use that as a valid memory location for this
127 Parser
.TmpArgs
.emplace_back();
128 Parser
.TmpArgs
.back().KeyIdx
= Record
[0];
129 Parser
.TmpArgs
.back().ValueIdx
= Record
[1];
130 Parser
.TmpArgs
.back().SourceFileNameIdx
= Record
[2];
131 Parser
.TmpArgs
.back().SourceLine
= Record
[3];
132 Parser
.TmpArgs
.back().SourceColumn
= Record
[4];
134 ArrayRef
<BitstreamRemarkParserHelper::Argument
>(Parser
.TmpArgs
);
137 case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC
: {
138 if (Record
.size() != 2)
139 return malformedRecord("BLOCK_REMARK",
140 "RECORD_REMARK_ARG_WITHOUT_DEBUGLOC");
141 // Create a temporary argument. Use that as a valid memory location for this
143 Parser
.TmpArgs
.emplace_back();
144 Parser
.TmpArgs
.back().KeyIdx
= Record
[0];
145 Parser
.TmpArgs
.back().ValueIdx
= Record
[1];
147 ArrayRef
<BitstreamRemarkParserHelper::Argument
>(Parser
.TmpArgs
);
151 return unknownRecord("BLOCK_REMARK", *RecordID
);
153 return Error::success();
156 template <typename T
>
157 static Error
parseBlock(T
&ParserHelper
, unsigned BlockID
,
158 const char *BlockName
) {
159 BitstreamCursor
&Stream
= ParserHelper
.Stream
;
160 Expected
<BitstreamEntry
> Next
= Stream
.advance();
162 return Next
.takeError();
163 if (Next
->Kind
!= BitstreamEntry::SubBlock
|| Next
->ID
!= BlockID
)
164 return createStringError(
165 std::make_error_code(std::errc::illegal_byte_sequence
),
166 "Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].",
167 BlockName
, BlockName
);
168 if (Stream
.EnterSubBlock(BlockID
))
169 return createStringError(
170 std::make_error_code(std::errc::illegal_byte_sequence
),
171 "Error while entering %s.", BlockName
);
173 // Stop when there is nothing to read anymore or when we encounter an
175 while (!Stream
.AtEndOfStream()) {
176 Next
= Stream
.advance();
178 return Next
.takeError();
179 switch (Next
->Kind
) {
180 case BitstreamEntry::EndBlock
:
181 return Error::success();
182 case BitstreamEntry::Error
:
183 case BitstreamEntry::SubBlock
:
184 return createStringError(
185 std::make_error_code(std::errc::illegal_byte_sequence
),
186 "Error while parsing %s: expecting records.", BlockName
);
187 case BitstreamEntry::Record
:
188 if (Error E
= parseRecord(ParserHelper
, Next
->ID
))
193 // If we're here, it means we didn't get an END_BLOCK yet, but we're at the
194 // end of the stream. In this case, error.
195 return createStringError(
196 std::make_error_code(std::errc::illegal_byte_sequence
),
197 "Error while parsing %s: unterminated block.", BlockName
);
200 Error
BitstreamMetaParserHelper::parse() {
201 return parseBlock(*this, META_BLOCK_ID
, "META_BLOCK");
204 Error
BitstreamRemarkParserHelper::parse() {
205 return parseBlock(*this, REMARK_BLOCK_ID
, "REMARK_BLOCK");
208 BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer
)
211 Expected
<std::array
<char, 4>> BitstreamParserHelper::parseMagic() {
212 std::array
<char, 4> Result
;
213 for (unsigned i
= 0; i
< 4; ++i
)
214 if (Expected
<unsigned> R
= Stream
.Read(8))
217 return R
.takeError();
221 Error
BitstreamParserHelper::parseBlockInfoBlock() {
222 Expected
<BitstreamEntry
> Next
= Stream
.advance();
224 return Next
.takeError();
225 if (Next
->Kind
!= BitstreamEntry::SubBlock
||
226 Next
->ID
!= llvm::bitc::BLOCKINFO_BLOCK_ID
)
227 return createStringError(
228 std::make_error_code(std::errc::illegal_byte_sequence
),
229 "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, "
230 "BLOCKINFO_BLOCK, ...].");
232 Expected
<Optional
<BitstreamBlockInfo
>> MaybeBlockInfo
=
233 Stream
.ReadBlockInfoBlock();
235 return MaybeBlockInfo
.takeError();
237 if (!*MaybeBlockInfo
)
238 return createStringError(
239 std::make_error_code(std::errc::illegal_byte_sequence
),
240 "Error while parsing BLOCKINFO_BLOCK.");
242 BlockInfo
= **MaybeBlockInfo
;
244 Stream
.setBlockInfo(&BlockInfo
);
245 return Error::success();
248 static Expected
<bool> isBlock(BitstreamCursor
&Stream
, unsigned BlockID
) {
250 uint64_t PreviousBitNo
= Stream
.GetCurrentBitNo();
251 Expected
<BitstreamEntry
> Next
= Stream
.advance();
253 return Next
.takeError();
254 switch (Next
->Kind
) {
255 case BitstreamEntry::SubBlock
:
256 // Check for the block id.
257 Result
= Next
->ID
== BlockID
;
259 case BitstreamEntry::Error
:
260 return createStringError(
261 std::make_error_code(std::errc::illegal_byte_sequence
),
262 "Unexpected error while parsing bitstream.");
267 if (Error E
= Stream
.JumpToBit(PreviousBitNo
))
272 Expected
<bool> BitstreamParserHelper::isMetaBlock() {
273 return isBlock(Stream
, META_BLOCK_ID
);
276 Expected
<bool> BitstreamParserHelper::isRemarkBlock() {
277 return isBlock(Stream
, META_BLOCK_ID
);
280 static Error
validateMagicNumber(StringRef MagicNumber
) {
281 if (MagicNumber
!= remarks::ContainerMagic
)
282 return createStringError(std::make_error_code(std::errc::invalid_argument
),
283 "Unknown magic number: expecting %s, got %.4s.",
284 remarks::ContainerMagic
.data(), MagicNumber
.data());
285 return Error::success();
288 static Error
advanceToMetaBlock(BitstreamParserHelper
&Helper
) {
289 Expected
<std::array
<char, 4>> MagicNumber
= Helper
.parseMagic();
291 return MagicNumber
.takeError();
292 if (Error E
= validateMagicNumber(
293 StringRef(MagicNumber
->data(), MagicNumber
->size())))
295 if (Error E
= Helper
.parseBlockInfoBlock())
297 Expected
<bool> isMetaBlock
= Helper
.isMetaBlock();
299 return isMetaBlock
.takeError();
301 return createStringError(
302 std::make_error_code(std::errc::illegal_byte_sequence
),
303 "Expecting META_BLOCK after the BLOCKINFO_BLOCK.");
304 return Error::success();
307 Expected
<std::unique_ptr
<BitstreamRemarkParser
>>
308 remarks::createBitstreamParserFromMeta(
309 StringRef Buf
, Optional
<ParsedStringTable
> StrTab
,
310 Optional
<StringRef
> ExternalFilePrependPath
) {
311 BitstreamParserHelper
Helper(Buf
);
312 Expected
<std::array
<char, 4>> MagicNumber
= Helper
.parseMagic();
314 return MagicNumber
.takeError();
316 if (Error E
= validateMagicNumber(
317 StringRef(MagicNumber
->data(), MagicNumber
->size())))
321 StrTab
? std::make_unique
<BitstreamRemarkParser
>(Buf
, std::move(*StrTab
))
322 : std::make_unique
<BitstreamRemarkParser
>(Buf
);
324 if (ExternalFilePrependPath
)
325 Parser
->ExternalFilePrependPath
= std::string(*ExternalFilePrependPath
);
327 return std::move(Parser
);
330 Expected
<std::unique_ptr
<Remark
>> BitstreamRemarkParser::next() {
331 if (ParserHelper
.atEndOfStream())
332 return make_error
<EndOfFileError
>();
334 if (!ReadyToParseRemarks
) {
335 if (Error E
= parseMeta())
337 ReadyToParseRemarks
= true;
340 return parseRemark();
343 Error
BitstreamRemarkParser::parseMeta() {
344 // Advance and to the meta block.
345 if (Error E
= advanceToMetaBlock(ParserHelper
))
348 BitstreamMetaParserHelper
MetaHelper(ParserHelper
.Stream
,
349 ParserHelper
.BlockInfo
);
350 if (Error E
= MetaHelper
.parse())
353 if (Error E
= processCommonMeta(MetaHelper
))
356 switch (ContainerType
) {
357 case BitstreamRemarkContainerType::Standalone
:
358 return processStandaloneMeta(MetaHelper
);
359 case BitstreamRemarkContainerType::SeparateRemarksFile
:
360 return processSeparateRemarksFileMeta(MetaHelper
);
361 case BitstreamRemarkContainerType::SeparateRemarksMeta
:
362 return processSeparateRemarksMetaMeta(MetaHelper
);
364 llvm_unreachable("Unknown BitstreamRemarkContainerType enum");
367 Error
BitstreamRemarkParser::processCommonMeta(
368 BitstreamMetaParserHelper
&Helper
) {
369 if (Optional
<uint64_t> Version
= Helper
.ContainerVersion
)
370 ContainerVersion
= *Version
;
372 return createStringError(
373 std::make_error_code(std::errc::illegal_byte_sequence
),
374 "Error while parsing BLOCK_META: missing container version.");
376 if (Optional
<uint8_t> Type
= Helper
.ContainerType
) {
377 // Always >= BitstreamRemarkContainerType::First since it's unsigned.
378 if (*Type
> static_cast<uint8_t>(BitstreamRemarkContainerType::Last
))
379 return createStringError(
380 std::make_error_code(std::errc::illegal_byte_sequence
),
381 "Error while parsing BLOCK_META: invalid container type.");
383 ContainerType
= static_cast<BitstreamRemarkContainerType
>(*Type
);
385 return createStringError(
386 std::make_error_code(std::errc::illegal_byte_sequence
),
387 "Error while parsing BLOCK_META: missing container type.");
389 return Error::success();
392 static Error
processStrTab(BitstreamRemarkParser
&P
,
393 Optional
<StringRef
> StrTabBuf
) {
395 return createStringError(
396 std::make_error_code(std::errc::illegal_byte_sequence
),
397 "Error while parsing BLOCK_META: missing string table.");
398 // Parse and assign the string table.
399 P
.StrTab
.emplace(*StrTabBuf
);
400 return Error::success();
403 static Error
processRemarkVersion(BitstreamRemarkParser
&P
,
404 Optional
<uint64_t> RemarkVersion
) {
406 return createStringError(
407 std::make_error_code(std::errc::illegal_byte_sequence
),
408 "Error while parsing BLOCK_META: missing remark version.");
409 P
.RemarkVersion
= *RemarkVersion
;
410 return Error::success();
413 Error
BitstreamRemarkParser::processExternalFilePath(
414 Optional
<StringRef
> ExternalFilePath
) {
415 if (!ExternalFilePath
)
416 return createStringError(
417 std::make_error_code(std::errc::illegal_byte_sequence
),
418 "Error while parsing BLOCK_META: missing external file path.");
420 SmallString
<80> FullPath(ExternalFilePrependPath
);
421 sys::path::append(FullPath
, *ExternalFilePath
);
423 // External file: open the external file, parse it, check if its metadata
424 // matches the one from the separate metadata, then replace the current parser
425 // with the one parsing the remarks.
426 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferOrErr
=
427 MemoryBuffer::getFile(FullPath
);
428 if (std::error_code EC
= BufferOrErr
.getError())
429 return createFileError(FullPath
, EC
);
431 TmpRemarkBuffer
= std::move(*BufferOrErr
);
433 // Don't try to parse the file if it's empty.
434 if (TmpRemarkBuffer
->getBufferSize() == 0)
435 return make_error
<EndOfFileError
>();
437 // Create a separate parser used for parsing the separate file.
438 ParserHelper
= BitstreamParserHelper(TmpRemarkBuffer
->getBuffer());
439 // Advance and check until we can parse the meta block.
440 if (Error E
= advanceToMetaBlock(ParserHelper
))
442 // Parse the meta from the separate file.
443 // Note: here we overwrite the BlockInfo with the one from the file. This will
444 // be used to parse the rest of the file.
445 BitstreamMetaParserHelper
SeparateMetaHelper(ParserHelper
.Stream
,
446 ParserHelper
.BlockInfo
);
447 if (Error E
= SeparateMetaHelper
.parse())
450 uint64_t PreviousContainerVersion
= ContainerVersion
;
451 if (Error E
= processCommonMeta(SeparateMetaHelper
))
454 if (ContainerType
!= BitstreamRemarkContainerType::SeparateRemarksFile
)
455 return createStringError(
456 std::make_error_code(std::errc::illegal_byte_sequence
),
457 "Error while parsing external file's BLOCK_META: wrong container "
460 if (PreviousContainerVersion
!= ContainerVersion
)
461 return createStringError(
462 std::make_error_code(std::errc::illegal_byte_sequence
),
463 "Error while parsing external file's BLOCK_META: mismatching versions: "
464 "original meta: %lu, external file meta: %lu.",
465 PreviousContainerVersion
, ContainerVersion
);
467 // Process the meta from the separate file.
468 return processSeparateRemarksFileMeta(SeparateMetaHelper
);
471 Error
BitstreamRemarkParser::processStandaloneMeta(
472 BitstreamMetaParserHelper
&Helper
) {
473 if (Error E
= processStrTab(*this, Helper
.StrTabBuf
))
475 return processRemarkVersion(*this, Helper
.RemarkVersion
);
478 Error
BitstreamRemarkParser::processSeparateRemarksFileMeta(
479 BitstreamMetaParserHelper
&Helper
) {
480 return processRemarkVersion(*this, Helper
.RemarkVersion
);
483 Error
BitstreamRemarkParser::processSeparateRemarksMetaMeta(
484 BitstreamMetaParserHelper
&Helper
) {
485 if (Error E
= processStrTab(*this, Helper
.StrTabBuf
))
487 return processExternalFilePath(Helper
.ExternalFilePath
);
490 Expected
<std::unique_ptr
<Remark
>> BitstreamRemarkParser::parseRemark() {
491 BitstreamRemarkParserHelper
RemarkHelper(ParserHelper
.Stream
);
492 if (Error E
= RemarkHelper
.parse())
495 return processRemark(RemarkHelper
);
498 Expected
<std::unique_ptr
<Remark
>>
499 BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper
&Helper
) {
500 std::unique_ptr
<Remark
> Result
= std::make_unique
<Remark
>();
504 return createStringError(
505 std::make_error_code(std::errc::invalid_argument
),
506 "Error while parsing BLOCK_REMARK: missing string table.");
509 return createStringError(
510 std::make_error_code(std::errc::illegal_byte_sequence
),
511 "Error while parsing BLOCK_REMARK: missing remark type.");
513 // Always >= Type::First since it's unsigned.
514 if (*Helper
.Type
> static_cast<uint8_t>(Type::Last
))
515 return createStringError(
516 std::make_error_code(std::errc::illegal_byte_sequence
),
517 "Error while parsing BLOCK_REMARK: unknown remark type.");
519 R
.RemarkType
= static_cast<Type
>(*Helper
.Type
);
521 if (!Helper
.RemarkNameIdx
)
522 return createStringError(
523 std::make_error_code(std::errc::illegal_byte_sequence
),
524 "Error while parsing BLOCK_REMARK: missing remark name.");
526 if (Expected
<StringRef
> RemarkName
= (*StrTab
)[*Helper
.RemarkNameIdx
])
527 R
.RemarkName
= *RemarkName
;
529 return RemarkName
.takeError();
531 if (!Helper
.PassNameIdx
)
532 return createStringError(
533 std::make_error_code(std::errc::illegal_byte_sequence
),
534 "Error while parsing BLOCK_REMARK: missing remark pass.");
536 if (Expected
<StringRef
> PassName
= (*StrTab
)[*Helper
.PassNameIdx
])
537 R
.PassName
= *PassName
;
539 return PassName
.takeError();
541 if (!Helper
.FunctionNameIdx
)
542 return createStringError(
543 std::make_error_code(std::errc::illegal_byte_sequence
),
544 "Error while parsing BLOCK_REMARK: missing remark function name.");
545 if (Expected
<StringRef
> FunctionName
= (*StrTab
)[*Helper
.FunctionNameIdx
])
546 R
.FunctionName
= *FunctionName
;
548 return FunctionName
.takeError();
550 if (Helper
.SourceFileNameIdx
&& Helper
.SourceLine
&& Helper
.SourceColumn
) {
551 Expected
<StringRef
> SourceFileName
= (*StrTab
)[*Helper
.SourceFileNameIdx
];
553 return SourceFileName
.takeError();
555 R
.Loc
->SourceFilePath
= *SourceFileName
;
556 R
.Loc
->SourceLine
= *Helper
.SourceLine
;
557 R
.Loc
->SourceColumn
= *Helper
.SourceColumn
;
561 R
.Hotness
= *Helper
.Hotness
;
564 return std::move(Result
);
566 for (const BitstreamRemarkParserHelper::Argument
&Arg
: *Helper
.Args
) {
568 return createStringError(
569 std::make_error_code(std::errc::illegal_byte_sequence
),
570 "Error while parsing BLOCK_REMARK: missing key in remark argument.");
572 return createStringError(
573 std::make_error_code(std::errc::illegal_byte_sequence
),
574 "Error while parsing BLOCK_REMARK: missing value in remark "
577 // We have at least a key and a value, create an entry.
578 R
.Args
.emplace_back();
580 if (Expected
<StringRef
> Key
= (*StrTab
)[*Arg
.KeyIdx
])
581 R
.Args
.back().Key
= *Key
;
583 return Key
.takeError();
585 if (Expected
<StringRef
> Value
= (*StrTab
)[*Arg
.ValueIdx
])
586 R
.Args
.back().Val
= *Value
;
588 return Value
.takeError();
590 if (Arg
.SourceFileNameIdx
&& Arg
.SourceLine
&& Arg
.SourceColumn
) {
591 if (Expected
<StringRef
> SourceFileName
=
592 (*StrTab
)[*Arg
.SourceFileNameIdx
]) {
593 R
.Args
.back().Loc
.emplace();
594 R
.Args
.back().Loc
->SourceFilePath
= *SourceFileName
;
595 R
.Args
.back().Loc
->SourceLine
= *Arg
.SourceLine
;
596 R
.Args
.back().Loc
->SourceColumn
= *Arg
.SourceColumn
;
598 return SourceFileName
.takeError();
602 return std::move(Result
);