[tint][fuzzers] Add --concurrent flag to tint_wgsl_fuzzer
Checks that all the WGSL fuzzers can be run concurrently
Change-Id: Iad7a95918954848ac7ffd6cee17c5538cf5c2cd3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/155382
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.cmake b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
index b485bef..9ba6530 100644
--- a/src/tint/cmd/fuzz/wgsl/BUILD.cmake
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
@@ -53,6 +53,7 @@
tint_lang_wgsl_program
tint_lang_wgsl_sem
tint_lang_wgsl_fuzz
+ tint_utils_cli
tint_utils_containers
tint_utils_diagnostic
tint_utils_ice
@@ -62,6 +63,7 @@
tint_utils_memory
tint_utils_result
tint_utils_rtti
+ tint_utils_strconv
tint_utils_symbol
tint_utils_text
tint_utils_traits
@@ -113,6 +115,10 @@
tint_utils_traits
)
+tint_target_add_external_dependencies(tint_cmd_fuzz_wgsl_fuzz fuzz
+ "thread"
+)
+
if(TINT_BUILD_WGSL_READER)
tint_target_add_dependencies(tint_cmd_fuzz_wgsl_fuzz fuzz
tint_lang_wgsl_reader
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.gn b/src/tint/cmd/fuzz/wgsl/BUILD.gn
index 908cd3b..5c4ccc6 100644
--- a/src/tint/cmd/fuzz/wgsl/BUILD.gn
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.gn
@@ -44,6 +44,7 @@
"wgsl_fuzz.h",
]
deps = [
+ "${tint_src_dir}:thread",
"${tint_src_dir}/api/common",
"${tint_src_dir}/lang/core",
"${tint_src_dir}/lang/core/constant",
@@ -86,6 +87,7 @@
"${tint_src_dir}/lang/wgsl/ast",
"${tint_src_dir}/lang/wgsl/program",
"${tint_src_dir}/lang/wgsl/sem",
+ "${tint_src_dir}/utils/cli",
"${tint_src_dir}/utils/containers",
"${tint_src_dir}/utils/diagnostic",
"${tint_src_dir}/utils/ice",
@@ -95,6 +97,7 @@
"${tint_src_dir}/utils/memory",
"${tint_src_dir}/utils/result",
"${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/strconv",
"${tint_src_dir}/utils/symbol",
"${tint_src_dir}/utils/text",
"${tint_src_dir}/utils/traits",
diff --git a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
index 586493d..fa27edc 100644
--- a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
@@ -25,12 +25,64 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <iostream>
+
#include "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
+#include "src/tint/utils/cli/cli.h"
+
+namespace {
+
+tint::fuzz::wgsl::Options options;
+
+}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > 0) {
std::string_view wgsl(reinterpret_cast<const char*>(data), size);
- tint::fuzz::wgsl::Run(wgsl);
+ tint::fuzz::wgsl::Run(wgsl, options);
}
return 0;
}
+
+extern "C" int LLVMFuzzerInitialize(int* argc, const char*** argv) {
+ tint::cli::OptionSet opts;
+
+ tint::Vector<std::string_view, 8> arguments;
+ for (int i = 1; i < *argc; i++) {
+ std::string_view arg((*argv)[i]);
+ if (!arg.empty()) {
+ arguments.Push(arg);
+ }
+ }
+
+ auto show_help = [&] {
+ std::cerr << "Custom fuzzer options:" << std::endl;
+ opts.ShowHelp(std::cerr);
+ std::cerr << std::endl;
+ // Change args to show libfuzzer help
+ std::cerr << "Standard libfuzzer "; // libfuzzer will print 'Usage:'
+ static const char* help[] = {(*argv)[0], "-help=1"};
+ *argc = 2;
+ *argv = help;
+ };
+
+ auto& opt_help = opts.Add<tint::cli::BoolOption>("help", "shows the usage");
+ auto& opt_concurrent =
+ opts.Add<tint::cli::BoolOption>("concurrent", "runs the fuzzers concurrently");
+
+ tint::cli::ParseOptions parse_opts;
+ parse_opts.ignore_unknown = true;
+ if (auto res = opts.Parse(arguments, parse_opts); !res) {
+ show_help();
+ std::cerr << res.Failure();
+ return 0;
+ }
+
+ if (opt_help.value.value_or(false)) {
+ show_help();
+ return 0;
+ }
+
+ options.run_concurrently = opt_concurrent.value.value_or(false);
+ return 0;
+}
diff --git a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
index a139e91..69d3587 100644
--- a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
@@ -28,6 +28,7 @@
#include "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
#include <iostream>
+#include <thread>
#include "src/tint/lang/wgsl/reader/reader.h"
#include "src/tint/utils/containers/vector.h"
@@ -38,7 +39,7 @@
namespace {
Vector<ProgramFuzzer, 32> fuzzers;
-std::string_view currently_running;
+thread_local std::string_view currently_running;
[[noreturn]] void TintInternalCompilerErrorReporter(const tint::InternalCompilerError& err) {
std::cerr << "ICE while running fuzzer: '" << currently_running << "'" << std::endl;
@@ -52,7 +53,7 @@
fuzzers.Push(fuzzer);
}
-void Run(std::string_view wgsl) {
+void Run(std::string_view wgsl, const Options& options) {
tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
// Ensure that fuzzers are sorted. Without this, the fuzzers may be registered in any order,
@@ -69,10 +70,26 @@
}
// Run each of the program fuzzer functions
- TINT_DEFER(currently_running = "");
- for (auto& fuzzer : fuzzers) {
- currently_running = fuzzer.name;
- fuzzer.fn(program);
+ if (options.run_concurrently) {
+ size_t n = fuzzers.Length();
+ tint::Vector<std::thread, 32> threads;
+ threads.Resize(n);
+ for (size_t i = 0; i < n; i++) {
+ threads[i] = std::thread([i, &program] {
+ auto& fuzzer = fuzzers[i];
+ currently_running = fuzzer.name;
+ fuzzer.fn(program);
+ });
+ }
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ } else {
+ TINT_DEFER(currently_running = "");
+ for (auto& fuzzer : fuzzers) {
+ currently_running = fuzzer.name;
+ fuzzer.fn(program);
+ }
}
}
diff --git a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
index 2231743..909c394 100644
--- a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
+++ b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
@@ -47,9 +47,16 @@
Fn* fn = nullptr;
};
+/// Options for Run()
+struct Options {
+ /// If true, the fuzzers will be run concurrently on separate threads.
+ bool run_concurrently = false;
+};
+
/// Runs all the registered WGSL fuzzers with the supplied WGSL
/// @param wgsl the input WGSL
-void Run(std::string_view wgsl);
+/// @param options the options for running the fuzzers
+void Run(std::string_view wgsl, const Options& options);
/// Registers the fuzzer function with the WGSL fuzzer executable.
/// @param fuzzer the fuzzer
diff --git a/src/tint/utils/cli/cli.cc b/src/tint/utils/cli/cli.cc
index 5095b73..557d8bc 100644
--- a/src/tint/utils/cli/cli.cc
+++ b/src/tint/utils/cli/cli.cc
@@ -37,6 +37,7 @@
namespace tint::cli {
+Option::Option() = default;
Option::~Option() = default;
void OptionSet::ShowHelp(std::ostream& s_out) {
@@ -131,7 +132,8 @@
}
}
-Result<OptionSet::Unconsumed> OptionSet::Parse(VectorRef<std::string_view> arguments_raw) {
+Result<OptionSet::Unconsumed> OptionSet::Parse(VectorRef<std::string_view> arguments_raw,
+ const ParseOptions& parse_options /* = {} */) {
// Build a map of name to option, and set defaults
Hashmap<std::string, Option*, 32> options_by_name;
for (auto* opt : options.Objects()) {
@@ -171,7 +173,7 @@
if (auto err = (*opt)->Parse(arguments); !err.empty()) {
return Failure{err};
}
- } else {
+ } else if (!parse_options.ignore_unknown) {
StringStream err;
err << "unknown flag: " << arg << std::endl;
auto names = options_by_name.Keys();
diff --git a/src/tint/utils/cli/cli.h b/src/tint/utils/cli/cli.h
index 5bfa35a..b315884 100644
--- a/src/tint/utils/cli/cli.h
+++ b/src/tint/utils/cli/cli.h
@@ -101,6 +101,9 @@
/// An alias to std::string, used to hold error messages.
using Error = std::string;
+ /// Constructor
+ Option();
+
/// Destructor
virtual ~Option();
@@ -162,6 +165,18 @@
}
return err;
}
+
+ private:
+ Option(const Option&) = delete;
+ Option& operator=(const Option&) = delete;
+ Option(Option&&) = delete;
+ Option& operator=(Option&&) = delete;
+};
+
+/// Options for OptionSet::Parse
+struct ParseOptions {
+ /// If true, then unknown flags will be ignored instead of treated as an error
+ bool ignore_unknown = false;
};
/// OptionSet is a set of Options, which can parse the command line arguments.
@@ -186,8 +201,10 @@
/// Parses all the options in @p options.
/// @param arguments the command line arguments, excluding the initial executable name
+ /// @param parse_options the optional parser options
/// @return a Result holding a list of arguments that were not consumed as options
- Result<Unconsumed> Parse(VectorRef<std::string_view> arguments);
+ Result<Unconsumed> Parse(VectorRef<std::string_view> arguments,
+ const ParseOptions& parse_options = {});
private:
/// The list of options to parse
diff --git a/src/tint/utils/cli/cli_test.cc b/src/tint/utils/cli/cli_test.cc
index 6673a01..4db57f8 100644
--- a/src/tint/utils/cli/cli_test.cc
+++ b/src/tint/utils/cli/cli_test.cc
@@ -159,6 +159,28 @@
)");
}
+TEST_F(CLITest, UnknownFlag) {
+ OptionSet opts;
+ opts.Add<BoolOption>("my_option", "a boolean value");
+
+ auto res = opts.Parse(Split("--myoption false", " "));
+ ASSERT_FALSE(res) << res;
+ EXPECT_EQ(res.Failure().reason.str(), R"(error: unknown flag: --myoption
+Did you mean '--my_option'?)");
+}
+
+TEST_F(CLITest, UnknownFlag_Ignored) {
+ OptionSet opts;
+ auto& opt = opts.Add<BoolOption>("my_option", "a boolean value");
+
+ ParseOptions parse_opts;
+ parse_opts.ignore_unknown = true;
+
+ auto res = opts.Parse(Split("--myoption false", " "), parse_opts);
+ ASSERT_TRUE(res) << res;
+ EXPECT_EQ(opt.value, std::nullopt);
+}
+
TEST_F(CLITest, ParseBool_Flag) {
OptionSet opts;
auto& opt = opts.Add<BoolOption>("my_option", "a boolean value");