Add an implementation of the delta debugging algorithm.

- This is a pretty slow / memory intensive implementation, and I will likely
   change it to an iterative model, but it works.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@90447 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Daniel Dunbar 2009-12-03 11:12:42 +00:00
parent 44299c9507
commit a6d5e40e25
4 changed files with 296 additions and 0 deletions

View File

@ -0,0 +1,89 @@
//===--- DeltaAlgorithm.h - A Set Minimization Algorithm -------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
#ifndef LLVM_ADT_DELTAALGORITHM_H
#define LLVM_ADT_DELTAALGORITHM_H
#include <vector>
#include <set>
namespace llvm {
/// DeltaAlgorithm - Implements the delta debugging algorithm (A. Zeller '99)
/// for minimizing arbitrary sets using a predicate function.
///
/// The result of the algorithm is a subset of the input change set which is
/// guaranteed to satisfy the predicate, assuming that the input set did. For
/// well formed predicates, the result set is guaranteed to be such that
/// removing any single element would falsify the predicate.
///
/// For best results the predicate function *should* (but need not) satisfy
/// certain properties, in particular:
/// (1) The predicate should return false on an empty set and true on the full
/// set.
/// (2) If the predicate returns true for a set of changes, it should return
/// true for all supersets of that set.
///
/// It is not an error to provide a predicate that does not satisfy these
/// requirements, and the algorithm will generally produce reasonable
/// results. However, it may run substantially more tests than with a good
/// predicate.
class DeltaAlgorithm {
public:
typedef unsigned change_ty;
// FIXME: Use a decent data structure.
typedef std::set<change_ty> changeset_ty;
typedef std::vector<changeset_ty> changesetlist_ty;
private:
/// Cache of failed test results. Successful test results are never cached
/// since we always reduce following a success.
std::set<changeset_ty> FailedTestsCache;
/// GetTestResult - Get the test result for the \arg Changes from the
/// cache, executing the test if necessary.
///
/// \param Changes - The change set to test.
/// \return - The test result.
bool GetTestResult(const changeset_ty &Changes);
/// Split - Partition a set of changes \arg Sinto one or two subsets.
void Split(const changeset_ty &S, changesetlist_ty &Res);
/// Delta - Minimize a set of \arg Changes which has been partioned into
/// smaller sets, by attempting to remove individual subsets.
changeset_ty Delta(const changeset_ty &Changes,
const changesetlist_ty &Sets);
/// Search - Search for a subset (or subsets) in \arg Sets which can be
/// removed from \arg Changes while still satisfying the predicate.
///
/// \param Res - On success, a subset of Changes which satisfies the
/// predicate.
/// \return - True on success.
bool Search(const changeset_ty &Changes, const changesetlist_ty &Sets,
changeset_ty &Res);
protected:
/// UpdatedSearchState - Callback used when the search state changes.
virtual void UpdatedSearchState(const changeset_ty &Changes,
const changesetlist_ty &Sets) {}
/// ExecuteOneTest - Execute a single test predicate on the change set \arg S.
virtual bool ExecuteOneTest(const changeset_ty &S) = 0;
public:
/// Run - Minimize the set \arg Changes by executing \see ExecuteOneTest() on
/// subsets of changes and returning the smallest set which still satisfies
/// the test predicate.
changeset_ty Run(const changeset_ty &Changes);
};
} // end namespace llvm
#endif

View File

