//===- RemarkCounter.h ----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Generic tool to count remarks based on properties // //===----------------------------------------------------------------------===// #ifndef TOOLS_LLVM_REMARKCOUNTER_H #define TOOLS_LLVM_REMARKCOUNTER_H #include "RemarkUtilHelpers.h" #include "llvm/ADT/MapVector.h" #include "llvm/Support/Regex.h" namespace llvm { namespace remarks { /// Collect remarks by counting the existance of a remark or by looking through /// the keys and summing through the total count. enum class CountBy { REMARK, ARGUMENT }; /// Summarize the count by either emitting one count for the remark file, or /// grouping the count by source file or by function name. enum class GroupBy { TOTAL, PER_SOURCE, PER_FUNCTION, PER_FUNCTION_WITH_DEBUG_LOC }; /// Convert \p GroupBy to a std::string. inline std::string groupByToStr(GroupBy GroupBy) { switch (GroupBy) { default: return "Total"; case GroupBy::PER_FUNCTION: return "Function"; case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC: return "FuctionWithDebugLoc"; case GroupBy::PER_SOURCE: return "Source"; } } /// Filter object which can be either a string or a regex to match with the /// remark properties. struct FilterMatcher { Regex FilterRE; std::string FilterStr; bool IsRegex; FilterMatcher(std::string Filter, bool IsRegex) : IsRegex(IsRegex) { if (IsRegex) FilterRE = Regex(Filter); else FilterStr = Filter; } bool match(StringRef StringToMatch) const { if (IsRegex) return FilterRE.match(StringToMatch); return FilterStr == StringToMatch.trim().str(); } }; /// Filter out remarks based on remark properties based on name, pass name, /// argument and type. struct Filters { std::optional RemarkNameFilter; std::optional PassNameFilter; std::optional ArgFilter; std::optional RemarkTypeFilter; /// Returns a filter object if all the arguments provided are valid regex /// types otherwise return an error. static Expected createRemarkFilter(std::optional RemarkNameFilter, std::optional PassNameFilter, std::optional ArgFilter, std::optional RemarkTypeFilter) { Filters Filter; Filter.RemarkNameFilter = std::move(RemarkNameFilter); Filter.PassNameFilter = std::move(PassNameFilter); Filter.ArgFilter = std::move(ArgFilter); Filter.RemarkTypeFilter = std::move(RemarkTypeFilter); if (auto E = Filter.regexArgumentsValid()) return std::move(E); return std::move(Filter); } /// Returns true if \p Remark satisfies all the provided filters. bool filterRemark(const Remark &Remark); private: /// Check if arguments can be parsed as valid regex types. Error regexArgumentsValid(); }; /// Convert Regex string error to an error object. inline Error checkRegex(const Regex &Regex) { std::string Error; if (!Regex.isValid(Error)) return createStringError(make_error_code(std::errc::invalid_argument), Twine("Regex: ", Error)); return Error::success(); } /// Abstract counter class used to define the general required methods for /// counting a remark. struct Counter { GroupBy Group = GroupBy::TOTAL; Counter() = default; Counter(enum GroupBy GroupBy) : Group(GroupBy) {} /// Obtain the field for collecting remark info based on how we are /// collecting. Remarks are grouped by FunctionName, Source, Source and /// Function or collect by file. std::optional getGroupByKey(const Remark &Remark); /// Collect count information from \p Remark organized based on \p Group /// property. virtual void collect(const Remark &) = 0; /// Output the final count to the file \p OutputFileName virtual Error print(StringRef OutputFileName) = 0; virtual ~Counter() = default; }; /// Count remarks based on the provided \p Keys argument and summing up the /// value for each matching key organized by source, function or reporting a /// total for the specified remark file. /// Reporting count grouped by source: /// /// | source | key1 | key2 | key3 | /// |---------------|------|------|------| /// | path/to/file1 | 0 | 1 | 3 | /// | path/to/file2 | 1 | 0 | 2 | /// | path/to/file3 | 2 | 3 | 1 | /// /// Reporting count grouped by function: /// /// | Function | key1 | key2 | key3 | /// |---------------|------|------|------| /// | function1 | 0 | 1 | 3 | /// | function2 | 1 | 0 | 2 | /// | function3 | 2 | 3 | 1 | struct ArgumentCounter : Counter { /// The internal object to keep the count for the remarks. The first argument /// corresponds to the property we are collecting for this can be either a /// source or function. The second argument is a row of integers where each /// item in the row is the count for a specified key. std::map> CountByKeysMap; /// A set of all the remark argument found in the remark file. The second /// argument is the index of each of those arguments which can be used in /// `CountByKeysMap` to fill count information for that argument. MapVector ArgumentSetIdxMap; /// Create an argument counter. If the provided \p Arguments represent a regex /// vector then we need to check that the provided regular expressions are /// valid if not we return an Error. static Expected createArgumentCounter(GroupBy Group, ArrayRef Arguments, StringRef Buffer, Filters &Filter) { ArgumentCounter AC; AC.Group = Group; for (auto &Arg : Arguments) { if (Arg.IsRegex) { if (auto E = checkRegex(Arg.FilterRE)) return std::move(E); } } if (auto E = AC.getAllMatchingArgumentsInRemark(Buffer, Arguments, Filter)) return std::move(E); return AC; } /// Update the internal count map based on the remark integer arguments that /// correspond the the user specified argument keys to collect for. void collect(const Remark &) override; /// Print a CSV table consisting of an index which is specified by \p /// `Group` and can be a function name, source file name or function name /// with the full source path and columns of user specified remark arguments /// to collect the count for. Error print(StringRef OutputFileName) override; private: /// collect all the arguments that match the list of \p Arguments provided by /// parsing through \p Buffer of remarks and filling \p ArgumentSetIdxMap /// acting as a row for for all the keys that we are interested in collecting /// information for. Error getAllMatchingArgumentsInRemark(StringRef Buffer, ArrayRef Arguments, Filters &Filter); }; /// Collect remarks based by counting the existance of individual remarks. The /// reported table will be structured based on the provided \p Group argument /// by reporting count for functions, source or total count for the provided /// remark file. struct RemarkCounter : Counter { std::map CountedByRemarksMap; RemarkCounter(GroupBy Group) : Counter(Group) {} /// Advance the internal map count broken by \p Group when /// seeing \p Remark. void collect(const Remark &) override; /// Print a CSV table consisting of an index which is specified by \p /// `Group` and can be a function name, source file name or function name /// with the full source path and a counts column corresponding to the count /// of each individual remark at th index. Error print(StringRef OutputFileName) override; }; } // namespace remarks } // namespace llvm #endif // TOOLS_LLVM_REMARKCOUNTER_H