238 lines · cpp
1//===- RemarkCounter.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-exception6//7//===----------------------------------------------------------------------===//8//9// Generic tool to count remarks based on properties10//11//===----------------------------------------------------------------------===//12 13#include "RemarkCounter.h"14#include "RemarkUtilRegistry.h"15#include "llvm/Support/CommandLine.h"16#include "llvm/Support/InterleavedRange.h"17#include "llvm/Support/Regex.h"18 19using namespace llvm;20using namespace remarks;21using namespace llvm::remarkutil;22 23static cl::SubCommand CountSub("count",24 "Collect remarks based on specified criteria.");25 26INPUT_FORMAT_COMMAND_LINE_OPTIONS(CountSub)27INPUT_OUTPUT_COMMAND_LINE_OPTIONS(CountSub)28REMARK_FILTER_COMMAND_LINE_OPTIONS(CountSub)29 30REMARK_FILTER_SETUP_FUNC()31 32static cl::list<std::string>33 Keys("args", cl::desc("Specify remark argument/s to count by."),34 cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);35static cl::list<std::string> RKeys(36 "rargs",37 cl::desc(38 "Specify remark argument/s to count (accepts regular expressions)."),39 cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);40 41static cl::opt<CountBy> CountByOpt(42 "count-by", cl::desc("Specify the property to collect remarks by."),43 cl::values(44 clEnumValN(CountBy::REMARK, "remark-name",45 "Counts individual remarks based on how many of the remark "46 "exists."),47 clEnumValN(CountBy::ARGUMENT, "arg",48 "Counts based on the value each specified argument has. The "49 "argument has to have a number value to be considered.")),50 cl::init(CountBy::REMARK), cl::sub(CountSub));51static cl::opt<GroupBy> GroupByOpt(52 "group-by", cl::desc("Specify the property to group remarks by."),53 cl::values(54 clEnumValN(55 GroupBy::PER_SOURCE, "source",56 "Display the count broken down by the filepath of each remark "57 "emitted. Requires remarks to have DebugLoc information."),58 clEnumValN(GroupBy::PER_FUNCTION, "function",59 "Breakdown the count by function name."),60 clEnumValN(61 GroupBy::PER_FUNCTION_WITH_DEBUG_LOC, "function-with-loc",62 "Breakdown the count by function name taking into consideration "63 "the filepath info from the DebugLoc of the remark."),64 clEnumValN(GroupBy::TOTAL, "total",65 "Output the total number corresponding to the count for the "66 "provided input file.")),67 cl::init(GroupBy::PER_SOURCE), cl::sub(CountSub));68 69/// Look for matching argument with \p Key in \p Remark and return the parsed70/// integer value or 0 if it is has no integer value.71static unsigned getValForKey(StringRef Key, const Remark &Remark) {72 auto *RemarkArg = find_if(Remark.Args, [&Key](const Argument &Arg) {73 return Arg.Key == Key && Arg.getValAsInt<unsigned>();74 });75 if (RemarkArg == Remark.Args.end())76 return 0;77 return *RemarkArg->getValAsInt<unsigned>();78}79 80Error ArgumentCounter::getAllMatchingArgumentsInRemark(81 StringRef Buffer, ArrayRef<FilterMatcher> Arguments, Filters &Filter) {82 auto MaybeParser = createRemarkParser(InputFormat, Buffer);83 if (!MaybeParser)84 return MaybeParser.takeError();85 auto &Parser = **MaybeParser;86 auto MaybeRemark = Parser.next();87 for (; MaybeRemark; MaybeRemark = Parser.next()) {88 auto &Remark = **MaybeRemark;89 // Only collect keys from remarks included in the filter.90 if (!Filter.filterRemark(Remark))91 continue;92 for (auto &Key : Arguments) {93 for (Argument Arg : Remark.Args)94 if (Key.match(Arg.Key) && Arg.getValAsInt<unsigned>())95 ArgumentSetIdxMap.insert({Arg.Key, ArgumentSetIdxMap.size()});96 }97 }98 99 auto E = MaybeRemark.takeError();100 if (!E.isA<EndOfFileError>())101 return E;102 consumeError(std::move(E));103 return Error::success();104}105 106std::optional<std::string> Counter::getGroupByKey(const Remark &Remark) {107 switch (Group) {108 case GroupBy::PER_FUNCTION:109 return Remark.FunctionName.str();110 case GroupBy::TOTAL:111 return "Total";112 case GroupBy::PER_SOURCE:113 case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC:114 if (!Remark.Loc.has_value())115 return std::nullopt;116 117 if (Group == GroupBy::PER_FUNCTION_WITH_DEBUG_LOC)118 return Remark.Loc->SourceFilePath.str() + ":" + Remark.FunctionName.str();119 return Remark.Loc->SourceFilePath.str();120 }121 llvm_unreachable("Fully covered switch above!");122}123 124void ArgumentCounter::collect(const Remark &Remark) {125 SmallVector<unsigned, 4> Row(ArgumentSetIdxMap.size());126 std::optional<std::string> GroupByKey = getGroupByKey(Remark);127 // Early return if we don't have a value128 if (!GroupByKey)129 return;130 auto GroupVal = *GroupByKey;131 CountByKeysMap.insert({GroupVal, Row});132 for (auto [Key, Idx] : ArgumentSetIdxMap) {133 auto Count = getValForKey(Key, Remark);134 CountByKeysMap[GroupVal][Idx] += Count;135 }136}137 138void RemarkCounter::collect(const Remark &Remark) {139 if (std::optional<std::string> Key = getGroupByKey(Remark))140 ++CountedByRemarksMap[*Key];141}142 143Error ArgumentCounter::print(StringRef OutputFileName) {144 auto MaybeOF =145 getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);146 if (!MaybeOF)147 return MaybeOF.takeError();148 149 auto OF = std::move(*MaybeOF);150 OF->os() << groupByToStr(Group) << ",";151 OF->os() << llvm::interleaved(llvm::make_first_range(ArgumentSetIdxMap), ",");152 OF->os() << "\n";153 for (auto [Header, CountVector] : CountByKeysMap) {154 OF->os() << Header << ",";155 OF->os() << llvm::interleaved(CountVector, ",");156 OF->os() << "\n";157 }158 return Error::success();159}160 161Error RemarkCounter::print(StringRef OutputFileName) {162 auto MaybeOF =163 getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);164 if (!MaybeOF)165 return MaybeOF.takeError();166 167 auto OF = std::move(*MaybeOF);168 OF->os() << groupByToStr(Group) << ","169 << "Count\n";170 for (auto [Key, Count] : CountedByRemarksMap)171 OF->os() << Key << "," << Count << "\n";172 OF->keep();173 return Error::success();174}175 176Error useCollectRemark(StringRef Buffer, Counter &Counter, Filters &Filter) {177 // Create Parser.178 auto MaybeParser = createRemarkParser(InputFormat, Buffer);179 if (!MaybeParser)180 return MaybeParser.takeError();181 auto &Parser = **MaybeParser;182 auto MaybeRemark = Parser.next();183 for (; MaybeRemark; MaybeRemark = Parser.next()) {184 const Remark &Remark = **MaybeRemark;185 if (Filter.filterRemark(Remark))186 Counter.collect(Remark);187 }188 189 if (auto E = Counter.print(OutputFileName))190 return E;191 auto E = MaybeRemark.takeError();192 if (!E.isA<EndOfFileError>())193 return E;194 consumeError(std::move(E));195 return Error::success();196}197 198static Error collectRemarks() {199 // Create a parser for the user-specified input format.200 auto MaybeBuf = getInputMemoryBuffer(InputFileName);201 if (!MaybeBuf)202 return MaybeBuf.takeError();203 StringRef Buffer = (*MaybeBuf)->getBuffer();204 auto MaybeFilter = getRemarkFilters();205 if (!MaybeFilter)206 return MaybeFilter.takeError();207 auto &Filter = *MaybeFilter;208 if (CountByOpt == CountBy::REMARK) {209 RemarkCounter RC(GroupByOpt);210 if (auto E = useCollectRemark(Buffer, RC, Filter))211 return E;212 } else if (CountByOpt == CountBy::ARGUMENT) {213 SmallVector<FilterMatcher, 4> ArgumentsVector;214 if (!Keys.empty()) {215 for (auto &Key : Keys)216 ArgumentsVector.push_back(FilterMatcher::createExact(Key));217 } else if (!RKeys.empty())218 for (auto Key : RKeys) {219 auto FM = FilterMatcher::createRE(Key, RKeys);220 if (!FM)221 return FM.takeError();222 ArgumentsVector.push_back(std::move(*FM));223 }224 else225 ArgumentsVector.push_back(FilterMatcher::createAny());226 227 Expected<ArgumentCounter> AC = ArgumentCounter::createArgumentCounter(228 GroupByOpt, ArgumentsVector, Buffer, Filter);229 if (!AC)230 return AC.takeError();231 if (auto E = useCollectRemark(Buffer, *AC, Filter))232 return E;233 }234 return Error::success();235}236 237static CommandRegistration CountReg(&CountSub, collectRemarks);238