Path: blob/main/contrib/llvm-project/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
35231 views
//===- RemarkCounter.cpp --------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Generic tool to count remarks based on properties9//10//===----------------------------------------------------------------------===//1112#include "RemarkCounter.h"13#include "RemarkUtilRegistry.h"14#include "llvm/Support/CommandLine.h"15#include "llvm/Support/Regex.h"1617using namespace llvm;18using namespace remarks;19using namespace llvm::remarkutil;2021static cl::SubCommand CountSub("count",22"Collect remarks based on specified criteria.");2324INPUT_FORMAT_COMMAND_LINE_OPTIONS(CountSub)25INPUT_OUTPUT_COMMAND_LINE_OPTIONS(CountSub)2627static cl::list<std::string>28Keys("args", cl::desc("Specify remark argument/s to count by."),29cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);30static cl::list<std::string> RKeys(31"rargs",32cl::desc(33"Specify remark argument/s to count (accepts regular expressions)."),34cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);35static cl::opt<std::string>36RemarkNameOpt("remark-name",37cl::desc("Optional remark name to filter collection by."),38cl::ValueOptional, cl::sub(CountSub));39static cl::opt<std::string>40PassNameOpt("pass-name", cl::ValueOptional,41cl::desc("Optional remark pass name to filter collection by."),42cl::sub(CountSub));4344static cl::opt<std::string> RemarkFilterArgByOpt(45"filter-arg-by", cl::desc("Optional remark arg to filter collection by."),46cl::ValueOptional, cl::sub(CountSub));47static cl::opt<std::string>48RemarkNameOptRE("rremark-name",49cl::desc("Optional remark name to filter collection by "50"(accepts regular expressions)."),51cl::ValueOptional, cl::sub(CountSub));52static cl::opt<std::string>53RemarkArgFilterOptRE("rfilter-arg-by",54cl::desc("Optional remark arg to filter collection by "55"(accepts regular expressions)."),56cl::sub(CountSub), cl::ValueOptional);57static cl::opt<std::string>58PassNameOptRE("rpass-name", cl::ValueOptional,59cl::desc("Optional remark pass name to filter collection "60"by (accepts regular expressions)."),61cl::sub(CountSub));62static cl::opt<Type> RemarkTypeOpt(63"remark-type", cl::desc("Optional remark type to filter collection by."),64cl::values(clEnumValN(Type::Unknown, "unknown", "UNKOWN"),65clEnumValN(Type::Passed, "passed", "PASSED"),66clEnumValN(Type::Missed, "missed", "MISSED"),67clEnumValN(Type::Analysis, "analysis", "ANALYSIS"),68clEnumValN(Type::AnalysisFPCommute, "analysis-fp-commute",69"ANALYSIS_FP_COMMUTE"),70clEnumValN(Type::AnalysisAliasing, "analysis-aliasing",71"ANALYSIS_ALIASING"),72clEnumValN(Type::Failure, "failure", "FAILURE")),73cl::init(Type::Failure), cl::sub(CountSub));74static cl::opt<CountBy> CountByOpt(75"count-by", cl::desc("Specify the property to collect remarks by."),76cl::values(77clEnumValN(CountBy::REMARK, "remark-name",78"Counts individual remarks based on how many of the remark "79"exists."),80clEnumValN(CountBy::ARGUMENT, "arg",81"Counts based on the value each specified argument has. The "82"argument has to have a number value to be considered.")),83cl::init(CountBy::REMARK), cl::sub(CountSub));84static cl::opt<GroupBy> GroupByOpt(85"group-by", cl::desc("Specify the property to group remarks by."),86cl::values(87clEnumValN(88GroupBy::PER_SOURCE, "source",89"Display the count broken down by the filepath of each remark "90"emitted. Requires remarks to have DebugLoc information."),91clEnumValN(GroupBy::PER_FUNCTION, "function",92"Breakdown the count by function name."),93clEnumValN(94GroupBy::PER_FUNCTION_WITH_DEBUG_LOC, "function-with-loc",95"Breakdown the count by function name taking into consideration "96"the filepath info from the DebugLoc of the remark."),97clEnumValN(GroupBy::TOTAL, "total",98"Output the total number corresponding to the count for the "99"provided input file.")),100cl::init(GroupBy::PER_SOURCE), cl::sub(CountSub));101102/// Look for matching argument with \p Key in \p Remark and return the parsed103/// integer value or 0 if it is has no integer value.104static unsigned getValForKey(StringRef Key, const Remark &Remark) {105auto *RemarkArg = find_if(Remark.Args, [&Key](const Argument &Arg) {106return Arg.Key == Key && Arg.isValInt();107});108if (RemarkArg == Remark.Args.end())109return 0;110return *RemarkArg->getValAsInt();111}112113Error Filters::regexArgumentsValid() {114if (RemarkNameFilter && RemarkNameFilter->IsRegex)115if (auto E = checkRegex(RemarkNameFilter->FilterRE))116return E;117if (PassNameFilter && PassNameFilter->IsRegex)118if (auto E = checkRegex(PassNameFilter->FilterRE))119return E;120if (ArgFilter && ArgFilter->IsRegex)121if (auto E = checkRegex(ArgFilter->FilterRE))122return E;123return Error::success();124}125126bool Filters::filterRemark(const Remark &Remark) {127if (RemarkNameFilter && !RemarkNameFilter->match(Remark.RemarkName))128return false;129if (PassNameFilter && !PassNameFilter->match(Remark.PassName))130return false;131if (RemarkTypeFilter)132return *RemarkTypeFilter == Remark.RemarkType;133if (ArgFilter) {134if (!any_of(Remark.Args,135[this](Argument Arg) { return ArgFilter->match(Arg.Val); }))136return false;137}138return true;139}140141Error ArgumentCounter::getAllMatchingArgumentsInRemark(142StringRef Buffer, ArrayRef<FilterMatcher> Arguments, Filters &Filter) {143auto MaybeParser = createRemarkParser(InputFormat, Buffer);144if (!MaybeParser)145return MaybeParser.takeError();146auto &Parser = **MaybeParser;147auto MaybeRemark = Parser.next();148for (; MaybeRemark; MaybeRemark = Parser.next()) {149auto &Remark = **MaybeRemark;150// Only collect keys from remarks included in the filter.151if (!Filter.filterRemark(Remark))152continue;153for (auto &Key : Arguments) {154for (Argument Arg : Remark.Args)155if (Key.match(Arg.Key) && Arg.isValInt())156ArgumentSetIdxMap.insert({Arg.Key, ArgumentSetIdxMap.size()});157}158}159160auto E = MaybeRemark.takeError();161if (!E.isA<EndOfFileError>())162return E;163consumeError(std::move(E));164return Error::success();165}166167std::optional<std::string> Counter::getGroupByKey(const Remark &Remark) {168switch (Group) {169case GroupBy::PER_FUNCTION:170return Remark.FunctionName.str();171case GroupBy::TOTAL:172return "Total";173case GroupBy::PER_SOURCE:174case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC:175if (!Remark.Loc.has_value())176return std::nullopt;177178if (Group == GroupBy::PER_FUNCTION_WITH_DEBUG_LOC)179return Remark.Loc->SourceFilePath.str() + ":" + Remark.FunctionName.str();180return Remark.Loc->SourceFilePath.str();181}182llvm_unreachable("Fully covered switch above!");183}184185void ArgumentCounter::collect(const Remark &Remark) {186SmallVector<unsigned, 4> Row(ArgumentSetIdxMap.size());187std::optional<std::string> GroupByKey = getGroupByKey(Remark);188// Early return if we don't have a value189if (!GroupByKey)190return;191auto GroupVal = *GroupByKey;192CountByKeysMap.insert({GroupVal, Row});193for (auto [Key, Idx] : ArgumentSetIdxMap) {194auto Count = getValForKey(Key, Remark);195CountByKeysMap[GroupVal][Idx] += Count;196}197}198199void RemarkCounter::collect(const Remark &Remark) {200std::optional<std::string> Key = getGroupByKey(Remark);201if (!Key.has_value())202return;203auto Iter = CountedByRemarksMap.insert({*Key, 1});204if (!Iter.second)205Iter.first->second += 1;206}207208Error ArgumentCounter::print(StringRef OutputFileName) {209auto MaybeOF =210getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);211if (!MaybeOF)212return MaybeOF.takeError();213214auto OF = std::move(*MaybeOF);215OF->os() << groupByToStr(Group) << ",";216unsigned Idx = 0;217for (auto [Key, _] : ArgumentSetIdxMap) {218OF->os() << Key;219if (Idx != ArgumentSetIdxMap.size() - 1)220OF->os() << ",";221Idx++;222}223OF->os() << "\n";224for (auto [Header, CountVector] : CountByKeysMap) {225OF->os() << Header << ",";226unsigned Idx = 0;227for (auto Count : CountVector) {228OF->os() << Count;229if (Idx != ArgumentSetIdxMap.size() - 1)230OF->os() << ",";231Idx++;232}233OF->os() << "\n";234}235return Error::success();236}237238Error RemarkCounter::print(StringRef OutputFileName) {239auto MaybeOF =240getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);241if (!MaybeOF)242return MaybeOF.takeError();243244auto OF = std::move(*MaybeOF);245OF->os() << groupByToStr(Group) << ","246<< "Count\n";247for (auto [Key, Count] : CountedByRemarksMap)248OF->os() << Key << "," << Count << "\n";249OF->keep();250return Error::success();251}252253Expected<Filters> getRemarkFilter() {254// Create Filter properties.255std::optional<FilterMatcher> RemarkNameFilter;256std::optional<FilterMatcher> PassNameFilter;257std::optional<FilterMatcher> RemarkArgFilter;258std::optional<Type> RemarkType;259if (!RemarkNameOpt.empty())260RemarkNameFilter = {RemarkNameOpt, false};261else if (!RemarkNameOptRE.empty())262RemarkNameFilter = {RemarkNameOptRE, true};263if (!PassNameOpt.empty())264PassNameFilter = {PassNameOpt, false};265else if (!PassNameOptRE.empty())266PassNameFilter = {PassNameOptRE, true};267if (RemarkTypeOpt != Type::Failure)268RemarkType = RemarkTypeOpt;269if (!RemarkFilterArgByOpt.empty())270RemarkArgFilter = {RemarkFilterArgByOpt, false};271else if (!RemarkArgFilterOptRE.empty())272RemarkArgFilter = {RemarkArgFilterOptRE, true};273// Create RemarkFilter.274return Filters::createRemarkFilter(std::move(RemarkNameFilter),275std::move(PassNameFilter),276std::move(RemarkArgFilter), RemarkType);277}278279Error useCollectRemark(StringRef Buffer, Counter &Counter, Filters &Filter) {280// Create Parser.281auto MaybeParser = createRemarkParser(InputFormat, Buffer);282if (!MaybeParser)283return MaybeParser.takeError();284auto &Parser = **MaybeParser;285auto MaybeRemark = Parser.next();286for (; MaybeRemark; MaybeRemark = Parser.next()) {287const Remark &Remark = **MaybeRemark;288if (Filter.filterRemark(Remark))289Counter.collect(Remark);290}291292if (auto E = Counter.print(OutputFileName))293return E;294auto E = MaybeRemark.takeError();295if (!E.isA<EndOfFileError>())296return E;297consumeError(std::move(E));298return Error::success();299}300301static Error collectRemarks() {302// Create a parser for the user-specified input format.303auto MaybeBuf = getInputMemoryBuffer(InputFileName);304if (!MaybeBuf)305return MaybeBuf.takeError();306StringRef Buffer = (*MaybeBuf)->getBuffer();307auto MaybeFilter = getRemarkFilter();308if (!MaybeFilter)309return MaybeFilter.takeError();310auto &Filter = *MaybeFilter;311if (CountByOpt == CountBy::REMARK) {312RemarkCounter RC(GroupByOpt);313if (auto E = useCollectRemark(Buffer, RC, Filter))314return E;315} else if (CountByOpt == CountBy::ARGUMENT) {316SmallVector<FilterMatcher, 4> ArgumentsVector;317if (!Keys.empty()) {318for (auto &Key : Keys)319ArgumentsVector.push_back({Key, false});320} else if (!RKeys.empty())321for (auto Key : RKeys)322ArgumentsVector.push_back({Key, true});323else324ArgumentsVector.push_back({".*", true});325326Expected<ArgumentCounter> AC = ArgumentCounter::createArgumentCounter(327GroupByOpt, ArgumentsVector, Buffer, Filter);328if (!AC)329return AC.takeError();330if (auto E = useCollectRemark(Buffer, *AC, Filter))331return E;332}333return Error::success();334}335336static CommandRegistration CountReg(&CountSub, collectRemarks);337338339