summaryrefslogtreecommitdiff
path: root/src/maintenance/controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/maintenance/controller.cc')
-rw-r--r--src/maintenance/controller.cc633
1 files changed, 0 insertions, 633 deletions
diff --git a/src/maintenance/controller.cc b/src/maintenance/controller.cc
deleted file mode 100644
index 8929057..0000000
--- a/src/maintenance/controller.cc
+++ /dev/null
@@ -1,633 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "compiler/compiler.h"
-#include "maintenance/controller.h"
-
-#include "common/cmd_utils.h"
-#include "common/debug.h"
-#include "common/expected.h"
-#include "common/trace.h"
-
-#include "db/models.h"
-#include "inode2filename/inode.h"
-#include "inode2filename/search_directories.h"
-#include "prefetcher/read_ahead.h"
-
-#include <android-base/file.h>
-#include <utils/Printer.h>
-
-#include <chrono>
-#include <ctime>
-#include <iostream>
-#include <filesystem>
-#include <fstream>
-#include <limits>
-#include <mutex>
-#include <optional>
-#include <vector>
-#include <string>
-#include <sys/wait.h>
-
-namespace iorap::maintenance {
-
-const constexpr int64_t kCompilerCheckIntervalMs = 10;
-static constexpr size_t kMinTracesForCompilation = 1;
-const constexpr char* kDenyListFilterDexFiles = "[.](art|oat|odex|vdex|dex)$";
-
-struct LastJobInfo {
- time_t last_run_ns_{0};
- size_t activities_last_compiled_{0};
-};
-
-LastJobInfo last_job_info_;
-std::mutex last_job_info_mutex_;
-
-// Gets the path of output compiled trace.
-db::CompiledTraceFileModel CalculateNewestFilePath(
- const std::string& package_name,
- const std::string& activity_name,
- int version) {
- db::VersionedComponentName versioned_component_name{
- package_name, activity_name, version};
-
- db::CompiledTraceFileModel output_file =
- db::CompiledTraceFileModel::CalculateNewestFilePath(versioned_component_name);
-
- return output_file;
-}
-
-using ArgString = const char*;
-
-static constexpr const char kCommandFileName[] = "/system/bin/iorap.cmd.compiler";
-
-int Exec::Execve(const std::string& pathname,
- std::vector<std::string>& argv_vec,
- char *const envp[]) {
- std::unique_ptr<ArgString[]> argv_ptr =
- common::VecToArgv(kCommandFileName, argv_vec);
-
- return execve(pathname.c_str(), (char**)argv_ptr.get(), envp);
-}
-
-pid_t Exec::Fork() {
- return fork();
-}
-
-// Represents the parameters used when fork+exec compiler.
-struct CompilerForkParameters {
- std::vector<std::string> input_pbs;
- std::vector<uint64_t> timestamp_limit_ns;
- std::string output_proto;
- std::vector<int32_t> pids;
- ControllerParameters controller_params;
-
- CompilerForkParameters(const std::vector<compiler::CompilationInput>& perfetto_traces,
- const std::string& output_proto,
- ControllerParameters controller_params) :
- output_proto(output_proto), controller_params(controller_params) {
- for (compiler::CompilationInput perfetto_trace : perfetto_traces) {
- input_pbs.push_back(perfetto_trace.filename);
- timestamp_limit_ns.push_back(perfetto_trace.timestamp_limit_ns);
- pids.push_back(perfetto_trace.pid);
- }
- }
-};
-
-std::vector<std::string> MakeCompilerParams(const CompilerForkParameters& params) {
- std::vector<std::string> argv;
- ControllerParameters controller_params = params.controller_params;
-
- common::AppendArgsRepeatedly(argv, params.input_pbs);
- common::AppendArgsRepeatedly(argv, "--timestamp_limit_ns", params.timestamp_limit_ns);
- common::AppendArgsRepeatedly(argv, "--pid", params.pids);
-
- if (controller_params.output_text) {
- argv.push_back("--output-text");
- }
-
- common::AppendArgs(argv, "--output-proto", params.output_proto);
-
- if (controller_params.inode_textcache) {
- common::AppendArgs(argv, "--inode-textcache", *controller_params.inode_textcache);
- }
-
- if (controller_params.verbose) {
- argv.push_back("--verbose");
- }
-
- if (controller_params.exclude_dex_files) {
- common::AppendArgs(argv, "--denylist-filter", kDenyListFilterDexFiles);
- }
-
- return argv;
-}
-
-// Sets a watch dog for the given pid and kill it if timeout.
-std::thread SetTimeoutWatchDog(pid_t pid, int64_t timeout_ms, std::atomic<bool>& cancel_watchdog) {
- std::thread watchdog_thread{[pid, timeout_ms, &cancel_watchdog]() {
- std::chrono::time_point start = std::chrono::system_clock::now();
- std::chrono::milliseconds timeout(timeout_ms);
- while (!cancel_watchdog) {
- int status = kill(pid, 0);
- if (status != 0) {
- LOG(DEBUG) << "Process (" << pid << ") doesn't exist now.";
- break;
- }
- std::chrono::time_point cur = std::chrono::system_clock::now();
- if (cur - start > timeout) {
- LOG(INFO) << "Process (" << pid << ") is timeout!";
- LOG(INFO) << "start time: "
- << std::chrono::system_clock::to_time_t(start)
- << " end time: "
- << std::chrono::system_clock::to_time_t(cur)
- << " timeout: "
- << timeout_ms;
- kill(pid, SIGKILL);
- break;
- }
- usleep(kCompilerCheckIntervalMs * 1000);
- }
- }};
-
- return watchdog_thread;
-}
-
-bool StartViaFork(const CompilerForkParameters& params) {
- const ControllerParameters& controller_params = params.controller_params;
- pid_t child = controller_params.exec->Fork();
-
- if (child == -1) {
- LOG(FATAL) << "Failed to fork a process for compilation";
- } else if (child > 0) { // we are the caller of this function
- LOG(DEBUG) << "forked into a process for compilation , pid = " << child;
-
- int64_t compiler_timeout_ms =
- android::base::GetIntProperty("iorapd.maintenance.compiler_timeout_ms",
- /*default*/ 10 * 60 * 1000); // 10 min
- std::atomic<bool> cancel_watchdog(false);
- std::thread watchdog_thread = SetTimeoutWatchDog(child, compiler_timeout_ms, cancel_watchdog);
- int wstatus;
- waitpid(child, /*out*/&wstatus, /*options*/0);
-
- // Terminate the thread after the compiler process is killed or done.
- LOG(DEBUG) << "Terminate the watch dog thread.";
- cancel_watchdog = true;
- watchdog_thread.join();
-
- if (!WIFEXITED(wstatus)) {
- LOG(ERROR) << "Child terminated abnormally, status: " << WEXITSTATUS(wstatus);
- return false;
- }
-
- int status = WEXITSTATUS(wstatus);
- LOG(DEBUG) << "Child terminated, status: " << status;
- if (status == 0) {
- LOG(DEBUG) << "Iorap compilation succeeded";
- return true;
- } else {
- LOG(ERROR) << "Iorap compilation failed";
- return false;
- }
- } else {
- // we are the child that was forked.
- std::vector<std::string> argv_vec = MakeCompilerParams(params);
- std::unique_ptr<ArgString[]> argv_ptr =
- common::VecToArgv(kCommandFileName, argv_vec);
-
- std::stringstream argv; // for debugging.
- for (std::string arg : argv_vec) {
- argv << arg << ' ';
- }
- LOG(DEBUG) << "fork+exec: " << kCommandFileName << " " << argv.str();
-
- controller_params.exec->Execve(kCommandFileName,
- argv_vec,
- /*envp*/nullptr);
- // This should never return.
- }
- return false;
-}
-
-// Gets the perfetto trace infos in the histories.
-std::vector<compiler::CompilationInput> GetPerfettoTraceInfo(
- const db::DbHandle& db,
- const std::vector<db::AppLaunchHistoryModel>& histories) {
- std::vector<compiler::CompilationInput> perfetto_traces;
-
- for(db::AppLaunchHistoryModel history : histories) {
- // Get perfetto trace.
- std::optional<db::RawTraceModel> raw_trace =
- db::RawTraceModel::SelectByHistoryId(db, history.id);
- if (!raw_trace) {
- // This is normal: non-cold launches do not have traces.
- continue;
- }
-
- if (!history.pid) {
- LOG(DEBUG) << "Missing pid for history " << history.id;
- continue;
- }
-
- uint64_t timestamp_limit = std::numeric_limits<uint64_t>::max();
- // Get corresponding timestamp limit.
- if (history.report_fully_drawn_ns) {
- timestamp_limit = *history.report_fully_drawn_ns;
- } else if (history.total_time_ns) {
- timestamp_limit = *history.total_time_ns;
- } else {
- LOG(DEBUG) << " No timestamp exists. Using the max value.";
- }
- perfetto_traces.push_back({raw_trace->file_path, timestamp_limit, history.pid});
- }
- return perfetto_traces;
-}
-
-// Helper struct for printing vector.
-template <class T>
-struct VectorPrinter {
- std::vector<T>& values;
-};
-
-std::ostream& operator<<(std::ostream& os,
- const struct compiler::CompilationInput& perfetto_trace) {
- os << "file_path: " << perfetto_trace.filename << " "
- << "timestamp_limit: " << perfetto_trace.timestamp_limit_ns;
- return os;
-}
-
-template <class T>
-std::ostream& operator<<(std::ostream& os, const struct VectorPrinter<T>& printer) {
- os << "[\n";
- for (T i : printer.values) {
- os << i << ",\n";
- }
- os << "]\n";
- return os;
-}
-
-// Compiled the perfetto traces for an activity.
-bool CompileActivity(const db::DbHandle& db,
- int package_id,
- const std::string& package_name,
- const std::string& activity_name,
- int version,
- const ControllerParameters& params) {
- ScopedFormatTrace atrace_compile_package(ATRACE_TAG_PACKAGE_MANAGER,
- "Compile activity %s",
- activity_name.c_str());
-
- LOG(DEBUG) << "CompileActivity: " << package_name << "/" << activity_name << "@" << version;
-
- db::CompiledTraceFileModel output_file =
- CalculateNewestFilePath(package_name, activity_name, version);
-
- std::string file_path = output_file.FilePath();
-
- if (!params.recompile) {
- if (std::filesystem::exists(file_path)) {
- LOG(DEBUG) << "compiled trace exists in " << file_path;
-
- db::VersionedComponentName vcn{package_name, activity_name, version};
- std::optional<db::PrefetchFileModel> prefetch_file =
- db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
- if (prefetch_file) {
- return true;
- } else {
- LOG(WARNING) << "Missing corresponding prefetch_file db row for " << vcn;
- // let it go and compile again. we'll insert the prefetch_file at the bottom.
- }
- }
- }
-
- std::optional<db::ActivityModel> activity =
- db::ActivityModel::SelectByNameAndPackageId(db, activity_name.c_str(), package_id);
- if (!activity) {
- LOG(ERROR) << "Cannot find activity for package_id: " << package_id
- <<" activity_name: " <<activity_name;
- return false;
- }
-
- int activity_id = activity->id;
-
- std::vector<db::AppLaunchHistoryModel> histories =
- db::AppLaunchHistoryModel::SelectActivityHistoryForCompile(db, activity_id);
-
- {
- std::vector<compiler::CompilationInput> perfetto_traces =
- GetPerfettoTraceInfo(db, histories);
-
- if (perfetto_traces.size() < params.min_traces) {
- LOG(DEBUG) << "The number of perfetto traces is " << perfetto_traces.size()
- <<", which is less than " << params.min_traces;
- return false;
- }
-
- {
- std::lock_guard<std::mutex> last_job_info_guard{last_job_info_mutex_};
- last_job_info_.activities_last_compiled_++;
- }
-
- // Show the compilation config.
- LOG(DEBUG) << "Try to compiled package_id: " << package_id
- << " package_name: " << package_name
- << " activity_name: " << activity_name
- << " version: " << version
- << " file_path: " << file_path
- << " verbose: " << params.verbose
- << " perfetto_traces: "
- << VectorPrinter<compiler::CompilationInput>{perfetto_traces};
- if (params.inode_textcache) {
- LOG(DEBUG) << "inode_textcache: " << *params.inode_textcache;
- }
-
- CompilerForkParameters compiler_params{perfetto_traces, file_path, params};
-
- if (!output_file.MkdirWithParents()) {
- LOG(ERROR) << "Compile activity failed. Failed to mkdirs " << file_path;
- return false;
- }
-
- ScopedFormatTrace atrace_compile_fork(ATRACE_TAG_PACKAGE_MANAGER,
- "Fork+exec iorap.cmd.compiler",
- activity_name.c_str());
- if (!StartViaFork(compiler_params)) {
- LOG(ERROR) << "Compilation failed for package_id:" << package_id
- << " activity_name: " << activity_name;
- return false;
- }
- }
-
- std::optional<db::PrefetchFileModel> compiled_trace =
- db::PrefetchFileModel::Insert(db, activity_id, file_path);
- if (!compiled_trace) {
- LOG(ERROR) << "Cannot insert compiled trace activity_id: " << activity_id
- << " file_path: " << file_path;
- return false;
- }
- return true;
-}
-
-// Compiled the perfetto traces for activities in an package.
-bool CompilePackage(const db::DbHandle& db,
- const std::string& package_name,
- int version,
- const ControllerParameters& params) {
- ScopedFormatTrace atrace_compile_package(ATRACE_TAG_PACKAGE_MANAGER,
- "Compile package %s",
- package_name.c_str());
-
- std::optional<db::PackageModel> package =
- db::PackageModel::SelectByNameAndVersion(db, package_name.c_str(), version);
-
- if (!package) {
- LOG(ERROR) << "Cannot find package for package_name: "
- << package_name
- << " and version "
- << version;
- return false;
- }
-
- std::vector<db::ActivityModel> activities =
- db::ActivityModel::SelectByPackageId(db, package->id);
-
- bool ret = true;
- for (db::ActivityModel activity : activities) {
- if (!CompileActivity(db, package->id, package->name, activity.name, version, params)) {
- ret = false;
- }
- }
- return ret;
-}
-
-// Compiled the perfetto traces for packages in a device.
-bool CompileAppsOnDevice(const db::DbHandle& db, const ControllerParameters& params) {
- {
- std::lock_guard<std::mutex> last_job_info_guard{last_job_info_mutex_};
- last_job_info_.activities_last_compiled_ = 0;
- }
-
- std::vector<db::PackageModel> packages = db::PackageModel::SelectAll(db);
- bool ret = true;
- for (db::PackageModel package : packages) {
- if (!CompilePackage(db, package.name, package.version, params)) {
- ret = false;
- }
- }
-
- {
- std::lock_guard<std::mutex> last_job_info_guard{last_job_info_mutex_};
- last_job_info_.last_run_ns_ = time(nullptr);
- }
-
- return ret;
-}
-
-// Compiled the perfetto traces for a single package in a device.
-bool CompileSingleAppOnDevice(const db::DbHandle& db,
- const ControllerParameters& params,
- const std::string& package_name) {
- std::vector<db::PackageModel> packages = db::PackageModel::SelectByName(db, package_name.c_str());
- bool ret = true;
- for (db::PackageModel package : packages) {
- if (!CompilePackage(db, package.name, package.version, params)) {
- ret = false;
- }
- }
-
- return ret;
-}
-
-bool Compile(const std::string& db_path, const ControllerParameters& params) {
- iorap::db::SchemaModel db_schema = db::SchemaModel::GetOrCreate(db_path);
- db::DbHandle db{db_schema.db()};
- return CompileAppsOnDevice(db, params);
-}
-
-bool Compile(const std::string& db_path,
- const std::string& package_name,
- int version,
- const ControllerParameters& params) {
- iorap::db::SchemaModel db_schema = db::SchemaModel::GetOrCreate(db_path);
- db::DbHandle db{db_schema.db()};
- return CompilePackage(db, package_name, version, params);
-}
-
-bool Compile(const std::string& db_path,
- const std::string& package_name,
- const std::string& activity_name,
- int version,
- const ControllerParameters& params) {
- iorap::db::SchemaModel db_schema = db::SchemaModel::GetOrCreate(db_path);
- db::DbHandle db{db_schema.db()};
-
- std::optional<db::PackageModel> package =
- db::PackageModel::SelectByNameAndVersion(db, package_name.c_str(), version);
-
- if (!package) {
- LOG(ERROR) << "Cannot find package with name "
- << package_name
- << " and version "
- << version;
- return false;
- }
- return CompileActivity(db, package->id, package_name, activity_name, version, params);
-}
-
-static std::string TimeToString(time_t the_time) {
- tm tm_buf{};
- tm* tm_ptr = localtime_r(&the_time, &tm_buf);
-
- if (tm_ptr != nullptr) {
- char time_buffer[256];
- strftime(time_buffer, sizeof(time_buffer), "%a %b %d %H:%M:%S %Y", tm_ptr);
- return std::string{time_buffer};
- } else {
- return std::string{"(nullptr)"};
- }
-}
-
-static std::string GetTimestampForPrefetchFile(const db::PrefetchFileModel& prefetch_file) {
- std::filesystem::path path{prefetch_file.file_path};
-
- std::error_code ec{};
- auto last_write_time = std::filesystem::last_write_time(path, /*out*/ec);
- if (ec) {
- return std::string("Failed to get last write time: ") + ec.message();
- }
-
- time_t time = decltype(last_write_time)::clock::to_time_t(last_write_time);
-
- std::string time_str = TimeToString(time);
- return time_str;
-}
-
-void DumpPackageActivity(const db::DbHandle& db,
- ::android::Printer& printer,
- const db::PackageModel& package,
- const db::ActivityModel& activity) {
- int package_id = package.id;
- const std::string& package_name = package.name;
- int package_version = package.version;
- const std::string& activity_name = activity.name;
- db::VersionedComponentName vcn{package_name, activity_name, package_version};
-
- // com.google.Settings/com.google.Settings.ActivityMain@1234567890
- printer.printFormatLine(" %s/%s@%d",
- package_name.c_str(),
- activity_name.c_str(),
- package_version);
-
- std::optional<db::PrefetchFileModel> prefetch_file =
- db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
-
- std::vector<db::AppLaunchHistoryModel> histories =
- db::AppLaunchHistoryModel::SelectActivityHistoryForCompile(db, activity.id);
- std::vector<compiler::CompilationInput> perfetto_traces =
- GetPerfettoTraceInfo(db, histories);
-
- if (prefetch_file) {
- bool exists_on_disk = std::filesystem::exists(prefetch_file->file_path);
-
- std::optional<size_t> prefetch_byte_sum =
- prefetcher::ReadAhead::PrefetchSizeInBytes(prefetch_file->file_path);
-
- if (exists_on_disk) {
- printer.printFormatLine(" Compiled Status: Usable compiled trace");
- } else {
- printer.printFormatLine(" Compiled Status: Prefetch file deleted from disk.");
- }
-
- if (prefetch_byte_sum) {
- printer.printFormatLine(" Bytes to be prefetched: %zu", *prefetch_byte_sum);
- } else {
- printer.printFormatLine(" Bytes to be prefetched: (bad file path)" );
- }
-
- printer.printFormatLine(" Time compiled: %s",
- GetTimestampForPrefetchFile(*prefetch_file).c_str());
- printer.printFormatLine(" %s", prefetch_file->file_path.c_str());
- } else {
- size_t size = perfetto_traces.size();
-
- if (size >= kMinTracesForCompilation) {
- printer.printFormatLine(" Compiled Status: Raw traces pending compilation (%zu)",
- perfetto_traces.size());
- } else {
- size_t remaining = kMinTracesForCompilation - size;
- printer.printFormatLine(" Compiled Status: Need %zu more traces for compilation",
- remaining);
- }
- }
-
- printer.printFormatLine(" Raw traces:");
- printer.printFormatLine(" Trace count: %zu", perfetto_traces.size());
-
- for (compiler::CompilationInput& compilation_input : perfetto_traces) {
- std::string& raw_trace_file_name = compilation_input.filename;
-
- printer.printFormatLine(" %s", raw_trace_file_name.c_str());
- }
-}
-
-void DumpPackage(const db::DbHandle& db,
- ::android::Printer& printer,
- db::PackageModel package) {
- std::vector<db::ActivityModel> activities =
- db::ActivityModel::SelectByPackageId(db, package.id);
-
- for (db::ActivityModel& activity : activities) {
- DumpPackageActivity(db, printer, package, activity);
- }
-}
-
-void DumpAllPackages(const db::DbHandle& db, ::android::Printer& printer) {
- printer.printLine("Package history in database:");
-
- std::vector<db::PackageModel> packages = db::PackageModel::SelectAll(db);
- for (db::PackageModel package : packages) {
- DumpPackage(db, printer, package);
- }
-
- printer.printLine("");
-}
-
-void Dump(const db::DbHandle& db, ::android::Printer& printer) {
- bool locked = last_job_info_mutex_.try_lock();
-
- LastJobInfo info = last_job_info_;
-
- printer.printFormatLine("Background job:");
- if (!locked) {
- printer.printLine(""""" (possible deadlock)");
- }
- if (info.last_run_ns_ != time_t{0}) {
- std::string time_str = TimeToString(info.last_run_ns_);
-
- printer.printFormatLine(" Last run at: %s", time_str.c_str());
- } else {
- printer.printFormatLine(" Last run at: (None)");
- }
- printer.printFormatLine(" Activities last compiled: %zu", info.activities_last_compiled_);
-
- printer.printLine("");
-
- if (locked) {
- last_job_info_mutex_.unlock();
- }
-
- DumpAllPackages(db, printer);
-}
-
-} // namespace iorap::maintenance