diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 262b17933..6c739fc19 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -229,6 +229,7 @@ cc_binary { "liblog", "liblp", "libprotobuf-cpp-lite", + "libstatslog", "libutils", // TODO(b/148818798): remove when parent bug is fixed. diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp index e35ad4b2d..34d3d69d1 100644 --- a/fs_mgr/libsnapshot/snapshotctl.cpp +++ b/fs_mgr/libsnapshot/snapshotctl.cpp @@ -24,9 +24,11 @@ #include #include #include +#include #include -#include "utility.h" +#include +#include "snapshot_stats.h" #include "utility.h" using namespace std::string_literals; @@ -37,17 +39,29 @@ int Usage() { "Actions:\n" " dump\n" " Print snapshot states.\n" - " merge [--logcat] [--log-to-file]\n" + " merge [--logcat] [--log-to-file] [--report] [--dry-run]\n" " Initialize merge and wait for it to be completed.\n" " If --logcat is specified, log to logcat.\n" " If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n" - " If both specified, log to both. If none specified, log to stdout.\n"; + " If both specified, log to both. If none specified, log to stdout.\n" + " If --report is specified, send merge statistics to statsd.\n" + " If --dry-run flag, no real merge operation is is triggered, and\n" + " sample statistics are sent to statsd for testing purpose.\n"; return EX_USAGE; } namespace android { namespace snapshot { +static SnapshotMergeReport GetDummySnapshotMergeReport() { + SnapshotMergeReport fake_report; + + fake_report.set_state(UpdateState::MergeCompleted); + fake_report.set_resume_count(56); + + return fake_report; +} + bool DumpCmdHandler(int /*argc*/, char** argv) { android::base::InitLogging(argv, &android::base::StderrLogger); return SnapshotManager::New()->Dump(std::cout); @@ -113,24 +127,53 @@ class MergeCmdLogger { }; bool MergeCmdHandler(int argc, char** argv) { - auto begin = std::chrono::steady_clock::now(); + std::chrono::milliseconds passed_ms; + + bool report_to_statsd = false; + bool dry_run = false; + for (int i = 2; i < argc; ++i) { + if (argv[i] == "--report"s) { + report_to_statsd = true; + } else if (argv[i] == "--dry-run"s) { + dry_run = true; + } + } // 'snapshotctl merge' is stripped away from arguments to // Logger. android::base::InitLogging(argv); android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2)); - auto state = SnapshotManager::New()->InitiateMergeAndWait(); + UpdateState state; + SnapshotMergeReport merge_report; + if (dry_run) { + merge_report = GetDummySnapshotMergeReport(); + state = merge_report.state(); + passed_ms = std::chrono::milliseconds(1234); + } else { + auto begin = std::chrono::steady_clock::now(); + + state = SnapshotManager::New()->InitiateMergeAndWait(&merge_report); + + // We could wind up in the Unverified state if the device rolled back or + // hasn't fully rebooted. Ignore this. + if (state == UpdateState::None || state == UpdateState::Unverified) { + return true; + } - // We could wind up in the Unverified state if the device rolled back or - // hasn't fully rebooted. Ignore this. - if (state == UpdateState::None || state == UpdateState::Unverified) { - return true; - } - if (state == UpdateState::MergeCompleted) { auto end = std::chrono::steady_clock::now(); - auto passed = std::chrono::duration_cast(end - begin).count(); - LOG(INFO) << "Snapshot merged in " << passed << " ms."; + passed_ms = std::chrono::duration_cast(end - begin); + } + + if (report_to_statsd) { + android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED, + static_cast(merge_report.state()), + static_cast(passed_ms.count()), + static_cast(merge_report.resume_count())); + } + + if (state == UpdateState::MergeCompleted) { + LOG(INFO) << "Snapshot merged in " << passed_ms.count() << " ms."; return true; }