@ -6,6 +6,7 @@ add_llvm_library(LLVMSupport
CommandLine.cpp
ConstantRange.cpp
Debug.cpp
DeltaAlgorithm.cpp
Dwarf.cpp
ErrorHandling.cpp
FileUtilities.cpp

View File

@ -0,0 +1,110 @@
//===--- DeltaAlgorithm.h - A Set Minimization Algorithm -------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
#include "llvm/ADT/DeltaAlgorithm.h"
#include <algorithm>
using namespace llvm;
bool DeltaAlgorithm::GetTestResult(const changeset_ty &Changes) {
if (FailedTestsCache.count(Changes))
return false;
bool Result = ExecuteOneTest(Changes);
if (!Result)
FailedTestsCache.insert(Changes);
return Result;
}
void DeltaAlgorithm::Split(const changeset_ty &S, changesetlist_ty &Res) {
// FIXME: Allow clients to provide heuristics for improved splitting.
// FIXME: This is really slow.
changeset_ty LHS, RHS;
unsigned idx = 0;
for (changeset_ty::const_iterator it = S.begin(),
ie = S.end(); it != ie; ++it, ++idx)
((idx & 1) ? LHS : RHS).insert(*it);
if (!LHS.empty())
Res.push_back(LHS);
if (!RHS.empty())
Res.push_back(RHS);
}
DeltaAlgorithm::changeset_ty
DeltaAlgorithm::Delta(const changeset_ty &Changes,
const changesetlist_ty &Sets) {
// Invariant: union(Res) == Changes
UpdatedSearchState(Changes, Sets);
// If there is nothing left we can remove, we are done.
if (Sets.size() <= 1)
return Changes;
// Look for a passing subset.
changeset_ty Res;
if (Search(Changes, Sets, Res))
return Res;
// Otherwise, partition the sets if possible; if not we are done.
changesetlist_ty SplitSets;
for (changesetlist_ty::const_iterator it = Sets.begin(),
ie = Sets.end(); it != ie; ++it)
Split(*it, SplitSets);
if (SplitSets.size() == Sets.size())
return Changes;
return Delta(Changes, SplitSets);
}
bool DeltaAlgorithm::Search(const changeset_ty &Changes,
const changesetlist_ty &Sets,
changeset_ty &Res) {
// FIXME: Parallelize.
for (changesetlist_ty::const_iterator it = Sets.begin(),
ie = Sets.end(); it != ie; ++it) {
// If the test passes on this subset alone, recurse.
if (GetTestResult(*it)) {
changesetlist_ty Sets;
Split(*it, Sets);
Res = Delta(*it, Sets);
return true;
}
// Otherwise, if we have more than two sets, see if test passes on the
// complement.
if (Sets.size() > 2) {
// FIXME: This is really slow.
changeset_ty Complement;
std::set_difference(
Changes.begin(), Changes.end(), it->begin(), it->end(),
std::insert_iterator<changeset_ty>(Complement, Complement.begin()));
if (GetTestResult(Complement)) {
changesetlist_ty ComplementSets;
ComplementSets.insert(ComplementSets.end(), Sets.begin(), it);
ComplementSets.insert(ComplementSets.end(), it + 1, Sets.end());
Res = Delta(Complement, ComplementSets);
return true;
}
}
}
return false;
}
DeltaAlgorithm::changeset_ty DeltaAlgorithm::Run(const changeset_ty &Changes) {
// Check empty set first to quickly find poor test functions.
if (GetTestResult(changeset_ty()))
return changeset_ty();
// Otherwise run the real delta algorithm.
changesetlist_ty Sets;
Split(Changes, Sets);
return Delta(Changes, Sets);
}

View File

@ -0,0 +1,96 @@
//===- llvm/unittest/ADT/DeltaAlgorithmTest.cpp ---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "llvm/ADT/DeltaAlgorithm.h"
#include <algorithm>
#include <cstdarg>
using namespace llvm;
std::ostream &operator<<(std::ostream &OS,
const std::set<unsigned> &S) {
OS << "{";
for (std::set<unsigned>::const_iterator it = S.begin(),
ie = S.end(); it != ie; ++it) {
if (it != S.begin())
OS << ",";
OS << *it;
}
OS << "}";
return OS;
}
namespace {
class FixedDeltaAlgorithm : public DeltaAlgorithm {
changeset_ty FailingSet;
unsigned NumTests;
protected:
virtual bool ExecuteOneTest(const changeset_ty &Changes) {
++NumTests;
return std::includes(Changes.begin(), Changes.end(),
FailingSet.begin(), FailingSet.end());
}
public:
FixedDeltaAlgorithm(const changeset_ty &_FailingSet)
: FailingSet(_FailingSet),
NumTests(0) {}
unsigned getNumTests() const { return NumTests; }
};
std::set<unsigned> fixed_set(unsigned N, ...) {
std::set<unsigned> S;
va_list ap;
va_start(ap, N);
for (unsigned i = 0; i != N; ++i)
S.insert(va_arg(ap, unsigned));
va_end(ap);
return S;
}
std::set<unsigned> range(unsigned Start, unsigned End) {
std::set<unsigned> S;
while (Start != End)
S.insert(Start++);
return S;
}
std::set<unsigned> range(unsigned N) {
return range(0, N);
}
TEST(DeltaAlgorithmTest, Basic) {
// P = {3,5,7} \in S
// [0, 20) should minimize to {3,5,7} in a reasonable number of tests.
std::set<unsigned> Fails = fixed_set(3, 3, 5, 7);
FixedDeltaAlgorithm FDA(Fails);
EXPECT_EQ(fixed_set(3, 3, 5, 7), FDA.Run(range(20)));
EXPECT_GE(33U, FDA.getNumTests());
// P = {3,5,7} \in S
// [10, 20) should minimize to [10,20)
EXPECT_EQ(range(10,20), FDA.Run(range(10,20)));
// P = [0,4) \in S
// [0, 4) should minimize to [0,4) in 11 tests.
//
// 11 = |{ {},
// {0}, {1}, {2}, {3},
// {1, 2, 3}, {0, 2, 3}, {0, 1, 3}, {0, 1, 2},
// {0, 1}, {2, 3} }|
FDA = FixedDeltaAlgorithm(range(10));
EXPECT_EQ(range(4), FDA.Run(range(4)));
EXPECT_EQ(11U, FDA.getNumTests());
}
}