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/Remarks/Remark.h"
17 #include "llvm/Support/MemoryBuffer.h"
18 #include "llvm/Support/Path.h"
22 using namespace llvm::remarks
;
24 static Error
unknownRecord(const char *BlockName
, unsigned RecordID
) {
25 return createStringError(
26 std::make_error_code(std::errc::illegal_byte_sequence
),
27 "Error while parsing %s: unknown record entry (%lu).", BlockName
,
31 static Error
malformedRecord(const char *BlockName
, const char *RecordName
) {
32 return createStringError(
33 std::make_error_code(std::errc::illegal_byte_sequence
),
34 "Error while parsing %s: malformed record entry (%s).", BlockName
,
38 BitstreamMetaParserHelper::BitstreamMetaParserHelper(
39 BitstreamCursor
&Stream
, BitstreamBlockInfo
&BlockInfo
)
40 : Stream(Stream
), BlockInfo(BlockInfo
) {}
42 /// Parse a record and fill in the fields in the parser.
43 static Error
parseRecord(BitstreamMetaParserHelper
&Parser
, unsigned Code
) {
44 BitstreamCursor
&Stream
= Parser
.Stream
;
45 // Note: 2 is used here because it's the max number of fields we have per
47 SmallVector
<uint64_t, 2> Record
;
49 Expected
<unsigned> RecordID
= Stream
.readRecord(Code
, Record
, &Blob
);
51 return RecordID
.takeError();
54 case RECORD_META_CONTAINER_INFO
: {
55 if (Record
.size() != 2)
56 return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO");
57 Parser
.ContainerVersion
= Record
[0];
58 Parser
.ContainerType
= Record
[1];
61 case RECORD_META_REMARK_VERSION
: {
62 if (Record
.size() != 1)
63 return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION");
64 Parser
.RemarkVersion
= Record
[0];
67 case RECORD_META_STRTAB
: {
68 if (Record
.size() != 0)
69 return malformedRecord("BLOCK_META", "RECORD_META_STRTAB");
70 Parser
.StrTabBuf
= Blob
;
73 case RECORD_META_EXTERNAL_FILE
: {
74 if (Record
.size() != 0)
75 return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE");
76 Parser
.ExternalFilePath
= Blob
;
80 return unknownRecord("BLOCK_META", *RecordID
);
82 return Error::success();
85 BitstreamRemarkParserHelper::BitstreamRemarkParserHelper(
86 BitstreamCursor
&Stream
)
89 /// Parse a record and fill in the fields in the parser.
90 static Error
parseRecord(BitstreamRemarkParserHelper
&Parser
, unsigned Code
) {
91 BitstreamCursor
&Stream
= Parser
.Stream
;
92 // Note: 5 is used here because it's the max number of fields we have per
94 SmallVector
<uint64_t, 5> Record
;
96 Expected
<unsigned> RecordID
= Stream
.readRecord(Code
, Record
, &Blob
);
98 return RecordID
.takeError();
101 case RECORD_REMARK_HEADER
: {
102 if (Record
.size() != 4)
103 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER");
104 Parser
.Type
= Record
[0];
105 Parser
.RemarkNameIdx
= Record
[1];
106 Parser
.PassNameIdx
= Record
[2];
107 Parser
.FunctionNameIdx
= Record
[3];
110 case RECORD_REMARK_DEBUG_LOC
: {
111 if (Record
.size() != 3)
112 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC");
113 Parser
.SourceFileNameIdx
= Record
[0];
114 Parser
.SourceLine
= Record
[1];
115 Parser
.SourceColumn
= Record
[2];
118 case RECORD_REMARK_HOTNESS
: {
119 if (Record
.size() != 1)
120 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS");
121 Parser
.Hotness
= Record
[0];
124 case RECORD_REMARK_ARG_WITH_DEBUGLOC
: {
125 if (Record
.size() != 5)
126 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC");
127 // Create a temporary argument. Use that as a valid memory location for this
129 Parser
.TmpArgs
.emplace_back();
130 Parser
.TmpArgs
.back().KeyIdx
= Record
[0];
131 Parser
.TmpArgs
.back().ValueIdx
= Record
[1];
132 Parser
.TmpArgs
.back().SourceFileNameIdx
= Record
[2];
133 Parser
.TmpArgs
.back().SourceLine
= Record
[3];
134 Parser
.TmpArgs
.back().SourceColumn
= Record
[4];
136 ArrayRef
<BitstreamRemarkParserHelper::Argument
>(Parser
.TmpArgs
);
139 case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC
: {
140 if (Record
.size() != 2)
141 return malformedRecord("BLOCK_REMARK",
142 "RECORD_REMARK_ARG_WITHOUT_DEBUGLOC");
143 // Create a temporary argument. Use that as a valid memory location for this
145 Parser
.TmpArgs
.emplace_back();
146 Parser
.TmpArgs
.back().KeyIdx
= Record
[0];
147 Parser
.TmpArgs
.back().ValueIdx
= Record
[1];
149 ArrayRef
<BitstreamRemarkParserHelper::Argument
>(Parser
.TmpArgs
);
153 return unknownRecord("BLOCK_REMARK", *RecordID
);
155 return Error::success();
158 template <typename T
>
159 static Error
parseBlock(T
&ParserHelper
, unsigned BlockID
,
160 const char *BlockName
) {
161 BitstreamCursor
&Stream
= ParserHelper
.Stream
;
162 Expected
<BitstreamEntry
> Next
= Stream
.advance();
164 return Next
.takeError();
165 if (Next
->Kind
!= BitstreamEntry::SubBlock
|| Next
->ID
!= BlockID
)
166 return createStringError(
167 std::make_error_code(std::errc::illegal_byte_sequence
),
168 "Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].",
169 BlockName
, BlockName
);
170 if (Stream
.EnterSubBlock(BlockID
))
171 return createStringError(
172 std::make_error_code(std::errc::illegal_byte_sequence
),
173 "Error while entering %s.", BlockName
);
175 // Stop when there is nothing to read anymore or when we encounter an
177 while (!Stream
.AtEndOfStream()) {
178 Next
= Stream
.advance();
180 return Next
.takeError();
181 switch (Next
->Kind
) {
182 case BitstreamEntry::EndBlock
:
183 return Error::success();
184 case BitstreamEntry::Error
:
185 case BitstreamEntry::SubBlock
:
186 return createStringError(
187 std::make_error_code(std::errc::illegal_byte_sequence
),
188 "Error while parsing %s: expecting records.", BlockName
);
189 case BitstreamEntry::Record
:
190 if (Error E
= parseRecord(ParserHelper
, Next
->ID
))
195 // If we're here, it means we didn't get an END_BLOCK yet, but we're at the
196 // end of the stream. In this case, error.
197 return createStringError(
198 std::make_error_code(std::errc::illegal_byte_sequence
),
199 "Error while parsing %s: unterminated block.", BlockName
);
202 Error
BitstreamMetaParserHelper::parse() {
203 return parseBlock(*this, META_BLOCK_ID
, "META_BLOCK");
206 Error
BitstreamRemarkParserHelper::parse() {
207 return parseBlock(*this, REMARK_BLOCK_ID
, "REMARK_BLOCK");
210 BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer
)
213 Expected
<std::array
<char, 4>> BitstreamParserHelper::parseMagic() {
214 std::array
<char, 4> Result
;
215 for (unsigned i
= 0; i
< 4; ++i
)
216 if (Expected
<unsigned> R
= Stream
.Read(8))
219 return R
.takeError();
223 Error
BitstreamParserHelper::parseBlockInfoBlock() {
224 Expected
<BitstreamEntry
> Next
= Stream
.advance();
226 return Next
.takeError();
227 if (Next
->Kind
!= BitstreamEntry::SubBlock
||
228 Next
->ID
!= llvm::bitc::BLOCKINFO_BLOCK_ID
)
229 return createStringError(
230 std::make_error_code(std::errc::illegal_byte_sequence
),
231 "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, "
232 "BLOCKINFO_BLOCK, ...].");
234 Expected
<std::optional
<BitstreamBlockInfo
>> MaybeBlockInfo
=
235 Stream
.ReadBlockInfoBlock();
237 return MaybeBlockInfo
.takeError();
239 if (!*MaybeBlockInfo
)
240 return createStringError(
241 std::make_error_code(std::errc::illegal_byte_sequence
),
242 "Error while parsing BLOCKINFO_BLOCK.");
244 BlockInfo
= **MaybeBlockInfo
;
246 Stream
.setBlockInfo(&BlockInfo
);
247 return Error::success();
250 static Expected
<bool> isBlock(BitstreamCursor
&Stream
, unsigned BlockID
) {
252 uint64_t PreviousBitNo
= Stream
.GetCurrentBitNo();
253 Expected
<BitstreamEntry
> Next
= Stream
.advance();
255 return Next
.takeError();
256 switch (Next
->Kind
) {
257 case BitstreamEntry::SubBlock
:
258 // Check for the block id.
259 Result
= Next
->ID
== BlockID
;
261 case BitstreamEntry::Error
:
262 return createStringError(
263 std::make_error_code(std::errc::illegal_byte_sequence
),
264 "Unexpected error while parsing bitstream.");
269 if (Error E
= Stream
.JumpToBit(PreviousBitNo
))
274 Expected
<bool> BitstreamParserHelper::isMetaBlock() {
275 return isBlock(Stream
, META_BLOCK_ID
);
278 Expected
<bool> BitstreamParserHelper::isRemarkBlock() {
279 return isBlock(Stream
, META_BLOCK_ID
);
282 static Error
validateMagicNumber(StringRef MagicNumber
) {
283 if (MagicNumber
!= remarks::ContainerMagic
)
284 return createStringError(std::make_error_code(std::errc::invalid_argument
),
285 "Unknown magic number: expecting %s, got %.4s.",
286 remarks::ContainerMagic
.data(), MagicNumber
.data());
287 return Error::success();
290 static Error
advanceToMetaBlock(BitstreamParserHelper
&Helper
) {
291 Expected
<std::array
<char, 4>> MagicNumber
= Helper
.parseMagic();
293 return MagicNumber
.takeError();
294 if (Error E
= validateMagicNumber(
295 StringRef(MagicNumber
->data(), MagicNumber
->size())))
297 if (Error E
= Helper
.parseBlockInfoBlock())
299 Expected
<bool> isMetaBlock
= Helper
.isMetaBlock();
301 return isMetaBlock
.takeError();
303 return createStringError(
304 std::make_error_code(std::errc::illegal_byte_sequence
),
305 "Expecting META_BLOCK after the BLOCKINFO_BLOCK.");
306 return Error::success();
309 Expected
<std::unique_ptr
<BitstreamRemarkParser
>>
310 remarks::createBitstreamParserFromMeta(
311 StringRef Buf
, std::optional
<ParsedStringTable
> StrTab
,
312 std::optional
<StringRef
> ExternalFilePrependPath
) {
313 BitstreamParserHelper
Helper(Buf
);
314 Expected
<std::array
<char, 4>> MagicNumber
= Helper
.parseMagic();
316 return MagicNumber
.takeError();
318 if (Error E
= validateMagicNumber(
319 StringRef(MagicNumber
->data(), MagicNumber
->size())))
323 StrTab
? std::make_unique
<BitstreamRemarkParser
>(Buf
, std::move(*StrTab
))
324 : std::make_unique
<BitstreamRemarkParser
>(Buf
);
326 if (ExternalFilePrependPath
)
327 Parser
->ExternalFilePrependPath
= std::string(*ExternalFilePrependPath
);
329 return std::move(Parser
);
332 Expected
<std::unique_ptr
<Remark
>> BitstreamRemarkParser::next() {
333 if (ParserHelper
.atEndOfStream())
334 return make_error
<EndOfFileError
>();
336 if (!ReadyToParseRemarks
) {
337 if (Error E
= parseMeta())
339 ReadyToParseRemarks
= true;
342 return parseRemark();
345 Error
BitstreamRemarkParser::parseMeta() {
346 // Advance and to the meta block.
347 if (Error E
= advanceToMetaBlock(ParserHelper
))
350 BitstreamMetaParserHelper
MetaHelper(ParserHelper
.Stream
,
351 ParserHelper
.BlockInfo
);
352 if (Error E
= MetaHelper
.parse())
355 if (Error E
= processCommonMeta(MetaHelper
))
358 switch (ContainerType
) {
359 case BitstreamRemarkContainerType::Standalone
:
360 return processStandaloneMeta(MetaHelper
);
361 case BitstreamRemarkContainerType::SeparateRemarksFile
:
362 return processSeparateRemarksFileMeta(MetaHelper
);
363 case BitstreamRemarkContainerType::SeparateRemarksMeta
:
364 return processSeparateRemarksMetaMeta(MetaHelper
);
366 llvm_unreachable("Unknown BitstreamRemarkContainerType enum");
369 Error
BitstreamRemarkParser::processCommonMeta(
370 BitstreamMetaParserHelper
&Helper
) {
371 if (std::optional
<uint64_t> Version
= Helper
.ContainerVersion
)
372 ContainerVersion
= *Version
;
374 return createStringError(
375 std::make_error_code(std::errc::illegal_byte_sequence
),
376 "Error while parsing BLOCK_META: missing container version.");
378 if (std::optional
<uint8_t> Type
= Helper
.ContainerType
) {
379 // Always >= BitstreamRemarkContainerType::First since it's unsigned.
380 if (*Type
> static_cast<uint8_t>(BitstreamRemarkContainerType::Last
))
381 return createStringError(
382 std::make_error_code(std::errc::illegal_byte_sequence
),
383 "Error while parsing BLOCK_META: invalid container type.");
385 ContainerType
= static_cast<BitstreamRemarkContainerType
>(*Type
);
387 return createStringError(
388 std::make_error_code(std::errc::illegal_byte_sequence
),
389 "Error while parsing BLOCK_META: missing container type.");
391 return Error::success();
394 static Error
processStrTab(BitstreamRemarkParser
&P
,
395 std::optional
<StringRef
> StrTabBuf
) {
397 return createStringError(
398 std::make_error_code(std::errc::illegal_byte_sequence
),
399 "Error while parsing BLOCK_META: missing string table.");
400 // Parse and assign the string table.
401 P
.StrTab
.emplace(*StrTabBuf
);
402 return Error::success();
405 static Error
processRemarkVersion(BitstreamRemarkParser
&P
,
406 std::optional
<uint64_t> RemarkVersion
) {
408 return createStringError(
409 std::make_error_code(std::errc::illegal_byte_sequence
),
410 "Error while parsing BLOCK_META: missing remark version.");
411 P
.RemarkVersion
= *RemarkVersion
;
412 return Error::success();
415 Error
BitstreamRemarkParser::processExternalFilePath(
416 std::optional
<StringRef
> ExternalFilePath
) {
417 if (!ExternalFilePath
)
418 return createStringError(
419 std::make_error_code(std::errc::illegal_byte_sequence
),
420 "Error while parsing BLOCK_META: missing external file path.");
422 SmallString
<80> FullPath(ExternalFilePrependPath
);
423 sys::path::append(FullPath
, *ExternalFilePath
);
425 // External file: open the external file, parse it, check if its metadata
426 // matches the one from the separate metadata, then replace the current parser
427 // with the one parsing the remarks.
428 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferOrErr
=
429 MemoryBuffer::getFile(FullPath
);
430 if (std::error_code EC
= BufferOrErr
.getError())
431 return createFileError(FullPath
, EC
);
433 TmpRemarkBuffer
= std::move(*BufferOrErr
);
435 // Don't try to parse the file if it's empty.
436 if (TmpRemarkBuffer
->getBufferSize() == 0)
437 return make_error
<EndOfFileError
>();
439 // Create a separate parser used for parsing the separate file.
440 ParserHelper
= BitstreamParserHelper(TmpRemarkBuffer
->getBuffer());
441 // Advance and check until we can parse the meta block.
442 if (Error E
= advanceToMetaBlock(ParserHelper
))
444 // Parse the meta from the separate file.
445 // Note: here we overwrite the BlockInfo with the one from the file. This will
446 // be used to parse the rest of the file.
447 BitstreamMetaParserHelper
SeparateMetaHelper(ParserHelper
.Stream
,
448 ParserHelper
.BlockInfo
);
449 if (Error E
= SeparateMetaHelper
.parse())
452 uint64_t PreviousContainerVersion
= ContainerVersion
;
453 if (Error E
= processCommonMeta(SeparateMetaHelper
))
456 if (ContainerType
!= BitstreamRemarkContainerType::SeparateRemarksFile
)
457 return createStringError(
458 std::make_error_code(std::errc::illegal_byte_sequence
),
459 "Error while parsing external file's BLOCK_META: wrong container "
462 if (PreviousContainerVersion
!= ContainerVersion
)
463 return createStringError(
464 std::make_error_code(std::errc::illegal_byte_sequence
),
465 "Error while parsing external file's BLOCK_META: mismatching versions: "
466 "original meta: %lu, external file meta: %lu.",
467 PreviousContainerVersion
, ContainerVersion
);
469 // Process the meta from the separate file.
470 return processSeparateRemarksFileMeta(SeparateMetaHelper
);
473 Error
BitstreamRemarkParser::processStandaloneMeta(
474 BitstreamMetaParserHelper
&Helper
) {
475 if (Error E
= processStrTab(*this, Helper
.StrTabBuf
))
477 return processRemarkVersion(*this, Helper
.RemarkVersion
);
480 Error
BitstreamRemarkParser::processSeparateRemarksFileMeta(
481 BitstreamMetaParserHelper
&Helper
) {
482 return processRemarkVersion(*this, Helper
.RemarkVersion
);
485 Error
BitstreamRemarkParser::processSeparateRemarksMetaMeta(
486 BitstreamMetaParserHelper
&Helper
) {
487 if (Error E
= processStrTab(*this, Helper
.StrTabBuf
))
489 return processExternalFilePath(Helper
.ExternalFilePath
);
492 Expected
<std::unique_ptr
<Remark
>> BitstreamRemarkParser::parseRemark() {
493 BitstreamRemarkParserHelper
RemarkHelper(ParserHelper
.Stream
);
494 if (Error E
= RemarkHelper
.parse())
497 return processRemark(RemarkHelper
);
500 Expected
<std::unique_ptr
<Remark
>>
501 BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper
&Helper
) {
502 std::unique_ptr
<Remark
> Result
= std::make_unique
<Remark
>();
505 if (StrTab
== std::nullopt
)
506 return createStringError(
507 std::make_error_code(std::errc::invalid_argument
),
508 "Error while parsing BLOCK_REMARK: missing string table.");
511 return createStringError(
512 std::make_error_code(std::errc::illegal_byte_sequence
),
513 "Error while parsing BLOCK_REMARK: missing remark type.");
515 // Always >= Type::First since it's unsigned.
516 if (*Helper
.Type
> static_cast<uint8_t>(Type::Last
))
517 return createStringError(
518 std::make_error_code(std::errc::illegal_byte_sequence
),
519 "Error while parsing BLOCK_REMARK: unknown remark type.");
521 R
.RemarkType
= static_cast<Type
>(*Helper
.Type
);
523 if (!Helper
.RemarkNameIdx
)
524 return createStringError(
525 std::make_error_code(std::errc::illegal_byte_sequence
),
526 "Error while parsing BLOCK_REMARK: missing remark name.");
528 if (Expected
<StringRef
> RemarkName
= (*StrTab
)[*Helper
.RemarkNameIdx
])
529 R
.RemarkName
= *RemarkName
;
531 return RemarkName
.takeError();
533 if (!Helper
.PassNameIdx
)
534 return createStringError(
535 std::make_error_code(std::errc::illegal_byte_sequence
),
536 "Error while parsing BLOCK_REMARK: missing remark pass.");
538 if (Expected
<StringRef
> PassName
= (*StrTab
)[*Helper
.PassNameIdx
])
539 R
.PassName
= *PassName
;
541 return PassName
.takeError();
543 if (!Helper
.FunctionNameIdx
)
544 return createStringError(
545 std::make_error_code(std::errc::illegal_byte_sequence
),
546 "Error while parsing BLOCK_REMARK: missing remark function name.");
547 if (Expected
<StringRef
> FunctionName
= (*StrTab
)[*Helper
.FunctionNameIdx
])
548 R
.FunctionName
= *FunctionName
;
550 return FunctionName
.takeError();
552 if (Helper
.SourceFileNameIdx
&& Helper
.SourceLine
&& Helper
.SourceColumn
) {
553 Expected
<StringRef
> SourceFileName
= (*StrTab
)[*Helper
.SourceFileNameIdx
];
555 return SourceFileName
.takeError();
557 R
.Loc
->SourceFilePath
= *SourceFileName
;
558 R
.Loc
->SourceLine
= *Helper
.SourceLine
;
559 R
.Loc
->SourceColumn
= *Helper
.SourceColumn
;
563 R
.Hotness
= *Helper
.Hotness
;
566 return std::move(Result
);
568 for (const BitstreamRemarkParserHelper::Argument
&Arg
: *Helper
.Args
) {
570 return createStringError(
571 std::make_error_code(std::errc::illegal_byte_sequence
),
572 "Error while parsing BLOCK_REMARK: missing key in remark argument.");
574 return createStringError(
575 std::make_error_code(std::errc::illegal_byte_sequence
),
576 "Error while parsing BLOCK_REMARK: missing value in remark "
579 // We have at least a key and a value, create an entry.
580 R
.Args
.emplace_back();
582 if (Expected
<StringRef
> Key
= (*StrTab
)[*Arg
.KeyIdx
])
583 R
.Args
.back().Key
= *Key
;
585 return Key
.takeError();
587 if (Expected
<StringRef
> Value
= (*StrTab
)[*Arg
.ValueIdx
])
588 R
.Args
.back().Val
= *Value
;
590 return Value
.takeError();
592 if (Arg
.SourceFileNameIdx
&& Arg
.SourceLine
&& Arg
.SourceColumn
) {
593 if (Expected
<StringRef
> SourceFileName
=
594 (*StrTab
)[*Arg
.SourceFileNameIdx
]) {
595 R
.Args
.back().Loc
.emplace();
596 R
.Args
.back().Loc
->SourceFilePath
= *SourceFileName
;
597 R
.Args
.back().Loc
->SourceLine
= *Arg
.SourceLine
;
598 R
.Args
.back().Loc
->SourceColumn
= *Arg
.SourceColumn
;
600 return SourceFileName
.takeError();
604 return std::move(Result
);