Home | History | Annotate | Line # | Download | only in fuzzer
      1 //===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 // FuzzerCommand represents a command to run in a subprocess.  It allows callers
     10 // to manage command line arguments and output and error streams.
     11 //===----------------------------------------------------------------------===//
     12 
     13 #ifndef LLVM_FUZZER_COMMAND_H
     14 #define LLVM_FUZZER_COMMAND_H
     15 
     16 #include "FuzzerDefs.h"
     17 #include "FuzzerIO.h"
     18 
     19 #include <algorithm>
     20 #include <sstream>
     21 #include <string>
     22 #include <vector>
     23 
     24 namespace fuzzer {
     25 
     26 class Command final {
     27 public:
     28   // This command line flag is used to indicate that the remaining command line
     29   // is immutable, meaning this flag effectively marks the end of the mutable
     30   // 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 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 to
     54   // "-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 after
     61   // "-ignore-remaining-args=1".
     62   const Vector<std::string> &getArguments() const { return Args; }
     63 
     64   // Adds the given argument before "-ignore_remaining_args=1", or at the end
     65   // 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 end
     71   // if that flag isn't present.
     72   void addArguments(const 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 any
     77   // 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 string
     93   // if the flag isn't present.  Ignores any occurrences after
     94   // "-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 will
    140   // be the equivalent command line.
    141   std::string toString() const {
    142     std::stringstream SS;
    143     for (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 
    155 private:
    156   Command(Command &&Other) = delete;
    157   Command &operator=(Command &&Other) = delete;
    158 
    159   Vector<std::string>::iterator endMutableArgs() {
    160     return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
    161   }
    162 
    163   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   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 fuzzer
    178 
    179 #endif // LLVM_FUZZER_COMMAND_H
    180