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