1//===- RemarkCounter.h ----------------------------------------------------===//
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// Generic tool to count remarks based on properties
10//
11//===----------------------------------------------------------------------===//
12#ifndef TOOLS_LLVM_REMARKCOUNTER_H
13#define TOOLS_LLVM_REMARKCOUNTER_H
14#include "RemarkUtilHelpers.h"
15#include "llvm/ADT/MapVector.h"
16#include "llvm/Support/Regex.h"
17
18namespace llvm {
19namespace remarks {
20
21/// Collect remarks by counting the existance of a remark or by looking through
22/// the keys and summing through the total count.
23enum class CountBy { REMARK, ARGUMENT };
24
25/// Summarize the count by either emitting one count for the remark file, or
26/// grouping the count by source file or by function name.
27enum class GroupBy {
28 TOTAL,
29 PER_SOURCE,
30 PER_FUNCTION,
31 PER_FUNCTION_WITH_DEBUG_LOC
32};
33
34/// Convert \p GroupBy to a std::string.
35inline std::string groupByToStr(GroupBy GroupBy) {
36 switch (GroupBy) {
37 default:
38 return "Total";
39 case GroupBy::PER_FUNCTION:
40 return "Function";
41 case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC:
42 return "FuctionWithDebugLoc";
43 case GroupBy::PER_SOURCE:
44 return "Source";
45 }
46}
47
48/// Filter object which can be either a string or a regex to match with the
49/// remark properties.
50struct FilterMatcher {
51 Regex FilterRE;
52 std::string FilterStr;
53 bool IsRegex;
54 FilterMatcher(std::string Filter, bool IsRegex) : IsRegex(IsRegex) {
55 if (IsRegex)
56 FilterRE = Regex(Filter);
57 else
58 FilterStr = Filter;
59 }
60
61 bool match(StringRef StringToMatch) const {
62 if (IsRegex)
63 return FilterRE.match(String: StringToMatch);
64 return FilterStr == StringToMatch.trim().str();
65 }
66};
67
68/// Filter out remarks based on remark properties based on name, pass name,
69/// argument and type.
70struct Filters {
71 std::optional<FilterMatcher> RemarkNameFilter;
72 std::optional<FilterMatcher> PassNameFilter;
73 std::optional<FilterMatcher> ArgFilter;
74 std::optional<Type> RemarkTypeFilter;
75 /// Returns a filter object if all the arguments provided are valid regex
76 /// types otherwise return an error.
77 static Expected<Filters>
78 createRemarkFilter(std::optional<FilterMatcher> RemarkNameFilter,
79 std::optional<FilterMatcher> PassNameFilter,
80 std::optional<FilterMatcher> ArgFilter,
81 std::optional<Type> RemarkTypeFilter) {
82 Filters Filter;
83 Filter.RemarkNameFilter = std::move(RemarkNameFilter);
84 Filter.PassNameFilter = std::move(PassNameFilter);
85 Filter.ArgFilter = std::move(ArgFilter);
86 Filter.RemarkTypeFilter = std::move(RemarkTypeFilter);
87 if (auto E = Filter.regexArgumentsValid())
88 return std::move(E);
89 return std::move(Filter);
90 }
91 /// Returns true if \p Remark satisfies all the provided filters.
92 bool filterRemark(const Remark &Remark);
93
94private:
95 /// Check if arguments can be parsed as valid regex types.
96 Error regexArgumentsValid();
97};
98
99/// Convert Regex string error to an error object.
100inline Error checkRegex(const Regex &Regex) {
101 std::string Error;
102 if (!Regex.isValid(Error))
103 return createStringError(EC: make_error_code(e: std::errc::invalid_argument),
104 S: Twine("Regex: ", Error));
105 return Error::success();
106}
107
108/// Abstract counter class used to define the general required methods for
109/// counting a remark.
110struct Counter {
111 GroupBy Group = GroupBy::TOTAL;
112 Counter() = default;
113 Counter(enum GroupBy GroupBy) : Group(GroupBy) {}
114 /// Obtain the field for collecting remark info based on how we are
115 /// collecting. Remarks are grouped by FunctionName, Source, Source and
116 /// Function or collect by file.
117 std::optional<std::string> getGroupByKey(const Remark &Remark);
118
119 /// Collect count information from \p Remark organized based on \p Group
120 /// property.
121 virtual void collect(const Remark &) = 0;
122 /// Output the final count to the file \p OutputFileName
123 virtual Error print(StringRef OutputFileName) = 0;
124 virtual ~Counter() = default;
125};
126
127/// Count remarks based on the provided \p Keys argument and summing up the
128/// value for each matching key organized by source, function or reporting a
129/// total for the specified remark file.
130/// Reporting count grouped by source:
131///
132/// | source | key1 | key2 | key3 |
133/// |---------------|------|------|------|
134/// | path/to/file1 | 0 | 1 | 3 |
135/// | path/to/file2 | 1 | 0 | 2 |
136/// | path/to/file3 | 2 | 3 | 1 |
137///
138/// Reporting count grouped by function:
139///
140/// | Function | key1 | key2 | key3 |
141/// |---------------|------|------|------|
142/// | function1 | 0 | 1 | 3 |
143/// | function2 | 1 | 0 | 2 |
144/// | function3 | 2 | 3 | 1 |
145struct ArgumentCounter : Counter {
146 /// The internal object to keep the count for the remarks. The first argument
147 /// corresponds to the property we are collecting for this can be either a
148 /// source or function. The second argument is a row of integers where each
149 /// item in the row is the count for a specified key.
150 std::map<std::string, SmallVector<unsigned, 4>> CountByKeysMap;
151 /// A set of all the remark argument found in the remark file. The second
152 /// argument is the index of each of those arguments which can be used in
153 /// `CountByKeysMap` to fill count information for that argument.
154 MapVector<StringRef, unsigned> ArgumentSetIdxMap;
155 /// Create an argument counter. If the provided \p Arguments represent a regex
156 /// vector then we need to check that the provided regular expressions are
157 /// valid if not we return an Error.
158 static Expected<ArgumentCounter>
159 createArgumentCounter(GroupBy Group, ArrayRef<FilterMatcher> Arguments,
160 StringRef Buffer, Filters &Filter) {
161 ArgumentCounter AC;
162 AC.Group = Group;
163 for (auto &Arg : Arguments) {
164 if (Arg.IsRegex) {
165 if (auto E = checkRegex(Regex: Arg.FilterRE))
166 return std::move(E);
167 }
168 }
169 if (auto E = AC.getAllMatchingArgumentsInRemark(Buffer, Arguments, Filter))
170 return std::move(E);
171 return AC;
172 }
173
174 /// Update the internal count map based on the remark integer arguments that
175 /// correspond the the user specified argument keys to collect for.
176 void collect(const Remark &) override;
177
178 /// Print a CSV table consisting of an index which is specified by \p
179 /// `Group` and can be a function name, source file name or function name
180 /// with the full source path and columns of user specified remark arguments
181 /// to collect the count for.
182 Error print(StringRef OutputFileName) override;
183
184private:
185 /// collect all the arguments that match the list of \p Arguments provided by
186 /// parsing through \p Buffer of remarks and filling \p ArgumentSetIdxMap
187 /// acting as a row for for all the keys that we are interested in collecting
188 /// information for.
189 Error getAllMatchingArgumentsInRemark(StringRef Buffer,
190 ArrayRef<FilterMatcher> Arguments,
191 Filters &Filter);
192};
193
194/// Collect remarks based by counting the existance of individual remarks. The
195/// reported table will be structured based on the provided \p Group argument
196/// by reporting count for functions, source or total count for the provided
197/// remark file.
198struct RemarkCounter : Counter {
199 std::map<std::string, unsigned> CountedByRemarksMap;
200 RemarkCounter(GroupBy Group) : Counter(Group) {}
201
202 /// Advance the internal map count broken by \p Group when
203 /// seeing \p Remark.
204 void collect(const Remark &) override;
205
206 /// Print a CSV table consisting of an index which is specified by \p
207 /// `Group` and can be a function name, source file name or function name
208 /// with the full source path and a counts column corresponding to the count
209 /// of each individual remark at th index.
210 Error print(StringRef OutputFileName) override;
211};
212} // namespace remarks
213
214} // namespace llvm
215#endif // TOOLS_LLVM_REMARKCOUNTER_H
216