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 
18 namespace llvm {
19 namespace 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.
23 enum 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.
27 enum 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.
35 inline 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.
50 struct 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(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.
70 struct 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 
94 private:
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.
100 inline Error checkRegex(const Regex &Regex) {
101   std::string Error;
102   if (!Regex.isValid(Error))
103     return createStringError(make_error_code(std::errc::invalid_argument),
104                              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.
110 struct 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    |
145 struct 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(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 
184 private:
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.
198 struct 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