xref: /freebsd/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h (revision 349cc55c9796c4596a5b9904cd3281af295f878f)
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>
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric namespace fuzzer {
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric class Command final {
260b57cec5SDimitry Andric public:
270b57cec5SDimitry Andric   // This command line flag is used to indicate that the remaining command line
280b57cec5SDimitry Andric   // is immutable, meaning this flag effectively marks the end of the mutable
290b57cec5SDimitry Andric   // argument list.
300b57cec5SDimitry Andric   static inline const char *ignoreRemainingArgs() {
310b57cec5SDimitry Andric     return "-ignore_remaining_args=1";
320b57cec5SDimitry Andric   }
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric   Command() : CombinedOutAndErr(false) {}
350b57cec5SDimitry Andric 
36*349cc55cSDimitry Andric   explicit Command(const std::vector<std::string> &ArgsToAdd)
370b57cec5SDimitry Andric       : Args(ArgsToAdd), CombinedOutAndErr(false) {}
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric   explicit Command(const Command &Other)
400b57cec5SDimitry Andric       : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
410b57cec5SDimitry Andric         OutputFile(Other.OutputFile) {}
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   Command &operator=(const Command &Other) {
440b57cec5SDimitry Andric     Args = Other.Args;
450b57cec5SDimitry Andric     CombinedOutAndErr = Other.CombinedOutAndErr;
460b57cec5SDimitry Andric     OutputFile = Other.OutputFile;
470b57cec5SDimitry Andric     return *this;
480b57cec5SDimitry Andric   }
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric   ~Command() {}
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   // Returns true if the given Arg is present in Args.  Only checks up to
530b57cec5SDimitry Andric   // "-ignore_remaining_args=1".
540b57cec5SDimitry Andric   bool hasArgument(const std::string &Arg) const {
550b57cec5SDimitry Andric     auto i = endMutableArgs();
560b57cec5SDimitry Andric     return std::find(Args.begin(), i, Arg) != i;
570b57cec5SDimitry Andric   }
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric   // Gets all of the current command line arguments, **including** those after
600b57cec5SDimitry Andric   // "-ignore-remaining-args=1".
61*349cc55cSDimitry Andric   const std::vector<std::string> &getArguments() const { return Args; }
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric   // Adds the given argument before "-ignore_remaining_args=1", or at the end
640b57cec5SDimitry Andric   // if that flag isn't present.
650b57cec5SDimitry Andric   void addArgument(const std::string &Arg) {
660b57cec5SDimitry Andric     Args.insert(endMutableArgs(), Arg);
670b57cec5SDimitry Andric   }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   // Adds all given arguments before "-ignore_remaining_args=1", or at the end
700b57cec5SDimitry Andric   // if that flag isn't present.
71*349cc55cSDimitry Andric   void addArguments(const std::vector<std::string> &ArgsToAdd) {
720b57cec5SDimitry Andric     Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
730b57cec5SDimitry Andric   }
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   // Removes the given argument from the command argument list.  Ignores any
760b57cec5SDimitry Andric   // occurrences after "-ignore_remaining_args=1", if present.
770b57cec5SDimitry Andric   void removeArgument(const std::string &Arg) {
780b57cec5SDimitry Andric     auto i = endMutableArgs();
790b57cec5SDimitry Andric     Args.erase(std::remove(Args.begin(), i, Arg), i);
800b57cec5SDimitry Andric   }
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric   // Like hasArgument, but checks for "-[Flag]=...".
830b57cec5SDimitry Andric   bool hasFlag(const std::string &Flag) const {
840b57cec5SDimitry Andric     std::string Arg("-" + Flag + "=");
850b57cec5SDimitry Andric     auto IsMatch = [&](const std::string &Other) {
860b57cec5SDimitry Andric       return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
870b57cec5SDimitry Andric     };
880b57cec5SDimitry Andric     return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
890b57cec5SDimitry Andric   }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   // Returns the value of the first instance of a given flag, or an empty string
920b57cec5SDimitry Andric   // if the flag isn't present.  Ignores any occurrences after
930b57cec5SDimitry Andric   // "-ignore_remaining_args=1", if present.
940b57cec5SDimitry Andric   std::string getFlagValue(const std::string &Flag) const {
950b57cec5SDimitry Andric     std::string Arg("-" + Flag + "=");
960b57cec5SDimitry Andric     auto IsMatch = [&](const std::string &Other) {
970b57cec5SDimitry Andric       return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
980b57cec5SDimitry Andric     };
990b57cec5SDimitry Andric     auto i = endMutableArgs();
1000b57cec5SDimitry Andric     auto j = std::find_if(Args.begin(), i, IsMatch);
1010b57cec5SDimitry Andric     std::string result;
1020b57cec5SDimitry Andric     if (j != i) {
1030b57cec5SDimitry Andric       result = j->substr(Arg.length());
1040b57cec5SDimitry Andric     }
1050b57cec5SDimitry Andric     return result;
1060b57cec5SDimitry Andric   }
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   // Like AddArgument, but adds "-[Flag]=[Value]".
1090b57cec5SDimitry Andric   void addFlag(const std::string &Flag, const std::string &Value) {
1100b57cec5SDimitry Andric     addArgument("-" + Flag + "=" + Value);
1110b57cec5SDimitry Andric   }
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   // Like RemoveArgument, but removes "-[Flag]=...".
1140b57cec5SDimitry Andric   void removeFlag(const std::string &Flag) {
1150b57cec5SDimitry Andric     std::string Arg("-" + Flag + "=");
1160b57cec5SDimitry Andric     auto IsMatch = [&](const std::string &Other) {
1170b57cec5SDimitry Andric       return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
1180b57cec5SDimitry Andric     };
1190b57cec5SDimitry Andric     auto i = endMutableArgs();
1200b57cec5SDimitry Andric     Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
1210b57cec5SDimitry Andric   }
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric   // Returns whether the command's stdout is being written to an output file.
1240b57cec5SDimitry Andric   bool hasOutputFile() const { return !OutputFile.empty(); }
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric   // Returns the currently set output file.
1270b57cec5SDimitry Andric   const std::string &getOutputFile() const { return OutputFile; }
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   // Configures the command to redirect its output to the name file.
1300b57cec5SDimitry Andric   void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   // Returns whether the command's stderr is redirected to stdout.
1330b57cec5SDimitry Andric   bool isOutAndErrCombined() const { return CombinedOutAndErr; }
1340b57cec5SDimitry Andric 
1350b57cec5SDimitry Andric   // Sets whether to redirect the command's stderr to its stdout.
1360b57cec5SDimitry Andric   void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric   // Returns a string representation of the command.  On many systems this will
1390b57cec5SDimitry Andric   // be the equivalent command line.
1400b57cec5SDimitry Andric   std::string toString() const {
1410b57cec5SDimitry Andric     std::stringstream SS;
1420b57cec5SDimitry Andric     for (auto arg : getArguments())
1430b57cec5SDimitry Andric       SS << arg << " ";
1440b57cec5SDimitry Andric     if (hasOutputFile())
1450b57cec5SDimitry Andric       SS << ">" << getOutputFile() << " ";
1460b57cec5SDimitry Andric     if (isOutAndErrCombined())
1470b57cec5SDimitry Andric       SS << "2>&1 ";
1480b57cec5SDimitry Andric     std::string result = SS.str();
1490b57cec5SDimitry Andric     if (!result.empty())
1500b57cec5SDimitry Andric       result = result.substr(0, result.length() - 1);
1510b57cec5SDimitry Andric     return result;
1520b57cec5SDimitry Andric   }
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric private:
1550b57cec5SDimitry Andric   Command(Command &&Other) = delete;
1560b57cec5SDimitry Andric   Command &operator=(Command &&Other) = delete;
1570b57cec5SDimitry Andric 
158*349cc55cSDimitry Andric   std::vector<std::string>::iterator endMutableArgs() {
1590b57cec5SDimitry Andric     return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
1600b57cec5SDimitry Andric   }
1610b57cec5SDimitry Andric 
162*349cc55cSDimitry Andric   std::vector<std::string>::const_iterator endMutableArgs() const {
1630b57cec5SDimitry Andric     return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
1640b57cec5SDimitry Andric   }
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   // The command arguments.  Args[0] is the command name.
167*349cc55cSDimitry Andric   std::vector<std::string> Args;
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric   // True indicates stderr is redirected to stdout.
1700b57cec5SDimitry Andric   bool CombinedOutAndErr;
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   // If not empty, stdout is redirected to the named file.
1730b57cec5SDimitry Andric   std::string OutputFile;
1740b57cec5SDimitry Andric };
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric } // namespace fuzzer
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric #endif // LLVM_FUZZER_COMMAND_H
179