odrefresh: Add support for uploading stats
Adds support for loading odrefresh metrics saved to file and uploading
them when system_server starts.
Bug: 169925964
Test: manual module update with instrumentation showing statsd got data
(cherry picked from commit 55ca8ab5216e4c20638f34b44c6bc53866b991ce)
Merged-In: Iccc6ede1583235d09dbfd42996eb4ba118b700f8
Change-Id: Iad588f05622864b37d40333d081bd89fbf76051c
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index 8a9acd3..e504064 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -134,6 +134,34 @@
],
}
+cc_library_static {
+ name: "libodrstatslog",
+ defaults: ["art_defaults"],
+ host_supported: true,
+ export_include_dirs: ["include"],
+
+ local_include_dirs: ["include"],
+ shared_libs: ["libartbase"],
+ target: {
+ android: {
+ generated_headers: ["statslog_odrefresh.h"],
+ generated_sources: ["statslog_odrefresh.cpp"],
+ srcs: [
+ "odr_metrics_record.cc",
+ "odr_statslog_android.cc",
+ ],
+ shared_libs: ["libstatssocket"],
+ },
+ host: {
+ srcs: ["odr_statslog_host.cc"],
+ },
+ },
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
art_cc_test {
name: "art_odrefresh_tests",
defaults: [
@@ -154,6 +182,24 @@
shared_libs: ["libbase"],
}
+genrule {
+ name: "statslog_odrefresh.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_odrefresh.h --module art --namespace art,metrics,statsd",
+ out: [
+ "statslog_odrefresh.h",
+ ],
+}
+
+genrule {
+ name: "statslog_odrefresh.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_odrefresh.cpp --module art --namespace art,metrics,statsd --importHeader statslog_odrefresh.h",
+ out: [
+ "statslog_odrefresh.cpp",
+ ],
+}
+
xsd_config {
name: "art-apex-cache-info",
srcs: ["CacheInfo.xsd"],
diff --git a/odrefresh/include/odr_statslog/odr_statslog.h b/odrefresh/include/odr_statslog/odr_statslog.h
new file mode 100644
index 0000000..0a27475
--- /dev/null
+++ b/odrefresh/include/odr_statslog/odr_statslog.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ART_ODREFRESH_INCLUDE_ODR_STATSLOG_ODR_STATSLOG_H_
+#define ART_ODREFRESH_INCLUDE_ODR_STATSLOG_ODR_STATSLOG_H_
+
+#include <iosfwd> // For forward-declaration of std::string.
+namespace art {
+namespace odrefresh {
+
+// Upload the metrics (if any) generated by odrefresh by passing the data `statsd` process.
+//
+// Metrics from odrefresh a persisted to the path specified by `OdrefreshMetricsFile`. This method
+// reads the saved metrics, passes them to statsd, then removes the file to avoid uploading them
+// in future.
+//
+// Returns true on success. On failure `error_msg` summarizes the failure and this method returns
+// false.
+bool UploadStatsIfAvailable(/*out*/std::string* error_msg);
+
+} // namespace odrefresh
+} // namespace art
+
+#endif // ART_ODREFRESH_INCLUDE_ODR_STATSLOG_ODR_STATSLOG_H_
diff --git a/odrefresh/odr_statslog_android.cc b/odrefresh/odr_statslog_android.cc
new file mode 100644
index 0000000..7db348e
--- /dev/null
+++ b/odrefresh/odr_statslog_android.cc
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 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 "odr_statslog/odr_statslog.h"
+
+#include <cstdint>
+#include <fstream>
+#include <istream>
+#include <string>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "odr_metrics.h"
+#include "odr_metrics_record.h"
+#include "statslog_odrefresh.h"
+
+namespace art {
+namespace odrefresh {
+
+using android::base::StringPrintf;
+
+namespace {
+
+// Convert bare value from art::metrics::Stage to value defined in atoms.proto.
+int32_t TranslateStage(int32_t art_metrics_stage) {
+ switch (static_cast<OdrMetrics::Stage>(art_metrics_stage)) {
+ case OdrMetrics::Stage::kUnknown:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_UNKNOWN;
+ case OdrMetrics::Stage::kCheck:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_CHECK;
+ case OdrMetrics::Stage::kPreparation:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_PREPARATION;
+ case OdrMetrics::Stage::kPrimaryBootClasspath:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_PRIMARY_BOOT_CLASSPATH;
+ case OdrMetrics::Stage::kSecondaryBootClasspath:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_SECONDARY_BOOT_CLASSPATH;
+ case OdrMetrics::Stage::kSystemServerClasspath:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_SYSTEM_SERVER_CLASSPATH;
+ case OdrMetrics::Stage::kComplete:
+ return metrics::statsd::ODREFRESH_REPORTED__STAGE_REACHED__STAGE_COMPLETE;
+ }
+
+ LOG(ERROR) << "Unknown stage value: " << art_metrics_stage;
+ return -1;
+}
+
+// Convert bare value from art::metrics::Status to value defined in atoms.proto.
+int32_t TranslateStatus(int32_t art_metrics_status) {
+ switch (static_cast<OdrMetrics::Status>(art_metrics_status)) {
+ case OdrMetrics::Status::kUnknown:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_UNKNOWN;
+ case OdrMetrics::Status::kOK:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_OK;
+ case OdrMetrics::Status::kNoSpace:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_NO_SPACE;
+ case OdrMetrics::Status::kIoError:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_IO_ERROR;
+ case OdrMetrics::Status::kDex2OatError:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_DEX2OAT_ERROR;
+ case OdrMetrics::Status::kTimeLimitExceeded:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_TIME_LIMIT_EXCEEDED;
+ case OdrMetrics::Status::kStagingFailed:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_STAGING_FAILED;
+ case OdrMetrics::Status::kInstallFailed:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_INSTALL_FAILED;
+ }
+
+ LOG(ERROR) << "Unknown status value: " << art_metrics_status;
+ return -1;
+}
+
+// Convert bare value from art::metrics::Trigger to value defined in atoms.proto.
+int32_t TranslateTrigger(int32_t art_metrics_trigger) {
+ switch (static_cast<OdrMetrics::Trigger>(art_metrics_trigger)) {
+ case OdrMetrics::Trigger::kUnknown:
+ return metrics::statsd::ODREFRESH_REPORTED__TRIGGER__TRIGGER_UNKNOWN;
+ case OdrMetrics::Trigger::kApexVersionMismatch:
+ return metrics::statsd::ODREFRESH_REPORTED__TRIGGER__TRIGGER_APEX_VERSION_MISMATCH;
+ case OdrMetrics::Trigger::kDexFilesChanged:
+ return metrics::statsd::ODREFRESH_REPORTED__TRIGGER__TRIGGER_DEX_FILES_CHANGED;
+ case OdrMetrics::Trigger::kMissingArtifacts:
+ return metrics::statsd::ODREFRESH_REPORTED__TRIGGER__TRIGGER_MISSING_ARTIFACTS;
+ }
+
+ LOG(ERROR) << "Unknown trigger value: " << art_metrics_trigger;
+ return -1;
+}
+
+bool ReadValues(const char* metrics_file,
+ /*out*/ OdrMetricsRecord* record,
+ /*out*/ std::string* error_msg) {
+ std::ifstream ifs(metrics_file);
+ if (!ifs) {
+ *error_msg = android::base::StringPrintf(
+ "metrics file '%s' could not be opened: %s", metrics_file, strerror(errno));
+ return false;
+ }
+
+ ifs >> *record;
+ if (!ifs) {
+ *error_msg = "file parsing error.";
+ return false;
+ }
+
+ //
+ // Convert values defined as enums to their statsd values.
+ //
+
+ record->trigger = TranslateTrigger(record->trigger);
+ if (record->trigger < 0) {
+ *error_msg = "failed to parse trigger.";
+ return false;
+ }
+
+ record->stage_reached = TranslateStage(record->stage_reached);
+ if (record->stage_reached < 0) {
+ *error_msg = "failed to parse stage_reached.";
+ return false;
+ }
+
+ record->status = TranslateStatus(record->status);
+ if (record->status < 0) {
+ *error_msg = "failed to parse status.";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool UploadStatsIfAvailable(/*out*/std::string* error_msg) {
+ OdrMetricsRecord record;
+ if (!ReadValues(kOdrefreshMetricsFile, &record, error_msg)) {
+ return false;
+ }
+
+ // Write values to statsd. The order of values passed is the same as the order of the
+ // fields in OdrMetricsRecord.
+ int bytes_written = art::metrics::statsd::stats_write(metrics::statsd::ODREFRESH_REPORTED,
+ record.art_apex_version,
+ record.trigger,
+ record.stage_reached,
+ record.status,
+ record.primary_bcp_compilation_seconds,
+ record.secondary_bcp_compilation_seconds,
+ record.system_server_compilation_seconds,
+ record.cache_space_free_start_mib,
+ record.cache_space_free_end_mib);
+ if (bytes_written <= 0) {
+ *error_msg = android::base::StringPrintf("stats_write returned %d", bytes_written);
+ return false;
+ }
+
+ if (unlink(kOdrefreshMetricsFile) != 0) {
+ *error_msg = StringPrintf("failed to unlink '%s': %s", kOdrefreshMetricsFile, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace odrefresh
+} // namespace art
diff --git a/odrefresh/odr_statslog_host.cc b/odrefresh/odr_statslog_host.cc
new file mode 100644
index 0000000..118563a
--- /dev/null
+++ b/odrefresh/odr_statslog_host.cc
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 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 "odr_statslog/odr_statslog.h" // for declararation of UploadStatsIfAvailable().
+
+#include <iosfwd> // for forward declaration of std::string.
+namespace art {
+namespace odrefresh {
+
+bool UploadStatsIfAvailable(/*out*/std::string* /*error_msg*/) {
+ // No stats reported from host, report success.
+ return true;
+}
+
+} // namespace odrefresh
+} // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index dd4be7c..f66faf4 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -440,6 +440,7 @@
"libsigchain",
"libunwindstack",
],
+ static_libs: ["libodrstatslog"],
export_include_dirs: ["."],
// ART's macros.h depends on libbase's macros.h.
// Note: runtime_options.h depends on cmdline. But we don't really want to export this
@@ -458,6 +459,7 @@
"liblzma", // libelffile dependency; must be repeated here since it's a static lib.
"libnativebridge",
"libnativeloader",
+ "libodrstatslog",
"libsigchain_fake",
"libunwindstack",
"libz",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c950aaa..a4e5550 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -150,6 +150,7 @@
#include "oat_file_manager.h"
#include "oat_quick_method_header.h"
#include "object_callbacks.h"
+#include "odr_statslog/odr_statslog.h"
#include "parsed_options.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
@@ -1111,6 +1112,16 @@
LOG(WARNING) << "Failed to load perfetto_javaheapprof: " << err;
}
}
+ if (Runtime::Current()->IsSystemServer()) {
+ std::string err;
+ ScopedTrace tr("odrefresh stats logging");
+ ScopedThreadSuspension sts(Thread::Current(), ThreadState::kNative);
+ // Report stats if available. This should be moved into ART Services when they are ready.
+ if (!odrefresh::UploadStatsIfAvailable(&err)) {
+ LOG(WARNING) << "Failed to upload odrefresh metrics: " << err;
+ }
+ }
+
if (LIKELY(automatically_set_jni_ids_indirection_) && CanSetJniIdType()) {
if (IsJavaDebuggable()) {
SetJniIdType(JniIdType::kIndices);