diff options
author | Calin Juravle <calin@google.com> | 2021-06-25 15:34:09 -0700 |
---|---|---|
committer | Lokesh Gidra <lokeshgidra@google.com> | 2021-07-01 02:11:38 +0000 |
commit | dba72d888fdfa88ce8152d5116f8237c02dab775 (patch) | |
tree | 49d788fe387cd620f6e59441fbe9039d1054a87d | |
parent | c5288040c3450bc4316783ee1e7a02c02c9c53b2 (diff) |
Fix compiler filter / reason reporting and add the ISA to the metrics
The compiler filter / reason reporting was not accurate for a variety
of reasons. (e.g. reporting was only done at startup, it was relying
on imprecise APIs and had errors in the logic).
In order to keep track of the precise optimization status, this CL
introduces the concept of AppInfo, which encapsulates the data about
the application / system server code paths, their optimization status
and possible other metadata (e.g. profiles).
To populate it, we rely on 2 distinct events:
1) The framework calling VMRuntime#registerAppInfo to inform the
runtime about the applications code paths and their types (e.g. primary,
split, secondary).
2) Class loading, when we can determine the actual optimization status
like filters, reasons, and whether or not we can load the odex files.
These events may happen in any order so we could deal with a partial
state at some point in time, but in the majority of cases they always
happen at Class Loading, followed by RegisterAppInfo.
This CL also deletes the OatFileManager#getPrimaryOatFile which was
a misleading API as it didn't work in most cases. It also adds more
tests to the metrics/reporting infra for previous missing or
unimplemented cases.
Test: gtest
Bug: 170149255
Merged-In: If0a7a25d06ff6fb89fe4861139b7dee61c05814d
Change-Id: If0a7a25d06ff6fb89fe4861139b7dee61c05814d
(cherry picked from commit c2753e6beec483b5b14161b6bbc8e0a86aef9397)
-rw-r--r-- | libartbase/base/metrics/metrics.h | 191 | ||||
-rw-r--r-- | libartbase/base/metrics/metrics_common.cc | 9 | ||||
-rw-r--r-- | libartbase/base/metrics/metrics_test.cc | 135 | ||||
-rw-r--r-- | libartbase/base/metrics/metrics_test.h | 2 | ||||
-rw-r--r-- | runtime/Android.bp | 2 | ||||
-rw-r--r-- | runtime/app_info.cc | 133 | ||||
-rw-r--r-- | runtime/app_info.h | 121 | ||||
-rw-r--r-- | runtime/app_info_test.cc | 121 | ||||
-rw-r--r-- | runtime/metrics/reporter.cc | 27 | ||||
-rw-r--r-- | runtime/metrics/reporter.h | 12 | ||||
-rw-r--r-- | runtime/metrics/reporter_test.cc | 46 | ||||
-rw-r--r-- | runtime/metrics/statsd.cc | 78 | ||||
-rw-r--r-- | runtime/native/dalvik_system_VMRuntime.cc | 15 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 30 | ||||
-rw-r--r-- | runtime/oat_file_manager.h | 5 | ||||
-rw-r--r-- | runtime/runtime.cc | 63 | ||||
-rw-r--r-- | runtime/runtime.h | 16 |
17 files changed, 813 insertions, 193 deletions
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h index c9110ee198..2a71c2cefd 100644 --- a/libartbase/base/metrics/metrics.h +++ b/libartbase/base/metrics/metrics.h @@ -29,7 +29,6 @@ #include "android-base/logging.h" #include "base/bit_utils.h" -#include "base/compiler_filter.h" #include "base/time_utils.h" #pragma clang diagnostic push @@ -82,127 +81,96 @@ enum class DatumId { #undef METRIC }; +// Names come from PackageManagerServiceCompilerMapping.java +#define REASON_NAME_LIST(V) \ + V(kError, "error") \ + V(kUnknown, "unknown") \ + V(kFirstBoot, "first-boot") \ + V(kBootAfterOTA, "boot-after-ota") \ + V(kPostBoot, "post-boot") \ + V(kInstall, "install") \ + V(kInstallFast, "install-fast") \ + V(kInstallBulk, "install-bulk") \ + V(kInstallBulkSecondary, "install-bulk-secondary") \ + V(kInstallBulkDowngraded, "install-bulk-downgraded") \ + V(kInstallBulkSecondaryDowngraded, "install-bulk-secondary-downgraded") \ + V(kBgDexopt, "bg-dexopt") \ + V(kABOTA, "ab-ota") \ + V(kInactive, "inactive") \ + V(kShared, "shared") \ + V(kInstallWithDexMetadata, "install-with-dex-metadata") \ + V(kPrebuilt, "prebuilt") \ + V(kCmdLine, "cmdline") + // We log compilation reasons as part of the metadata we report. Since elsewhere compilation reasons // are specified as a string, we define them as an enum here which indicates the reasons that we // support. enum class CompilationReason { - kError, - kUnknown, - kFirstBoot, - kBootAfterOTA, - kPostBoot, - kInstall, - kInstallFast, - kInstallBulk, - kInstallBulkSecondary, - kInstallBulkDowngraded, - kInstallBulkSecondaryDowngraded, - kBgDexopt, - kABOTA, - kInactive, - kShared, - kInstallWithDexMetadata, - kPrebuilt, - kCmdLine +#define REASON(kind, name) kind, + REASON_NAME_LIST(REASON) +#undef REASON }; +#define REASON_NAME(kind, kind_name) \ + case CompilationReason::kind: return kind_name; +#define REASON_FROM_NAME(kind, kind_name) \ + if (name == kind_name) { return CompilationReason::kind; } + constexpr const char* CompilationReasonName(CompilationReason reason) { switch (reason) { - case CompilationReason::kError: - return "error"; - case CompilationReason::kUnknown: - return "unknown"; - case CompilationReason::kFirstBoot: - return "first-boot"; - case CompilationReason::kBootAfterOTA: - return "boot-after-ota"; - case CompilationReason::kPostBoot: - return "post-boot"; - case CompilationReason::kInstall: - return "install"; - case CompilationReason::kInstallFast: - return "install-fast"; - case CompilationReason::kInstallBulk: - return "install-bulk"; - case CompilationReason::kInstallBulkSecondary: - return "install-bulk-secondary"; - case CompilationReason::kInstallBulkDowngraded: - return "install-bulk-downgraded"; - case CompilationReason::kInstallBulkSecondaryDowngraded: - return "install-bulk-secondary-downgraded"; - case CompilationReason::kBgDexopt: - return "bg-dexopt"; - case CompilationReason::kABOTA: - return "ab-ota"; - case CompilationReason::kInactive: - return "inactive"; - case CompilationReason::kShared: - return "shared"; - case CompilationReason::kInstallWithDexMetadata: - return "install-with-dex-metadata"; - case CompilationReason::kPrebuilt: - return "prebuilt"; - case CompilationReason::kCmdLine: - return "cmdline"; + REASON_NAME_LIST(REASON_NAME) } } constexpr CompilationReason CompilationReasonFromName(std::string_view name) { - // Names come from PackageManagerServiceCompilerMapping.java - if (name == "unknown") { - return CompilationReason::kUnknown; - } - if (name == "first-boot") { - return CompilationReason::kFirstBoot; - } - if (name == "boot-after-ota") { - return CompilationReason::kBootAfterOTA; - } - if (name == "post-boot") { - return CompilationReason::kPostBoot; - } - if (name == "install") { - return CompilationReason::kInstall; - } - if (name == "install-fast") { - return CompilationReason::kInstallFast; - } - if (name == "install-bulk") { - return CompilationReason::kInstallBulk; - } - if (name == "install-bulk-secondary") { - return CompilationReason::kInstallBulkSecondary; - } - if (name == "install-bulk-downgraded") { - return CompilationReason::kInstallBulkDowngraded; - } - if (name == "install-bulk-secondary-downgraded") { - return CompilationReason::kInstallBulkSecondaryDowngraded; - } - if (name == "bg-dexopt") { - return CompilationReason::kBgDexopt; - } - if (name == "ab-ota") { - return CompilationReason::kABOTA; - } - if (name == "inactive") { - return CompilationReason::kInactive; - } - if (name == "shared") { - return CompilationReason::kShared; - } - if (name == "install-with-dex-metadata") { - return CompilationReason::kInstallWithDexMetadata; - } - if (name == "prebuilt") { - return CompilationReason::kPrebuilt; - } - if (name == "cmdline") { - return CompilationReason::kCmdLine; - } + REASON_NAME_LIST(REASON_FROM_NAME) return CompilationReason::kError; } +#undef REASON_NAME +#undef ReasonFromName + +#define COMPILER_FILTER_REPORTING_LIST(V) \ + V(kError, "error") /* Error (invalid value) condition */ \ + V(kUnknown, "unknown") /* Unknown (not set) condition */ \ + V(kAssumeVerified, "assume-verified") /* Standard compiler filters */ \ + V(kExtract, "extract") \ + V(kVerify, "verify") \ + V(kSpaceProfile, "space-profile") \ + V(kSpace, "space") \ + V(kSpeedProfile, "speed-profile") \ + V(kSpeed, "speed") \ + V(kEverythingProfile, "everything-profile") \ + V(kEverything, "everything") \ + V(kRunFromApk, "run-from-apk") /* Augmented compiler filters as produces by OatFileAssistant#GetOptimizationStatus */ \ + V(kRunFromApkFallback, "run-from-apk-fallback") + +// Augmented compiler filter enum, used in the reporting infra. +enum class CompilerFilterReporting { +#define FILTER(kind, name) kind, + COMPILER_FILTER_REPORTING_LIST(FILTER) +#undef FILTER +}; + +#define FILTER_NAME(kind, kind_name) \ + case CompilerFilterReporting::kind: return kind_name; +#define FILTER_FROM_NAME(kind, kind_name) \ + if (name == kind_name) { return CompilerFilterReporting::kind; } + +constexpr const char* CompilerFilterReportingName(CompilerFilterReporting filter) { + switch (filter) { + COMPILER_FILTER_REPORTING_LIST(FILTER_NAME) + } +} + +constexpr CompilerFilterReporting CompilerFilterReportingFromName(std::string_view name) { + COMPILER_FILTER_REPORTING_LIST(FILTER_FROM_NAME) + return CompilerFilterReporting::kError; +} + +#undef FILTER_NAME +#undef FILTER_FROM_NAME + // SessionData contains metadata about a metrics session (basically the lifetime of an ART process). // This information should not change for the lifetime of the session. struct SessionData { @@ -214,7 +182,7 @@ struct SessionData { int64_t session_id; int32_t uid; CompilationReason compilation_reason; - std::optional<CompilerFilter::Filter> compiler_filter; + CompilerFilterReporting compiler_filter; }; // MetricsBackends are used by a metrics reporter to write metrics to some external location. For @@ -229,7 +197,10 @@ class MetricsBackend { // includes a session id which is used to correlate any metric reports with the same instance of // the ART runtime. Additionally, session_data includes useful metadata such as the package name // for this process. - virtual void BeginSession(const SessionData& session_data) = 0; + // + // It may also be called whenever there is an update to the session metadata (e.g. optimization + // state). + virtual void BeginOrUpdateSession(const SessionData& session_data) = 0; protected: // Called by the metrics reporter to indicate that a new metrics report is starting. @@ -468,7 +439,7 @@ class StringBackend : public MetricsBackend { public: StringBackend(); - void BeginSession(const SessionData& session_data) override; + void BeginOrUpdateSession(const SessionData& session_data) override; void BeginReport(uint64_t timestamp_millis) override; diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc index c8c8701327..f09987ba8e 100644 --- a/libartbase/base/metrics/metrics_common.cc +++ b/libartbase/base/metrics/metrics_common.cc @@ -51,7 +51,7 @@ SessionData SessionData::CreateDefault() { return SessionData{ .compilation_reason = CompilationReason::kUnknown, - .compiler_filter = std::nullopt, + .compiler_filter = CompilerFilterReporting::kUnknown, .session_id = kInvalidSessionId, .uid = uid, }; @@ -97,7 +97,7 @@ std::string StringBackend::GetAndResetBuffer() { return result; } -void StringBackend::BeginSession(const SessionData& session_data) { +void StringBackend::BeginOrUpdateSession(const SessionData& session_data) { session_data_ = session_data; } @@ -110,10 +110,7 @@ void StringBackend::BeginReport(uint64_t timestamp_since_start_ms) { os_ << " uid: " << session_data_->uid << "\n"; os_ << " compilation_reason: " << CompilationReasonName(session_data_->compilation_reason) << "\n"; - os_ << " compiler_filter: " - << (session_data_->compiler_filter.has_value() - ? CompilerFilter::NameOfFilter(session_data_->compiler_filter.value()) - : "(unspecified)") + os_ << " compiler_filter: " << CompilerFilterReportingName(session_data_->compiler_filter) << "\n"; } os_ << " Metrics:\n"; diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc index 77f1b9729b..e7882ecffd 100644 --- a/libartbase/base/metrics/metrics_test.cc +++ b/libartbase/base/metrics/metrics_test.cc @@ -305,6 +305,141 @@ TEST_F(MetricsTest, ResetMetrics) { metrics.ReportAllMetrics(&zero_backend); } +TEST(CompilerFilterReportingTest, FromName) { + ASSERT_EQ(CompilerFilterReportingFromName("error"), + CompilerFilterReporting::kError); + ASSERT_EQ(CompilerFilterReportingFromName("unknown"), + CompilerFilterReporting::kUnknown); + ASSERT_EQ(CompilerFilterReportingFromName("assume-verified"), + CompilerFilterReporting::kAssumeVerified); + ASSERT_EQ(CompilerFilterReportingFromName("extract"), + CompilerFilterReporting::kExtract); + ASSERT_EQ(CompilerFilterReportingFromName("verify"), + CompilerFilterReporting::kVerify); + ASSERT_EQ(CompilerFilterReportingFromName("space-profile"), + CompilerFilterReporting::kSpaceProfile); + ASSERT_EQ(CompilerFilterReportingFromName("space"), + CompilerFilterReporting::kSpace); + ASSERT_EQ(CompilerFilterReportingFromName("speed-profile"), + CompilerFilterReporting::kSpeedProfile); + ASSERT_EQ(CompilerFilterReportingFromName("speed"), + CompilerFilterReporting::kSpeed); + ASSERT_EQ(CompilerFilterReportingFromName("everything-profile"), + CompilerFilterReporting::kEverythingProfile); + ASSERT_EQ(CompilerFilterReportingFromName("everything"), + CompilerFilterReporting::kEverything); + ASSERT_EQ(CompilerFilterReportingFromName("run-from-apk"), + CompilerFilterReporting::kRunFromApk); + ASSERT_EQ(CompilerFilterReportingFromName("run-from-apk-fallback"), + CompilerFilterReporting::kRunFromApkFallback); +} + +TEST(CompilerFilterReportingTest, Name) { + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kError), + "error"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kUnknown), + "unknown"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kAssumeVerified), + "assume-verified"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kExtract), + "extract"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kVerify), + "verify"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpaceProfile), + "space-profile"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpace), + "space"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpeedProfile), + "speed-profile"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpeed), + "speed"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kEverythingProfile), + "everything-profile"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kEverything), + "everything"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kRunFromApk), + "run-from-apk"); + ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kRunFromApkFallback), + "run-from-apk-fallback"); +} + +TEST(CompilerReason, FromName) { + ASSERT_EQ(CompilationReasonFromName("unknown"), + CompilationReason::kUnknown); + ASSERT_EQ(CompilationReasonFromName("first-boot"), + CompilationReason::kFirstBoot); + ASSERT_EQ(CompilationReasonFromName("boot-after-ota"), + CompilationReason::kBootAfterOTA); + ASSERT_EQ(CompilationReasonFromName("post-boot"), + CompilationReason::kPostBoot); + ASSERT_EQ(CompilationReasonFromName("install"), + CompilationReason::kInstall); + ASSERT_EQ(CompilationReasonFromName("install-fast"), + CompilationReason::kInstallFast); + ASSERT_EQ(CompilationReasonFromName("install-bulk"), + CompilationReason::kInstallBulk); + ASSERT_EQ(CompilationReasonFromName("install-bulk-secondary"), + CompilationReason::kInstallBulkSecondary); + ASSERT_EQ(CompilationReasonFromName("install-bulk-downgraded"), + CompilationReason::kInstallBulkDowngraded); + ASSERT_EQ(CompilationReasonFromName("install-bulk-secondary-downgraded"), + CompilationReason::kInstallBulkSecondaryDowngraded); + ASSERT_EQ(CompilationReasonFromName("bg-dexopt"), + CompilationReason::kBgDexopt); + ASSERT_EQ(CompilationReasonFromName("ab-ota"), + CompilationReason::kABOTA); + ASSERT_EQ(CompilationReasonFromName("inactive"), + CompilationReason::kInactive); + ASSERT_EQ(CompilationReasonFromName("shared"), + CompilationReason::kShared); + ASSERT_EQ(CompilationReasonFromName("install-with-dex-metadata"), + CompilationReason::kInstallWithDexMetadata); + ASSERT_EQ(CompilationReasonFromName("prebuilt"), + CompilationReason::kPrebuilt); + ASSERT_EQ(CompilationReasonFromName("cmdline"), + CompilationReason::kCmdLine); + ASSERT_EQ(CompilationReasonFromName("error"), + CompilationReason::kError); +} + +TEST(CompilerReason, Name) { + ASSERT_EQ(CompilationReasonName(CompilationReason::kUnknown), + "unknown"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kFirstBoot), + "first-boot"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kBootAfterOTA), + "boot-after-ota"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kPostBoot), + "post-boot"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstall), + "install"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallFast), + "install-fast"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulk), + "install-bulk"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulkSecondary), + "install-bulk-secondary"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulkDowngraded), + "install-bulk-downgraded"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulkSecondaryDowngraded), + "install-bulk-secondary-downgraded"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kBgDexopt), + "bg-dexopt"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kABOTA), + "ab-ota"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInactive), + "inactive"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kShared), + "shared"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallWithDexMetadata), + "install-with-dex-metadata"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kPrebuilt), + "prebuilt"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kCmdLine), + "cmdline"); + ASSERT_EQ(CompilationReasonName(CompilationReason::kError), + "error"); +} } // namespace metrics } // namespace art diff --git a/libartbase/base/metrics/metrics_test.h b/libartbase/base/metrics/metrics_test.h index 48fd517b15..3e8b42a5cc 100644 --- a/libartbase/base/metrics/metrics_test.h +++ b/libartbase/base/metrics/metrics_test.h @@ -33,7 +33,7 @@ namespace test { // test cases to test specific behaviors. class TestBackendBase : public MetricsBackend { public: - void BeginSession([[maybe_unused]] const SessionData& session_data) override {} + void BeginOrUpdateSession([[maybe_unused]] const SessionData& session_data) override {} void BeginReport([[maybe_unused]] uint64_t timestamp_since_start_ms) override {} diff --git a/runtime/Android.bp b/runtime/Android.bp index ed714bebd3..6f94a2c6a1 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -71,6 +71,7 @@ libart_cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "app_info.cc", "aot_class_linker.cc", "art_field.cc", "sdk_checker.cc", @@ -691,6 +692,7 @@ art_cc_test { ":art-gtest-jars-XandY", ], srcs: [ + "app_info_test.cc", "arch/arch_test.cc", "arch/instruction_set_features_test.cc", "arch/memcmp16_test.cc", diff --git a/runtime/app_info.cc b/runtime/app_info.cc new file mode 100644 index 0000000000..c72951eebc --- /dev/null +++ b/runtime/app_info.cc @@ -0,0 +1,133 @@ +/* + * 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 <app_info.h> + +#include "base/logging.h" +#include "base/mutex.h" +#include "base/safe_map.h" +#include "thread-inl.h" + +namespace art { + +static constexpr const char* kUnknownValue = "unknown"; + +AppInfo::AppInfo() + : update_mutex_("app_info_update_mutex", LockLevel::kGenericBottomLock) {} + +// Converts VMRuntime.java constansts to a CodeType. +AppInfo::CodeType AppInfo::FromVMRuntimeConstants(uint32_t code_type) { + switch (code_type) { + case kVMRuntimePrimaryApk : return CodeType::kPrimaryApk; + case kVMRuntimeSplitApk : return CodeType::kPrimaryApk; + case kVMRuntimeSecondaryDex : return CodeType::kSecondaryDex; + default: + LOG(WARNING) << "Unknown code type: " << code_type; + return CodeType::kUnknown; + } +} + +static const char* CodeTypeName(AppInfo::CodeType code_type) { + switch (code_type) { + case AppInfo::CodeType::kPrimaryApk : return "primary-apk"; + case AppInfo::CodeType::kSplitApk : return "split-apk"; + case AppInfo::CodeType::kSecondaryDex : return "secondary-dex"; + case AppInfo::CodeType::kUnknown : return "unknown"; + } +} + +void AppInfo::RegisterAppInfo(const std::string& package_name, + const std::vector<std::string>& code_paths, + const std::string& cur_profile_path, + const std::string& ref_profile_path, + AppInfo::CodeType code_type) { + MutexLock mu(Thread::Current(), update_mutex_); + + package_name_ = package_name; + + for (const std::string& code_path : code_paths) { + CodeLocationInfo& cli = registered_code_locations_.GetOrCreate( + code_path, []() { return CodeLocationInfo(); }); + cli.cur_profile_path = cur_profile_path; + cli.ref_profile_path = ref_profile_path; + cli.code_type = code_type; + + VLOG(startup) << "Registering code path. " + << "\npackage_name=" << package_name + << "\ncode_path=" << code_path + << "\ncode_type=" << CodeTypeName(code_type) + << "\ncur_profile=" << cur_profile_path + << "\nref_profile=" << ref_profile_path; + } +} + +void AppInfo::RegisterOdexStatus(const std::string& code_path, + const std::string& compiler_filter, + const std::string& compilation_reason, + const std::string& odex_status) { + MutexLock mu(Thread::Current(), update_mutex_); + + CodeLocationInfo& cli = registered_code_locations_.GetOrCreate( + code_path, []() { return CodeLocationInfo(); }); + cli.compiler_filter = compiler_filter; + cli.compilation_reason = compilation_reason; + cli.odex_status = odex_status; + + VLOG(startup) << "Registering odex status. " + << "\ncode_path=" << code_path + << "\ncompiler_filter=" << compiler_filter + << "\ncompilation_reason=" << compilation_reason + << "\nodex_status=" << odex_status; +} + +void AppInfo::GetPrimaryApkOptimizationStatus( + std::string* out_compiler_filter, + std::string* out_compilation_reason) { + MutexLock mu(Thread::Current(), update_mutex_); + + for (const auto& it : registered_code_locations_) { + const CodeLocationInfo& cli = it.second; + if (cli.code_type == CodeType::kPrimaryApk) { + *out_compiler_filter = cli.compiler_filter.value_or(kUnknownValue); + *out_compilation_reason = cli.compilation_reason.value_or(kUnknownValue); + return; + } + } + *out_compiler_filter = kUnknownValue; + *out_compilation_reason = kUnknownValue; +} + +std::ostream& operator<<(std::ostream& os, AppInfo& rhs) { + MutexLock mu(Thread::Current(), rhs.update_mutex_); + + os << "AppInfo for package_name=" << rhs.package_name_.value_or(kUnknownValue) << "\n"; + for (const auto& it : rhs.registered_code_locations_) { + const std::string code_path = it.first; + const AppInfo::CodeLocationInfo& cli = it.second; + + os << "\ncode_path=" << code_path + << "\ncode_type=" << CodeTypeName(cli.code_type) + << "\ncompiler_filter=" << cli.compiler_filter.value_or(kUnknownValue) + << "\ncompilation_reason=" << cli.compilation_reason.value_or(kUnknownValue) + << "\nodex_status=" << cli.odex_status.value_or(kUnknownValue) + << "\ncur_profile=" << cli.cur_profile_path.value_or(kUnknownValue) + << "\nref_profile=" << cli.ref_profile_path.value_or(kUnknownValue) + << "\n"; + } + return os; +} + +} // namespace art diff --git a/runtime/app_info.h b/runtime/app_info.h new file mode 100644 index 0000000000..68f2c586da --- /dev/null +++ b/runtime/app_info.h @@ -0,0 +1,121 @@ +/* + * 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_RUNTIME_APP_INFO_H_ +#define ART_RUNTIME_APP_INFO_H_ + +#include <vector> + +#include "base/mutex.h" +#include <base/safe_map.h> + +namespace art { + +// Constants used by VMRuntime.java to interface with the runtime. +// We could get them from the well known class but it's simpler to +// redefine them here. + +// VMRuntime.CODE_PATH_TYPE_PRIMARY_APK +static constexpr int32_t kVMRuntimePrimaryApk = 1 << 0; +// VMRuntime.CODE_PATH_TYPE_SPLIT_APK +static constexpr int32_t kVMRuntimeSplitApk = 1 << 1; +// VMRuntime.CODE_PATH_TYPE_SECONDARY_DEX +static constexpr int32_t kVMRuntimeSecondaryDex = 1 << 2; + +// Encapsulates the information the runtime has about the application. +// +// The app's info comes from 2 channels: +// 1) during class loading, when we load oat files. +// 2) during app startup, when the framework calls VMRuntime#registerAppInfo. +// In general the class loading event happens before VMRuntime#registerAppInfo. +class AppInfo { + public: + enum class CodeType { + kUnknown, + kPrimaryApk, + kSplitApk, + kSecondaryDex, + }; + + // Converts VMRuntime.java constansts to a CodeType. + static CodeType FromVMRuntimeConstants(uint32_t code_type); + + AppInfo(); + + // Registers the application code paths, types, and associated profiles. + void RegisterAppInfo(const std::string& package_name, + const std::vector<std::string>& code_paths, + const std::string& profile_output_filename, + const std::string& ref_profile_filename, + CodeType code_type); + + // Registers the optimization status for single code path. + void RegisterOdexStatus(const std::string& code_path, + const std::string& compiler_filter, + const std::string& compilation_reason, + const std::string& odex_status); + + // Extracts the optimization status of the primary apk into the given arguments. + // If there are multiple primary APKs registed via RegisterAppInfo, the method + // will assign the status of the first APK, sorted by the location name. + // + // Assigns "unknown" if there is no primary apk or the optimization status was + // not set via RegisterOdexStatus, + void GetPrimaryApkOptimizationStatus(std::string* out_compiler_filter, + std::string* out_compilation_reason); + + private: + // Encapsulates optimization information about a particular code location. + struct CodeLocationInfo { + // The type of the code location (primary, split, secondary, unknown). + CodeType code_type{CodeType::kUnknown}; + + // The compiler filter of the oat file. Note that this contains + // the output of OatFileAssistant#GetOptimizationStatus() which may + // contain values outside the scope of the CompilerFilter enum. + std::optional<std::string> compiler_filter; + + // The compiler reason of the oat file. Note that this contains + // the output of OatFileAssistant#GetOptimizationStatus(). + std::optional<std::string> compilation_reason; + + // The odes status as produced by OatFileAssistant#GetOptimizationStatus(). + std::optional<std::string> odex_status; + + // The path to the primary profile if given. + std::optional<std::string> cur_profile_path; + + // The path to the reference profile if given. + std::optional<std::string> ref_profile_path; + }; + + // The name of the package if set. + std::optional<std::string> package_name_ GUARDED_BY(update_mutex_); + + // The registered code locations. + SafeMap<std::string, CodeLocationInfo> registered_code_locations_ GUARDED_BY(update_mutex_); + + // Lock to touch the state ot the AppInfo object. + art::Mutex update_mutex_ BOTTOM_MUTEX_ACQUIRED_AFTER; + + friend std::ostream& operator<<(std::ostream& os, AppInfo& rhs); +}; + +std::ostream& operator<<(std::ostream& os, AppInfo& rhs); + +} // namespace art + +#endif // ART_RUNTIME_APP_INFO_H_ diff --git a/runtime/app_info_test.cc b/runtime/app_info_test.cc new file mode 100644 index 0000000000..4a365dec96 --- /dev/null +++ b/runtime/app_info_test.cc @@ -0,0 +1,121 @@ +/* + * 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 "app_info.h" + +#include <vector> + +#include "gtest/gtest.h" + +namespace art { + +TEST(AppInfoTest, RegisterAppInfo) { + AppInfo app_info; + app_info.RegisterAppInfo( + "package_name", + std::vector<std::string>({"code_location"}), + "", + "", + AppInfo::CodeType::kPrimaryApk); + + std::string filter; + std::string reason; + app_info.GetPrimaryApkOptimizationStatus(&filter, &reason); + + // Odex status was not registered. + ASSERT_EQ(filter, "unknown"); + ASSERT_EQ(reason, "unknown"); +} + +TEST(AppInfoTest, RegisterAppInfoWithOdexStatus) { + AppInfo app_info; + app_info.RegisterAppInfo( + "package_name", + std::vector<std::string>({"code_location"}), + "", + "", + AppInfo::CodeType::kPrimaryApk); + app_info.RegisterOdexStatus( + "code_location", + "filter", + "reason", + "odex_status"); + + std::string filter; + std::string reason; + app_info.GetPrimaryApkOptimizationStatus(&filter, &reason); + + ASSERT_EQ(filter, "filter"); + ASSERT_EQ(reason, "reason"); +} + +TEST(AppInfoTest, RegisterAppInfoWithOdexStatusMultiplePrimary) { + AppInfo app_info; + app_info.RegisterOdexStatus( + "code_location", + "filter", + "reason", + "odex_status"); + app_info.RegisterOdexStatus( + "code_location2", + "filter2", + "reason2", + "odex_status"); + app_info.RegisterAppInfo( + "package_name", + std::vector<std::string>({"code_location"}), + "", + "", + AppInfo::CodeType::kPrimaryApk); + + std::string filter; + std::string reason; + app_info.GetPrimaryApkOptimizationStatus(&filter, &reason); + + // The optimization status should be the one of the first apk. + ASSERT_EQ(filter, "filter"); + ASSERT_EQ(reason, "reason"); +} + +TEST(AppInfoTest, RegisterAppInfoWithOdexStatusNoPrimary) { + AppInfo app_info; + + // Check that the status is not known in an empty app_info. + std::string filter; + std::string reason; + app_info.GetPrimaryApkOptimizationStatus(&filter, &reason); + + // Register a split.s + app_info.RegisterAppInfo( + "package_name", + std::vector<std::string>({"code_location"}), + "", + "", + AppInfo::CodeType::kSplitApk); + app_info.RegisterOdexStatus( + "code_location", + "filter", + "reason", + "odex_status"); + + + // The optimization status is unknown since we don't have primary apks. + app_info.GetPrimaryApkOptimizationStatus(&filter, &reason); + ASSERT_EQ(filter, "unknown"); + ASSERT_EQ(reason, "unknown"); +} + +} // namespace art diff --git a/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc index 2398ea9bee..5104bd3917 100644 --- a/runtime/metrics/reporter.cc +++ b/runtime/metrics/reporter.cc @@ -21,6 +21,7 @@ #include <android-base/parseint.h> #include "base/flags.h" +#include "oat_file_manager.h" #include "runtime.h" #include "runtime_options.h" #include "statsd.h" @@ -84,6 +85,18 @@ void MetricsReporter::NotifyStartupCompleted() { } } +void MetricsReporter::NotifyAppInfoUpdated(AppInfo* app_info) { + std::string compilation_reason; + std::string compiler_filter; + + app_info->GetPrimaryApkOptimizationStatus( + &compiler_filter, &compilation_reason); + + SetCompilationInfo( + CompilationReasonFromName(compilation_reason), + CompilerFilterReportingFromName(compiler_filter)); +} + void MetricsReporter::RequestMetricsReport(bool synchronous) { if (thread_.has_value()) { messages_.SendMessage(RequestMetricsReportMessage{synchronous}); @@ -94,7 +107,7 @@ void MetricsReporter::RequestMetricsReport(bool synchronous) { } void MetricsReporter::SetCompilationInfo(CompilationReason compilation_reason, - CompilerFilter::Filter compiler_filter) { + CompilerFilterReporting compiler_filter) { if (thread_.has_value()) { messages_.SendMessage(CompilationInfoMessage{compilation_reason, compiler_filter}); } @@ -159,6 +172,8 @@ void MetricsReporter::BackgroundThreadRun() { LOG_STREAM(DEBUG) << "Compilation info received " << session_data_.session_id; session_data_.compilation_reason = message.compilation_reason; session_data_.compiler_filter = message.compiler_filter; + + UpdateSessionInBackends(); }); } @@ -183,7 +198,7 @@ void MetricsReporter::ReportMetrics() { if (!session_started_) { for (auto& backend : backends_) { - backend->BeginSession(session_data_); + backend->BeginOrUpdateSession(session_data_); } session_started_ = true; } @@ -193,6 +208,14 @@ void MetricsReporter::ReportMetrics() { } } +void MetricsReporter::UpdateSessionInBackends() { + if (session_started_) { + for (auto& backend : backends_) { + backend->BeginOrUpdateSession(session_data_); + } + } +} + bool MetricsReporter::ShouldReportAtStartup() const { return IsMetricsReportingEnabled(session_data_) && config_.period_spec.has_value() && diff --git a/runtime/metrics/reporter.h b/runtime/metrics/reporter.h index 4db5df163f..ed7022dac4 100644 --- a/runtime/metrics/reporter.h +++ b/runtime/metrics/reporter.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_METRICS_REPORTER_H_ #define ART_RUNTIME_METRICS_REPORTER_H_ +#include "app_info.h" #include "base/message_queue.h" #include "base/metrics/metrics.h" @@ -110,6 +111,10 @@ class MetricsReporter { // completes. void NotifyStartupCompleted(); + // Notifies the reporter that the app info was updated. This is used to detect / infer + // the compiler filter / reason of primary apks. + void NotifyAppInfoUpdated(AppInfo* app_info); + // Requests a metrics report // // If synchronous is set to true, this function will block until the report has completed. @@ -120,7 +125,7 @@ class MetricsReporter { void ReloadConfig(const ReportingConfig& config); void SetCompilationInfo(CompilationReason compilation_reason, - CompilerFilter::Filter compiler_filter); + CompilerFilterReporting compiler_filter); static constexpr const char* kBackgroundThreadName = "Metrics Background Reporting Thread"; @@ -145,6 +150,9 @@ class MetricsReporter { // Outputs the current state of the metrics to the destination set by config_. void ReportMetrics(); + // Updates the session data in all the backends. + void UpdateSessionInBackends(); + // Whether or not we should wait for startup before reporting for the first time. bool ShouldReportAtStartup() const; @@ -182,7 +190,7 @@ class MetricsReporter { struct CompilationInfoMessage { CompilationReason compilation_reason; - CompilerFilter::Filter compiler_filter; + CompilerFilterReporting compiler_filter; }; MessageQueue<ShutdownRequestedMessage, diff --git a/runtime/metrics/reporter_test.cc b/runtime/metrics/reporter_test.cc index 8c2a581ac1..3807c77991 100644 --- a/runtime/metrics/reporter_test.cc +++ b/runtime/metrics/reporter_test.cc @@ -55,7 +55,7 @@ class TestBackend : public MetricsBackend { SafeMap<DatumId, uint64_t> data; }; - void BeginSession(const SessionData& session_data) override { + void BeginOrUpdateSession(const SessionData& session_data) override { session_data_ = session_data; } @@ -84,6 +84,10 @@ class TestBackend : public MetricsBackend { return reports_; } + const SessionData& GetSessionData() { + return session_data_; + } + private: SessionData session_data_; std::vector<Report> reports_; @@ -163,7 +167,11 @@ class MetricsReporterTest : public CommonRuntimeTest { // 1) don't add metrics (with_metrics = false) // 2) or always add the same metrics (see MaybeStartBackgroundThread) // So we can write a global verify method. - void VerifyReports(uint32_t size, bool with_metrics) { + void VerifyReports( + uint32_t size, + bool with_metrics, + CompilerFilterReporting filter = CompilerFilterReporting::kUnknown, + CompilationReason reason = CompilationReason::kUnknown) { // TODO: we should iterate through all the other metrics to make sure they were not // reported. However, we don't have an easy to use iteration mechanism over metrics yet. // We should ads one @@ -172,6 +180,9 @@ class MetricsReporterTest : public CommonRuntimeTest { ASSERT_EQ(report.data.Get(DatumId::kClassVerificationCount), with_metrics ? 2u : 0u); ASSERT_EQ(report.data.Get(DatumId::kJitMethodCompileCount), with_metrics ? 1u : 0u); } + + ASSERT_EQ(backend_->GetSessionData().compiler_filter, filter); + ASSERT_EQ(backend_->GetSessionData().compilation_reason, reason); } // Sleeps until the backend received the give number of reports. @@ -184,6 +195,10 @@ class MetricsReporterTest : public CommonRuntimeTest { } } + void NotifyAppInfoUpdated(AppInfo* app_info) { + reporter_->NotifyAppInfoUpdated(app_info); + } + private: std::unique_ptr<MockMetricsReporter> reporter_; TestBackend* backend_; @@ -352,13 +367,38 @@ TEST_F(MetricsReporterTest, SampleRateEnable50) { TEST_F(MetricsReporterTest, SampleRateEnableAll) { SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100); - // The background thread should not start. + // The background thread should start. ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false)); ASSERT_FALSE(ShouldReportAtStartup()); ASSERT_TRUE(ShouldContinueReporting()); } +TEST_F(MetricsReporterTest, CompilerFilter) { + SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100); + ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ true)); + + AppInfo app_info; + app_info.RegisterOdexStatus( + "code_location", + "verify", + "install", + "odex_status"); + app_info.RegisterAppInfo( + "package_name", + std::vector<std::string>({"code_location"}), + "", + "", + AppInfo::CodeType::kPrimaryApk); + NotifyAppInfoUpdated(&app_info); + + WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500); + VerifyReports( + /*size=*/ 1, + /*with_metrics*/ true, + CompilerFilterReporting::kVerify, + CompilationReason::kInstall); +} // Test class for period spec parsing class ReportingPeriodSpecTest : public testing::Test { diff --git a/runtime/metrics/statsd.cc b/runtime/metrics/statsd.cc index 725a9655f7..b92fd83b9d 100644 --- a/runtime/metrics/statsd.cc +++ b/runtime/metrics/statsd.cc @@ -16,6 +16,7 @@ #include "statsd.h" +#include "arch/instruction_set.h" #include "base/compiler_filter.h" #include "base/metrics/metrics.h" #include "statslog_art.h" @@ -108,31 +109,35 @@ constexpr std::optional<int32_t> EncodeDatumId(DatumId datum_id) { } } -constexpr int32_t EncodeCompileFilter(std::optional<CompilerFilter::Filter> filter) { - if (filter.has_value()) { - switch (filter.value()) { - case CompilerFilter::kAssumeVerified: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED; - case CompilerFilter::kExtract: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT; - case CompilerFilter::kVerify: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY; - case CompilerFilter::kSpaceProfile: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE; - case CompilerFilter::kSpace: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE; - case CompilerFilter::kSpeedProfile: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE; - case CompilerFilter::kSpeed: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED; - case CompilerFilter::kEverythingProfile: - return statsd:: - ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE; - case CompilerFilter::kEverything: - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING; - } - } else { - return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN; +constexpr int32_t EncodeCompileFilter(CompilerFilterReporting filter) { + switch (filter) { + case CompilerFilterReporting::kAssumeVerified: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED; + case CompilerFilterReporting::kExtract: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT; + case CompilerFilterReporting::kVerify: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY; + case CompilerFilterReporting::kSpaceProfile: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE; + case CompilerFilterReporting::kSpace: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE; + case CompilerFilterReporting::kSpeedProfile: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE; + case CompilerFilterReporting::kSpeed: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED; + case CompilerFilterReporting::kEverythingProfile: + return statsd:: + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE; + case CompilerFilterReporting::kEverything: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING; + case CompilerFilterReporting::kError: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR; + case CompilerFilterReporting::kUnknown: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN; + case CompilerFilterReporting::kRunFromApk: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK; + case CompilerFilterReporting::kRunFromApkFallback: + return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; } } @@ -181,9 +186,28 @@ constexpr int32_t EncodeCompilationReason(CompilationReason reason) { } } +constexpr int32_t EncodeInstructionSet(InstructionSet isa) { + switch (isa) { + case InstructionSet::kArm: + // Fall-through. + case InstructionSet::kThumb2: + return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM; + case InstructionSet::kArm64: + return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM64; + case InstructionSet::kX86: + return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_X86; + case InstructionSet::kX86_64: + return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_X86_64; + case InstructionSet::kNone: + return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN; + } +} + class StatsdBackend : public MetricsBackend { public: - void BeginSession(const SessionData& session_data) override { session_data_ = session_data; } + void BeginOrUpdateSession(const SessionData& session_data) override { + session_data_ = session_data; + } protected: void BeginReport(uint64_t timestamp_since_start_ms) override { @@ -206,7 +230,7 @@ class StatsdBackend : public MetricsBackend { static_cast<int64_t>(value), statsd::ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN, statsd::ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN, - statsd::ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN); + EncodeInstructionSet(kRuntimeISA)); } } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index d46a36fd25..842590408d 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -370,11 +370,11 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env ATTRIBUTE_UNUSED, jobject) { */ static void VMRuntime_registerAppInfo(JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED, - jstring package_name ATTRIBUTE_UNUSED, + jstring package_name, jstring cur_profile_file, jstring ref_profile_file, jobjectArray code_paths, - jint code_path_type ATTRIBUTE_UNUSED) { + jint code_path_type) { std::vector<std::string> code_paths_vec; int code_paths_length = env->GetArrayLength(code_paths); for (int i = 0; i < code_paths_length; i++) { @@ -392,7 +392,16 @@ static void VMRuntime_registerAppInfo(JNIEnv* env, std::string ref_profile_file_str(raw_ref_profile_file); env->ReleaseStringUTFChars(ref_profile_file, raw_ref_profile_file); - Runtime::Current()->RegisterAppInfo(code_paths_vec, cur_profile_file_str, ref_profile_file_str); + const char* raw_package_name = env->GetStringUTFChars(package_name, nullptr); + std::string package_name_str(raw_package_name); + env->ReleaseStringUTFChars(package_name, raw_package_name); + + Runtime::Current()->RegisterAppInfo( + package_name_str, + code_paths_vec, + cur_profile_file_str, + ref_profile_file_str, + static_cast<int32_t>(code_path_type)); } static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) { diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 391d597b83..a891aa3b41 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -141,26 +141,6 @@ std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const { return oat_files; } -bool OatFileManager::GetPrimaryOatFileInfo(std::string* compilation_reason, - CompilerFilter::Filter* compiler_filter) const { - ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); - std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); - if (!boot_oat_files.empty()) { - for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { - if (std::find(boot_oat_files.begin(), boot_oat_files.end(), oat_file.get()) == - boot_oat_files.end()) { - const char* reason = oat_file->GetCompilationReason(); - if (reason != nullptr) { - *compilation_reason = reason; - } - *compiler_filter = oat_file->GetCompilerFilter(); - return true; - } - } - } - return false; -} - OatFileManager::OatFileManager() : only_use_system_oat_files_(false) {} @@ -231,6 +211,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( &compilation_reason, &odex_status); + Runtime::Current()->GetAppInfo()->RegisterOdexStatus( + dex_location, + compilation_filter, + compilation_reason, + odex_status); + ScopedTrace odex_loading(StringPrintf( "location=%s status=%s filter=%s reason=%s", odex_location.c_str(), @@ -420,6 +406,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( Runtime::Current()->GetJit()->RegisterDexFiles(dex_files, class_loader); } + // Now that we loaded the dex/odex files, notify the runtime. + // Note that we do this everytime we load dex files. + Runtime::Current()->NotifyDexFileLoaded(); + return dex_files; } diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index f8a734148a..abf4ea00c1 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -71,11 +71,6 @@ class OatFileManager { // Returns the boot image oat files. std::vector<const OatFile*> GetBootOatFiles() const; - // Fetches information from the primary oat file. - bool GetPrimaryOatFileInfo(std::string* compilation_reason, - CompilerFilter::Filter* compiler_filter) - const REQUIRES(!Locks::oat_file_manager_lock_); - // Returns the oat files for the images, registers the oat files. // Takes ownership of the imagespace's underlying oat files. std::vector<const OatFile*> RegisterImageOatFiles( diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 3235eea3ab..9093bd22c3 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -34,6 +34,7 @@ #include <cstdio> #include <cstdlib> #include <limits> +#include <string.h> #include <thread> #include <unordered_set> #include <vector> @@ -1004,12 +1005,19 @@ bool Runtime::Start() { !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) { std::vector<std::string> dex_filenames; Split(class_path_string_, ':', &dex_filenames); - // It's ok to pass "" to the ref profile filename. It indicates we don't have + + // We pass "" as the package name because at this point we don't know it. It could be the + // Zygote or it could be a dalvikvm cmd line execution. The package name will be re-set during + // post-fork or during RegisterAppInfo. + // + // Also, it's ok to pass "" to the ref profile filename. It indicates we don't have // a reference profile. RegisterAppInfo( + /*package_name=*/ "", dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath(), - /*ref_profile_filename=*/ ""); + /*ref_profile_filename=*/ "", + kVMRuntimePrimaryApk); } return true; @@ -1056,6 +1064,22 @@ void Runtime::InitNonZygoteOrPostFork( DCHECK(!IsZygote()); if (is_system_server) { + // Register the system server code paths. + // TODO: Ideally this should be done by the VMRuntime#RegisterAppInfo. However, right now + // the method is only called when we set up the profile. It should be called all the time + // (simillar to the apps). Once that's done this manual registration can be removed. + const char* system_server_classpath = getenv("SYSTEMSERVERCLASSPATH"); + if (system_server_classpath == nullptr || (strlen(system_server_classpath) == 0)) { + LOG(WARNING) << "System server class path not set"; + } else { + std::vector<std::string> jars = android::base::Split(system_server_classpath, ":"); + app_info_.RegisterAppInfo("android", + jars, + /*cur_profile_path=*/ "", + /*ref_profile_path=*/ "", + AppInfo::CodeType::kPrimaryApk); + } + // Set the system server package name to "android". // This is used to tell the difference between samples provided by system server // and samples generated by other apps when processing boot image profiles. @@ -1101,6 +1125,8 @@ void Runtime::InitNonZygoteOrPostFork( session_data.session_id = GetRandomNumber<int64_t>(1, std::numeric_limits<int64_t>::max()); // TODO: set session_data.compilation_reason and session_data.compiler_filter metrics_reporter_->MaybeStartBackgroundThread(session_data); + // Also notify about any updates to the app info. + metrics_reporter_->NotifyAppInfoUpdated(&app_info_); } StartSignalCatcher(); @@ -2557,9 +2583,22 @@ void Runtime::ClearCalleeSaveMethods() { } } -void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, +void Runtime::RegisterAppInfo(const std::string& package_name, + const std::vector<std::string>& code_paths, const std::string& profile_output_filename, - const std::string& ref_profile_filename) { + const std::string& ref_profile_filename, + int32_t code_type) { + app_info_.RegisterAppInfo( + package_name, + code_paths, + profile_output_filename, + ref_profile_filename, + AppInfo::FromVMRuntimeConstants(code_type)); + + if (metrics_reporter_ != nullptr) { + metrics_reporter_->NotifyAppInfoUpdated(&app_info_); + } + if (jit_.get() == nullptr) { // We are not JITing. Nothing to do. return; @@ -3131,6 +3170,8 @@ void Runtime::NotifyStartupCompleted() { return; } + VLOG(startup) << app_info_; + VLOG(startup) << "Adding NotifyStartupCompleted task"; // Use the heap task processor since we want to be exclusive with the GC and we don't want to // block the caller if the GC is running. @@ -3142,18 +3183,16 @@ void Runtime::NotifyStartupCompleted() { ProfileSaver::NotifyStartupCompleted(); if (metrics_reporter_ != nullptr) { - std::string compilation_reason; - CompilerFilter::Filter compiler_filter; - if (oat_file_manager_->GetPrimaryOatFileInfo(&compilation_reason, &compiler_filter)) { - metrics_reporter_->SetCompilationInfo( - compilation_reason.empty() ? metrics::CompilationReasonFromName(compilation_reason) - : metrics::CompilationReason::kUnknown, - compiler_filter); - } metrics_reporter_->NotifyStartupCompleted(); } } +void Runtime::NotifyDexFileLoaded() { + if (metrics_reporter_ != nullptr) { + metrics_reporter_->NotifyAppInfoUpdated(&app_info_); + } +} + bool Runtime::GetStartupCompleted() const { return startup_completed_.load(std::memory_order_seq_cst); } diff --git a/runtime/runtime.h b/runtime/runtime.h index 5bea321fb3..2759aa56c6 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -27,6 +27,7 @@ #include <utility> #include <vector> +#include "app_info.h" #include "base/locks.h" #include "base/macros.h" #include "base/mem_map.h" @@ -524,9 +525,11 @@ class Runtime { return &instrumentation_; } - void RegisterAppInfo(const std::vector<std::string>& code_paths, + void RegisterAppInfo(const std::string& package_name, + const std::vector<std::string>& code_paths, const std::string& profile_output_filename, - const std::string& ref_profile_filename); + const std::string& ref_profile_filename, + int32_t code_type); // Transaction support. bool IsActiveTransaction() const; @@ -967,6 +970,10 @@ class Runtime { // first call. void NotifyStartupCompleted(); + // Notify the runtime that the application finished loading some dex/odex files. This is + // called everytime we load a set of dex files in a class loader. + void NotifyDexFileLoaded(); + // Return true if startup is already completed. bool GetStartupCompleted() const; @@ -994,6 +1001,8 @@ class Runtime { metrics::ArtMetrics* GetMetrics() { return &metrics_; } + AppInfo* GetAppInfo() { return &app_info_; } + void RequestMetricsReport(bool synchronous = true); static void MadviseFileForRange(size_t madvise_size_limit_bytes, @@ -1404,6 +1413,9 @@ class Runtime { // the third entry in the example above). std::string apex_versions_; + // The info about the application code paths. + AppInfo app_info_; + // Note: See comments on GetFaultMessage. friend std::string GetFaultMessageForAbortLogging(); friend class Dex2oatImageTest; |