aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-remarkutil/RemarkCounter.cpp')
-rw-r--r--llvm/tools/llvm-remarkutil/RemarkCounter.cpp337
1 files changed, 337 insertions, 0 deletions
diff --git a/llvm/tools/llvm-remarkutil/RemarkCounter.cpp b/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
new file mode 100644
index 000000000000..dc0685f34288
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
@@ -0,0 +1,337 @@
+//===- RemarkCounter.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic tool to count remarks based on properties
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemarkCounter.h"
+#include "RemarkUtilRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Regex.h"
+
+using namespace llvm;
+using namespace remarks;
+using namespace llvm::remarkutil;
+
+static cl::SubCommand CountSub("count",
+ "Collect remarks based on specified criteria.");
+
+INPUT_FORMAT_COMMAND_LINE_OPTIONS(CountSub)
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(CountSub)
+
+static cl::list<std::string>
+ Keys("args", cl::desc("Specify remark argument/s to count by."),
+ cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);
+static cl::list<std::string> RKeys(
+ "rargs",
+ cl::desc(
+ "Specify remark argument/s to count (accepts regular expressions)."),
+ cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);
+static cl::opt<std::string>
+ RemarkNameOpt("remark-name",
+ cl::desc("Optional remark name to filter collection by."),
+ cl::ValueOptional, cl::sub(CountSub));
+static cl::opt<std::string>
+ PassNameOpt("pass-name", cl::ValueOptional,
+ cl::desc("Optional remark pass name to filter collection by."),
+ cl::sub(CountSub));
+
+static cl::opt<std::string> RemarkFilterArgByOpt(
+ "filter-arg-by", cl::desc("Optional remark arg to filter collection by."),
+ cl::ValueOptional, cl::sub(CountSub));
+static cl::opt<std::string>
+ RemarkNameOptRE("rremark-name",
+ cl::desc("Optional remark name to filter collection by "
+ "(accepts regular expressions)."),
+ cl::ValueOptional, cl::sub(CountSub));
+static cl::opt<std::string>
+ RemarkArgFilterOptRE("rfilter-arg-by",
+ cl::desc("Optional remark arg to filter collection by "
+ "(accepts regular expressions)."),
+ cl::sub(CountSub), cl::ValueOptional);
+static cl::opt<std::string>
+ PassNameOptRE("rpass-name", cl::ValueOptional,
+ cl::desc("Optional remark pass name to filter collection "
+ "by (accepts regular expressions)."),
+ cl::sub(CountSub));
+static cl::opt<Type> RemarkTypeOpt(
+ "remark-type", cl::desc("Optional remark type to filter collection by."),
+ cl::values(clEnumValN(Type::Unknown, "unknown", "UNKOWN"),
+ clEnumValN(Type::Passed, "passed", "PASSED"),
+ clEnumValN(Type::Missed, "missed", "MISSED"),
+ clEnumValN(Type::Analysis, "analysis", "ANALYSIS"),
+ clEnumValN(Type::AnalysisFPCommute, "analysis-fp-commute",
+ "ANALYSIS_FP_COMMUTE"),
+ clEnumValN(Type::AnalysisAliasing, "analysis-aliasing",
+ "ANALYSIS_ALIASING"),
+ clEnumValN(Type::Failure, "failure", "FAILURE")),
+ cl::init(Type::Failure), cl::sub(CountSub));
+static cl::opt<CountBy> CountByOpt(
+ "count-by", cl::desc("Specify the property to collect remarks by."),
+ cl::values(
+ clEnumValN(CountBy::REMARK, "remark-name",
+ "Counts individual remarks based on how many of the remark "
+ "exists."),
+ clEnumValN(CountBy::ARGUMENT, "arg",
+ "Counts based on the value each specified argument has. The "
+ "argument has to have a number value to be considered.")),
+ cl::init(CountBy::REMARK), cl::sub(CountSub));
+static cl::opt<GroupBy> GroupByOpt(
+ "group-by", cl::desc("Specify the property to group remarks by."),
+ cl::values(
+ clEnumValN(
+ GroupBy::PER_SOURCE, "source",
+ "Display the count broken down by the filepath of each remark "
+ "emitted. Requires remarks to have DebugLoc information."),
+ clEnumValN(GroupBy::PER_FUNCTION, "function",
+ "Breakdown the count by function name."),
+ clEnumValN(
+ GroupBy::PER_FUNCTION_WITH_DEBUG_LOC, "function-with-loc",
+ "Breakdown the count by function name taking into consideration "
+ "the filepath info from the DebugLoc of the remark."),
+ clEnumValN(GroupBy::TOTAL, "total",
+ "Output the total number corresponding to the count for the "
+ "provided input file.")),
+ cl::init(GroupBy::PER_SOURCE), cl::sub(CountSub));
+
+/// Look for matching argument with \p Key in \p Remark and return the parsed
+/// integer value or 0 if it is has no integer value.
+static unsigned getValForKey(StringRef Key, const Remark &Remark) {
+ auto *RemarkArg = find_if(Remark.Args, [&Key](const Argument &Arg) {
+ return Arg.Key == Key && Arg.isValInt();
+ });
+ if (RemarkArg == Remark.Args.end())
+ return 0;
+ return *RemarkArg->getValAsInt();
+}
+
+Error Filters::regexArgumentsValid() {
+ if (RemarkNameFilter && RemarkNameFilter->IsRegex)
+ if (auto E = checkRegex(RemarkNameFilter->FilterRE))
+ return E;
+ if (PassNameFilter && PassNameFilter->IsRegex)
+ if (auto E = checkRegex(PassNameFilter->FilterRE))
+ return E;
+ if (ArgFilter && ArgFilter->IsRegex)
+ if (auto E = checkRegex(ArgFilter->FilterRE))
+ return E;
+ return Error::success();
+}
+
+bool Filters::filterRemark(const Remark &Remark) {
+ if (RemarkNameFilter && !RemarkNameFilter->match(Remark.RemarkName))
+ return false;
+ if (PassNameFilter && !PassNameFilter->match(Remark.PassName))
+ return false;
+ if (RemarkTypeFilter)
+ return *RemarkTypeFilter == Remark.RemarkType;
+ if (ArgFilter) {
+ if (!any_of(Remark.Args,
+ [this](Argument Arg) { return ArgFilter->match(Arg.Val); }))
+ return false;
+ }
+ return true;
+}
+
+Error ArgumentCounter::getAllMatchingArgumentsInRemark(
+ StringRef Buffer, ArrayRef<FilterMatcher> Arguments, Filters &Filter) {
+ auto MaybeParser = createRemarkParser(InputFormat, Buffer);
+ if (!MaybeParser)
+ return MaybeParser.takeError();
+ auto &Parser = **MaybeParser;
+ auto MaybeRemark = Parser.next();
+ for (; MaybeRemark; MaybeRemark = Parser.next()) {
+ auto &Remark = **MaybeRemark;
+ // Only collect keys from remarks included in the filter.
+ if (!Filter.filterRemark(Remark))
+ continue;
+ for (auto &Key : Arguments) {
+ for (Argument Arg : Remark.Args)
+ if (Key.match(Arg.Key) && Arg.isValInt())
+ ArgumentSetIdxMap.insert({Arg.Key, ArgumentSetIdxMap.size()});
+ }
+ }
+
+ auto E = MaybeRemark.takeError();
+ if (!E.isA<EndOfFileError>())
+ return E;
+ consumeError(std::move(E));
+ return Error::success();
+}
+
+std::optional<std::string> Counter::getGroupByKey(const Remark &Remark) {
+ switch (Group) {
+ case GroupBy::PER_FUNCTION:
+ return Remark.FunctionName.str();
+ case GroupBy::TOTAL:
+ return "Total";
+ case GroupBy::PER_SOURCE:
+ case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC:
+ if (!Remark.Loc.has_value())
+ return std::nullopt;
+
+ if (Group == GroupBy::PER_FUNCTION_WITH_DEBUG_LOC)
+ return Remark.Loc->SourceFilePath.str() + ":" + Remark.FunctionName.str();
+ return Remark.Loc->SourceFilePath.str();
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+void ArgumentCounter::collect(const Remark &Remark) {
+ SmallVector<unsigned, 4> Row(ArgumentSetIdxMap.size());
+ std::optional<std::string> GroupByKey = getGroupByKey(Remark);
+ // Early return if we don't have a value
+ if (!GroupByKey)
+ return;
+ auto GroupVal = *GroupByKey;
+ CountByKeysMap.insert({GroupVal, Row});
+ for (auto [Key, Idx] : ArgumentSetIdxMap) {
+ auto Count = getValForKey(Key, Remark);
+ CountByKeysMap[GroupVal][Idx] += Count;
+ }
+}
+
+void RemarkCounter::collect(const Remark &Remark) {
+ std::optional<std::string> Key = getGroupByKey(Remark);
+ if (!Key.has_value())
+ return;
+ auto Iter = CountedByRemarksMap.insert({*Key, 1});
+ if (!Iter.second)
+ Iter.first->second += 1;
+}
+
+Error ArgumentCounter::print(StringRef OutputFileName) {
+ auto MaybeOF =
+ getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);
+ if (!MaybeOF)
+ return MaybeOF.takeError();
+
+ auto OF = std::move(*MaybeOF);
+ OF->os() << groupByToStr(Group) << ",";
+ unsigned Idx = 0;
+ for (auto [Key, _] : ArgumentSetIdxMap) {
+ OF->os() << Key;
+ if (Idx != ArgumentSetIdxMap.size() - 1)
+ OF->os() << ",";
+ Idx++;
+ }
+ OF->os() << "\n";
+ for (auto [Header, CountVector] : CountByKeysMap) {
+ OF->os() << Header << ",";
+ unsigned Idx = 0;
+ for (auto Count : CountVector) {
+ OF->os() << Count;
+ if (Idx != ArgumentSetIdxMap.size() - 1)
+ OF->os() << ",";
+ Idx++;
+ }
+ OF->os() << "\n";
+ }
+ return Error::success();
+}
+
+Error RemarkCounter::print(StringRef OutputFileName) {
+ auto MaybeOF =
+ getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);
+ if (!MaybeOF)
+ return MaybeOF.takeError();
+
+ auto OF = std::move(*MaybeOF);
+ OF->os() << groupByToStr(Group) << ","
+ << "Count\n";
+ for (auto [Key, Count] : CountedByRemarksMap)
+ OF->os() << Key << "," << Count << "\n";
+ OF->keep();
+ return Error::success();
+}
+
+Expected<Filters> getRemarkFilter() {
+ // Create Filter properties.
+ std::optional<FilterMatcher> RemarkNameFilter;
+ std::optional<FilterMatcher> PassNameFilter;
+ std::optional<FilterMatcher> RemarkArgFilter;
+ std::optional<Type> RemarkType;
+ if (!RemarkNameOpt.empty())
+ RemarkNameFilter = {RemarkNameOpt, false};
+ else if (!RemarkNameOptRE.empty())
+ RemarkNameFilter = {RemarkNameOptRE, true};
+ if (!PassNameOpt.empty())
+ PassNameFilter = {PassNameOpt, false};
+ else if (!PassNameOptRE.empty())
+ PassNameFilter = {PassNameOptRE, true};
+ if (RemarkTypeOpt != Type::Failure)
+ RemarkType = RemarkTypeOpt;
+ if (!RemarkFilterArgByOpt.empty())
+ RemarkArgFilter = {RemarkFilterArgByOpt, false};
+ else if (!RemarkArgFilterOptRE.empty())
+ RemarkArgFilter = {RemarkArgFilterOptRE, true};
+ // Create RemarkFilter.
+ return Filters::createRemarkFilter(std::move(RemarkNameFilter),
+ std::move(PassNameFilter),
+ std::move(RemarkArgFilter), RemarkType);
+}
+
+Error useCollectRemark(StringRef Buffer, Counter &Counter, Filters &Filter) {
+ // Create Parser.
+ auto MaybeParser = createRemarkParser(InputFormat, Buffer);
+ if (!MaybeParser)
+ return MaybeParser.takeError();
+ auto &Parser = **MaybeParser;
+ auto MaybeRemark = Parser.next();
+ for (; MaybeRemark; MaybeRemark = Parser.next()) {
+ const Remark &Remark = **MaybeRemark;
+ if (Filter.filterRemark(Remark))
+ Counter.collect(Remark);
+ }
+
+ if (auto E = Counter.print(OutputFileName))
+ return E;
+ auto E = MaybeRemark.takeError();
+ if (!E.isA<EndOfFileError>())
+ return E;
+ consumeError(std::move(E));
+ return Error::success();
+}
+
+static Error collectRemarks() {
+ // Create a parser for the user-specified input format.
+ auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+ if (!MaybeBuf)
+ return MaybeBuf.takeError();
+ StringRef Buffer = (*MaybeBuf)->getBuffer();
+ auto MaybeFilter = getRemarkFilter();
+ if (!MaybeFilter)
+ return MaybeFilter.takeError();
+ auto &Filter = *MaybeFilter;
+ if (CountByOpt == CountBy::REMARK) {
+ RemarkCounter RC(GroupByOpt);
+ if (auto E = useCollectRemark(Buffer, RC, Filter))
+ return E;
+ } else if (CountByOpt == CountBy::ARGUMENT) {
+ SmallVector<FilterMatcher, 4> ArgumentsVector;
+ if (!Keys.empty()) {
+ for (auto &Key : Keys)
+ ArgumentsVector.push_back({Key, false});
+ } else if (!RKeys.empty())
+ for (auto Key : RKeys)
+ ArgumentsVector.push_back({Key, true});
+ else
+ ArgumentsVector.push_back({".*", true});
+
+ Expected<ArgumentCounter> AC = ArgumentCounter::createArgumentCounter(
+ GroupByOpt, ArgumentsVector, Buffer, Filter);
+ if (!AC)
+ return AC.takeError();
+ if (auto E = useCollectRemark(Buffer, *AC, Filter))
+ return E;
+ }
+ return Error::success();
+}
+
+static CommandRegistration CountReg(&CountSub, collectRemarks);