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