Orion Hodson | 3d877f0 | 2021-04-08 07:52:15 +0100 | [diff] [blame] | 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 | |
| 38 | OdrMetrics::OdrMetrics(const std::string& cache_directory, const std::string& metrics_file) |
| 39 | : cache_directory_(cache_directory), metrics_file_(metrics_file), status_(Status::kOK) { |
| 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 | |
| 58 | OdrMetrics::~OdrMetrics() { |
| 59 | cache_space_free_end_mib_ = GetFreeSpaceMiB(cache_directory_); |
| 60 | |
| 61 | // Log metrics only if odrefresh detected a reason to compile. |
| 62 | if (trigger_.has_value()) { |
| 63 | WriteToFile(metrics_file_, this); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | void OdrMetrics::SetCompilationTime(int32_t seconds) { |
| 68 | switch (stage_) { |
| 69 | case Stage::kPrimaryBootClasspath: |
| 70 | primary_bcp_compilation_seconds_ = seconds; |
| 71 | break; |
| 72 | case Stage::kSecondaryBootClasspath: |
| 73 | secondary_bcp_compilation_seconds_ = seconds; |
| 74 | break; |
| 75 | case Stage::kSystemServerClasspath: |
| 76 | system_server_compilation_seconds_ = seconds; |
| 77 | break; |
| 78 | case Stage::kCheck: |
| 79 | case Stage::kComplete: |
| 80 | case Stage::kPreparation: |
| 81 | case Stage::kUnknown: |
| 82 | break; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | void OdrMetrics::SetStage(Stage stage) { |
| 87 | if (status_ == Status::kOK) { |
| 88 | stage_ = stage; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | int32_t OdrMetrics::GetFreeSpaceMiB(const std::string& path) { |
| 93 | static constexpr uint32_t kBytesPerMiB = 1024 * 1024; |
| 94 | static constexpr uint64_t kNominalMaximumCacheBytes = 1024 * kBytesPerMiB; |
| 95 | |
| 96 | // Assume nominal cache space is 1GiB (much larger than expected, ~100MB). |
| 97 | uint64_t used_space_bytes; |
| 98 | if (!GetUsedSpace(path, &used_space_bytes)) { |
| 99 | used_space_bytes = 0; |
| 100 | } |
| 101 | uint64_t nominal_free_space_bytes = kNominalMaximumCacheBytes - used_space_bytes; |
| 102 | |
| 103 | // Get free space on partition containing `path`. |
| 104 | uint64_t free_space_bytes; |
| 105 | if (!GetFreeSpace(path, &free_space_bytes)) { |
| 106 | free_space_bytes = kNominalMaximumCacheBytes; |
| 107 | } |
| 108 | |
| 109 | // Pick the smallest free space, ie space on partition or nominal space in cache. |
| 110 | // There are two things of interest for metrics: |
| 111 | // (i) identifying failed compilations due to low space. |
| 112 | // (ii) understanding what the storage requirements are for the spectrum of boot classpaths and |
| 113 | // system_server classpaths. |
| 114 | uint64_t free_space_mib = std::min(free_space_bytes, nominal_free_space_bytes) / kBytesPerMiB; |
| 115 | return static_cast<int32_t>(free_space_mib); |
| 116 | } |
| 117 | |
| 118 | bool OdrMetrics::ToRecord(/*out*/OdrMetricsRecord* record) const { |
| 119 | if (!trigger_.has_value()) { |
| 120 | return false; |
| 121 | } |
| 122 | record->art_apex_version = art_apex_version_; |
| 123 | record->trigger = static_cast<uint32_t>(trigger_.value()); |
| 124 | record->stage_reached = static_cast<uint32_t>(stage_); |
| 125 | record->status = static_cast<uint32_t>(status_); |
| 126 | record->primary_bcp_compilation_seconds = primary_bcp_compilation_seconds_; |
| 127 | record->secondary_bcp_compilation_seconds = secondary_bcp_compilation_seconds_; |
| 128 | record->system_server_compilation_seconds = system_server_compilation_seconds_; |
| 129 | record->cache_space_free_start_mib = cache_space_free_start_mib_; |
| 130 | record->cache_space_free_end_mib = cache_space_free_end_mib_; |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | void OdrMetrics::WriteToFile(const std::string& path, const OdrMetrics* metrics) { |
| 135 | OdrMetricsRecord record; |
| 136 | if (!metrics->ToRecord(&record)) { |
| 137 | LOG(ERROR) << "Attempting to report metrics without a compilation trigger."; |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | // Preserve order from frameworks/proto_logging/stats/atoms.proto in metrics file written. |
| 142 | std::ofstream ofs(path); |
| 143 | ofs << record; |
| 144 | } |
| 145 | |
| 146 | } // namespace odrefresh |
| 147 | } // namespace art |