1 //===- RemarkParser.cpp --------------------------------------------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file provides utility methods used by clients that want to use the 10 // parser for remark diagnostics in LLVM. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Remarks/RemarkParser.h" 15 #include "YAMLRemarkParser.h" 16 #include "llvm-c/Remarks.h" 17 #include "llvm/ADT/STLExtras.h" 18 #include "llvm/Support/CBindingWrapping.h" 19 20 using namespace llvm; 21 using namespace llvm::remarks; 22 23 char EndOfFileError::ID = 0; 24 25 ParsedStringTable::ParsedStringTable(StringRef InBuffer) : Buffer(InBuffer) { 26 while (!InBuffer.empty()) { 27 // Strings are separated by '\0' bytes. 28 std::pair<StringRef, StringRef> Split = InBuffer.split('\0'); 29 // We only store the offset from the beginning of the buffer. 30 Offsets.push_back(Split.first.data() - Buffer.data()); 31 InBuffer = Split.second; 32 } 33 } 34 35 Expected<StringRef> ParsedStringTable::operator[](size_t Index) const { 36 if (Index >= Offsets.size()) 37 return createStringError( 38 std::make_error_code(std::errc::invalid_argument), 39 "String with index %u is out of bounds (size = %u).", Index, 40 Offsets.size()); 41 42 size_t Offset = Offsets[Index]; 43 // If it's the last offset, we can't use the next offset to know the size of 44 // the string. 45 size_t NextOffset = 46 (Index == Offsets.size() - 1) ? Buffer.size() : Offsets[Index + 1]; 47 return StringRef(Buffer.data() + Offset, NextOffset - Offset - 1); 48 } 49 50 Expected<std::unique_ptr<RemarkParser>> 51 llvm::remarks::createRemarkParser(Format ParserFormat, StringRef Buf) { 52 switch (ParserFormat) { 53 case Format::YAML: 54 return llvm::make_unique<YAMLRemarkParser>(Buf); 55 case Format::YAMLStrTab: 56 return createStringError( 57 std::make_error_code(std::errc::invalid_argument), 58 "The YAML with string table format requires a parsed string table."); 59 case Format::Unknown: 60 return createStringError(std::make_error_code(std::errc::invalid_argument), 61 "Unknown remark parser format."); 62 } 63 llvm_unreachable("unhandled ParseFormat"); 64 } 65 66 Expected<std::unique_ptr<RemarkParser>> 67 llvm::remarks::createRemarkParser(Format ParserFormat, StringRef Buf, 68 ParsedStringTable StrTab) { 69 switch (ParserFormat) { 70 case Format::YAML: 71 return createStringError(std::make_error_code(std::errc::invalid_argument), 72 "The YAML format can't be used with a string " 73 "table. Use yaml-strtab instead."); 74 case Format::YAMLStrTab: 75 return llvm::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(StrTab)); 76 case Format::Unknown: 77 return createStringError(std::make_error_code(std::errc::invalid_argument), 78 "Unknown remark parser format."); 79 } 80 llvm_unreachable("unhandled ParseFormat"); 81 } 82 83 Expected<std::unique_ptr<RemarkParser>> 84 llvm::remarks::createRemarkParserFromMeta(Format ParserFormat, StringRef Buf, 85 Optional<ParsedStringTable> StrTab) { 86 switch (ParserFormat) { 87 // Depending on the metadata, the format can be either yaml or yaml-strtab, 88 // regardless of the input argument. 89 case Format::YAML: 90 case Format::YAMLStrTab: 91 return createYAMLParserFromMeta(Buf, std::move(StrTab)); 92 case Format::Unknown: 93 return createStringError(std::make_error_code(std::errc::invalid_argument), 94 "Unknown remark parser format."); 95 } 96 llvm_unreachable("unhandled ParseFormat"); 97 } 98 99 // Wrapper that holds the state needed to interact with the C API. 100 struct CParser { 101 std::unique_ptr<RemarkParser> TheParser; 102 Optional<std::string> Err; 103 104 CParser(Format ParserFormat, StringRef Buf, 105 Optional<ParsedStringTable> StrTab = None) 106 : TheParser(cantFail( 107 StrTab ? createRemarkParser(ParserFormat, Buf, std::move(*StrTab)) 108 : createRemarkParser(ParserFormat, Buf))) {} 109 110 void handleError(Error E) { Err.emplace(toString(std::move(E))); } 111 bool hasError() const { return Err.hasValue(); } 112 const char *getMessage() const { return Err ? Err->c_str() : nullptr; }; 113 }; 114 115 // Create wrappers for C Binding types (see CBindingWrapping.h). 116 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(CParser, LLVMRemarkParserRef) 117 118 extern "C" LLVMRemarkParserRef LLVMRemarkParserCreateYAML(const void *Buf, 119 uint64_t Size) { 120 return wrap(new CParser(Format::YAML, 121 StringRef(static_cast<const char *>(Buf), Size))); 122 } 123 124 extern "C" LLVMRemarkEntryRef 125 LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser) { 126 CParser &TheCParser = *unwrap(Parser); 127 remarks::RemarkParser &TheParser = *TheCParser.TheParser; 128 129 Expected<std::unique_ptr<Remark>> MaybeRemark = TheParser.next(); 130 if (Error E = MaybeRemark.takeError()) { 131 if (E.isA<EndOfFileError>()) { 132 consumeError(std::move(E)); 133 return nullptr; 134 } 135 136 // Handle the error. Allow it to be checked through HasError and 137 // GetErrorMessage. 138 TheCParser.handleError(std::move(E)); 139 return nullptr; 140 } 141 142 // Valid remark. 143 return wrap(MaybeRemark->release()); 144 } 145 146 extern "C" LLVMBool LLVMRemarkParserHasError(LLVMRemarkParserRef Parser) { 147 return unwrap(Parser)->hasError(); 148 } 149 150 extern "C" const char * 151 LLVMRemarkParserGetErrorMessage(LLVMRemarkParserRef Parser) { 152 return unwrap(Parser)->getMessage(); 153 } 154 155 extern "C" void LLVMRemarkParserDispose(LLVMRemarkParserRef Parser) { 156 delete unwrap(Parser); 157 } 158