Write odsign metrics to a file.
Bug: 257028435
Test: adb shell cmd stats print-stats | grep 548
Change-Id: I95ddf86978a3ca6e3884fd982c459f5cc2d440e4
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index d73f8fe..f56cfab 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -101,6 +101,15 @@
recovery_available: true,
}
+genrule {
+ name: "statslog_odsign.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_odsign.h --module art --namespace art,metrics,statsd",
+ out: [
+ "statslog_odsign.h",
+ ],
+}
+
cc_binary {
name: "odsign",
defaults: [
@@ -114,6 +123,7 @@
"odsign_main.cpp",
"StatsReporter.cpp",
],
+ generated_headers: ["statslog_odsign.h"],
header_libs: ["odrefresh_headers"],
diff --git a/ondevice-signing/StatsReporter.cpp b/ondevice-signing/StatsReporter.cpp
index 65e645a..e4e4a03 100644
--- a/ondevice-signing/StatsReporter.cpp
+++ b/ondevice-signing/StatsReporter.cpp
@@ -20,12 +20,13 @@
#include <string>
#include <sys/stat.h>
-// Keep these constant in sync with COMPOS_METRIC_NAME & METRICS_FILE in OdsignStatsLogger.java.
+// Keep these constants in sync with those in OdsignStatsLogger.java.
constexpr const char* kOdsignMetricsFile = "/data/misc/odsign/metrics/odsign-metrics.txt";
constexpr const char* kComposMetricName = "comp_os_artifacts_check_record";
+constexpr const char* kOdsignMetricName = "odsign_record";
StatsReporter::~StatsReporter() {
- if (comp_os_artifacts_check_record_ == nullptr) {
+ if (comp_os_artifacts_check_record_ == nullptr && !odsign_record_enabled_) {
LOG(INFO) << "Metrics report is empty";
// Remove the metrics file if any old version of the file already exists
@@ -42,24 +43,31 @@
PLOG(ERROR) << "Could not open file: " << kOdsignMetricsFile;
return;
}
-
- odsign_metrics_file_ << kComposMetricName << ' ';
- odsign_metrics_file_ << comp_os_artifacts_check_record_->current_artifacts_ok << ' ';
- odsign_metrics_file_ << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
- << ' ';
- odsign_metrics_file_ << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
- << '\n';
if (chmod(kOdsignMetricsFile, 0644) != 0) {
PLOG(ERROR) << "Could not set correct file permissions for " << kOdsignMetricsFile;
return;
}
+
+ if (comp_os_artifacts_check_record_ != nullptr) {
+ odsign_metrics_file_ << kComposMetricName << ' '
+ << comp_os_artifacts_check_record_->current_artifacts_ok << ' '
+ << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
+ << ' '
+ << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
+ << '\n';
+ }
+
+ if (odsign_record_enabled_) {
+ odsign_metrics_file_ << kOdsignMetricName << ' ' << odsign_record_.status << '\n';
+ }
+
odsign_metrics_file_.close();
if (!odsign_metrics_file_) {
PLOG(ERROR) << "Failed to close the file";
}
}
-StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetComposArtifactsCheckRecord() {
+StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetOrCreateComposArtifactsCheckRecord() {
if (comp_os_artifacts_check_record_ == nullptr) {
comp_os_artifacts_check_record_ = std::make_unique<CompOsArtifactsCheckRecord>();
}
diff --git a/ondevice-signing/StatsReporter.h b/ondevice-signing/StatsReporter.h
index 2682b96..add7a11 100644
--- a/ondevice-signing/StatsReporter.h
+++ b/ondevice-signing/StatsReporter.h
@@ -18,27 +18,44 @@
#include <fstream>
+#include "statslog_odsign.h"
+
// Class to store CompOsArtifactsCheck related metrics.
// These are flushed to a file kOdsignMetricsFile and consumed by
// System Server (in class OdsignStatsLogger) & sent to statsd.
class StatsReporter {
public:
- // Keep sync with EarlyBootCompOsArtifactsCheckReported
- // definition in proto_logging/stats/atoms.proto.
+ // Keep in sync with the EarlyBootCompOsArtifactsCheckReported definition in
+ // proto_logging/stats/atoms.proto.
struct CompOsArtifactsCheckRecord {
bool current_artifacts_ok = false;
bool comp_os_pending_artifacts_exists = false;
bool use_comp_os_generated_artifacts = false;
};
+ // Keep in sync with the OdsignReported definition in proto_logging/stats/atoms.proto.
+ struct OdsignRecord {
+ int32_t status = art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_UNSPECIFIED;
+ };
+
// The report is flushed (from buffer) into a file by the destructor.
~StatsReporter();
- // Get pointer to comp_os_artifacts_check_record, caller can then modify it.
- // Note: pointer remains valid for the lifetime of this StatsReporter.
- CompOsArtifactsCheckRecord* GetComposArtifactsCheckRecord();
+ // Returns a mutable CompOS record. The pointer remains valid for the lifetime of this
+ // StatsReporter. If this function is not called, no CompOS record will be logged.
+ CompOsArtifactsCheckRecord* GetOrCreateComposArtifactsCheckRecord();
+
+ // Returns a mutable odsign record. The pointer remains valid for the lifetime of this
+ // StatsReporter.
+ OdsignRecord* GetOdsignRecord() { return &odsign_record_; }
+
+ // Enables/disables odsign metrics.
+ void SetOdsignRecordEnabled(bool value) { odsign_record_enabled_ = value; }
private:
// Temporary buffer which stores the metrics.
std::unique_ptr<CompOsArtifactsCheckRecord> comp_os_artifacts_check_record_;
+
+ OdsignRecord odsign_record_;
+ bool odsign_record_enabled_ = true;
};
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index c45e308..93ec3e4 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -35,6 +35,7 @@
#include "KeystoreKey.h"
#include "StatsReporter.h"
#include "VerityUtils.h"
+#include "statslog_odsign.h"
#include "odsign_info.pb.h"
@@ -370,7 +371,7 @@
bool* digests_verified,
StatsReporter* stats_reporter) {
StatsReporter::CompOsArtifactsCheckRecord* compos_check_record =
- stats_reporter->GetComposArtifactsCheckRecord();
+ stats_reporter->GetOrCreateComposArtifactsCheckRecord();
if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
// No pending CompOS artifacts, all that matters is the current ones.
@@ -468,12 +469,9 @@
} // namespace
int main(int /* argc */, char** argv) {
- // stats_reporter is a pointer so that we can explicitly delete it
- // instead of waiting for the program to die & its destrcutor be called
- auto stats_reporter = std::make_unique<StatsReporter>();
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
- auto errorScopeGuard = []() {
+ auto scope_guard = android::base::make_scope_guard([]() {
// In case we hit any error, remove the artifacts and tell Zygote not to use
// anything
removeDirectory(kArtArtifactsDir);
@@ -485,17 +483,24 @@
SetProperty(kOdsignVerificationDoneProp, "1");
// Tell init it shouldn't try to restart us - see odsign.rc
SetProperty(kStopServiceProp, "odsign");
- };
- auto scope_guard = android::base::make_scope_guard(errorScopeGuard);
+ });
+
+ // `stats_reporter` must come after `scope_guard` so that its destructor is called before
+ // `scope_guard`.
+ auto stats_reporter = std::make_unique<StatsReporter>();
+ StatsReporter::OdsignRecord* odsign_record = stats_reporter->GetOdsignRecord();
if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
+ stats_reporter->SetOdsignRecordEnabled(false);
return 0;
}
auto keystoreResult =
KeystoreKey::getInstance(kPublicKeySignature, kKeyAlias, kKeyNspace, kKeyBootLevel);
if (!keystoreResult.ok()) {
LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error();
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_KEYSTORE_FAILED;
return -1;
}
SigningKey* key = keystoreResult.value();
@@ -517,6 +522,8 @@
if (!new_cert.ok()) {
LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error();
// TODO apparently the key become invalid - delete the blob / cert
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CERT_FAILED;
return -1;
}
} else {
@@ -526,6 +533,8 @@
if (!cert_add_result.ok()) {
LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
<< cert_add_result.error();
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CERT_FAILED;
return -1;
}
}
@@ -535,12 +544,6 @@
useCompOs ? CheckCompOsPendingArtifacts(*key, &digests_verified, stats_reporter.get())
: checkArtifacts();
- // Explicitly reset the pointer - We rely on stats_reporter's
- // destructor for actually writing the buffered metrics. This will otherwise not be called
- // if the program doesn't exit normally (for ex, killed by init, which actually happens
- // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
- stats_reporter.reset();
-
// The artifacts dir doesn't necessarily need to exist; if the existing
// artifacts on the system partition are valid, those can be used.
int err = access(kArtArtifactsDir.c_str(), F_OK);
@@ -578,6 +581,8 @@
// instead prevent Zygote from using them (which is taken care of
// in the exit handler).
LOG(ERROR) << "Failed to remove unknown artifacts.";
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CLEANUP_FAILED;
return -1;
}
}
@@ -591,11 +596,16 @@
if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
// No new artifacts generated, and we verified existing ones above, nothing left to do.
LOG(INFO) << "odrefresh said artifacts are VALID";
+ stats_reporter->SetOdsignRecordEnabled(false);
} else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
LOG(INFO) << "odrefresh compiled " << (compiled_all ? "all" : "partial")
<< " artifacts, returned " << odrefresh_status;
+ // This value may be overwritten later.
+ odsign_record->status =
+ compiled_all ? art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ALL_OK
+ : art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_PARTIAL_OK;
Result<std::map<std::string, std::string>> digests;
if (supportsFsVerity) {
digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
@@ -606,24 +616,39 @@
}
if (!digests.ok()) {
LOG(ERROR) << digests.error();
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_SIGNING_FAILED;
return -1;
}
auto persistStatus = persistDigests(*digests, *key);
if (!persistStatus.ok()) {
LOG(ERROR) << persistStatus.error();
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_SIGNING_FAILED;
return -1;
}
} else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
LOG(ERROR) << "odrefresh failed cleaning up existing artifacts";
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ODREFRESH_FAILED;
return -1;
} else {
LOG(ERROR) << "odrefresh exited unexpectedly, returned " << odrefresh_status;
+ odsign_record->status =
+ art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ODREFRESH_FAILED;
return -1;
}
LOG(INFO) << "On-device signing done.";
scope_guard.Disable();
+
+ // Explicitly reset the pointer - We rely on stats_reporter's
+ // destructor for actually writing the buffered metrics. This will otherwise not be called
+ // if the program doesn't exit normally (for ex, killed by init, which actually happens
+ // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
+ stats_reporter.reset();
+
// At this point, we're done with the key for sure
SetProperty(kOdsignKeyDoneProp, "1");
// And we did a successful verification