Avoid interference from destructed Commands by owning the redirected files. This is needed because sometimes when a command hangs we destruct the object and move on, but the command may still run for a short but uncontrollable period, which still writes to the redirected files. Before this CL we create new Commands using the same redirected file paths. This can cause issue when we try to read them later. By owning the redirected files, Command can rename them and clean them up to avoid conflict and interference. There is a bit of changes needed at the use sites (mainly CentipedeCallbacks) to save the last log path/file when needed. But it seems not hard to understand with the new ownership model. PiperOrigin-RevId: 876461673
diff --git a/centipede/binary_info.cc b/centipede/binary_info.cc index 227bb88..4517c98 100644 --- a/centipede/binary_info.cc +++ b/centipede/binary_info.cc
@@ -54,14 +54,15 @@ ScopedFile pc_table_path(tmp_dir_path, "pc_table_tmp"); ScopedFile cf_table_path(tmp_dir_path, "cf_table_tmp"); ScopedFile dso_table_path(tmp_dir_path, "dso_table_tmp"); - ScopedFile log_path(tmp_dir_path, "binary_info_log_tmp"); + const auto log_prefix = + std::filesystem::path{tmp_dir_path} / "binary_info_log_tmp"; FUZZTEST_LOG(INFO) << __func__ << ": tmp_dir: " << tmp_dir; Command::Options cmd_options; cmd_options.env_add = {absl::StrCat( "CENTIPEDE_RUNNER_FLAGS=:dump_binary_info:arg1=", pc_table_path.path(), ":arg2=", cf_table_path.path(), ":arg3=", dso_table_path.path(), ":")}; - cmd_options.stdout_file = std::string(log_path.path()); + cmd_options.stdout_file_prefix = log_prefix; Command cmd{binary_path_with_args, std::move(cmd_options)}; int exit_code = cmd.Execute(); if (exit_code != EXIT_SUCCESS) {
diff --git a/centipede/centipede.cc b/centipede/centipede.cc index b94f246..b18f995 100644 --- a/centipede/centipede.cc +++ b/centipede/centipede.cc
@@ -126,8 +126,10 @@ input_filter_cmd_{[&] { Command::Options cmd_options; cmd_options.args = {input_filter_path_}; - cmd_options.stdout_file = "/dev/null"; - cmd_options.stderr_file = "/dev/null"; + cmd_options.stdout_file_prefix = + std::filesystem::path(TemporaryLocalDirPath()) + .append("filter-input.log"); + cmd_options.stderr_file_prefix = cmd_options.stdout_file_prefix; return Command{env_.input_filter, std::move(cmd_options)}; }()}, rusage_profiler_(
diff --git a/centipede/centipede_callbacks.cc b/centipede/centipede_callbacks.cc index 3f6f817..14e11e8 100644 --- a/centipede/centipede_callbacks.cc +++ b/centipede/centipede_callbacks.cc
@@ -435,8 +435,8 @@ Command::Options cmd_options; cmd_options.env_add = std::move(env); cmd_options.env_remove = EnvironmentVariablesToUnset(); - cmd_options.stdout_file = execute_log_path_; - cmd_options.stderr_file = execute_log_path_; + cmd_options.stdout_file_prefix = execute_log_prefix_; + cmd_options.stderr_file_prefix = execute_log_prefix_; cmd_options.temp_file_path = temp_input_file_path_; CommandContext& command_context = @@ -485,8 +485,12 @@ std::min(absl::Now() + amortized_timeout, GetStopTime()); int exit_code = EXIT_SUCCESS; const bool should_clean_up = [&] { - if (!cmd.is_executing() && !cmd.ExecuteAsync()) { - return true; + if (!cmd.is_executing()) { + const bool execute_ret = cmd.ExecuteAsync(); + last_execute_log_path_ = cmd.stdout_file(); + if (!execute_ret) { + return true; + } } if (command_context.persistent_mode_server != nullptr && command_context.persistent_mode_server->RunBatch(deadline, exit_code)) { @@ -508,6 +512,18 @@ FUZZTEST_LOG(ERROR) << "Failed to wait for the batch execution cleanup."; return EXIT_FAILURE; }(); + // We need to save any execution log before the destruction of the command. + std::error_code ec; + std::filesystem::rename(last_execute_log_path_, saved_execute_log_path_, + ec); + if (ec) { + FUZZTEST_LOG(ERROR) << "Failed to save the execution log " + << last_execute_log_path_ << " to " + << saved_execute_log_path_ << "(" << ec.message() + << "). Left with an empty log ..."; + (void)std::filesystem::remove(saved_execute_log_path_, ec); + }; + last_execute_log_path_ = saved_execute_log_path_; command_contexts_.erase( std::find_if(command_contexts_.begin(), command_contexts_.end(), [=](const auto& command_context) { @@ -574,7 +590,7 @@ // TODO: b/467103298 - Handle failures when the exit code is zero, e.g., when // the target exits via `std::_Exit(0)`. if (exit_code != EXIT_SUCCESS) { - ReadFromLocalFile(execute_log_path_, batch_result.log()); + ReadFromLocalFile(last_execute_log_path_, batch_result.log()); if (std::filesystem::exists(failure_description_path_)) { ReadFromLocalFile(failure_description_path_, @@ -627,12 +643,14 @@ Command::Options cmd_options; cmd_options.env_add = {std::move(centipede_runner_flags)}; cmd_options.env_remove = EnvironmentVariablesToUnset(); - cmd_options.stdout_file = execute_log_path_; - cmd_options.stderr_file = execute_log_path_; + cmd_options.stdout_file_prefix = execute_log_prefix_; + cmd_options.stderr_file_prefix = execute_log_prefix_; cmd_options.temp_file_path = temp_input_file_path_; Command cmd{binary, std::move(cmd_options)}; + const bool execute_ret = cmd.ExecuteAsync(); + last_execute_log_path_ = cmd.stdout_file(); const int retval = [&] { - if (!cmd.ExecuteAsync()) { + if (!execute_ret) { FUZZTEST_LOG(ERROR) << "Failed to execute seeding command " << cmd.ToString(); return EXIT_FAILURE; @@ -690,11 +708,12 @@ Command::Options cmd_options; cmd_options.env_add = {std::move(centipede_runner_flags)}; cmd_options.env_remove = EnvironmentVariablesToUnset(); - cmd_options.stdout_file = execute_log_path_; - cmd_options.stderr_file = execute_log_path_; + cmd_options.stdout_file_prefix = execute_log_prefix_; + cmd_options.stderr_file_prefix = execute_log_prefix_; cmd_options.temp_file_path = temp_input_file_path_; Command cmd{binary, std::move(cmd_options)}; const bool is_success = cmd.Execute() == 0; + last_execute_log_path_ = cmd.stdout_file(); if (is_success) { if (std::filesystem::exists(config_file_path)) { @@ -789,14 +808,14 @@ } void CentipedeCallbacks::PrintExecutionLog() const { - if (!std::filesystem::exists(execute_log_path_)) { + if (!std::filesystem::exists(last_execute_log_path_)) { FUZZTEST_LOG(WARNING) << "Log file for the last executed binary does not exist: " - << execute_log_path_; + << last_execute_log_path_; return; } std::string log_text; - ReadFromLocalFile(execute_log_path_, log_text); + ReadFromLocalFile(last_execute_log_path_, log_text); absl::MutexLock lock(GetExecutionLoggingMutex()); for (const auto& log_line : absl::StrSplit(absl::StripAsciiWhitespace(log_text), '\n')) {
diff --git a/centipede/centipede_callbacks.h b/centipede/centipede_callbacks.h index 068dfc6..0a946f1 100644 --- a/centipede/centipede_callbacks.h +++ b/centipede/centipede_callbacks.h
@@ -186,8 +186,13 @@ std::string temp_dir_ = TemporaryLocalDirPath(); std::string temp_input_file_path_ = std::filesystem::path(temp_dir_).append("temp_input_file"); - const std::string execute_log_path_ = + const std::string execute_log_prefix_ = std::filesystem::path(temp_dir_).append("log"); + // An owned file path to save the last execution log before it goes out of + // scope. + const std::string saved_execute_log_path_ = + std::filesystem::path(temp_dir_).append("saved_log"); + std::string last_execute_log_path_; std::string failure_description_path_ = std::filesystem::path(temp_dir_).append("failure_description"); std::string failure_signature_path_ =
diff --git a/centipede/command.cc b/centipede/command.cc index 50d26ff..3536d93 100644 --- a/centipede/command.cc +++ b/centipede/command.cc
@@ -29,7 +29,9 @@ #endif // __APPLE__ #include <algorithm> +#include <atomic> #include <csignal> +#include <cstdint> #include <cstdlib> #include <filesystem> // NOLINT #include <fstream> @@ -111,6 +113,11 @@ #endif } +std::string GetUniqueSuffix() { + static std::atomic<uint64_t> suffix_counter = {0}; + return absl::StrCat(suffix_counter++); +} + } // namespace // TODO(ussuri): Encapsulate as much of the fork server functionality from @@ -185,6 +192,7 @@ << " still running. Requesting it to stop without waiting for it..."; RequestStop(); } + ResetRedirectionFiles(/*new_suffix=*/""); } Command::Command(std::string_view path, Options options) @@ -224,12 +232,12 @@ ss.push_back(arg); } // out/err. - if (!options_.stdout_file.empty()) { - ss.push_back(absl::StrCat("> ", options_.stdout_file)); + if (!stdout_file_.empty()) { + ss.push_back(absl::StrCat("> ", stdout_file_)); } - if (!options_.stderr_file.empty()) { - if (options_.stdout_file != options_.stderr_file) { - ss.push_back(absl::StrCat("2> ", options_.stderr_file)); + if (!stderr_file_.empty()) { + if (stdout_file_ != stderr_file_) { + ss.push_back(absl::StrCat("2> ", stderr_file_)); } else { ss.push_back("2>&1"); } @@ -244,8 +252,12 @@ FUZZTEST_VLOG(2) << "Fork server disabled for " << path(); return false; } + FUZZTEST_CHECK(!is_executing_ && !fork_server_); FUZZTEST_VLOG(2) << "Starting fork server for " << path(); + ResetRedirectionFiles(GetUniqueSuffix()); + command_line_ = ToString(); + fork_server_.reset(new ForkServerProps); fork_server_->fifo_path_[0] = std::filesystem::path(temp_dir_path) .append(absl::StrCat(prefix, "_FIFO0")); @@ -274,7 +286,7 @@ const std::string fork_server_command = absl::StrFormat( kForkServerCommandStub, fork_server_->fifo_path_[0], fork_server_->fifo_path_[1], command_line_, pid_file_path); - FUZZTEST_VLOG(2) << "Fork server command:" << fork_server_command; + FUZZTEST_VLOG(1) << "Fork server command:" << fork_server_command; const int exit_code = system(fork_server_command.c_str()); @@ -324,6 +336,27 @@ return true; } +void Command::ResetRedirectionFiles(std::string_view new_suffix) { + if (!stdout_file_.empty()) { + std::error_code ec; + (void)std::filesystem::remove(stdout_file_, ec); + } + if (!stderr_file_.empty() && stderr_file_ != stdout_file_) { + std::error_code ec; + (void)std::filesystem::remove(stderr_file_, ec); + } + if (!options_.stdout_file_prefix.empty() && !new_suffix.empty()) { + stdout_file_ = absl::StrCat(options_.stdout_file_prefix, new_suffix); + } else { + stdout_file_.clear(); + } + if (!options_.stderr_file_prefix.empty() && !new_suffix.empty()) { + stderr_file_ = absl::StrCat(options_.stderr_file_prefix, new_suffix); + } else { + stderr_file_.clear(); + } +} + absl::Status Command::VerifyForkServerIsHealthy() { // Preconditions: the callers (`Execute()`) should call us only when the fork // server is presumed to be running (`fork_server_pid_` >= 0). If it is, the @@ -354,7 +387,6 @@ bool Command::ExecuteAsync() { FUZZTEST_CHECK(!is_executing()); - FUZZTEST_VLOG(1) << "Executing command '" << command_line_ << "'..."; if (fork_server_ != nullptr) { FUZZTEST_VLOG(1) << "Sending execution request to fork server"; @@ -374,6 +406,11 @@ FUZZTEST_CHECK(fork_server_->ReadPipe(absl::Now() + absl::Seconds(60), x)); } else { FUZZTEST_CHECK_EQ(pid_, -1); + + ResetRedirectionFiles(GetUniqueSuffix()); + command_line_ = ToString(); + FUZZTEST_VLOG(1) << "Executing command '" << command_line_ << "'..."; + std::vector<std::string> argv_strs = {"/bin/sh", "-c", command_line_}; std::vector<char*> argv; argv.reserve(argv_strs.size() + 1); @@ -496,8 +533,8 @@ std::string Command::ReadRedirectedStdout() const { std::string ret; - if (!options_.stdout_file.empty()) { - ReadFromLocalFile(options_.stdout_file, ret); + if (!stdout_file_.empty()) { + ReadFromLocalFile(stdout_file_, ret); if (ret.empty()) ret = "<EMPTY>"; } return ret; @@ -505,12 +542,11 @@ std::string Command::ReadRedirectedStderr() const { std::string ret; - if (!options_.stderr_file.empty()) { - if (options_.stderr_file == "2>&1" || - options_.stderr_file == options_.stdout_file) { + if (!stderr_file_.empty()) { + if (stderr_file_ == stdout_file_) { ret = "<DUPED TO STDOUT>"; } else { - ReadFromLocalFile(options_.stderr_file, ret); + ReadFromLocalFile(stderr_file_, ret); if (ret.empty()) ret = "<EMPTY>"; } }
diff --git a/centipede/command.h b/centipede/command.h index 88165ca..a2fab51 100644 --- a/centipede/command.h +++ b/centipede/command.h
@@ -40,11 +40,19 @@ std::vector<std::string> env_add; // Environment variables to unset in the subprocess executing the command. std::vector<std::string> env_remove; - // Redirect stdout to this file. If empty, use parent's STDOUT. - std::string stdout_file; - // Redirect stderr to this file. If empty, use parent's STDERR. If `out` == - // `err` and both are non-empty, stdout/stderr are combined. - std::string stderr_file; + // Redirect stdout to this file path with an opaque suffix. If empty, use + // parent's STDOUT. + // + // `Command` automatically unlinks any previous redirected files on + // execution and destruction. + std::string stdout_file_prefix; + // Redirect stderr to this file path with an opaque suffix. If empty, use + // parent's STDERR. If `out` == `err` and both are non-empty, stdout/stderr + // are combined. + // + // `Command` automatically unlinks any previous redirected files on + // execution and destruction. + std::string stderr_file_prefix; // "@@" in the command will be replaced with `temp_file_path`. std::string temp_file_path; }; @@ -68,6 +76,10 @@ // Returns a string representing the command, e.g. like this // "env -u ENV1 ENV2=VAL2 path arg1 arg2 > out 2>& err" + // + // Note that the result reflects the latest command executed and can be + // changed after next execution. It can be missing some parts (e.g. output + // redirection) if there is no previous execution. std::string ToString() const; // Execute the command asynchronously. Returns true if it starts a new @@ -104,12 +116,29 @@ // Accessors. const std::string& path() const { return path_; } + // Accessors to the paths of the {stdout,stderr} of the last command + // execution, if redirected to files. Otherwise the paths will be empty. Note + // that it is possible that the files does not exist on the paths, e.g. if the + // command haven't touch them yet. + const std::string& stdout_file() const { return stdout_file_; } + const std::string& stderr_file() const { return stderr_file_; } + private: struct ForkServerProps; int pid_ = -1; bool is_executing_ = false; + // Derived from Options::{stdout,stderr}_file_prefix, with the realized suffix + // if the options are non-empty. + std::string stdout_file_; + std::string stderr_file_; + + // Removes previous files (if any), and sets {stdout,stderr}_file_ with + // `new_suffix` if both parts are not empty. Note that it only sets the new + // file paths without touching the files. + void ResetRedirectionFiles(std::string_view new_suffix); + // Returns the status of the fork server process. Expects that the server was // previously started using `StartForkServer()`. absl::Status VerifyForkServerIsHealthy(); @@ -131,7 +160,7 @@ const std::string path_; const Options options_; - const std::string command_line_ = ToString(); + std::string command_line_; std::unique_ptr<ForkServerProps> fork_server_; };
diff --git a/centipede/command_test.cc b/centipede/command_test.cc index ab6d6f0..f4aaa36 100644 --- a/centipede/command_test.cc +++ b/centipede/command_test.cc
@@ -50,32 +50,6 @@ EXPECT_EQ((Command{"x", std::move(cmd_options)}.ToString()), "env \\\n-u K3 \\\nK1=V1 \\\nK2=V2 \\\nx"); } - { - Command::Options cmd_options; - cmd_options.stdout_file = "out"; - EXPECT_EQ((Command{"x", std::move(cmd_options)}.ToString()), - "env \\\nx \\\n> out"); - } - { - Command::Options cmd_options; - cmd_options.stderr_file = "err"; - EXPECT_EQ((Command{"x", std::move(cmd_options)}.ToString()), - "env \\\nx \\\n2> err"); - } - { - Command::Options cmd_options; - cmd_options.stdout_file = "out"; - cmd_options.stderr_file = "err"; - EXPECT_EQ((Command{"x", std::move(cmd_options)}.ToString()), - "env \\\nx \\\n> out \\\n2> err"); - } - { - Command::Options cmd_options; - cmd_options.stdout_file = "out"; - cmd_options.stderr_file = "out"; - EXPECT_EQ((Command{"x", std::move(cmd_options)}.ToString()), - "env \\\nx \\\n> out \\\n2>&1"); - } } TEST(CommandTest, Execute) { @@ -114,79 +88,79 @@ { const std::string input = "success"; - const std::string log = std::filesystem::path{test_tmpdir} / input; + const std::string log_prefix = std::filesystem::path{test_tmpdir} / input; Command::Options cmd_options; cmd_options.args = {input}; - cmd_options.stdout_file = log; - cmd_options.stderr_file = log; + cmd_options.stdout_file_prefix = log_prefix; + cmd_options.stderr_file_prefix = log_prefix; Command cmd{helper, std::move(cmd_options)}; EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer")); EXPECT_EQ(cmd.Execute(), EXIT_SUCCESS); std::string log_contents; - ReadFromLocalFile(log, log_contents); + ReadFromLocalFile(cmd.stdout_file(), log_contents); EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input)); } { const std::string input = "fail"; - const std::string log = std::filesystem::path{test_tmpdir} / input; + const std::string log_prefix = std::filesystem::path{test_tmpdir} / input; Command::Options cmd_options; cmd_options.args = {input}; - cmd_options.stdout_file = log; - cmd_options.stderr_file = log; + cmd_options.stdout_file_prefix = log_prefix; + cmd_options.stderr_file_prefix = log_prefix; Command cmd{helper, std::move(cmd_options)}; EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer")); EXPECT_EQ(cmd.Execute(), EXIT_FAILURE); std::string log_contents; - ReadFromLocalFile(log, log_contents); + ReadFromLocalFile(cmd.stdout_file(), log_contents); EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input)); } { const std::string input = "ret42"; - const std::string log = std::filesystem::path{test_tmpdir} / input; + const std::string log_prefix = std::filesystem::path{test_tmpdir} / input; Command::Options cmd_options; cmd_options.args = {input}; - cmd_options.stdout_file = log; - cmd_options.stderr_file = log; + cmd_options.stdout_file_prefix = log_prefix; + cmd_options.stderr_file_prefix = log_prefix; Command cmd{helper, std::move(cmd_options)}; EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer")); EXPECT_EQ(cmd.Execute(), 42); std::string log_contents; - ReadFromLocalFile(log, log_contents); + ReadFromLocalFile(cmd.stdout_file(), log_contents); EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input)); } { const std::string input = "abort"; - const std::string log = std::filesystem::path{test_tmpdir} / input; + const std::string log_prefix = std::filesystem::path{test_tmpdir} / input; Command::Options cmd_options; cmd_options.args = {input}; - cmd_options.stdout_file = log; - cmd_options.stderr_file = log; + cmd_options.stdout_file_prefix = log_prefix; + cmd_options.stderr_file_prefix = log_prefix; Command cmd{helper, std::move(cmd_options)}; EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer")); // WTERMSIG() needs an lvalue on some platforms. const int ret = cmd.Execute(); EXPECT_EQ(WTERMSIG(ret), SIGABRT); std::string log_contents; - ReadFromLocalFile(log, log_contents); + ReadFromLocalFile(cmd.stdout_file(), log_contents); EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input)); } { const std::string input = "hang"; - const std::string log = std::filesystem::path{test_tmpdir} / input; + const std::string log_prefix = std::filesystem::path{test_tmpdir} / input; Command::Options cmd_options; cmd_options.args = {input}; - cmd_options.stdout_file = log; - cmd_options.stderr_file = log; + cmd_options.stdout_file_prefix = log_prefix; + cmd_options.stderr_file_prefix = log_prefix; Command cmd{helper, std::move(cmd_options)}; ASSERT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer")); ASSERT_TRUE(cmd.ExecuteAsync()); EXPECT_EQ(cmd.Wait(absl::Now() + absl::Seconds(2)), std::nullopt); std::string log_contents; - ReadFromLocalFile(log, log_contents); + ReadFromLocalFile(cmd.stdout_file(), log_contents); EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input)); }
diff --git a/centipede/control_flow.cc b/centipede/control_flow.cc index e053a80..b33a37e 100644 --- a/centipede/control_flow.cc +++ b/centipede/control_flow.cc
@@ -56,27 +56,23 @@ PCTable GetPcTableFromBinaryWithTracePC(std::string_view binary_path, std::string_view objdump_path, std::string_view tmp_path) { - const std::string stderr_path = absl::StrCat(tmp_path, ".log"); Command::Options cmd_options; cmd_options.args = {"-d", std::string(binary_path)}; - cmd_options.stdout_file = std::string(tmp_path); - cmd_options.stderr_file = stderr_path; + cmd_options.stdout_file_prefix = absl::StrCat(tmp_path, ".out"); + cmd_options.stderr_file_prefix = absl::StrCat(tmp_path, ".log"); Command cmd{objdump_path, std::move(cmd_options)}; int exit_code = cmd.Execute(); if (exit_code != EXIT_SUCCESS) { std::string log_text; - ReadFromLocalFile(stderr_path, log_text); + ReadFromLocalFile(cmd.stderr_file(), log_text); FUZZTEST_LOG(ERROR) << "Failed to use objdump to get PC table; stderr is:"; for (const auto &line : absl::StrSplit(log_text, '\n')) { FUZZTEST_LOG(ERROR).NoPrefix() << line; } - std::filesystem::remove(tmp_path); - std::filesystem::remove(stderr_path); return {}; } - std::filesystem::remove(stderr_path); PCTable pc_table; - std::ifstream in(std::string{tmp_path}); + std::ifstream in(cmd.stdout_file()); FUZZTEST_CHECK(in.good()) << VV(tmp_path); bool saw_new_function = false; @@ -98,7 +94,6 @@ saw_new_function = false; // next trace_pc will be in the same function. pc_table.push_back({pc, flags}); } - std::filesystem::remove(tmp_path); return pc_table; }
diff --git a/centipede/symbol_table.cc b/centipede/symbol_table.cc index 2168c00..e693b48 100644 --- a/centipede/symbol_table.cc +++ b/centipede/symbol_table.cc
@@ -102,8 +102,8 @@ const std::string dso_basename = std::filesystem::path{dso_path}.filename(); const ScopedFile pcs_file{ tmp_dir_path, absl::StrCat(dso_basename, ".pcs.", unique_id_value)}; - const ScopedFile symbols_file{ - tmp_dir_path, absl::StrCat(dso_basename, ".symbols.", unique_id_value)}; + const auto symbols_file_prefix = std::filesystem::path{tmp_dir_path} / + absl::StrCat(dso_basename, ".symbols"); // Create the input file (one PC per line). std::string pcs_string; @@ -123,7 +123,7 @@ "<", std::string(pcs_file.path()), }; - cmd_options.stdout_file = std::string(symbols_file.path()); + cmd_options.stdout_file_prefix = symbols_file_prefix; Command cmd{symbolizer_path, std::move(cmd_options)}; int exit_code = cmd.Execute(); if (exit_code != EXIT_SUCCESS) { @@ -134,7 +134,7 @@ } // Get and process the symbolizer output. - std::ifstream symbolizer_output{std::string{symbols_file.path()}}; + std::ifstream symbolizer_output{cmd.stdout_file()}; size_t old_size = size(); ReadFromLLVMSymbolizer(symbolizer_output); size_t new_size = size();