Skip to content

Commit

Permalink
Make --mutation-info-file optional
Browse files Browse the repository at this point in the history
Make `--mutation-info-file` parameter optional as writing this file can cause issues if
the mutation tree is too deep.
  • Loading branch information
JamesLee-Jones committed Jul 16, 2024
1 parent 1c853fd commit 854321c
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 32 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,7 @@ We first show how to apply Dredd to a simple stand-alone program. We will then s

```
# This will modify pi.cc in-place.
# The --mutation-info-file argument is used to specify a JSON file to
# which Dredd will output machine-readable information about the
# mutations it applied. For the purposes of this example, this file
# can be ignored.
${DREDD_EXECUTABLE} examples/simple/pi.cc --mutation-info-file temp.json
${DREDD_EXECUTABLE} examples/simple/pi.cc
# Now compile the mutated version of the example
${DREDD_CLANG_BIN_DIR}/clang++ examples/simple/pi.cc -o examples/simple/pi
```
Expand All @@ -98,7 +94,6 @@ You can also enable multiple mutants by setting the environment variable to a co
To clean up and restore the file `pi.cc` to it's initial state, run
```
rm examples/simple/pi
rm temp.json
git checkout HEAD examples/simple/pi.cc
```

Expand Down Expand Up @@ -141,6 +136,10 @@ ${DREDD_EXECUTABLE} -p build math/src/*.cc --mutation-info-file mutant-info.json
The `-p` option allows the compilation database generated by CMake above to be passed to Dredd: the `compile_commands.json` file in `build` will be used as a compilation database.
This is so that Dredd knows the correct compiler options to use when processing each source file.

The optional `--mutation-info-file` argument is used to specify a JSON file to which Dredd will output
machine-readable information about the mutations it applied. We explain how the `mutant-info.json` file created
via the `--mutation-info-file` argument can be used to query the mutants that Dredd has introduced.

You can run `git status` to see which files have changed, and `git diff` to see
the effect that Dredd has had on these files. These changes will be hard to understand as they are not intended to be
readable by humans.
Expand Down
15 changes: 11 additions & 4 deletions src/dredd/src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <fstream>
#include <memory>
#include <optional>
#include <string>

#include "clang/Tooling/CommonOptionsParser.h"
Expand Down Expand Up @@ -61,7 +62,7 @@ static llvm::cl::opt<bool> dump_asts(
llvm::cl::cat(mutate_category));
// NOLINTNEXTLINE
static llvm::cl::opt<std::string> mutation_info_file(
"mutation-info-file", llvm::cl::Required,
"mutation-info-file",
llvm::cl::desc(
".json file into which mutation information should be written"),
llvm::cl::cat(mutate_category));
Expand Down Expand Up @@ -94,7 +95,13 @@ int main(int argc, const char** argv) {

// Keeps track of the mutations that are applied to each source file,
// including their hierarchical structure.
dredd::protobufs::MutationInfo mutation_info;
std::optional<dredd::protobufs::MutationInfo> mutation_info;

if (mutation_info_file.empty()) {
mutation_info = std::nullopt;
} else {
mutation_info = dredd::protobufs::MutationInfo();
}

const std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
dredd::NewMutateFrontendActionFactory(!no_mutation_opts, dump_asts,
Expand All @@ -103,15 +110,15 @@ int main(int argc, const char** argv) {

const int return_code = Tool.run(factory.get());

if (return_code == 0) {
if (mutation_info.has_value() && return_code == 0) {
// Application of mutations was successful, so write out the mutation info
// in JSON format.
std::string json_string;
auto json_options = google::protobuf::util::JsonOptions();
json_options.add_whitespace = true;
json_options.always_print_primitive_fields = true;
auto json_generation_status = google::protobuf::util::MessageToJsonString(
mutation_info, &json_string, json_options);
mutation_info.value(), &json_string, json_options);
if (json_generation_status.ok()) {
std::ofstream transformations_json_file(mutation_info_file);
transformations_json_file << json_string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define LIBDREDD_NEW_MUTATE_FRONTEND_ACTION_FACTORY_H

#include <memory>
#include <optional>

#include "clang/Tooling/Tooling.h"
#include "libdredd/protobufs/dredd_protobufs.h"
Expand All @@ -26,7 +27,7 @@ std::unique_ptr<clang::tooling::FrontendActionFactory>
NewMutateFrontendActionFactory(bool optimise_mutations, bool dump_asts,
bool only_track_mutant_coverage,
int& mutation_id,
protobufs::MutationInfo& mutation_info);
std::optional<protobufs::MutationInfo> &mutation_info);

} // namespace dredd

Expand Down
5 changes: 4 additions & 1 deletion src/libdredd/include/libdredd/protobufs/dredd.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ syntax = "proto3";

package dredd.protobufs;

message MutationInfo { repeated MutationInfoForFile info_for_files = 1; }
message MutationInfo {
repeated MutationInfoForFile info_for_files = 1;
bool use = 2;
}

message MutationInfoForFile {
string filename = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define LIBDREDD_MUTATE_AST_CONSUMER_H

#include <memory>
#include <optional>
#include <string>
#include <unordered_set>

Expand All @@ -34,7 +35,7 @@ class MutateAstConsumer : public clang::ASTConsumer {
MutateAstConsumer(const clang::CompilerInstance& compiler_instance,
bool optimise_mutations, bool dump_ast,
bool only_track_mutant_coverage, int& mutation_id,
protobufs::MutationInfo& mutation_info)
std::optional<protobufs::MutationInfo> &mutation_info)
: compiler_instance_(&compiler_instance),
optimise_mutations_(optimise_mutations),
dump_ast_(dump_ast),
Expand Down Expand Up @@ -68,7 +69,7 @@ class MutateAstConsumer : public clang::ASTConsumer {
clang::ASTContext& context,
protobufs::MutationInfoForFile& protobufs_mutation_info_for_file,
protobufs::MutationTreeNode& protobufs_mutation_tree_node,
std::unordered_set<std::string>& dredd_declarations);
std::unordered_set<std::string>& dredd_declarations, bool build_tree);

const clang::CompilerInstance* compiler_instance_;

Expand All @@ -89,7 +90,7 @@ class MutateAstConsumer : public clang::ASTConsumer {
// for different translation units.
int* mutation_id_;

protobufs::MutationInfo* mutation_info_;
std::optional<protobufs::MutationInfo>* mutation_info_;
};

} // namespace dredd
Expand Down
31 changes: 19 additions & 12 deletions src/libdredd/src/mutate_ast_consumer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,20 @@ void MutateAstConsumer::HandleTranslationUnit(clang::ASTContext& ast_context) {
std::unordered_set<std::string> dredd_declarations;

protobufs::MutationInfoForFile mutation_info_for_file;
mutation_info_for_file.set_filename(
ast_context.getSourceManager()
.getFileEntryForID(ast_context.getSourceManager().getMainFileID())
->getName()
.str());

if (mutation_info_->has_value()) {
mutation_info_for_file.set_filename(
ast_context.getSourceManager()
.getFileEntryForID(ast_context.getSourceManager().getMainFileID())
->getName()
.str());
}

protobufs::MutationTreeNode* root_protobuf_mutation_tree_node =
mutation_info_for_file.add_mutation_tree();
ApplyMutations(visitor_->GetMutations(), initial_mutation_id, ast_context,
mutation_info_for_file, *root_protobuf_mutation_tree_node,
dredd_declarations);
dredd_declarations, mutation_info_->has_value());

if (initial_mutation_id == *mutation_id_) {
// No possibilities for mutation were found; nothing else to do.
Expand Down Expand Up @@ -135,7 +139,9 @@ void MutateAstConsumer::HandleTranslationUnit(clang::ASTContext& ast_context) {
}
}

*mutation_info_->add_info_for_files() = mutation_info_for_file;
if (mutation_info_->has_value()) {
*mutation_info_->value().add_info_for_files() = mutation_info_for_file;
}

auto& source_manager = ast_context.getSourceManager();
const clang::SourceLocation start_of_source_file =
Expand Down Expand Up @@ -411,7 +417,7 @@ void MutateAstConsumer::ApplyMutations(
clang::ASTContext& context,
protobufs::MutationInfoForFile& protobufs_mutation_info_for_file,
protobufs::MutationTreeNode& protobufs_mutation_tree_node,
std::unordered_set<std::string>& dredd_declarations) {
std::unordered_set<std::string>& dredd_declarations, bool build_tree) {
assert(!(dredd_mutation_tree_node.IsEmpty() &&
dredd_mutation_tree_node.GetChildren().size() == 1) &&
"The mutation tree should already be compressed.");
Expand All @@ -422,17 +428,18 @@ void MutateAstConsumer::ApplyMutations(
protobufs_mutation_info_for_file.mutation_tree_size()));
protobufs::MutationTreeNode* new_protobufs_mutation_tree_node =
protobufs_mutation_info_for_file.add_mutation_tree();
ApplyMutations(*child, initial_mutation_id, context,
protobufs_mutation_info_for_file,
*new_protobufs_mutation_tree_node, dredd_declarations);
ApplyMutations(
*child, initial_mutation_id, context, protobufs_mutation_info_for_file,
*new_protobufs_mutation_tree_node, dredd_declarations, build_tree);
}

for (const auto& mutation : dredd_mutation_tree_node.GetMutations()) {
const int mutation_id_old = *mutation_id_;
const auto mutation_group = mutation->Apply(
context, compiler_instance_->getPreprocessor(), optimise_mutations_,
only_track_mutant_coverage_, initial_mutation_id, *mutation_id_,
rewriter_, dredd_declarations);
if (*mutation_id_ > mutation_id_old) {
if (build_tree && *mutation_id_ > mutation_id_old) {
// Only add the result of applying the mutation if it had an effect.
*protobufs_mutation_tree_node.add_mutation_groups() = mutation_group;
}
Expand Down
10 changes: 5 additions & 5 deletions src/libdredd/src/new_mutate_frontend_action_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class MutateFrontendAction : public clang::ASTFrontendAction {
public:
MutateFrontendAction(bool optimise_mutations, bool dump_asts,
bool only_track_mutant_coverage, int& mutation_id,
protobufs::MutationInfo& mutation_info,
std::optional<protobufs::MutationInfo> &mutation_info,
std::set<std::string>& processed_files)
: optimise_mutations_(optimise_mutations),
dump_asts_(dump_asts),
Expand Down Expand Up @@ -65,22 +65,22 @@ class MutateFrontendAction : public clang::ASTFrontendAction {
bool dump_asts_;
bool only_track_mutant_coverage_;
int* mutation_id_;
protobufs::MutationInfo* mutation_info_;
std::optional<protobufs::MutationInfo>* mutation_info_;
std::set<std::string>* processed_files_;
};

std::unique_ptr<clang::tooling::FrontendActionFactory>
NewMutateFrontendActionFactory(bool optimise_mutations, bool dump_asts,
bool only_track_mutant_coverage,
int& mutation_id,
protobufs::MutationInfo& mutation_info) {
std::optional<protobufs::MutationInfo> &mutation_info) {
class MutateFrontendActionFactory
: public clang::tooling::FrontendActionFactory {
public:
MutateFrontendActionFactory(bool optimise_mutations, bool dump_asts,
bool only_track_mutant_coverage,
int& mutation_id,
protobufs::MutationInfo& mutation_info)
std::optional<protobufs::MutationInfo> &mutation_info)
: optimise_mutations_(optimise_mutations),
dump_asts_(dump_asts),
only_track_mutant_coverage_(only_track_mutant_coverage),
Expand All @@ -98,7 +98,7 @@ NewMutateFrontendActionFactory(bool optimise_mutations, bool dump_asts,
bool dump_asts_;
bool only_track_mutant_coverage_;
int* mutation_id_;
protobufs::MutationInfo* mutation_info_;
std::optional<protobufs::MutationInfo>* mutation_info_;

// Stores the ids of the files that have been processed so far, to avoid
// processing a file multiple times.
Expand Down

0 comments on commit 854321c

Please sign in to comment.