FuzzerCommand.h 6.0 KB

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