Index: include/llvm/Support/DebugCounter.h =================================================================== --- /dev/null +++ include/llvm/Support/DebugCounter.h @@ -0,0 +1,110 @@ +//===- llvm/Support/DebugCounter.h - Debug counter support ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// \file This file provides an implementation of debug counters. Debug counters +// are a tool that let you narrow down a miscompilation to a specific thing +// happening. To give a use case: Imagine you have a file, very large, and you +// are trying to understand the minimal transformation that breaks it. Bugpoint +// and bisection is often helpful here in narrowing it down to a specific pass, +// but it's still a very large file, and a very complicated pass to try to +// debug. That is where debug counting steps in. You can instrument the pass +// with a debug counter before it does a certain thing, and depending on the +// counts, it will either execute that thing or not. The debug counter itself +// consists of a skip and a count. Skip is the number of times shouldExecute +// needs to be called before it returns true. Count is the number of times to +// return true once Skip is 0. So a skip=47, count=2 ,would skip the first 47 +// executions by returning false from shouldExecute, then execute twice, and +// then return false again. + +// For a concrete example, during predicateinfo creation, the renaming pass +// replaces each use with a renamed use. +/// +// If I register a counter called "predicateinfo" using registerCounter, +// and then instrument this renaming with a debug counter, like so: +// +// if (!DebugCounter::shouldExecute("predicateinfo")) +// +// +// Now I can, from the command line, make it rename or not rename certain uses +// by setting the skip and count. +// So for example +// bin/opt -debug-counter=predicateinfo-skip=47,predicateinfo-count=1 +// will skip renaming the first 47 uses, then rename one, then skip the rest. +// This + +#ifndef LLVM_SUPPORT_DEBUGCOUNTER_H +#define LLVM_SUPPORT_DEBUGCOUNTER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace llvm { + +class DebugCounter { +public: + /// \brief Returns a reference to the singleton instance. + static DebugCounter &instance(); + + // Used by the command line option parser to push a new value it parsed. + void push_back(const std::string &); + + // Register a counter with the specified name. + // + // FIXME: Currently, counter registration is required to happen before command + // line option parsing. The main reason to register counters is to produce a + // nice list of them on the command line, but i'm not sure this is worth it. + static void registerCounter(StringRef Name) { instance().addCounter(Name); } + inline static bool shouldExecute(StringRef CounterName) { +// Compile to nothing when debugging is off +#ifdef NDEBUG + return true; +#else + auto &Us = instance(); + auto Result = Us.Counters.find(CounterName); + if (Result != Us.Counters.end()) { + auto &CounterPair = Result->second; + // We only execute while the skip (first) is zero and the count (second) + // is non-zero + if (CounterPair.first < 0) + return true; + if (CounterPair.first != 0) { + --CounterPair.first; + return false; + } + if (CounterPair.second < 0) + return true; + if (CounterPair.second != 0) { + --CounterPair.second; + return true; + } + return false; + } + // Didn't find the counter, should we warn? + return true; +#endif // NDEBUG + } + + // Dump or print the current counter set. + LLVM_DUMP_METHOD void dump() { print(dbgs()); } + + void print(raw_ostream &OS); + +private: + void addCounter(const std::string &Name) { RegisteredCounters.insert(Name); } + + // FIXME: DensemapInfo has not been overloaded for strings + std::unordered_map> Counters; + std::unordered_set RegisteredCounters; +}; +} // namespace llvm +#endif Index: lib/Support/CMakeLists.txt =================================================================== --- lib/Support/CMakeLists.txt +++ lib/Support/CMakeLists.txt @@ -47,6 +47,7 @@ CrashRecoveryContext.cpp DataExtractor.cpp Debug.cpp + DebugCounter.cpp DeltaAlgorithm.cpp DAGDeltaAlgorithm.cpp Dwarf.cpp Index: lib/Support/DebugCounter.cpp =================================================================== --- /dev/null +++ lib/Support/DebugCounter.cpp @@ -0,0 +1,54 @@ +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Options.h" + +using namespace llvm; + +// Create our command line option +static cl::list DebugCounterOption( + "debug-counter", cl::desc("Comma separated list of debug counters"), + cl::CommaSeparated, cl::ZeroOrMore, cl::location(DebugCounter::instance())); + +static ManagedStatic DC; + +DebugCounter &DebugCounter::instance() { return *DC; } + +void DebugCounter::push_back(const std::string &Val) { + if (Val.empty()) + return; + // The strings should come in as counter=value + auto CounterPair = StringRef(Val).split('='); + if (CounterPair.second.empty()) + // TODO: Figure out proper way to warn user on command line + return; + // Now we have counter=value. + // First, process value. + long CounterVal; + if (CounterPair.second.getAsInteger(0, CounterVal)) + // Ditto above + return; + // Now we need to see if this is the skip or the count, remove the suffix, and + // add it to the counter values. + if (CounterPair.first.endswith("-skip")) { + auto CounterName = CounterPair.first.drop_back(5); + if (!RegisteredCounters.count(CounterName)) + return; + Counters[CounterName].first = CounterVal; + } else if (CounterPair.first.endswith("-count")) { + auto CounterName = CounterPair.first.drop_back(6); + if (!RegisteredCounters.count(CounterName)) + return; + Counters[CounterName].second = CounterVal; + } else { + // FIXME: Issue warning? + } +} + +void DebugCounter::print(raw_ostream &OS) { + OS << "Counters and values:\n"; + for (const auto &KV : Counters) + OS << left_justify(KV.first, 32) << ": {" << KV.second.first << "," + << KV.second.second << "}\n"; +} Index: utils/bisect-skip-count =================================================================== --- /dev/null +++ utils/bisect-skip-count @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import os +import sys +import argparse +import subprocess32 as subprocess + +parser = argparse.ArgumentParser() + +parser.add_argument('--skipstart', type=int, default=0) +parser.add_argument('--skipend', type=int, default=(1 << 32)) +parser.add_argument('--countstart', type=int, default=0) +parser.add_argument('--countend', type=int, default=(1 << 32)) +parser.add_argument('--timeout', type=int, default=None) +parser.add_argument('command', nargs='+') + +args = parser.parse_args() + +start = args.skipstart +end = args.skipend + +print("Bisect Starting!") +print("Start: %d" % start) +print("End: %d" % end) + +last = None +while start != end and start != end-1: + count = start + (end - start)/2 + print("Visiting Skip: %d with (Start, End) = (%d,%d)" % (count, start, end)) + cmd = [x % {'skip':count, 'count':-1} for x in args.command] + print cmd + try: + result = subprocess.call(cmd, shell=True, timeout=args.timeout) + if result == 0: + print(" PASSES! Setting start to count") + end = count + else: + print(" FAILS! Setting end to count") + start = count + except: + print(" TIMEOUT, setting end to count") + end = count +firstcount = start +print("Last good count: %d" % start) +start = args.countstart +end = args.countend +while start != end and start != end-1: + count = start + (end - start)/2 + print("Visiting Count: %d with (Start, End) = (%d,%d)" % (count, start, end)) + cmd = [x % {'count':count, 'skip':firstcount } for x in args.command] + print cmd + try: + result = subprocess.call(cmd, shell=True, timeout=20) + if result == 0: + print(" PASSES! Setting start to count") + start = count + else: + print(" FAILS! Setting end to count") + end = count + except: + print(" TIMEOUT, setting end to count") + start = count + +print("Last good count: %d" % start)