mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-02-21 04:41:34 +00:00
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. llvm-svn: 90447
This commit is contained in:
parent
39cc7661ad
commit
63e2201f3e
89
include/llvm/ADT/DeltaAlgorithm.h
Normal file
89
include/llvm/ADT/DeltaAlgorithm.h
Normal 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
|
@ -6,6 +6,7 @@ add_llvm_library(LLVMSupport
|
||||
CommandLine.cpp
|
||||
ConstantRange.cpp
|
||||
Debug.cpp
|
||||
DeltaAlgorithm.cpp
|
||||
Dwarf.cpp
|
||||
ErrorHandling.cpp
|
||||
FileUtilities.cpp
|
||||
|
110
lib/Support/DeltaAlgorithm.cpp
Normal file
110
lib/Support/DeltaAlgorithm.cpp
Normal 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);
|
||||
}
|
96
unittests/ADT/DeltaAlgorithmTest.cpp
Normal file
96
unittests/ADT/DeltaAlgorithmTest.cpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user