1 //===--------- llvm-remarkutil/RemarkUtil.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 /// Utility for remark files. 9 //===----------------------------------------------------------------------===// 10 11 #include "llvm-c/Remarks.h" 12 #include "llvm/ADT/StringRef.h" 13 #include "llvm/Remarks/Remark.h" 14 #include "llvm/Remarks/RemarkFormat.h" 15 #include "llvm/Remarks/RemarkParser.h" 16 #include "llvm/Remarks/YAMLRemarkSerializer.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/Compiler.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/FileSystem.h" 21 #include "llvm/Support/InitLLVM.h" 22 #include "llvm/Support/MemoryBuffer.h" 23 #include "llvm/Support/ToolOutputFile.h" 24 #include "llvm/Support/WithColor.h" 25 26 using namespace llvm; 27 using namespace remarks; 28 29 static ExitOnError ExitOnErr; 30 static cl::OptionCategory RemarkUtilCategory("llvm-remarkutil options"); 31 namespace subopts { 32 static cl::SubCommand 33 YAML2Bitstream("yaml2bitstream", 34 "Convert YAML remarks to bitstream remarks"); 35 static cl::SubCommand 36 Bitstream2YAML("bitstream2yaml", 37 "Convert bitstream remarks to YAML remarks"); 38 static cl::SubCommand InstructionCount( 39 "instruction-count", 40 "Function instruction count information (requires asm-printer remarks)"); 41 static cl::SubCommand 42 AnnotationCount("annotation-count", 43 "Collect count information from annotation remarks (uses " 44 "AnnotationRemarksPass)"); 45 } // namespace subopts 46 47 // Keep input + output help + names consistent across the various modes via a 48 // hideous macro. 49 #define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT) \ 50 static cl::opt<std::string> InputFileName( \ 51 cl::Positional, cl::cat(RemarkUtilCategory), cl::init("-"), \ 52 cl::desc("<input file>"), cl::sub(SUBOPT)); \ 53 static cl::opt<std::string> OutputFileName( \ 54 "o", cl::init("-"), cl::cat(RemarkUtilCategory), cl::desc("Output"), \ 55 cl::value_desc("filename"), cl::sub(SUBOPT)); 56 57 // Keep Input format and names consistent accross the modes via a macro. 58 #define INPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT) \ 59 static cl::opt<Format> InputFormat( \ 60 "parser", cl::desc("Input remark format to parse"), \ 61 cl::values(clEnumValN(Format::YAML, "yaml", "YAML"), \ 62 clEnumValN(Format::Bitstream, "bitstream", "Bitstream")), \ 63 cl::sub(SUBOPT)); 64 65 #define DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(SUBOPT) \ 66 static cl::opt<bool> UseDebugLoc( \ 67 "use-debug-loc", \ 68 cl::desc( \ 69 "Add debug loc information when generating tables for " \ 70 "functions. The loc is represented as (path:line number:column " \ 71 "number)"), \ 72 cl::init(false), cl::sub(SUBOPT)); 73 namespace yaml2bitstream { 74 /// Remark format to parse. 75 static constexpr Format InputFormat = Format::YAML; 76 /// Remark format to output. 77 static constexpr Format OutputFormat = Format::Bitstream; 78 INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream) 79 } // namespace yaml2bitstream 80 81 namespace bitstream2yaml { 82 /// Remark format to parse. 83 static constexpr Format InputFormat = Format::Bitstream; 84 /// Remark format to output. 85 static constexpr Format OutputFormat = Format::YAML; 86 INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML) 87 } // namespace bitstream2yaml 88 89 namespace instructioncount { 90 INPUT_FORMAT_COMMAND_LINE_OPTIONS(subopts::InstructionCount) 91 INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::InstructionCount) 92 DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(subopts::InstructionCount) 93 } // namespace instructioncount 94 95 namespace annotationcount { 96 INPUT_FORMAT_COMMAND_LINE_OPTIONS(subopts::AnnotationCount) 97 static cl::opt<std::string> AnnotationTypeToCollect( 98 "annotation-type", cl::desc("annotation-type remark to collect count for"), 99 cl::sub(subopts::AnnotationCount)); 100 INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::AnnotationCount) 101 DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(subopts::AnnotationCount) 102 } // namespace annotationcount 103 104 /// \returns A MemoryBuffer for the input file on success, and an Error 105 /// otherwise. 106 static Expected<std::unique_ptr<MemoryBuffer>> 107 getInputMemoryBuffer(StringRef InputFileName) { 108 auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName); 109 if (auto ErrorCode = MaybeBuf.getError()) 110 return createStringError(ErrorCode, 111 Twine("Cannot open file '" + InputFileName + 112 "': " + ErrorCode.message())); 113 return std::move(*MaybeBuf); 114 } 115 116 /// \returns A ToolOutputFile which can be used for outputting the results of 117 /// some tool mode. 118 /// \p OutputFileName is the desired destination. 119 /// \p Flags controls whether or not the file is opened for writing in text 120 /// mode, as a binary, etc. See sys::fs::OpenFlags for more detail. 121 static Expected<std::unique_ptr<ToolOutputFile>> 122 getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) { 123 if (OutputFileName == "") 124 OutputFileName = "-"; 125 std::error_code ErrorCode; 126 auto OF = std::make_unique<ToolOutputFile>(OutputFileName, ErrorCode, Flags); 127 if (ErrorCode) 128 return errorCodeToError(ErrorCode); 129 return std::move(OF); 130 } 131 132 /// \returns A ToolOutputFile which can be used for writing remarks on success, 133 /// and an Error otherwise. 134 /// \p OutputFileName is the desired destination. 135 /// \p OutputFormat 136 static Expected<std::unique_ptr<ToolOutputFile>> 137 getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat) { 138 assert((OutputFormat == Format::YAML || OutputFormat == Format::Bitstream) && 139 "Expected one of YAML or Bitstream!"); 140 return getOutputFileWithFlags(OutputFileName, OutputFormat == Format::YAML 141 ? sys::fs::OF_TextWithCRLF 142 : sys::fs::OF_None); 143 } 144 145 static bool shouldSkipRemark(bool UseDebugLoc, Remark &Remark) { 146 return UseDebugLoc && !Remark.Loc.has_value(); 147 } 148 149 namespace yaml2bitstream { 150 /// Parses all remarks in the input YAML file. 151 /// \p [out] ParsedRemarks - Filled with remarks parsed from the input file. 152 /// \p [out] StrTab - A string table populated for later remark serialization. 153 /// \returns Error::success() if all remarks were successfully parsed, and an 154 /// Error otherwise. 155 static Error 156 tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> &ParsedRemarks, 157 StringTable &StrTab) { 158 auto MaybeBuf = getInputMemoryBuffer(InputFileName); 159 if (!MaybeBuf) 160 return MaybeBuf.takeError(); 161 auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 162 if (!MaybeParser) 163 return MaybeParser.takeError(); 164 auto &Parser = **MaybeParser; 165 auto MaybeRemark = Parser.next(); 166 for (; MaybeRemark; MaybeRemark = Parser.next()) { 167 StrTab.internalize(**MaybeRemark); 168 ParsedRemarks.push_back(std::move(*MaybeRemark)); 169 } 170 auto E = MaybeRemark.takeError(); 171 if (!E.isA<EndOfFileError>()) 172 return E; 173 consumeError(std::move(E)); 174 return Error::success(); 175 } 176 177 /// Reserialize a list of parsed YAML remarks into bitstream remarks. 178 /// \p ParsedRemarks - A list of remarks. 179 /// \p StrTab - The string table for the remarks. 180 /// \returns Error::success() on success. 181 static Error tryReserializeYAML2Bitstream( 182 const std::vector<std::unique_ptr<Remark>> &ParsedRemarks, 183 StringTable &StrTab) { 184 auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); 185 if (!MaybeOF) 186 return MaybeOF.takeError(); 187 auto OF = std::move(*MaybeOF); 188 auto MaybeSerializer = createRemarkSerializer( 189 OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab)); 190 if (!MaybeSerializer) 191 return MaybeSerializer.takeError(); 192 auto Serializer = std::move(*MaybeSerializer); 193 for (const auto &Remark : ParsedRemarks) 194 Serializer->emit(*Remark); 195 OF->keep(); 196 return Error::success(); 197 } 198 199 /// Parse YAML remarks and reserialize as bitstream remarks. 200 /// \returns Error::success() on success, and an Error otherwise. 201 static Error tryYAML2Bitstream() { 202 StringTable StrTab; 203 std::vector<std::unique_ptr<Remark>> ParsedRemarks; 204 ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab)); 205 return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab); 206 } 207 } // namespace yaml2bitstream 208 209 namespace bitstream2yaml { 210 /// Parse bitstream remarks and reserialize as YAML remarks. 211 /// \returns An Error if reserialization fails, or Error::success() on success. 212 static Error tryBitstream2YAML() { 213 // Create the serializer. 214 auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); 215 if (!MaybeOF) 216 return MaybeOF.takeError(); 217 auto OF = std::move(*MaybeOF); 218 auto MaybeSerializer = createRemarkSerializer( 219 OutputFormat, SerializerMode::Standalone, OF->os()); 220 if (!MaybeSerializer) 221 return MaybeSerializer.takeError(); 222 223 // Create the parser. 224 auto MaybeBuf = getInputMemoryBuffer(InputFileName); 225 if (!MaybeBuf) 226 return MaybeBuf.takeError(); 227 auto Serializer = std::move(*MaybeSerializer); 228 auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 229 if (!MaybeParser) 230 return MaybeParser.takeError(); 231 auto &Parser = **MaybeParser; 232 233 // Parse + reserialize all remarks. 234 auto MaybeRemark = Parser.next(); 235 for (; MaybeRemark; MaybeRemark = Parser.next()) 236 Serializer->emit(**MaybeRemark); 237 auto E = MaybeRemark.takeError(); 238 if (!E.isA<EndOfFileError>()) 239 return E; 240 consumeError(std::move(E)); 241 return Error::success(); 242 } 243 } // namespace bitstream2yaml 244 245 namespace instructioncount { 246 /// Outputs all instruction count remarks in the file as a CSV. 247 /// \returns Error::success() on success, and an Error otherwise. 248 static Error tryInstructionCount() { 249 // Create the output buffer. 250 auto MaybeOF = getOutputFileWithFlags(OutputFileName, 251 /*Flags = */ sys::fs::OF_TextWithCRLF); 252 if (!MaybeOF) 253 return MaybeOF.takeError(); 254 auto OF = std::move(*MaybeOF); 255 // Create a parser for the user-specified input format. 256 auto MaybeBuf = getInputMemoryBuffer(InputFileName); 257 if (!MaybeBuf) 258 return MaybeBuf.takeError(); 259 auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 260 if (!MaybeParser) 261 return MaybeParser.takeError(); 262 // Emit CSV header. 263 if (UseDebugLoc) 264 OF->os() << "Source,"; 265 OF->os() << "Function,InstructionCount\n"; 266 // Parse all remarks. Whenever we see an instruction count remark, output 267 // the file name and the number of instructions. 268 auto &Parser = **MaybeParser; 269 auto MaybeRemark = Parser.next(); 270 for (; MaybeRemark; MaybeRemark = Parser.next()) { 271 auto &Remark = **MaybeRemark; 272 if (Remark.RemarkName != "InstructionCount") 273 continue; 274 if (shouldSkipRemark(UseDebugLoc, Remark)) 275 continue; 276 auto *InstrCountArg = find_if(Remark.Args, [](const Argument &Arg) { 277 return Arg.Key == "NumInstructions"; 278 }); 279 assert(InstrCountArg != Remark.Args.end() && 280 "Expected instruction count remarks to have a NumInstructions key?"); 281 if (UseDebugLoc) { 282 std::string Loc = Remark.Loc->SourceFilePath.str() + ":" + 283 std::to_string(Remark.Loc->SourceLine) + +":" + 284 std::to_string(Remark.Loc->SourceColumn); 285 OF->os() << Loc << ","; 286 } 287 OF->os() << Remark.FunctionName << "," << InstrCountArg->Val << "\n"; 288 } 289 auto E = MaybeRemark.takeError(); 290 if (!E.isA<EndOfFileError>()) 291 return E; 292 consumeError(std::move(E)); 293 OF->keep(); 294 return Error::success(); 295 } 296 } // namespace instructioncount 297 298 namespace annotationcount { 299 static Error tryAnnotationCount() { 300 // Create the output buffer. 301 auto MaybeOF = getOutputFileWithFlags(OutputFileName, 302 /*Flags = */ sys::fs::OF_TextWithCRLF); 303 if (!MaybeOF) 304 return MaybeOF.takeError(); 305 auto OF = std::move(*MaybeOF); 306 // Create a parser for the user-specified input format. 307 auto MaybeBuf = getInputMemoryBuffer(InputFileName); 308 if (!MaybeBuf) 309 return MaybeBuf.takeError(); 310 auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 311 if (!MaybeParser) 312 return MaybeParser.takeError(); 313 // Emit CSV header. 314 if (UseDebugLoc) 315 OF->os() << "Source,"; 316 OF->os() << "Function,Count\n"; 317 // Parse all remarks. When we see the specified remark collect the count 318 // information. 319 auto &Parser = **MaybeParser; 320 auto MaybeRemark = Parser.next(); 321 for (; MaybeRemark; MaybeRemark = Parser.next()) { 322 auto &Remark = **MaybeRemark; 323 if (Remark.RemarkName != "AnnotationSummary") 324 continue; 325 if (shouldSkipRemark(UseDebugLoc, Remark)) 326 continue; 327 auto *RemarkNameArg = find_if(Remark.Args, [](const Argument &Arg) { 328 return Arg.Key == "type" && Arg.Val == AnnotationTypeToCollect; 329 }); 330 if (RemarkNameArg == Remark.Args.end()) 331 continue; 332 auto *CountArg = find_if( 333 Remark.Args, [](const Argument &Arg) { return Arg.Key == "count"; }); 334 assert(CountArg != Remark.Args.end() && 335 "Expected annotation-type remark to have a count key?"); 336 if (UseDebugLoc) { 337 std::string Loc = Remark.Loc->SourceFilePath.str() + ":" + 338 std::to_string(Remark.Loc->SourceLine) + +":" + 339 std::to_string(Remark.Loc->SourceColumn); 340 OF->os() << Loc << ","; 341 } 342 OF->os() << Remark.FunctionName << "," << CountArg->Val << "\n"; 343 } 344 auto E = MaybeRemark.takeError(); 345 if (!E.isA<EndOfFileError>()) 346 return E; 347 consumeError(std::move(E)); 348 OF->keep(); 349 return Error::success(); 350 } 351 352 } // namespace annotationcount 353 /// Handle user-specified suboptions (e.g. yaml2bitstream, bitstream2yaml). 354 /// \returns An Error if the specified suboption fails or if no suboption was 355 /// specified. Otherwise, Error::success(). 356 static Error handleSuboptions() { 357 if (subopts::Bitstream2YAML) 358 return bitstream2yaml::tryBitstream2YAML(); 359 if (subopts::YAML2Bitstream) 360 return yaml2bitstream::tryYAML2Bitstream(); 361 if (subopts::InstructionCount) 362 return instructioncount::tryInstructionCount(); 363 if (subopts::AnnotationCount) 364 return annotationcount::tryAnnotationCount(); 365 366 return make_error<StringError>( 367 "Please specify a subcommand. (See -help for options)", 368 inconvertibleErrorCode()); 369 } 370 371 int main(int argc, const char **argv) { 372 InitLLVM X(argc, argv); 373 cl::HideUnrelatedOptions(RemarkUtilCategory); 374 cl::ParseCommandLineOptions(argc, argv, "Remark file utilities\n"); 375 ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); 376 ExitOnErr(handleSuboptions()); 377 } 378