Skip to content

Commit

Permalink
Move coverage support to mull-runner
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexDenisov committed Feb 1, 2022
1 parent 3342510 commit 9405088
Show file tree
Hide file tree
Showing 31 changed files with 200 additions and 280 deletions.
1 change: 0 additions & 1 deletion include/mull/Config/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ struct Configuration {

std::string executable;
std::string outputFile;
std::string coverageInfo;

std::string linker;
std::vector<std::string> linkerFlags;
Expand Down
1 change: 1 addition & 0 deletions include/mull/Config/ConfigurationOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct DebugConfig {
bool printIRBefore = false;
bool printIRAfter = false;
bool traceMutants = false;
bool coverage = false;
};

} // namespace mull
3 changes: 1 addition & 2 deletions include/mull/Filters/MutationFilter.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include "mull/Filters/Filter.h"

#include <string>

namespace mull {
Expand All @@ -12,7 +11,7 @@ class MutationFilter : virtual public Filter {
public:
virtual bool shouldSkip(MutationPoint *point) = 0;
virtual std::string name() = 0;
virtual ~MutationFilter() {};
~MutationFilter() override = default;
};

} // namespace mull
6 changes: 1 addition & 5 deletions include/mull/FunctionUnderTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,15 @@ class Bitcode;

class FunctionUnderTest {
public:
FunctionUnderTest(llvm::Function *function, Bitcode *bitcode, bool covered=false, std::vector<llvm::coverage::CountedRegion> linecoverage = {});
FunctionUnderTest(llvm::Function *function, Bitcode *bitcode);
llvm::Function *getFunction() const;
Bitcode *getBitcode() const;
const std::vector<llvm::Instruction *> &getSelectedInstructions() const;
bool isCovered() const;
bool isCovered(const SourceLocation& location) const;
void selectInstructions(const std::vector<InstructionFilter *> &filters);

private:
llvm::Function *function;
Bitcode *bitcode;
bool covered;
std::vector<llvm::coverage::CountedRegion> linecoverage;
std::vector<llvm::Instruction *> selectedInstructions;
};

Expand Down
3 changes: 2 additions & 1 deletion include/mull/Mutant.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ struct SourceLocation;
class Mutant {
public:
Mutant(std::string identifier, std::string mutatorIdentifier, SourceLocation sourceLocation,
SourceLocation endLocation, bool covered);
SourceLocation endLocation);

const std::string &getIdentifier() const;
const SourceLocation &getSourceLocation() const;
const SourceLocation &getEndLocation() const;
const std::string &getMutatorIdentifier() const;
void setCovered(bool covered);
bool isCovered() const;

