| //===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // FuzzerCommand represents a command to run in a subprocess. It allows callers | |
| // to manage command line arguments and output and error streams. | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_FUZZER_COMMAND_H | |
| #define LLVM_FUZZER_COMMAND_H | |
| #include "FuzzerDefs.h" | |
| #include "FuzzerIO.h" | |
| #include <algorithm> | |
| #include <sstream> | |
| #include <string> | |
| #include <vector> | |
| #include <thread> | |
| namespace fuzzer { | |
| class Command final { | |
| public: | |
| // This command line flag is used to indicate that the remaining command line | |
| // is immutable, meaning this flag effectively marks the end of the mutable | |
| // argument list. | |
| static inline const char *ignoreRemainingArgs() { | |
| return "-ignore_remaining_args=1"; | |
| } | |
| Command() : CombinedOutAndErr(false) {} | |
| explicit Command(const std::vector<std::string> &ArgsToAdd) | |
| : Args(ArgsToAdd), CombinedOutAndErr(false) {} | |
| explicit Command(const Command &Other) | |
| : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), | |
| OutputFile(Other.OutputFile) {} | |
| Command &operator=(const Command &Other) { | |
| Args = Other.Args; | |
| CombinedOutAndErr = Other.CombinedOutAndErr; | |
| OutputFile = Other.OutputFile; | |
| return *this; | |
| } | |
| ~Command() {} | |
| // Returns true if the given Arg is present in Args. Only checks up to | |
| // "-ignore_remaining_args=1". | |
| bool hasArgument(const std::string &Arg) const { | |
| auto i = endMutableArgs(); | |
| return std::find(Args.begin(), i, Arg) != i; | |
| } | |
| // Gets all of the current command line arguments, **including** those after | |
| // "-ignore-remaining-args=1". | |
| const std::vector<std::string> &getArguments() const { return Args; } | |
| // Adds the given argument before "-ignore_remaining_args=1", or at the end | |
| // if that flag isn't present. | |
| void addArgument(const std::string &Arg) { | |
| Args.insert(endMutableArgs(), Arg); | |
| } | |
| // Adds all given arguments before "-ignore_remaining_args=1", or at the end | |
| // if that flag isn't present. | |
| void addArguments(const std::vector<std::string> &ArgsToAdd) { | |
| Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); | |
| } | |
| // Removes the given argument from the command argument list. Ignores any | |
| // occurrences after "-ignore_remaining_args=1", if present. | |
| void removeArgument(const std::string &Arg) { | |
| auto i = endMutableArgs(); | |
| Args.erase(std::remove(Args.begin(), i, Arg), i); | |
| } | |
| // Like hasArgument, but checks for "-[Flag]=...". | |
| bool hasFlag(const std::string &Flag) const { | |
| std::string Arg("-" + Flag + "="); | |
| auto IsMatch = [&](const std::string &Other) { | |
| return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; | |
| }; | |
| return std::any_of(Args.begin(), endMutableArgs(), IsMatch); | |
| } | |
| // Returns the value of the first instance of a given flag, or an empty string | |
| // if the flag isn't present. Ignores any occurrences after | |
| // "-ignore_remaining_args=1", if present. | |
| std::string getFlagValue(const std::string &Flag) const { | |
| std::string Arg("-" + Flag + "="); | |
| auto IsMatch = [&](const std::string &Other) { | |
| return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; | |
| }; | |
| auto i = endMutableArgs(); | |
| auto j = std::find_if(Args.begin(), i, IsMatch); | |
| std::string result; | |
| if (j != i) { | |
| result = j->substr(Arg.length()); | |
| } | |
| return result; | |
| } | |
| // Like AddArgument, but adds "-[Flag]=[Value]". | |
| void addFlag(const std::string &Flag, const std::string &Value) { | |
| addArgument("-" + Flag + "=" + Value); | |
| } | |
| // Like RemoveArgument, but removes "-[Flag]=...". | |
| void removeFlag(const std::string &Flag) { | |
| std::string Arg("-" + Flag + "="); | |
| auto IsMatch = [&](const std::string &Other) { | |
| return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; | |
| }; | |
| auto i = endMutableArgs(); | |
| Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); | |
| } | |
| // Returns whether the command's stdout is being written to an output file. | |
| bool hasOutputFile() const { return !OutputFile.empty(); } | |
| // Returns the currently set output file. | |
| const std::string &getOutputFile() const { return OutputFile; } | |
| // Configures the command to redirect its output to the name file. | |
| void setOutputFile(const std::string &FileName) { OutputFile = FileName; } | |
| // Returns whether the command's stderr is redirected to stdout. | |
| bool isOutAndErrCombined() const { return CombinedOutAndErr; } | |
| // Sets whether to redirect the command's stderr to its stdout. | |
| void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } | |
| // Returns a string representation of the command. On many systems this will | |
| // be the equivalent command line. | |
| std::string toString() const { | |
| std::stringstream SS; | |
| for (const auto &arg : getArguments()) | |
| SS << arg << " "; | |
| if (hasOutputFile()) | |
| SS << ">" << getOutputFile() << " "; | |
| if (isOutAndErrCombined()) | |
| SS << "2>&1 "; | |
| std::string result = SS.str(); | |
| if (!result.empty()) | |
| result = result.substr(0, result.length() - 1); | |
| return result; | |
| } | |
| private: | |
| Command(Command &&Other) = delete; | |
| Command &operator=(Command &&Other) = delete; | |
| std::vector<std::string>::iterator endMutableArgs() { | |
| return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); | |
| } | |
| std::vector<std::string>::const_iterator endMutableArgs() const { | |
| return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); | |
| } | |
| // The command arguments. Args[0] is the command name. | |
| std::vector<std::string> Args; | |
| // True indicates stderr is redirected to stdout. | |
| bool CombinedOutAndErr; | |
| // If not empty, stdout is redirected to the named file. | |
| std::string OutputFile; | |
| }; | |
| } // namespace fuzzer | |
| #endif // LLVM_FUZZER_COMMAND_H |