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