/// needed by AST search
Expand Down
4 changes: 0 additions & 4 deletions include/mull/MutationPoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ class MutationPoint {
const SourceLocation sourceLocation;
irm::IRMutation *irMutator;
std::string userIdentifier;
bool covered;

SourceLocation endLocation;

Expand All @@ -72,9 +71,6 @@ class MutationPoint {

~MutationPoint() = default;

void setCovered(bool isCovered);
bool isCovered();

void setEndLocation(int line, int column);

Mutator *getMutator();
Expand Down
2 changes: 0 additions & 2 deletions lib/Config/ConfigurationParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ template <> struct llvm::yaml::MappingTraits<Configuration> {
io.mapOptional("linkerTimeout", config.linkerTimeout);
io.mapOptional("diagnostics", config.diagnostics);
io.mapOptional("mutators", config.mutators);
io.mapOptional("executable", config.executable);
io.mapOptional("outputFile", config.outputFile);
io.mapOptional("coverageInfo", config.coverageInfo);
io.mapOptional("linker", config.linker);
io.mapOptional("linkerFlags", config.linkerFlags);
io.mapOptional("parallelization", config.parallelization);
Expand Down
155 changes: 7 additions & 148 deletions lib/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
#include "mull/Toolchain/Runner.h"

#include <llvm/IR/Verifier.h>
#include <llvm/ProfileData/Coverage/CoverageMapping.h>
#include <llvm/Support/DynamicLibrary.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>

#include <algorithm>
#include <sstream>
Expand Down Expand Up @@ -60,17 +58,8 @@ std::unique_ptr<Result> Driver::run() {
std::string mutatorIdentifier = anyPoint->getMutatorIdentifier();
const SourceLocation &sourceLocation = anyPoint->getSourceLocation();
const SourceLocation &endLocation = anyPoint->getEndLocation();
bool covered = false;
for (MutationPoint *point : pair.second) {
/// Consider a mutant covered if at least one of the mutation points is covered
if (point->isCovered()) {
covered = true;
break;
}
}

mutants.push_back(std::make_unique<Mutant>(
identifier, mutatorIdentifier, sourceLocation, endLocation, covered));
mutants.push_back(
std::make_unique<Mutant>(identifier, mutatorIdentifier, sourceLocation, endLocation));
mutants.back()->setMutatorKind(anyPoint->getMutator()->mutatorKind());
}
std::sort(std::begin(mutants), std::end(mutants), MutantComparator());
Expand Down Expand Up @@ -299,87 +288,13 @@ Driver::Driver(Diagnostics &diagnostics, const Configuration &config, Program &p
}
}

static std::unique_ptr<llvm::coverage::CoverageMapping>
loadCoverage(const Configuration &configuration, Diagnostics &diagnostics) {
if (configuration.coverageInfo.empty()) {
return nullptr;
}
llvm::Expected<std::unique_ptr<llvm::coverage::CoverageMapping>> maybeMapping =
llvm::coverage::CoverageMapping::load({ configuration.executable },
configuration.coverageInfo);
if (!maybeMapping) {
std::string error;
llvm::raw_string_ostream os(error);
llvm::logAllUnhandledErrors(maybeMapping.takeError(), os, "Cannot read coverage info: ");
diagnostics.warning(os.str());
return nullptr;
}
return std::move(maybeMapping.get());
}

std::vector<FunctionUnderTest> Driver::getFunctionsUnderTest() {
std::vector<FunctionUnderTest> functionsUnderTest;

singleTask.execute("Gathering functions under test", [&]() {
std::unique_ptr<llvm::coverage::CoverageMapping> coverage = loadCoverage(config, diagnostics);
if (coverage) {
/// Some of the function records contain just name, the others are prefixed with the filename
/// to avoid collisions
/// TODO: check case when filename:functionName is not enough to resolve collisions
/// TODO: pick a proper data structure
std::unordered_map<
std::string,
std::unordered_map<std::string, std::vector<llvm::coverage::CountedRegion>>>
scopedFunctions;
std::unordered_map<std::string, std::vector<llvm::coverage::CountedRegion>> unscopedFunctions;
for (auto &it : coverage->getCoveredFunctions()) {
if (!it.ExecutionCount) {
continue;
}
std::string scope;
std::string name = it.Name;
size_t idx = name.find(':');
if (idx != std::string::npos) {
scope = name.substr(0, idx);
name = name.substr(idx + 1);
}
if (scope.empty()) {
unscopedFunctions[name] = it.CountedRegions;
} else {
scopedFunctions[scope][name] = it.CountedRegions;
}
}
for (auto &bitcode : program.bitcode()) {
for (llvm::Function &function : *bitcode->getModule()) {
bool covered = false;
std::vector<llvm::coverage::CountedRegion> linecoverage = {};
std::string name = function.getName().str();
if (unscopedFunctions.count(name)) {
covered = true;
std::swap(linecoverage, unscopedFunctions[name]);
} else {
std::string filepath = SourceLocation::locationFromFunction(&function).unitFilePath;
std::string scope = llvm::sys::path::filename(filepath).str();
if (scopedFunctions[scope].count(name)) {
covered = true;
std::swap(linecoverage, scopedFunctions[scope][name]);
}
}
if (covered) {
functionsUnderTest.emplace_back(&function, bitcode.get(), true, linecoverage);
} else if (config.includeNotCovered) {
functionsUnderTest.emplace_back(&function, bitcode.get());
}
}
}
} else {
if (config.includeNotCovered) {
diagnostics.warning("-include-not-covered is enabled, but there is no coverage info!");
}
for (auto &bitcode : program.bitcode()) {
for (llvm::Function &function : *bitcode->getModule()) {
functionsUnderTest.emplace_back(&function, bitcode.get(), true);
}
for (auto &bitcode : program.bitcode()) {
for (llvm::Function &function : *bitcode->getModule()) {
functionsUnderTest.emplace_back(&function, bitcode.get());
}
}
});
Expand Down Expand Up @@ -547,64 +462,8 @@ void mull::mutateBitcode(llvm::Module &module) {
Bitcode bitcode(&module);
std::vector<FunctionUnderTest> functionsUnderTest;
singleTask.execute("Gathering functions under test", [&]() {
std::unique_ptr<llvm::coverage::CoverageMapping> coverage =
loadCoverage(configuration, diagnostics);
if (coverage) {
/// Some of the function records contain just name, the others are prefixed with the filename
/// to avoid collisions
/// TODO: check case when filename:functionName is not enough to resolve collisions
/// TODO: pick a proper data structure
std::unordered_map<
std::string,
std::unordered_map<std::string, std::vector<llvm::coverage::CountedRegion>>>
scopedFunctions;
std::unordered_map<std::string, std::vector<llvm::coverage::CountedRegion>> unscopedFunctions;
for (auto &it : coverage->getCoveredFunctions()) {
if (!it.ExecutionCount) {
continue;
}
std::string scope;
std::string name = it.Name;
size_t idx = name.find(':');
if (idx != std::string::npos) {
scope = name.substr(0, idx);
name = name.substr(idx + 1);
}
if (scope.empty()) {
unscopedFunctions[name] = it.CountedRegions;
} else {
scopedFunctions[scope][name] = it.CountedRegions;
}
}

for (llvm::Function &function : module) {
bool covered = false;
std::vector<llvm::coverage::CountedRegion> linecoverage = {};
std::string name = function.getName().str();
if (unscopedFunctions.count(name)) {
covered = true;
std::swap(linecoverage, unscopedFunctions[name]);
} else {
std::string filepath = SourceLocation::locationFromFunction(&function).unitFilePath;
std::string scope = llvm::sys::path::filename(filepath).str();
if (scopedFunctions[scope].count(name)) {
covered = true;
std::swap(linecoverage, scopedFunctions[scope][name]);
}
}
if (covered) {
functionsUnderTest.emplace_back(&function, &bitcode, true, linecoverage);
} else if (configuration.includeNotCovered) {
functionsUnderTest.emplace_back(&function, &bitcode);
}
}
} else {
if (configuration.includeNotCovered) {
diagnostics.warning("-include-not-covered is enabled, but there is no coverage info!");
}
for (llvm::Function &function : module) {
functionsUnderTest.emplace_back(&function, &bitcode, true);
}
for (llvm::Function &function : module) {
functionsUnderTest.emplace_back(&function, &bitcode);
}
});

Expand Down
27 changes: 2 additions & 25 deletions lib/FunctionUnderTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

using namespace mull;

FunctionUnderTest::FunctionUnderTest(llvm::Function *function, Bitcode *bitcode, bool covered, std::vector<llvm::coverage::CountedRegion> linecoverage)
: function(function), bitcode(bitcode), covered(covered), linecoverage(linecoverage) {}
FunctionUnderTest::FunctionUnderTest(llvm::Function *function, Bitcode *bitcode)
: function(function), bitcode(bitcode) {}

llvm::Function *FunctionUnderTest::getFunction() const {
return function;
Expand All @@ -23,29 +23,6 @@ const std::vector<llvm::Instruction *> &FunctionUnderTest::getSelectedInstructio
return selectedInstructions;
}

bool FunctionUnderTest::isCovered() const {
return covered;
}

bool FunctionUnderTest::isCovered(const SourceLocation& location) const {
if (linecoverage.size() == 0){
return covered;
}
bool iscovered = true;
auto after_start = [&location](auto LineStart, auto ColumnStart){
return (LineStart < location.line || (LineStart == location.line && ColumnStart <= location.column));
};
auto before_end = [&location](auto LineEnd, auto ColumnEnd){
return (LineEnd > location.line || (LineEnd == location.line && ColumnEnd >= location.column));
};
for_each(linecoverage.begin(), linecoverage.end(), [&](const auto & cov){
if (after_start(cov.LineStart, cov.ColumnStart) && before_end(cov.LineEnd, cov.ColumnEnd)){
iscovered &= cov.ExecutionCount > 0;
}
});
return iscovered;
}

void FunctionUnderTest::selectInstructions(const std::vector<InstructionFilter *> &filters) {
for (llvm::Instruction &instruction : llvm::instructions(function)) {
bool selected = true;
Expand Down
8 changes: 6 additions & 2 deletions lib/Mutant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
using namespace mull;

Mutant::Mutant(std::string identifier, std::string mutatorIdentifier, SourceLocation sourceLocation,
SourceLocation endLocation, bool covered)
SourceLocation endLocation)
: identifier(std::move(identifier)), mutatorIdentifier(std::move(mutatorIdentifier)),
sourceLocation(std::move(sourceLocation)), endLocation(std::move(endLocation)),
covered(covered), mutatorKind(MutatorKind::InvalidKind) {}
covered(false), mutatorKind(MutatorKind::InvalidKind) {}

const std::string &Mutant::getIdentifier() const {
return identifier;
Expand Down Expand Up @@ -39,6 +39,10 @@ MutatorKind Mutant::getMutatorKind() const {
return mutatorKind;
}

void Mutant::setCovered(bool cover) {
covered = cover;
}

bool MutantComparator::operator()(std::unique_ptr<Mutant> &lhs, std::unique_ptr<Mutant> &rhs) {
return operator()(*lhs, *rhs);
}
Expand Down
12 changes: 2 additions & 10 deletions lib/MutationPoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,12 @@ MutationPoint::MutationPoint(Mutator *mutator, irm::IRMutation *irMutator,
: mutator(mutator), address(MutationPointAddress::addressFromInstruction(instruction)),
bitcode(m), originalFunction(instruction->getFunction()), mutatedFunction(nullptr),
sourceLocation(SourceLocation::locationFromInstruction(instruction)), irMutator(irMutator),
covered(true), endLocation(SourceLocation::nullSourceLocation()) {
endLocation(SourceLocation::nullSourceLocation()) {
userIdentifier = mutator->getUniqueIdentifier() + ':' + sourceLocation.filePath + ':' +
std::to_string(sourceLocation.line) + ':' +
std::to_string(sourceLocation.column);
}

void MutationPoint::setCovered(bool isCovered) {
covered = isCovered;
}

bool MutationPoint::isCovered() {
return covered;
}

Mutator *MutationPoint::getMutator() {
return mutator;
}
Expand Down Expand Up @@ -134,7 +126,7 @@ void MutationPoint::recordMutation() {
assert(originalFunction != nullptr);
llvm::Module *module = originalFunction->getParent();
std::string encoding = getUserIdentifier() + ':' + std::to_string(endLocation.line) + ':' +
std::to_string(endLocation.column) + ':' + std::to_string(isCovered());
std::to_string(endLocation.column);
llvm::Constant *constant =
llvm::ConstantDataArray::getString(module->getContext(), llvm::StringRef(encoding));
auto *global = new llvm::GlobalVariable(*module,
Expand Down
4 changes: 1 addition & 3 deletions lib/Parallelization/Tasks/ApplyMutationTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ void ApplyMutationTask::operator()(iterator begin, iterator end, Out &storage,
for (auto it = begin; it != end; ++it, counter.increment()) {
auto point = *it;
point->recordMutation();
if (point->isCovered()) {
point->applyMutation();
}
point->applyMutation();
}
}
Loading

0 comments on commit 9405088

Please sign in to comment.