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