forked from LineageOS/android_art
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds metrics to stages of odrefresh. Bug: 169925964 Test: atest art_odrefresh_tests Test: atest --host art_odrefresh_tests Change-Id: I768ce5f122b0c1b839f4cdf55aa6dafb68708eb2
- Loading branch information
Showing
8 changed files
with
866 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* 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_metrics.h" | ||
|
||
#include <unistd.h> | ||
|
||
#include <algorithm> | ||
#include <cstdint> | ||
#include <fstream> | ||
#include <iosfwd> | ||
#include <optional> | ||
#include <ostream> | ||
#include <string> | ||
|
||
#include <android-base/logging.h> | ||
#include <base/os.h> | ||
#include <base/string_view_cpp20.h> | ||
#include <odr_fs_utils.h> | ||
#include <odr_metrics_record.h> | ||
|
||
namespace art { | ||
namespace odrefresh { | ||
|
||
OdrMetrics::OdrMetrics(const std::string& cache_directory, const std::string& metrics_file) | ||
: cache_directory_(cache_directory), metrics_file_(metrics_file), status_(Status::kOK) { | ||
DCHECK(StartsWith(metrics_file_, "/")); | ||
|
||
// Remove existing metrics file if it exists. | ||
if (OS::FileExists(metrics_file.c_str())) { | ||
if (unlink(metrics_file.c_str()) != 0) { | ||
PLOG(ERROR) << "Failed to remove metrics file '" << metrics_file << "'"; | ||
} | ||
} | ||
|
||
// Create apexdata dalvik-cache directory if it does not exist. It is required before | ||
// calling GetFreeSpaceMiB(). | ||
if (!EnsureDirectoryExists(cache_directory)) { | ||
// This should never fail except for no space on device or configuration issues (e.g. SELinux). | ||
LOG(WARNING) << "Cache directory '" << cache_directory << "' could not be created."; | ||
} | ||
cache_space_free_start_mib_ = GetFreeSpaceMiB(cache_directory); | ||
} | ||
|
||
OdrMetrics::~OdrMetrics() { | ||
cache_space_free_end_mib_ = GetFreeSpaceMiB(cache_directory_); | ||
|
||
// Log metrics only if odrefresh detected a reason to compile. | ||
if (trigger_.has_value()) { | ||
WriteToFile(metrics_file_, this); | ||
} | ||
} | ||
|
||
void OdrMetrics::SetCompilationTime(int32_t seconds) { | ||
switch (stage_) { | ||
case Stage::kPrimaryBootClasspath: | ||
primary_bcp_compilation_seconds_ = seconds; | ||
break; | ||
case Stage::kSecondaryBootClasspath: | ||
secondary_bcp_compilation_seconds_ = seconds; | ||
break; | ||
case Stage::kSystemServerClasspath: | ||
system_server_compilation_seconds_ = seconds; | ||
break; | ||
case Stage::kCheck: | ||
case Stage::kComplete: | ||
case Stage::kPreparation: | ||
case Stage::kUnknown: | ||
break; | ||
} | ||
} | ||
|
||
void OdrMetrics::SetStage(Stage stage) { | ||
if (status_ == Status::kOK) { | ||
stage_ = stage; | ||
} | ||
} | ||
|
||
int32_t OdrMetrics::GetFreeSpaceMiB(const std::string& path) { | ||
static constexpr uint32_t kBytesPerMiB = 1024 * 1024; | ||
static constexpr uint64_t kNominalMaximumCacheBytes = 1024 * kBytesPerMiB; | ||
|
||
// Assume nominal cache space is 1GiB (much larger than expected, ~100MB). | ||
uint64_t used_space_bytes; | ||
if (!GetUsedSpace(path, &used_space_bytes)) { | ||
used_space_bytes = 0; | ||
} | ||
uint64_t nominal_free_space_bytes = kNominalMaximumCacheBytes - used_space_bytes; | ||
|
||
// Get free space on partition containing `path`. | ||
uint64_t free_space_bytes; | ||
if (!GetFreeSpace(path, &free_space_bytes)) { | ||
free_space_bytes = kNominalMaximumCacheBytes; | ||
} | ||
|
||
// Pick the smallest free space, ie space on partition or nominal space in cache. | ||
// There are two things of interest for metrics: | ||
// (i) identifying failed compilations due to low space. | ||
// (ii) understanding what the storage requirements are for the spectrum of boot classpaths and | ||
// system_server classpaths. | ||
uint64_t free_space_mib = std::min(free_space_bytes, nominal_free_space_bytes) / kBytesPerMiB; | ||
return static_cast<int32_t>(free_space_mib); | ||
} | ||
|
||
bool OdrMetrics::ToRecord(/*out*/OdrMetricsRecord* record) const { | ||
if (!trigger_.has_value()) { | ||
return false; | ||
} | ||
record->art_apex_version = art_apex_version_; | ||
record->trigger = static_cast<uint32_t>(trigger_.value()); | ||
record->stage_reached = static_cast<uint32_t>(stage_); | ||
record->status = static_cast<uint32_t>(status_); | ||
record->primary_bcp_compilation_seconds = primary_bcp_compilation_seconds_; | ||
record->secondary_bcp_compilation_seconds = secondary_bcp_compilation_seconds_; | ||
record->system_server_compilation_seconds = system_server_compilation_seconds_; | ||
record->cache_space_free_start_mib = cache_space_free_start_mib_; | ||
record->cache_space_free_end_mib = cache_space_free_end_mib_; | ||
return true; | ||
} | ||
|
||
void OdrMetrics::WriteToFile(const std::string& path, const OdrMetrics* metrics) { | ||
OdrMetricsRecord record; | ||
if (!metrics->ToRecord(&record)) { | ||
LOG(ERROR) << "Attempting to report metrics without a compilation trigger."; | ||
return; | ||
} | ||
|
||
// Preserve order from frameworks/proto_logging/stats/atoms.proto in metrics file written. | ||
std::ofstream ofs(path); | ||
ofs << record; | ||
} | ||
|
||
} // namespace odrefresh | ||
} // namespace art |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* 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_ODR_METRICS_H_ | ||
#define ART_ODREFRESH_ODR_METRICS_H_ | ||
|
||
#include <chrono> | ||
#include <cstdint> | ||
#include <iosfwd> | ||
#include <optional> | ||
#include <string> | ||
|
||
#include "base/macros.h" | ||
#include "odr_metrics_record.h" | ||
|
||
namespace art { | ||
namespace odrefresh { | ||
|
||
class OdrMetrics final { | ||
public: | ||
// Enumeration used to track the latest stage reached running odrefresh. | ||
// | ||
// These values mirror those in OdrefreshReported::Stage in frameworks/proto_logging/atoms.proto. | ||
// NB There are gaps between the values in case an additional stages are introduced. | ||
enum class Stage : uint8_t { | ||
kUnknown = 0, | ||
kCheck = 10, | ||
kPreparation = 20, | ||
kPrimaryBootClasspath = 30, | ||
kSecondaryBootClasspath = 40, | ||
kSystemServerClasspath = 50, | ||
kComplete = 60, | ||
}; | ||
|
||
// Enumeration describing the overall status, processing stops on the first error discovered. | ||
// | ||
// These values mirror those in OdrefreshReported::Status in frameworks/proto_logging/atoms.proto. | ||
enum class Status : uint8_t { | ||
kUnknown = 0, | ||
kOK = 1, | ||
kNoSpace = 2, | ||
kIoError = 3, | ||
kDex2OatError = 4, | ||
kTimeLimitExceeded = 5, | ||
kStagingFailed = 6, | ||
kInstallFailed = 7, | ||
}; | ||
|
||
// Enumeration describing the cause of compilation (if any) in odrefresh. | ||
// | ||
// These values mirror those in OdrefreshReported::Trigger in | ||
// frameworks/proto_logging/atoms.proto. | ||
enum class Trigger : uint8_t { | ||
kUnknown = 0, | ||
kApexVersionMismatch = 1, | ||
kDexFilesChanged = 2, | ||
kMissingArtifacts = 3, | ||
}; | ||
|
||
explicit OdrMetrics(const std::string& cache_directory, | ||
const std::string& metrics_file = kOdrefreshMetricsFile); | ||
~OdrMetrics(); | ||
|
||
// Sets the ART APEX that metrics are being collected on behalf of. | ||
void SetArtApexVersion(int64_t version) { | ||
art_apex_version_ = version; | ||
} | ||
|
||
// Sets the trigger for metrics collection. The trigger is the reason why odrefresh considers | ||
// compilation necessary. Only call this method if compilation is necessary as the presence | ||
// of a trigger means we will try to record and upload metrics. | ||
void SetTrigger(const Trigger trigger) { | ||
trigger_ = trigger; | ||
} | ||
|
||
// Sets the execution status of the current odrefresh processing stage. | ||
void SetStatus(const Status status) { | ||
status_ = status; | ||
} | ||
|
||
// Sets the current odrefresh processing stage. | ||
void SetStage(Stage stage); | ||
|
||
// Record metrics into an OdrMetricsRecord. | ||
// returns true on success, false if instance is not valid (because the trigger value is not set). | ||
bool ToRecord(/*out*/OdrMetricsRecord* record) const; | ||
|
||
private: | ||
OdrMetrics(const OdrMetrics&) = delete; | ||
OdrMetrics operator=(const OdrMetrics&) = delete; | ||
|
||
static int32_t GetFreeSpaceMiB(const std::string& path); | ||
static void WriteToFile(const std::string& path, const OdrMetrics* metrics); | ||
|
||
void SetCompilationTime(int32_t seconds); | ||
|
||
const std::string cache_directory_; | ||
const std::string metrics_file_; | ||
|
||
int64_t art_apex_version_ = 0; | ||
std::optional<Trigger> trigger_ = {}; // metrics are only logged if compilation is triggered. | ||
Stage stage_ = Stage::kUnknown; | ||
Status status_ = Status::kUnknown; | ||
|
||
int32_t primary_bcp_compilation_seconds_ = 0; | ||
int32_t secondary_bcp_compilation_seconds_ = 0; | ||
int32_t system_server_compilation_seconds_ = 0; | ||
int32_t cache_space_free_start_mib_ = 0; | ||
int32_t cache_space_free_end_mib_ = 0; | ||
|
||
friend class ScopedOdrCompilationTimer; | ||
}; | ||
|
||
// Timer used to measure compilation time (in seconds). Automatically associates the time recorded | ||
// with the current stage of the metrics used. | ||
class ScopedOdrCompilationTimer final { | ||
public: | ||
explicit ScopedOdrCompilationTimer(OdrMetrics& metrics) : | ||
metrics_(metrics), start_(std::chrono::steady_clock::now()) {} | ||
|
||
~ScopedOdrCompilationTimer() { | ||
auto elapsed_time = std::chrono::steady_clock::now() - start_; | ||
auto elapsed_seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed_time); | ||
metrics_.SetCompilationTime(static_cast<int32_t>(elapsed_seconds.count())); | ||
} | ||
|
||
private: | ||
OdrMetrics& metrics_; | ||
std::chrono::time_point<std::chrono::steady_clock> start_; | ||
|
||
DISALLOW_ALLOCATION(); | ||
}; | ||
|
||
// Generated ostream operators. | ||
std::ostream& operator<<(std::ostream& os, OdrMetrics::Status status); | ||
std::ostream& operator<<(std::ostream& os, OdrMetrics::Stage stage); | ||
std::ostream& operator<<(std::ostream& os, OdrMetrics::Trigger trigger); | ||
|
||
} // namespace odrefresh | ||
} // namespace art | ||
|
||
#endif // ART_ODREFRESH_ODR_METRICS_H_ |
Oops, something went wrong.