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