180 lines · c
1//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8// FuzzerCommand represents a command to run in a subprocess. It allows callers9// to manage command line arguments and output and error streams.10//===----------------------------------------------------------------------===//11 12#ifndef LLVM_FUZZER_COMMAND_H13#define LLVM_FUZZER_COMMAND_H14 15#include "FuzzerDefs.h"16#include "FuzzerIO.h"17 18#include <algorithm>19#include <sstream>20#include <string>21#include <vector>22#include <thread>23 24namespace fuzzer {25 26class Command final {27public:28 // This command line flag is used to indicate that the remaining command line29 // is immutable, meaning this flag effectively marks the end of the mutable30 // argument list.31 static inline const char *ignoreRemainingArgs() {32 return "-ignore_remaining_args=1";33 }34 35 Command() : CombinedOutAndErr(false) {}36 37 explicit Command(const std::vector<std::string> &ArgsToAdd)38 : Args(ArgsToAdd), CombinedOutAndErr(false) {}39 40 explicit Command(const Command &Other)41 : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),42 OutputFile(Other.OutputFile) {}43 44 Command &operator=(const Command &Other) {45 Args = Other.Args;46 CombinedOutAndErr = Other.CombinedOutAndErr;47 OutputFile = Other.OutputFile;48 return *this;49 }50 51 ~Command() {}52 53 // Returns true if the given Arg is present in Args. Only checks up to54 // "-ignore_remaining_args=1".55 bool hasArgument(const std::string &Arg) const {56 auto i = endMutableArgs();57 return std::find(Args.begin(), i, Arg) != i;58 }59 60 // Gets all of the current command line arguments, **including** those after61 // "-ignore-remaining-args=1".62 const std::vector<std::string> &getArguments() const { return Args; }63 64 // Adds the given argument before "-ignore_remaining_args=1", or at the end65 // if that flag isn't present.66 void addArgument(const std::string &Arg) {67 Args.insert(endMutableArgs(), Arg);68 }69 70 // Adds all given arguments before "-ignore_remaining_args=1", or at the end71 // if that flag isn't present.72 void addArguments(const std::vector<std::string> &ArgsToAdd) {73 Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());74 }75 76 // Removes the given argument from the command argument list. Ignores any77 // occurrences after "-ignore_remaining_args=1", if present.78 void removeArgument(const std::string &Arg) {79 auto i = endMutableArgs();80 Args.erase(std::remove(Args.begin(), i, Arg), i);81 }82 83 // Like hasArgument, but checks for "-[Flag]=...".84 bool hasFlag(const std::string &Flag) const {85 std::string Arg("-" + Flag + "=");86 auto IsMatch = [&](const std::string &Other) {87 return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;88 };89 return std::any_of(Args.begin(), endMutableArgs(), IsMatch);90 }91 92 // Returns the value of the first instance of a given flag, or an empty string93 // if the flag isn't present. Ignores any occurrences after94 // "-ignore_remaining_args=1", if present.95 std::string getFlagValue(const std::string &Flag) const {96 std::string Arg("-" + Flag + "=");97 auto IsMatch = [&](const std::string &Other) {98 return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;99 };100 auto i = endMutableArgs();101 auto j = std::find_if(Args.begin(), i, IsMatch);102 std::string result;103 if (j != i) {104 result = j->substr(Arg.length());105 }106 return result;107 }108 109 // Like AddArgument, but adds "-[Flag]=[Value]".110 void addFlag(const std::string &Flag, const std::string &Value) {111 addArgument("-" + Flag + "=" + Value);112 }113 114 // Like RemoveArgument, but removes "-[Flag]=...".115 void removeFlag(const std::string &Flag) {116 std::string Arg("-" + Flag + "=");117 auto IsMatch = [&](const std::string &Other) {118 return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;119 };120 auto i = endMutableArgs();121 Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);122 }123 124 // Returns whether the command's stdout is being written to an output file.125 bool hasOutputFile() const { return !OutputFile.empty(); }126 127 // Returns the currently set output file.128 const std::string &getOutputFile() const { return OutputFile; }129 130 // Configures the command to redirect its output to the name file.131 void setOutputFile(const std::string &FileName) { OutputFile = FileName; }132 133 // Returns whether the command's stderr is redirected to stdout.134 bool isOutAndErrCombined() const { return CombinedOutAndErr; }135 136 // Sets whether to redirect the command's stderr to its stdout.137 void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }138 139 // Returns a string representation of the command. On many systems this will140 // be the equivalent command line.141 std::string toString() const {142 std::stringstream SS;143 for (const auto &arg : getArguments())144 SS << arg << " ";145 if (hasOutputFile())146 SS << ">" << getOutputFile() << " ";147 if (isOutAndErrCombined())148 SS << "2>&1 ";149 std::string result = SS.str();150 if (!result.empty())151 result = result.substr(0, result.length() - 1);152 return result;153 }154 155private:156 Command(Command &&Other) = delete;157 Command &operator=(Command &&Other) = delete;158 159 std::vector<std::string>::iterator endMutableArgs() {160 return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());161 }162 163 std::vector<std::string>::const_iterator endMutableArgs() const {164 return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());165 }166 167 // The command arguments. Args[0] is the command name.168 std::vector<std::string> Args;169 170 // True indicates stderr is redirected to stdout.171 bool CombinedOutAndErr;172 173 // If not empty, stdout is redirected to the named file.174 std::string OutputFile;175};176 177} // namespace fuzzer178 179#endif // LLVM_FUZZER_COMMAND_H180