Path: blob/main/contrib/llvm-project/llvm/lib/Support/DebugCounter.cpp
35232 views
#include "llvm/Support/DebugCounter.h"12#include "DebugOptions.h"34#include "llvm/Support/CommandLine.h"5#include "llvm/Support/Format.h"67using namespace llvm;89namespace llvm {1011void DebugCounter::Chunk::print(llvm::raw_ostream &OS) {12if (Begin == End)13OS << Begin;14else15OS << Begin << "-" << End;16}1718void DebugCounter::printChunks(raw_ostream &OS, ArrayRef<Chunk> Chunks) {19if (Chunks.empty()) {20OS << "empty";21} else {22bool IsFirst = true;23for (auto E : Chunks) {24if (!IsFirst)25OS << ':';26else27IsFirst = false;28E.print(OS);29}30}31}3233bool DebugCounter::parseChunks(StringRef Str, SmallVector<Chunk> &Chunks) {34StringRef Remaining = Str;3536auto ConsumeInt = [&]() -> int64_t {37StringRef Number =38Remaining.take_until([](char c) { return c < '0' || c > '9'; });39int64_t Res;40if (Number.getAsInteger(10, Res)) {41errs() << "Failed to parse int at : " << Remaining << "\n";42return -1;43}44Remaining = Remaining.drop_front(Number.size());45return Res;46};4748while (1) {49int64_t Num = ConsumeInt();50if (Num == -1)51return true;52if (!Chunks.empty() && Num <= Chunks[Chunks.size() - 1].End) {53errs() << "Expected Chunks to be in increasing order " << Num54<< " <= " << Chunks[Chunks.size() - 1].End << "\n";55return true;56}57if (Remaining.starts_with("-")) {58Remaining = Remaining.drop_front();59int64_t Num2 = ConsumeInt();60if (Num2 == -1)61return true;62if (Num >= Num2) {63errs() << "Expected " << Num << " < " << Num2 << " in " << Num << "-"64<< Num2 << "\n";65return true;66}6768Chunks.push_back({Num, Num2});69} else {70Chunks.push_back({Num, Num});71}72if (Remaining.starts_with(":")) {73Remaining = Remaining.drop_front();74continue;75}76if (Remaining.empty())77break;78errs() << "Failed to parse at : " << Remaining;79return true;80}81return false;82}8384} // namespace llvm8586namespace {87// This class overrides the default list implementation of printing so we88// can pretty print the list of debug counter options. This type of89// dynamic option is pretty rare (basically this and pass lists).90class DebugCounterList : public cl::list<std::string, DebugCounter> {91private:92using Base = cl::list<std::string, DebugCounter>;9394public:95template <class... Mods>96explicit DebugCounterList(Mods &&... Ms) : Base(std::forward<Mods>(Ms)...) {}9798private:99void printOptionInfo(size_t GlobalWidth) const override {100// This is a variant of from generic_parser_base::printOptionInfo. Sadly,101// it's not easy to make it more usable. We could get it to print these as102// options if we were a cl::opt and registered them, but lists don't have103// options, nor does the parser for std::string. The other mechanisms for104// options are global and would pollute the global namespace with our105// counters. Rather than go that route, we have just overridden the106// printing, which only a few things call anyway.107outs() << " -" << ArgStr;108// All of the other options in CommandLine.cpp use ArgStr.size() + 6 for109// width, so we do the same.110Option::printHelpStr(HelpStr, GlobalWidth, ArgStr.size() + 6);111const auto &CounterInstance = DebugCounter::instance();112for (const auto &Name : CounterInstance) {113const auto Info =114CounterInstance.getCounterInfo(CounterInstance.getCounterId(Name));115size_t NumSpaces = GlobalWidth - Info.first.size() - 8;116outs() << " =" << Info.first;117outs().indent(NumSpaces) << " - " << Info.second << '\n';118}119}120};121122// All global objects associated to the DebugCounter, including the DebugCounter123// itself, are owned by a single global instance of the DebugCounterOwner124// struct. This makes it easier to control the order in which constructors and125// destructors are run.126struct DebugCounterOwner : DebugCounter {127DebugCounterList DebugCounterOption{128"debug-counter", cl::Hidden,129cl::desc("Comma separated list of debug counter skip and count"),130cl::CommaSeparated, cl::location<DebugCounter>(*this)};131cl::opt<bool, true> PrintDebugCounter{132"print-debug-counter",133cl::Hidden,134cl::Optional,135cl::location(this->ShouldPrintCounter),136cl::init(false),137cl::desc("Print out debug counter info after all counters accumulated")};138cl::opt<bool, true> BreakOnLastCount{139"debug-counter-break-on-last",140cl::Hidden,141cl::Optional,142cl::location(this->BreakOnLast),143cl::init(false),144cl::desc("Insert a break point on the last enabled count of a "145"chunks list")};146147DebugCounterOwner() {148// Our destructor uses the debug stream. By referencing it here, we149// ensure that its destructor runs after our destructor.150(void)dbgs();151}152153// Print information when destroyed, iff command line option is specified.154~DebugCounterOwner() {155if (ShouldPrintCounter)156print(dbgs());157}158};159160} // anonymous namespace161162void llvm::initDebugCounterOptions() { (void)DebugCounter::instance(); }163164DebugCounter &DebugCounter::instance() {165static DebugCounterOwner O;166return O;167}168169// This is called by the command line parser when it sees a value for the170// debug-counter option defined above.171void DebugCounter::push_back(const std::string &Val) {172if (Val.empty())173return;174175// The strings should come in as counter=chunk_list176auto CounterPair = StringRef(Val).split('=');177if (CounterPair.second.empty()) {178errs() << "DebugCounter Error: " << Val << " does not have an = in it\n";179return;180}181StringRef CounterName = CounterPair.first;182SmallVector<Chunk> Chunks;183184if (parseChunks(CounterPair.second, Chunks)) {185return;186}187188unsigned CounterID = getCounterId(std::string(CounterName));189if (!CounterID) {190errs() << "DebugCounter Error: " << CounterName191<< " is not a registered counter\n";192return;193}194enableAllCounters();195196CounterInfo &Counter = Counters[CounterID];197Counter.IsSet = true;198Counter.Chunks = std::move(Chunks);199}200201void DebugCounter::print(raw_ostream &OS) const {202SmallVector<StringRef, 16> CounterNames(RegisteredCounters.begin(),203RegisteredCounters.end());204sort(CounterNames);205206auto &Us = instance();207OS << "Counters and values:\n";208for (auto &CounterName : CounterNames) {209unsigned CounterID = getCounterId(std::string(CounterName));210OS << left_justify(RegisteredCounters[CounterID], 32) << ": {"211<< Us.Counters[CounterID].Count << ",";212printChunks(OS, Us.Counters[CounterID].Chunks);213OS << "}\n";214}215}216217bool DebugCounter::shouldExecuteImpl(unsigned CounterName) {218auto &Us = instance();219auto Result = Us.Counters.find(CounterName);220if (Result != Us.Counters.end()) {221auto &CounterInfo = Result->second;222int64_t CurrCount = CounterInfo.Count++;223uint64_t CurrIdx = CounterInfo.CurrChunkIdx;224225if (CounterInfo.Chunks.empty())226return true;227if (CurrIdx >= CounterInfo.Chunks.size())228return false;229230bool Res = CounterInfo.Chunks[CurrIdx].contains(CurrCount);231if (Us.BreakOnLast && CurrIdx == (CounterInfo.Chunks.size() - 1) &&232CurrCount == CounterInfo.Chunks[CurrIdx].End) {233LLVM_BUILTIN_DEBUGTRAP;234}235if (CurrCount > CounterInfo.Chunks[CurrIdx].End) {236CounterInfo.CurrChunkIdx++;237238/// Handle consecutive blocks.239if (CounterInfo.CurrChunkIdx < CounterInfo.Chunks.size() &&240CurrCount == CounterInfo.Chunks[CounterInfo.CurrChunkIdx].Begin)241return true;242}243return Res;244}245// Didn't find the counter, should we warn?246return true;247}248249LLVM_DUMP_METHOD void DebugCounter::dump() const {250print(dbgs());251}252253254