From ba1ac210ef087cdc4c2caf8d3c5feb9d31447d31 Mon Sep 17 00:00:00 2001 From: Brian Daniels Date: Wed, 29 Apr 2026 10:43:20 -0400 Subject: [PATCH] [assemble_cvd] Replace slow lsof for file cleanup CleanPriorFiles called lsof to ensure files weren't in use before deletion, but this was taking ~5 seconds, significantly slowing down startup. lsof was slow because it scanned all processes on the system and performed name resolutions by default. Replacing it with a targeted C++ scan of /proc/*/fd for the current user's processes avoids these overheads and reduces the execution time to ~132 ms. Bug: 401259067 Test: cvd create check launcher.log for ~5s speed up to UBOOT start --- .../host/commands/assemble_cvd/BUILD.bazel | 1 + .../host/commands/assemble_cvd/clean.cc | 83 ++++++++++++++----- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel b/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel index 7389abac3fa..0ccfd1dee08 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel @@ -163,6 +163,7 @@ cf_cc_library( hdrs = ["clean.h"], deps = [ "//cuttlefish/common/libs/utils:in_sandbox", + "//cuttlefish/common/libs/utils:proc_file_utils", "//cuttlefish/common/libs/utils:subprocess", "//cuttlefish/common/libs/utils:subprocess_managed_stdio", "//cuttlefish/host/libs/config:config_utils", diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/clean.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/clean.cc index f3b8e971579..7b97c208397 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/clean.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/clean.cc @@ -28,6 +28,7 @@ #include "absl/log/log.h" #include "cuttlefish/common/libs/utils/in_sandbox.h" +#include "cuttlefish/common/libs/utils/proc_file_utils.h" #include "cuttlefish/common/libs/utils/subprocess.h" #include "cuttlefish/common/libs/utils/subprocess_managed_stdio.h" #include "cuttlefish/host/libs/config/config_utils.h" @@ -85,6 +86,64 @@ Result CleanPriorFiles(const std::string& path, return {}; } +Result> GetPidsUsingFiles( + const std::vector& prior_dirs, + const std::vector& prior_files) { + std::vector pids_in_use; + + VLOG(0) << "Checking if prior dirs or files are in use: "; + for (const auto& prior_dir : prior_dirs) { + VLOG(0) << prior_dir; + } + for (const auto& prior_file : prior_files) { + VLOG(0) << prior_file; + } + auto pids = CF_EXPECT(CollectPids(getuid())); + for (const auto pid : pids) { + std::string fd_dir_path = fmt::format("/proc/{}/fd", pid); + std::unique_ptr dir(opendir(fd_dir_path.c_str()), + closedir); + if (!dir) { + continue; + } + for (auto entity = readdir(dir.get()); entity != nullptr; + entity = readdir(dir.get())) { + std::string entity_name(entity->d_name); + if (entity_name == "." || entity_name == "..") { + continue; + } + std::string fd_path = fd_dir_path + "/" + entity_name; + std::string target; + if (!android::base::Readlink(fd_path, &target)) { + continue; + } + + bool match = false; + for (const auto& prior_dir : prior_dirs) { + if (target.starts_with(prior_dir)) { + match = true; + break; + } + } + if (!match) { + for (const auto& prior_file : prior_files) { + if (target == prior_file) { + match = true; + break; + } + } + } + + if (match) { + pids_in_use.push_back(pid); + break; + } + } + } + + return pids_in_use; +} + Result CleanPriorFiles(const std::vector& paths, const std::set& preserving) { std::vector prior_dirs; @@ -105,25 +164,11 @@ Result CleanPriorFiles(const std::vector& paths, // TODO(schuffelen): Fix logic for host-sandboxing mode. if (!InSandbox() && (!prior_dirs.empty() || !prior_files.empty())) { - Command lsof("lsof"); - lsof.AddParameter("-t"); - lsof.AddParameter("-Q"); // ignore failed search terms - for (const auto& prior_dir : prior_dirs) { - lsof.AddParameter("+D").AddParameter(prior_dir); - } - for (const auto& prior_file : prior_files) { - lsof.AddParameter(prior_file); - } - - Result lsof_out = RunAndCaptureStdout(std::move(lsof)); - if (lsof_out.ok()) { - CF_EXPECTF( - lsof_out->empty(), - "Instance directory files in use. Try `cvd reset`? Observed PIDs: {}", - fmt::join(absl::StrSplit(*lsof_out, "\n"), ", ")); - } else { - LOG(ERROR) << "Failed to run `lsof`: " << lsof_out.error(); - } + auto pids = CF_EXPECT(GetPidsUsingFiles(prior_dirs, prior_files)); + CF_EXPECTF( + pids.empty(), + "Instance directory files in use. Try `cvd reset`? Observed PIDs: {}", + fmt::join(pids, ", ")); } for (const auto& path : paths) {