xref: /aosp_15_r20/art/odrefresh/odr_metrics.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "odr_metrics.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include <unistd.h>
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker #include <algorithm>
22*795d594fSAndroid Build Coastguard Worker #include <cstdint>
23*795d594fSAndroid Build Coastguard Worker #include <fstream>
24*795d594fSAndroid Build Coastguard Worker #include <iosfwd>
25*795d594fSAndroid Build Coastguard Worker #include <optional>
26*795d594fSAndroid Build Coastguard Worker #include <ostream>
27*795d594fSAndroid Build Coastguard Worker #include <string>
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker #include <android-base/logging.h>
30*795d594fSAndroid Build Coastguard Worker #include <base/os.h>
31*795d594fSAndroid Build Coastguard Worker #include <odr_fs_utils.h>
32*795d594fSAndroid Build Coastguard Worker #include <odr_metrics_record.h>
33*795d594fSAndroid Build Coastguard Worker 
34*795d594fSAndroid Build Coastguard Worker namespace art {
35*795d594fSAndroid Build Coastguard Worker namespace odrefresh {
36*795d594fSAndroid Build Coastguard Worker 
OdrMetrics(const std::string & cache_directory,const std::string & metrics_file)37*795d594fSAndroid Build Coastguard Worker OdrMetrics::OdrMetrics(const std::string& cache_directory, const std::string& metrics_file)
38*795d594fSAndroid Build Coastguard Worker     : cache_directory_(cache_directory), metrics_file_(metrics_file) {
39*795d594fSAndroid Build Coastguard Worker   DCHECK(metrics_file.starts_with("/"));
40*795d594fSAndroid Build Coastguard Worker 
41*795d594fSAndroid Build Coastguard Worker   // Remove existing metrics file if it exists.
42*795d594fSAndroid Build Coastguard Worker   if (OS::FileExists(metrics_file.c_str())) {
43*795d594fSAndroid Build Coastguard Worker     if (unlink(metrics_file.c_str()) != 0) {
44*795d594fSAndroid Build Coastguard Worker       PLOG(ERROR) << "Failed to remove metrics file '" << metrics_file << "'";
45*795d594fSAndroid Build Coastguard Worker     }
46*795d594fSAndroid Build Coastguard Worker   }
47*795d594fSAndroid Build Coastguard Worker 
48*795d594fSAndroid Build Coastguard Worker   // Create apexdata dalvik-cache directory if it does not exist. It is required before
49*795d594fSAndroid Build Coastguard Worker   // calling GetFreeSpaceMiB().
50*795d594fSAndroid Build Coastguard Worker   if (!EnsureDirectoryExists(cache_directory)) {
51*795d594fSAndroid Build Coastguard Worker     // This should never fail except for no space on device or configuration issues (e.g. SELinux).
52*795d594fSAndroid Build Coastguard Worker     LOG(WARNING) << "Cache directory '" << cache_directory << "' could not be created.";
53*795d594fSAndroid Build Coastguard Worker   }
54*795d594fSAndroid Build Coastguard Worker   cache_space_free_start_mib_ = GetFreeSpaceMiB(cache_directory);
55*795d594fSAndroid Build Coastguard Worker }
56*795d594fSAndroid Build Coastguard Worker 
~OdrMetrics()57*795d594fSAndroid Build Coastguard Worker OdrMetrics::~OdrMetrics() {
58*795d594fSAndroid Build Coastguard Worker   CaptureSpaceFreeEnd();
59*795d594fSAndroid Build Coastguard Worker 
60*795d594fSAndroid Build Coastguard Worker   // Log metrics only if this is explicitly enabled (typically when compilation was done or an error
61*795d594fSAndroid Build Coastguard Worker   // occurred).
62*795d594fSAndroid Build Coastguard Worker   if (enabled_) {
63*795d594fSAndroid Build Coastguard Worker     WriteToFile(metrics_file_, this);
64*795d594fSAndroid Build Coastguard Worker   }
65*795d594fSAndroid Build Coastguard Worker }
66*795d594fSAndroid Build Coastguard Worker 
CaptureSpaceFreeEnd()67*795d594fSAndroid Build Coastguard Worker void OdrMetrics::CaptureSpaceFreeEnd() {
68*795d594fSAndroid Build Coastguard Worker   cache_space_free_end_mib_ = GetFreeSpaceMiB(cache_directory_);
69*795d594fSAndroid Build Coastguard Worker }
70*795d594fSAndroid Build Coastguard Worker 
SetDex2OatResult(Stage stage,int64_t compilation_time_ms,const std::optional<ExecResult> & dex2oat_result)71*795d594fSAndroid Build Coastguard Worker void OdrMetrics::SetDex2OatResult(Stage stage,
72*795d594fSAndroid Build Coastguard Worker                                   int64_t compilation_time_ms,
73*795d594fSAndroid Build Coastguard Worker                                   const std::optional<ExecResult>& dex2oat_result) {
74*795d594fSAndroid Build Coastguard Worker   switch (stage) {
75*795d594fSAndroid Build Coastguard Worker     case Stage::kPrimaryBootClasspath:
76*795d594fSAndroid Build Coastguard Worker       primary_bcp_compilation_millis_ = compilation_time_ms;
77*795d594fSAndroid Build Coastguard Worker       primary_bcp_dex2oat_result_ = dex2oat_result;
78*795d594fSAndroid Build Coastguard Worker       break;
79*795d594fSAndroid Build Coastguard Worker     case Stage::kSecondaryBootClasspath:
80*795d594fSAndroid Build Coastguard Worker       secondary_bcp_compilation_millis_ = compilation_time_ms;
81*795d594fSAndroid Build Coastguard Worker       secondary_bcp_dex2oat_result_ = dex2oat_result;
82*795d594fSAndroid Build Coastguard Worker       break;
83*795d594fSAndroid Build Coastguard Worker     case Stage::kSystemServerClasspath:
84*795d594fSAndroid Build Coastguard Worker       system_server_compilation_millis_ = compilation_time_ms;
85*795d594fSAndroid Build Coastguard Worker       system_server_dex2oat_result_ = dex2oat_result;
86*795d594fSAndroid Build Coastguard Worker       break;
87*795d594fSAndroid Build Coastguard Worker     case Stage::kCheck:
88*795d594fSAndroid Build Coastguard Worker     case Stage::kComplete:
89*795d594fSAndroid Build Coastguard Worker     case Stage::kPreparation:
90*795d594fSAndroid Build Coastguard Worker     case Stage::kUnknown:
91*795d594fSAndroid Build Coastguard Worker       LOG(FATAL) << "Unexpected stage " << stage_ << " when setting dex2oat result";
92*795d594fSAndroid Build Coastguard Worker   }
93*795d594fSAndroid Build Coastguard Worker }
94*795d594fSAndroid Build Coastguard Worker 
SetBcpCompilationType(Stage stage,BcpCompilationType type)95*795d594fSAndroid Build Coastguard Worker void OdrMetrics::SetBcpCompilationType(Stage stage, BcpCompilationType type) {
96*795d594fSAndroid Build Coastguard Worker   switch (stage) {
97*795d594fSAndroid Build Coastguard Worker     case Stage::kPrimaryBootClasspath:
98*795d594fSAndroid Build Coastguard Worker       primary_bcp_compilation_type_ = type;
99*795d594fSAndroid Build Coastguard Worker       break;
100*795d594fSAndroid Build Coastguard Worker     case Stage::kSecondaryBootClasspath:
101*795d594fSAndroid Build Coastguard Worker       secondary_bcp_compilation_type_ = type;
102*795d594fSAndroid Build Coastguard Worker       break;
103*795d594fSAndroid Build Coastguard Worker     case Stage::kSystemServerClasspath:
104*795d594fSAndroid Build Coastguard Worker     case Stage::kCheck:
105*795d594fSAndroid Build Coastguard Worker     case Stage::kComplete:
106*795d594fSAndroid Build Coastguard Worker     case Stage::kPreparation:
107*795d594fSAndroid Build Coastguard Worker     case Stage::kUnknown:
108*795d594fSAndroid Build Coastguard Worker       LOG(FATAL) << "Unexpected stage " << stage_ << " when setting BCP compilation type";
109*795d594fSAndroid Build Coastguard Worker   }
110*795d594fSAndroid Build Coastguard Worker }
111*795d594fSAndroid Build Coastguard Worker 
GetFreeSpaceMiB(const std::string & path)112*795d594fSAndroid Build Coastguard Worker int32_t OdrMetrics::GetFreeSpaceMiB(const std::string& path) {
113*795d594fSAndroid Build Coastguard Worker   static constexpr uint32_t kBytesPerMiB = 1024 * 1024;
114*795d594fSAndroid Build Coastguard Worker   static constexpr uint64_t kNominalMaximumCacheBytes = 1024 * kBytesPerMiB;
115*795d594fSAndroid Build Coastguard Worker 
116*795d594fSAndroid Build Coastguard Worker   // Assume nominal cache space is 1GiB (much larger than expected, ~100MB).
117*795d594fSAndroid Build Coastguard Worker   uint64_t used_space_bytes;
118*795d594fSAndroid Build Coastguard Worker   if (!GetUsedSpace(path, &used_space_bytes)) {
119*795d594fSAndroid Build Coastguard Worker     used_space_bytes = 0;
120*795d594fSAndroid Build Coastguard Worker   }
121*795d594fSAndroid Build Coastguard Worker   uint64_t nominal_free_space_bytes = kNominalMaximumCacheBytes - used_space_bytes;
122*795d594fSAndroid Build Coastguard Worker 
123*795d594fSAndroid Build Coastguard Worker   // Get free space on partition containing `path`.
124*795d594fSAndroid Build Coastguard Worker   uint64_t free_space_bytes;
125*795d594fSAndroid Build Coastguard Worker   if (!GetFreeSpace(path, &free_space_bytes)) {
126*795d594fSAndroid Build Coastguard Worker     free_space_bytes = kNominalMaximumCacheBytes;
127*795d594fSAndroid Build Coastguard Worker   }
128*795d594fSAndroid Build Coastguard Worker 
129*795d594fSAndroid Build Coastguard Worker   // Pick the smallest free space, ie space on partition or nominal space in cache.
130*795d594fSAndroid Build Coastguard Worker   // There are two things of interest for metrics:
131*795d594fSAndroid Build Coastguard Worker   //  (i) identifying failed compilations due to low space.
132*795d594fSAndroid Build Coastguard Worker   // (ii) understanding what the storage requirements are for the spectrum of boot classpaths and
133*795d594fSAndroid Build Coastguard Worker   //      system_server classpaths.
134*795d594fSAndroid Build Coastguard Worker   uint64_t free_space_mib = std::min(free_space_bytes, nominal_free_space_bytes) / kBytesPerMiB;
135*795d594fSAndroid Build Coastguard Worker   return static_cast<int32_t>(free_space_mib);
136*795d594fSAndroid Build Coastguard Worker }
137*795d594fSAndroid Build Coastguard Worker 
ToRecord() const138*795d594fSAndroid Build Coastguard Worker OdrMetricsRecord OdrMetrics::ToRecord() const {
139*795d594fSAndroid Build Coastguard Worker   return {
140*795d594fSAndroid Build Coastguard Worker       .odrefresh_metrics_version = kOdrefreshMetricsVersion,
141*795d594fSAndroid Build Coastguard Worker       .art_apex_version = art_apex_version_,
142*795d594fSAndroid Build Coastguard Worker       .trigger = static_cast<int32_t>(trigger_),
143*795d594fSAndroid Build Coastguard Worker       .stage_reached = static_cast<int32_t>(stage_),
144*795d594fSAndroid Build Coastguard Worker       .status = static_cast<int32_t>(status_),
145*795d594fSAndroid Build Coastguard Worker       .cache_space_free_start_mib = cache_space_free_start_mib_,
146*795d594fSAndroid Build Coastguard Worker       .cache_space_free_end_mib = cache_space_free_end_mib_,
147*795d594fSAndroid Build Coastguard Worker       .primary_bcp_compilation_millis = primary_bcp_compilation_millis_,
148*795d594fSAndroid Build Coastguard Worker       .secondary_bcp_compilation_millis = secondary_bcp_compilation_millis_,
149*795d594fSAndroid Build Coastguard Worker       .system_server_compilation_millis = system_server_compilation_millis_,
150*795d594fSAndroid Build Coastguard Worker       .primary_bcp_dex2oat_result = ConvertExecResult(primary_bcp_dex2oat_result_),
151*795d594fSAndroid Build Coastguard Worker       .secondary_bcp_dex2oat_result = ConvertExecResult(secondary_bcp_dex2oat_result_),
152*795d594fSAndroid Build Coastguard Worker       .system_server_dex2oat_result = ConvertExecResult(system_server_dex2oat_result_),
153*795d594fSAndroid Build Coastguard Worker       .primary_bcp_compilation_type = static_cast<int32_t>(primary_bcp_compilation_type_),
154*795d594fSAndroid Build Coastguard Worker       .secondary_bcp_compilation_type = static_cast<int32_t>(secondary_bcp_compilation_type_),
155*795d594fSAndroid Build Coastguard Worker   };
156*795d594fSAndroid Build Coastguard Worker }
157*795d594fSAndroid Build Coastguard Worker 
ConvertExecResult(const std::optional<ExecResult> & result)158*795d594fSAndroid Build Coastguard Worker OdrMetricsRecord::Dex2OatExecResult OdrMetrics::ConvertExecResult(
159*795d594fSAndroid Build Coastguard Worker     const std::optional<ExecResult>& result) {
160*795d594fSAndroid Build Coastguard Worker   if (result.has_value()) {
161*795d594fSAndroid Build Coastguard Worker     return OdrMetricsRecord::Dex2OatExecResult(result.value());
162*795d594fSAndroid Build Coastguard Worker   } else {
163*795d594fSAndroid Build Coastguard Worker     return {};
164*795d594fSAndroid Build Coastguard Worker   }
165*795d594fSAndroid Build Coastguard Worker }
166*795d594fSAndroid Build Coastguard Worker 
WriteToFile(const std::string & path,const OdrMetrics * metrics)167*795d594fSAndroid Build Coastguard Worker void OdrMetrics::WriteToFile(const std::string& path, const OdrMetrics* metrics) {
168*795d594fSAndroid Build Coastguard Worker   OdrMetricsRecord record = metrics->ToRecord();
169*795d594fSAndroid Build Coastguard Worker 
170*795d594fSAndroid Build Coastguard Worker   const android::base::Result<void>& result = record.WriteToFile(path);
171*795d594fSAndroid Build Coastguard Worker   if (!result.ok()) {
172*795d594fSAndroid Build Coastguard Worker     LOG(ERROR) << "Failed to report metrics to file: " << path
173*795d594fSAndroid Build Coastguard Worker                << ", error: " << result.error().message();
174*795d594fSAndroid Build Coastguard Worker   }
175*795d594fSAndroid Build Coastguard Worker }
176*795d594fSAndroid Build Coastguard Worker 
177*795d594fSAndroid Build Coastguard Worker }  // namespace odrefresh
178*795d594fSAndroid Build Coastguard Worker }  // namespace art
179