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/BitstreamRemarkContainer.h"
17 #include "llvm/Support/MemoryBuffer.h"
18 #include "llvm/Support/Path.h"
21 using namespace llvm::remarks
;
23 static Error
unknownRecord(const char *BlockName
, unsigned RecordID
) {
24 return createStringError(
25 std::make_error_code(std::errc::illegal_byte_sequence
),
26 "Error while parsing %s: unknown record entry (%lu).", BlockName
,
30 static Error
malformedRecord(const char *BlockName
, const char *RecordName
) {
31 return createStringError(
32 std::make_error_code(std::errc::illegal_byte_sequence
),
33 "Error while parsing %s: malformed record entry (%s).", BlockName
,
37 BitstreamMetaParserHelper::BitstreamMetaParserHelper(
38 BitstreamCursor
&Stream
, BitstreamBlockInfo
&BlockInfo
)
39 : Stream(Stream
), BlockInfo(BlockInfo
) {}
41 /// Parse a record and fill in the fields in the parser.
42 static Error
parseRecord(BitstreamMetaParserHelper
&Parser
, unsigned Code
) {
43 BitstreamCursor
&Stream
= Parser
.Stream
;
44 // Note: 2 is used here because it's the max number of fields we have per
46 SmallVector
<uint64_t, 2> Record
;
48 Expected
<unsigned> RecordID
= Stream
.readRecord(Code
, Record
, &Blob
);
50 return RecordID
.takeError();
53 case RECORD_META_CONTAINER_INFO
: {
54 if (Record
.size() != 2)
55 return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO");
56 Parser
.ContainerVersion
= Record
[0];
57 Parser
.ContainerType
= Record
[1];
60 case RECORD_META_REMARK_VERSION
: {
61 if (Record
.size() != 1)
62 return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION");
63 Parser
.RemarkVersion
= Record
[0];
66 case RECORD_META_STRTAB
: {
67 if (Record
.size() != 0)
68 return malformedRecord("BLOCK_META", "RECORD_META_STRTAB");
69 Parser
.StrTabBuf
= Blob
;
72 case RECORD_META_EXTERNAL_FILE
: {
73 if (Record
.size() != 0)
74 return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE");
75 Parser
.ExternalFilePath
= Blob
;
79 return unknownRecord("BLOCK_META", *RecordID
);
81 return Error::success();
84 BitstreamRemarkParserHelper::BitstreamRemarkParserHelper(
85 BitstreamCursor
&Stream
)
88 /// Parse a record and fill in the fields in the parser.
89 static Error
parseRecord(BitstreamRemarkParserHelper
&Parser
, unsigned Code
) {
90 BitstreamCursor
&Stream
= Parser
.Stream
;
91 // Note: 5 is used here because it's the max number of fields we have per
93 SmallVector
<uint64_t, 5> Record
;
95 Expected
<unsigned> RecordID
= Stream
.readRecord(Code
, Record
, &Blob
);
97 return RecordID
.takeError();
100 case RECORD_REMARK_HEADER
: {
101 if (Record
.size() != 4)
102 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER");
103 Parser
.Type
= Record
[0];
104 Parser
.RemarkNameIdx
= Record
[1];
105 Parser
.PassNameIdx
= Record
[2];
106 Parser
.FunctionNameIdx
= Record
[3];
109 case RECORD_REMARK_DEBUG_LOC
: {
110 if (Record
.size() != 3)
111 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC");
112 Parser
.SourceFileNameIdx
= Record
[0];
113 Parser
.SourceLine
= Record
[1];
114 Parser
.SourceColumn
= Record
[2];
117 case RECORD_REMARK_HOTNESS
: {
118 if (Record
.size() != 1)
119 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS");
120 Parser
.Hotness
= Record
[0];
123 case RECORD_REMARK_ARG_WITH_DEBUGLOC
: {
124 if (Record
.size() != 5)
125 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC");
126 // Create a temporary argument. Use that as a valid memory location for this
128 Parser
.TmpArgs
.emplace_back();
129 Parser
.TmpArgs
.back().KeyIdx
= Record
[0];
130 Parser
.TmpArgs
.back().ValueIdx
= Record
[1];
131 Parser
.TmpArgs
.back().SourceFileNameIdx
= Record
[2];
132 Parser
.TmpArgs
.back().SourceLine
= Record
[3];
133 Parser
.TmpArgs
.back().SourceColumn
= Record
[4];
135 ArrayRef
<BitstreamRemarkParserHelper::Argument
>(Parser
.TmpArgs
);
138 case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC
: {
139 if (Record
.size() != 2)
140 return malformedRecord("BLOCK_REMARK",
141 "RECORD_REMARK_ARG_WITHOUT_DEBUGLOC");
142 // Create a temporary argument. Use that as a valid memory location for this
144 Parser
.TmpArgs
.emplace_back();
145 Parser
.TmpArgs
.back().KeyIdx
= Record
[0];
146 Parser
.TmpArgs
.back().ValueIdx
= Record
[1];
148 ArrayRef
<BitstreamRemarkParserHelper::Argument
>(Parser
.TmpArgs
);
152 return unknownRecord("BLOCK_REMARK", *RecordID
);
154 return Error::success();
157 template <typename T
>
158 static Error
parseBlock(T
&ParserHelper
, unsigned BlockID
,
159 const char *BlockName
) {
160 BitstreamCursor
&Stream
= ParserHelper
.Stream
;
161 Expected
<BitstreamEntry
> Next
= Stream
.advance();
163 return Next
.takeError();
164 if (Next
->Kind
!= BitstreamEntry::SubBlock
|| Next
->ID
!= BlockID
)
165 return createStringError(
166 std::make_error_code(std::errc::illegal_byte_sequence
),
167 "Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].",
168 BlockName
, BlockName
);
169 if (Stream
.EnterSubBlock(BlockID
))
170 return createStringError(
171 std::make_error_code(std::errc::illegal_byte_sequence
),
172 "Error while entering %s.", BlockName
);
174 // Stop when there is nothing to read anymore or when we encounter an
176 while (!Stream
.AtEndOfStream()) {
177 Expected
<BitstreamEntry
> Next
= Stream
.advance();
179 return Next
.takeError();
180 switch (Next
->Kind
) {
181 case BitstreamEntry::EndBlock
:
182 return Error::success();
183 case BitstreamEntry::Error
:
184 case BitstreamEntry::SubBlock
:
185 return createStringError(
186 std::make_error_code(std::errc::illegal_byte_sequence
),
187 "Error while parsing %s: expecting records.", BlockName
);
188 case BitstreamEntry::Record
:
189 if (Error E
= parseRecord(ParserHelper
, Next
->ID
))
194 // If we're here, it means we didn't get an END_BLOCK yet, but we're at the
195 // end of the stream. In this case, error.
196 return createStringError(
197 std::make_error_code(std::errc::illegal_byte_sequence
),
198 "Error while parsing %s: unterminated block.", BlockName
);
201 Error
BitstreamMetaParserHelper::parse() {
202 return parseBlock(*this, META_BLOCK_ID
, "META_BLOCK");
205 Error
BitstreamRemarkParserHelper::parse() {
206 return parseBlock(*this, REMARK_BLOCK_ID
, "REMARK_BLOCK");
209 BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer
)
212 Expected
<std::array
<char, 4>> BitstreamParserHelper::parseMagic() {
213 std::array
<char, 4> Result
;
214 for (unsigned i
= 0; i
< 4; ++i
)
215 if (Expected
<unsigned> R
= Stream
.Read(8))
218 return R
.takeError();
222 Error
BitstreamParserHelper::parseBlockInfoBlock() {
223 Expected
<BitstreamEntry
> Next
= Stream
.advance();
225 return Next
.takeError();
226 if (Next
->Kind
!= BitstreamEntry::SubBlock
||
227 Next
->ID
!= llvm::bitc::BLOCKINFO_BLOCK_ID
)
228 return createStringError(
229 std::make_error_code(std::errc::illegal_byte_sequence
),
230 "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, "
231 "BLOCKINFO_BLOCK, ...].");
233 Expected
<Optional
<BitstreamBlockInfo
>> MaybeBlockInfo
=
234 Stream
.ReadBlockInfoBlock();
236 return MaybeBlockInfo
.takeError();
238 if (!*MaybeBlockInfo
)
239 return createStringError(
240 std::make_error_code(std::errc::illegal_byte_sequence
),
241 "Error while parsing BLOCKINFO_BLOCK.");
243 BlockInfo
= **MaybeBlockInfo
;
245 Stream
.setBlockInfo(&BlockInfo
);
246 return Error::success();
249 static Expected
<bool> isBlock(BitstreamCursor
&Stream
, unsigned BlockID
) {
251 uint64_t PreviousBitNo
= Stream
.GetCurrentBitNo();
252 Expected
<BitstreamEntry
> Next
= Stream
.advance();
254 return Next
.takeError();
255 switch (Next
->Kind
) {
256 case BitstreamEntry::SubBlock
:
257 // Check for the block id.
258 Result
= Next
->ID
== BlockID
;
260 case BitstreamEntry::Error
:
261 return createStringError(
262 std::make_error_code(std::errc::illegal_byte_sequence
),
263 "Unexpected error while parsing bitstream.");
268 if (Error E
= Stream
.JumpToBit(PreviousBitNo
))
273 Expected
<bool> BitstreamParserHelper::isMetaBlock() {
274 return isBlock(Stream
, META_BLOCK_ID
);
277 Expected
<bool> BitstreamParserHelper::isRemarkBlock() {
278 return isBlock(Stream
, META_BLOCK_ID
);
281 static Error
validateMagicNumber(StringRef Magic
) {
282 if (Magic
!= remarks::ContainerMagic
)
283 return createStringError(std::make_error_code(std::errc::invalid_argument
),
284 "Unknown magic number: expecting %s, got %.4s.",
285 remarks::ContainerMagic
.data(), Magic
.data());
286 return Error::success();
289 static Error
advanceToMetaBlock(BitstreamParserHelper
&Helper
) {
290 Expected
<std::array
<char, 4>> Magic
= Helper
.parseMagic();
292 return Magic
.takeError();
293 if (Error E
= validateMagicNumber(StringRef(Magic
->data(), Magic
->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>> Magic
= Helper
.parseMagic();
314 return Magic
.takeError();
316 if (Error E
= validateMagicNumber(StringRef(Magic
->data(), Magic
->size())))
320 StrTab
? std::make_unique
<BitstreamRemarkParser
>(Buf
, std::move(*StrTab
))
321 : std::make_unique
<BitstreamRemarkParser
>(Buf
);
323 if (ExternalFilePrependPath
)
324 Parser
->ExternalFilePrependPath
= *ExternalFilePrependPath
;
326 return std::move(Parser
);
329 Expected
<std::unique_ptr
<Remark
>> BitstreamRemarkParser::next() {
330 if (ParserHelper
.atEndOfStream())
331 return make_error
<EndOfFileError
>();
333 if (!ReadyToParseRemarks
) {
334 if (Error E
= parseMeta())
336 ReadyToParseRemarks
= true;
339 return parseRemark();
342 Error
BitstreamRemarkParser::parseMeta() {
343 // Advance and to the meta block.
344 if (Error E
= advanceToMetaBlock(ParserHelper
))
347 BitstreamMetaParserHelper
MetaHelper(ParserHelper
.Stream
,
348 ParserHelper
.BlockInfo
);
349 if (Error E
= MetaHelper
.parse())
352 if (Error E
= processCommonMeta(MetaHelper
))
355 switch (ContainerType
) {
356 case BitstreamRemarkContainerType::Standalone
:
357 return processStandaloneMeta(MetaHelper
);
358 case BitstreamRemarkContainerType::SeparateRemarksFile
:
359 return processSeparateRemarksFileMeta(MetaHelper
);
360 case BitstreamRemarkContainerType::SeparateRemarksMeta
:
361 return processSeparateRemarksMetaMeta(MetaHelper
);
363 llvm_unreachable("Unknown BitstreamRemarkContainerType enum");
366 Error
BitstreamRemarkParser::processCommonMeta(
367 BitstreamMetaParserHelper
&MetaHelper
) {
368 if (Optional
<uint64_t> Version
= MetaHelper
.ContainerVersion
)
369 ContainerVersion
= *Version
;
371 return createStringError(
372 std::make_error_code(std::errc::illegal_byte_sequence
),
373 "Error while parsing BLOCK_META: missing container version.");
375 if (Optional
<uint8_t> Type
= MetaHelper
.ContainerType
) {
376 // Always >= BitstreamRemarkContainerType::First since it's unsigned.
377 if (*Type
> static_cast<uint8_t>(BitstreamRemarkContainerType::Last
))
378 return createStringError(
379 std::make_error_code(std::errc::illegal_byte_sequence
),
380 "Error while parsing BLOCK_META: invalid container type.");
382 ContainerType
= static_cast<BitstreamRemarkContainerType
>(*Type
);
384 return createStringError(
385 std::make_error_code(std::errc::illegal_byte_sequence
),
386 "Error while parsing BLOCK_META: missing container type.");
388 return Error::success();
391 static Error
processStrTab(BitstreamRemarkParser
&P
,
392 Optional
<StringRef
> StrTabBuf
) {
394 return createStringError(
395 std::make_error_code(std::errc::illegal_byte_sequence
),
396 "Error while parsing BLOCK_META: missing string table.");
397 // Parse and assign the string table.
398 P
.StrTab
.emplace(*StrTabBuf
);
399 return Error::success();
402 static Error
processRemarkVersion(BitstreamRemarkParser
&P
,
403 Optional
<uint64_t> RemarkVersion
) {
405 return createStringError(
406 std::make_error_code(std::errc::illegal_byte_sequence
),
407 "Error while parsing BLOCK_META: missing remark version.");
408 P
.RemarkVersion
= *RemarkVersion
;
409 return Error::success();
412 Error
BitstreamRemarkParser::processExternalFilePath(
413 Optional
<StringRef
> ExternalFilePath
) {
414 if (!ExternalFilePath
)
415 return createStringError(
416 std::make_error_code(std::errc::illegal_byte_sequence
),
417 "Error while parsing BLOCK_META: missing external file path.");
419 SmallString
<80> FullPath(ExternalFilePrependPath
);
420 sys::path::append(FullPath
, *ExternalFilePath
);
422 // External file: open the external file, parse it, check if its metadata
423 // matches the one from the separate metadata, then replace the current parser
424 // with the one parsing the remarks.
425 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferOrErr
=
426 MemoryBuffer::getFile(FullPath
);
427 if (std::error_code EC
= BufferOrErr
.getError())
428 return createFileError(FullPath
, EC
);
429 TmpRemarkBuffer
= std::move(*BufferOrErr
);
431 // Create a separate parser used for parsing the separate file.
432 ParserHelper
= BitstreamParserHelper(TmpRemarkBuffer
->getBuffer());
433 // Advance and check until we can parse the meta block.
434 if (Error E
= advanceToMetaBlock(ParserHelper
))
436 // Parse the meta from the separate file.
437 // Note: here we overwrite the BlockInfo with the one from the file. This will
438 // be used to parse the rest of the file.
439 BitstreamMetaParserHelper
SeparateMetaHelper(ParserHelper
.Stream
,
440 ParserHelper
.BlockInfo
);
441 if (Error E
= SeparateMetaHelper
.parse())
444 uint64_t PreviousContainerVersion
= ContainerVersion
;
445 if (Error E
= processCommonMeta(SeparateMetaHelper
))
448 if (ContainerType
!= BitstreamRemarkContainerType::SeparateRemarksFile
)
449 return createStringError(
450 std::make_error_code(std::errc::illegal_byte_sequence
),
451 "Error while parsing external file's BLOCK_META: wrong container "
454 if (PreviousContainerVersion
!= ContainerVersion
)
455 return createStringError(
456 std::make_error_code(std::errc::illegal_byte_sequence
),
457 "Error while parsing external file's BLOCK_META: mismatching versions: "
458 "original meta: %lu, external file meta: %lu.",
459 PreviousContainerVersion
, ContainerVersion
);
461 // Process the meta from the separate file.
462 return processSeparateRemarksFileMeta(SeparateMetaHelper
);
465 Error
BitstreamRemarkParser::processStandaloneMeta(
466 BitstreamMetaParserHelper
&Helper
) {
467 if (Error E
= processStrTab(*this, Helper
.StrTabBuf
))
469 return processRemarkVersion(*this, Helper
.RemarkVersion
);
472 Error
BitstreamRemarkParser::processSeparateRemarksFileMeta(
473 BitstreamMetaParserHelper
&Helper
) {
474 return processRemarkVersion(*this, Helper
.RemarkVersion
);
477 Error
BitstreamRemarkParser::processSeparateRemarksMetaMeta(
478 BitstreamMetaParserHelper
&Helper
) {
479 if (Error E
= processStrTab(*this, Helper
.StrTabBuf
))
481 return processExternalFilePath(Helper
.ExternalFilePath
);
484 Expected
<std::unique_ptr
<Remark
>> BitstreamRemarkParser::parseRemark() {
485 BitstreamRemarkParserHelper
RemarkHelper(ParserHelper
.Stream
);
486 if (Error E
= RemarkHelper
.parse())
489 return processRemark(RemarkHelper
);
492 Expected
<std::unique_ptr
<Remark
>>
493 BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper
&Helper
) {
494 std::unique_ptr
<Remark
> Result
= std::make_unique
<Remark
>();
498 return createStringError(
499 std::make_error_code(std::errc::invalid_argument
),
500 "Error while parsing BLOCK_REMARK: missing string table.");
503 return createStringError(
504 std::make_error_code(std::errc::illegal_byte_sequence
),
505 "Error while parsing BLOCK_REMARK: missing remark type.");
507 // Always >= Type::First since it's unsigned.
508 if (*Helper
.Type
> static_cast<uint8_t>(Type::Last
))
509 return createStringError(
510 std::make_error_code(std::errc::illegal_byte_sequence
),
511 "Error while parsing BLOCK_REMARK: unknown remark type.");
513 R
.RemarkType
= static_cast<Type
>(*Helper
.Type
);
515 if (!Helper
.RemarkNameIdx
)
516 return createStringError(
517 std::make_error_code(std::errc::illegal_byte_sequence
),
518 "Error while parsing BLOCK_REMARK: missing remark name.");
520 if (Expected
<StringRef
> RemarkName
= (*StrTab
)[*Helper
.RemarkNameIdx
])
521 R
.RemarkName
= *RemarkName
;
523 return RemarkName
.takeError();
525 if (!Helper
.PassNameIdx
)
526 return createStringError(
527 std::make_error_code(std::errc::illegal_byte_sequence
),
528 "Error while parsing BLOCK_REMARK: missing remark pass.");
530 if (Expected
<StringRef
> PassName
= (*StrTab
)[*Helper
.PassNameIdx
])
531 R
.PassName
= *PassName
;
533 return PassName
.takeError();
535 if (!Helper
.FunctionNameIdx
)
536 return createStringError(
537 std::make_error_code(std::errc::illegal_byte_sequence
),
538 "Error while parsing BLOCK_REMARK: missing remark function name.");
539 if (Expected
<StringRef
> FunctionName
= (*StrTab
)[*Helper
.FunctionNameIdx
])
540 R
.FunctionName
= *FunctionName
;
542 return FunctionName
.takeError();
544 if (Helper
.SourceFileNameIdx
&& Helper
.SourceLine
&& Helper
.SourceColumn
) {
545 Expected
<StringRef
> SourceFileName
= (*StrTab
)[*Helper
.SourceFileNameIdx
];
547 return SourceFileName
.takeError();
549 R
.Loc
->SourceFilePath
= *SourceFileName
;
550 R
.Loc
->SourceLine
= *Helper
.SourceLine
;
551 R
.Loc
->SourceColumn
= *Helper
.SourceColumn
;
555 R
.Hotness
= *Helper
.Hotness
;
558 return std::move(Result
);
560 for (const BitstreamRemarkParserHelper::Argument
&Arg
: *Helper
.Args
) {
562 return createStringError(
563 std::make_error_code(std::errc::illegal_byte_sequence
),
564 "Error while parsing BLOCK_REMARK: missing key in remark argument.");
566 return createStringError(
567 std::make_error_code(std::errc::illegal_byte_sequence
),
568 "Error while parsing BLOCK_REMARK: missing value in remark "
571 // We have at least a key and a value, create an entry.
572 R
.Args
.emplace_back();
574 if (Expected
<StringRef
> Key
= (*StrTab
)[*Arg
.KeyIdx
])
575 R
.Args
.back().Key
= *Key
;
577 return Key
.takeError();
579 if (Expected
<StringRef
> Value
= (*StrTab
)[*Arg
.ValueIdx
])
580 R
.Args
.back().Val
= *Value
;
582 return Value
.takeError();
584 if (Arg
.SourceFileNameIdx
&& Arg
.SourceLine
&& Arg
.SourceColumn
) {
585 if (Expected
<StringRef
> SourceFileName
=
586 (*StrTab
)[*Arg
.SourceFileNameIdx
]) {
587 R
.Args
.back().Loc
.emplace();
588 R
.Args
.back().Loc
->SourceFilePath
= *SourceFileName
;
589 R
.Args
.back().Loc
->SourceLine
= *Arg
.SourceLine
;
590 R
.Args
.back().Loc
->SourceColumn
= *Arg
.SourceColumn
;
592 return SourceFileName
.takeError();
596 return std::move(Result
);