xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric // FuzzerCommand represents a command to run in a subprocess.  It allows callers
90b57cec5SDimitry Andric // to manage command line arguments and output and error streams.
100b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric #ifndef LLVM_FUZZER_COMMAND_H
130b57cec5SDimitry Andric #define LLVM_FUZZER_COMMAND_H
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "FuzzerDefs.h"
160b57cec5SDimitry Andric #include "FuzzerIO.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #include <algorithm>
190b57cec5SDimitry Andric #include <sstream>
200b57cec5SDimitry Andric #include <string>
210b57cec5SDimitry Andric #include <vector>
22*5f757f3fSDimitry Andric #include <thread>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace fuzzer {
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric class Command final {
270b57cec5SDimitry Andric public:
280b57cec5SDimitry Andric   // This command line flag is used to indicate that the remaining command line
290b57cec5SDimitry Andric   // is immutable, meaning this flag effectively marks the end of the mutable
300b57cec5SDimitry Andric   // argument list.
ignoreRemainingArgs()310b57cec5SDimitry Andric   static inline const char *ignoreRemainingArgs() {
320b57cec5SDimitry Andric     return "-ignore_remaining_args=1";
330b57cec5SDimitry Andric   }
340b57cec5SDimitry Andric 
Command()350b57cec5SDimitry Andric   Command() : CombinedOutAndErr(false) {}
360b57cec5SDimitry Andric 
Command(const std::vector<std::string> & ArgsToAdd)37349cc55cSDimitry Andric   explicit Command(const std::vector<std::string> &ArgsToAdd)
380b57cec5SDimitry Andric       : Args(ArgsToAdd), CombinedOutAndErr(false) {}
390b57cec5SDimitry Andric 
Command(const Command & Other)400b57cec5SDimitry Andric   explicit Command(const Command &Other)
410b57cec5SDimitry Andric       : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
420b57cec5SDimitry Andric         OutputFile(Other.OutputFile) {}
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric   Command &operator=(const Command &Other) {
450b57cec5SDimitry Andric     Args = Other.Args;
460b57cec5SDimitry Andric     CombinedOutAndErr = Other.CombinedOutAndErr;
470b57cec5SDimitry Andric     OutputFile = Other.OutputFile;
480b57cec5SDimitry Andric     return *this;
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric 
~Command()510b57cec5SDimitry Andric   ~Command() {}
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   // Returns true if the given Arg is present in Args.  Only checks up to
540b57cec5SDimitry Andric   // "-ignore_remaining_args=1".
hasArgument(const std::string & Arg)550b57cec5SDimitry Andric   bool hasArgument(const std::string &Arg) const {
560b57cec5SDimitry Andric     auto i = endMutableArgs();
570b57cec5SDimitry Andric     return std::find(Args.begin(), i, Arg) != i;
580b57cec5SDimitry Andric   }
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   // Gets all of the current command line arguments, **including** those after
610b57cec5SDimitry Andric   // "-ignore-remaining-args=1".
getArguments()62349cc55cSDimitry Andric   const std::vector<std::string> &getArguments() const { return Args; }
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric   // Adds the given argument before "-ignore_remaining_args=1", or at the end
650b57cec5SDimitry Andric   // if that flag isn't present.
addArgument(const std::string & Arg)660b57cec5SDimitry Andric   void addArgument(const std::string &Arg) {
670b57cec5SDimitry Andric     Args.insert(endMutableArgs(), Arg);
680b57cec5SDimitry Andric   }
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric   // Adds all given arguments before "-ignore_remaining_args=1", or at the end
710b57cec5SDimitry Andric   // if that flag isn't present.
addArguments(const std::vector<std::string> & ArgsToAdd)72349cc55cSDimitry Andric   void addArguments(const std::vector<std::string> &ArgsToAdd) {
730b57cec5SDimitry Andric     Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
740b57cec5SDimitry Andric   }
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric   // Removes the given argument from the command argument list.  Ignores any
770b57cec5SDimitry Andric   // occurrences after "-ignore_remaining_args=1", if present.
removeArgument(const std::string & Arg)780b57cec5SDimitry Andric   void removeArgument(const std::string &Arg) {
790b57cec5SDimitry Andric     auto i = endMutableArgs();
800b57cec5SDimitry Andric     Args.erase(std::remove(Args.begin(), i, Arg), i);
810b57cec5SDimitry Andric   }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric   // Like hasArgument, but checks for "-[Flag]=...".
hasFlag(const std::string & Flag)840b57cec5SDimitry Andric   bool hasFlag(const std::string &Flag) const {
850b57cec5SDimitry Andric     std::string Arg("-" + Flag + "=");
860b57cec5SDimitry Andric     auto IsMatch = [&](const std::string &Other) {
870b57cec5SDimitry Andric       return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
880b57cec5SDimitry Andric     };
890b57cec5SDimitry Andric     return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
900b57cec5SDimitry Andric   }
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric   // Returns the value of the first instance of a given flag, or an empty string
930b57cec5SDimitry Andric   // if the flag isn't present.  Ignores any occurrences after
940b57cec5SDimitry Andric   // "-ignore_remaining_args=1", if present.
getFlagValue(const std::string & Flag)950b57cec5SDimitry Andric   std::string getFlagValue(const std::string &Flag) const {
960b57cec5SDimitry Andric     std::string Arg("-" + Flag + "=");
970b57cec5SDimitry Andric     auto IsMatch = [&](const std::string &Other) {
980b57cec5SDimitry Andric       return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
990b57cec5SDimitry Andric     };
1000b57cec5SDimitry Andric     auto i = endMutableArgs();
1010b57cec5SDimitry Andric     auto j = std::find_if(Args.begin(), i, IsMatch);
1020b57cec5SDimitry Andric     std::string result;
1030b57cec5SDimitry Andric     if (j != i) {
1040b57cec5SDimitry Andric       result = j->substr(Arg.length());
1050b57cec5SDimitry Andric     }
1060b57cec5SDimitry Andric     return result;
1070b57cec5SDimitry Andric   }
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   // Like AddArgument, but adds "-[Flag]=[Value]".
addFlag(const std::string & Flag,const std::string & Value)1100b57cec5SDimitry Andric   void addFlag(const std::string &Flag, const std::string &Value) {
1110b57cec5SDimitry Andric     addArgument("-" + Flag + "=" + Value);
1120b57cec5SDimitry Andric   }
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric   // Like RemoveArgument, but removes "-[Flag]=...".
removeFlag(const std::string & Flag)1150b57cec5SDimitry Andric   void removeFlag(const std::string &Flag) {
1160b57cec5SDimitry Andric     std::string Arg("-" + Flag + "=");
1170b57cec5SDimitry Andric     auto IsMatch = [&](const std::string &Other) {
1180b57cec5SDimitry Andric       return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
1190b57cec5SDimitry Andric     };
1200b57cec5SDimitry Andric     auto i = endMutableArgs();
1210b57cec5SDimitry Andric     Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
1220b57cec5SDimitry Andric   }
1230b57cec5SDimitry Andric 
1240b57cec5SDimitry Andric   // Returns whether the command's stdout is being written to an output file.
hasOutputFile()1250b57cec5SDimitry Andric   bool hasOutputFile() const { return !OutputFile.empty(); }
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   // Returns the currently set output file.
getOutputFile()1280b57cec5SDimitry Andric   const std::string &getOutputFile() const { return OutputFile; }
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric   // Configures the command to redirect its output to the name file.
setOutputFile(const std::string & FileName)1310b57cec5SDimitry Andric   void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   // Returns whether the command's stderr is redirected to stdout.
isOutAndErrCombined()1340b57cec5SDimitry Andric   bool isOutAndErrCombined() const { return CombinedOutAndErr; }
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   // Sets whether to redirect the command's stderr to its stdout.
1370b57cec5SDimitry Andric   void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric   // Returns a string representation of the command.  On many systems this will
1400b57cec5SDimitry Andric   // be the equivalent command line.
toString()1410b57cec5SDimitry Andric   std::string toString() const {
1420b57cec5SDimitry Andric     std::stringstream SS;
14306c3fb27SDimitry Andric     for (const auto &arg : getArguments())
1440b57cec5SDimitry Andric       SS << arg << " ";
1450b57cec5SDimitry Andric     if (hasOutputFile())
1460b57cec5SDimitry Andric       SS << ">" << getOutputFile() << " ";
1470b57cec5SDimitry Andric     if (isOutAndErrCombined())
1480b57cec5SDimitry Andric       SS << "2>&1 ";
1490b57cec5SDimitry Andric     std::string result = SS.str();
1500b57cec5SDimitry Andric     if (!result.empty())
1510b57cec5SDimitry Andric       result = result.substr(0, result.length() - 1);
1520b57cec5SDimitry Andric     return result;
1530b57cec5SDimitry Andric   }
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric private:
1560b57cec5SDimitry Andric   Command(Command &&Other) = delete;
1570b57cec5SDimitry Andric   Command &operator=(Command &&Other) = delete;
1580b57cec5SDimitry Andric 
endMutableArgs()159349cc55cSDimitry Andric   std::vector<std::string>::iterator endMutableArgs() {
1600b57cec5SDimitry Andric     return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
1610b57cec5SDimitry Andric   }
1620b57cec5SDimitry Andric 
endMutableArgs()163349cc55cSDimitry Andric   std::vector<std::string>::const_iterator endMutableArgs() const {
1640b57cec5SDimitry Andric     return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
1650b57cec5SDimitry Andric   }
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   // The command arguments.  Args[0] is the command name.
168349cc55cSDimitry Andric   std::vector<std::string> Args;
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   // True indicates stderr is redirected to stdout.
1710b57cec5SDimitry Andric   bool CombinedOutAndErr;
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric   // If not empty, stdout is redirected to the named file.
1740b57cec5SDimitry Andric   std::string OutputFile;
1750b57cec5SDimitry Andric };
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric } // namespace fuzzer
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric #endif // LLVM_FUZZER_COMMAND_H
180