1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/perf/metric_provider.h"
#include "ash/constants/ash_features.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/metrics_proto/device_state.pb.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
namespace {
// Name prefix of the histogram that counts the number of reports uploaded by a
// metric provider.
const char kUploadCountHistogramPrefix[] = "ChromeOS.CWP.Upload";
// Name prefix of the histogram that tracks the various outcomes of saving the
// collected profile to local cache.
const char kRecordOutcomeHistogramPrefix[] = "ChromeOS.CWP.Record";
// An upper bound on the count of reports expected to be uploaded by an UMA
// callback.
const int kMaxValueUploadReports = 10;
// The MD5 prefix to replace the original comm_md5_prefix of COMM events in perf
// data proto, if necessary. We used string "<redacted>" to compute this MD5
// prefix.
const uint64_t kRedactedCommMd5Prefix = 0xee1f021828a1fcbc;
// This function modifies the comm_md5_prefix of all the COMM events in the
// given perf data proto by replacing it with the md5 prefix of an artificial
// string.
void RedactCommMd5Prefixes(PerfDataProto* proto) {
for (PerfDataProto::PerfEvent& event : *proto->mutable_events()) {
if (event.has_comm_event()) {
event.mutable_comm_event()->set_comm_md5_prefix(kRedactedCommMd5Prefix);
}
}
}
ThermalState ToProtoThermalStateEnum(
base::PowerThermalObserver::DeviceThermalState state) {
switch (state) {
case base::PowerThermalObserver::DeviceThermalState::kUnknown:
return THERMAL_STATE_UNKNOWN;
case base::PowerThermalObserver::DeviceThermalState::kNominal:
return THERMAL_STATE_NOMINAL;
case base::PowerThermalObserver::DeviceThermalState::kFair:
return THERMAL_STATE_FAIR;
case base::PowerThermalObserver::DeviceThermalState::kSerious:
return THERMAL_STATE_SERIOUS;
case base::PowerThermalObserver::DeviceThermalState::kCritical:
return THERMAL_STATE_CRITICAL;
}
}
} // namespace
using MetricCollector = internal::MetricCollector;
MetricProvider::MetricProvider(std::unique_ptr<MetricCollector> collector,
ProfileManager* profile_manager)
: upload_uma_histogram_(std::string(kUploadCountHistogramPrefix) +
collector->ToolName()),
record_uma_histogram_(std::string(kRecordOutcomeHistogramPrefix) +
collector->ToolName()),
// Run the collector at a higher priority to enable fast triggering of
// profile collections. In particular, we want fast triggering when
// jankiness is detected, but even random based periodic collection
// benefits from a higher priority, to avoid biasing the collection to
// times when the system is not busy. The work performed on the dedicated
// sequence is short and infrequent. Expensive parsing operations are
// executed asynchronously on the thread pool.
collector_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_VISIBLE})),
metric_collector_(std::move(collector)),
profile_manager_(profile_manager),
weak_factory_(this) {
metric_collector_->set_profile_done_callback(base::BindRepeating(
&MetricProvider::OnProfileDone, weak_factory_.GetWeakPtr()));
}
MetricProvider::~MetricProvider() {
// Destroy the metric_collector_ on the collector sequence.
collector_task_runner_->PostTask(
FROM_HERE, base::DoNothingWithBoundArgs(std::move(metric_collector_)));
}
void MetricProvider::Init() {
// It is safe to use base::Unretained to post tasks to the metric_collector_
// on the collector sequence, since we control its lifetime. Any tasks
// posted to it are bound to run before we destroy it on the collector
// sequence.
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::Init,
base::Unretained(metric_collector_.get())));
}
bool MetricProvider::GetSampledProfiles(
std::vector<SampledProfile>* sampled_profiles) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (cached_profile_data_.empty()) {
base::UmaHistogramExactLinear(upload_uma_histogram_, 0,
kMaxValueUploadReports);
return false;
}
base::UmaHistogramExactLinear(upload_uma_histogram_,
cached_profile_data_.size(),
kMaxValueUploadReports);
sampled_profiles->insert(
sampled_profiles->end(),
std::make_move_iterator(cached_profile_data_.begin()),
std::make_move_iterator(cached_profile_data_.end()));
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::ResetCachedDataSize,
base::Unretained(metric_collector_.get())));
cached_profile_data_.clear();
return true;
}
void MetricProvider::OnUserLoggedIn() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const base::TimeTicks now = base::TimeTicks::Now();
collector_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MetricCollector::RecordUserLogin,
base::Unretained(metric_collector_.get()), now));
}
void MetricProvider::Deactivate() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Notifies the collector to turn off the timer. Does not delete any data that
// was already collected and stored in |cached_profile_data|.
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::StopTimer,
base::Unretained(metric_collector_.get())));
}
void MetricProvider::SuspendDone(base::TimeDelta sleep_duration) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::ScheduleSuspendDoneCollection,
base::Unretained(metric_collector_.get()),
sleep_duration));
}
void MetricProvider::OnSessionRestoreDone(int num_tabs_restored) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
collector_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MetricCollector::ScheduleSessionRestoreCollection,
base::Unretained(metric_collector_.get()),
num_tabs_restored));
}
// static
void MetricProvider::OnProfileDone(
base::WeakPtr<MetricProvider> provider,
std::unique_ptr<SampledProfile> sampled_profile) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&MetricProvider::AddProfileToCache, provider,
std::move(sampled_profile)));
}
void MetricProvider::OnJankStarted() {
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::OnJankStarted,
base::Unretained(metric_collector_.get())));
}
void MetricProvider::OnJankStopped() {
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::OnJankStopped,
base::Unretained(metric_collector_.get())));
}
void MetricProvider::EnableRecording() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
recording_enabled_ = true;
}
void MetricProvider::DisableRecording() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
recording_enabled_ = false;
}
MetricProvider::RecordAttemptStatus MetricProvider::AppSyncStateForUserProfile(
Profile* profile) {
syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfile(profile);
if (!sync_service)
return RecordAttemptStatus::kSyncServiceUnavailable;
syncer::SyncUserSettings* sync_settings = sync_service->GetUserSettings();
if (!sync_service->IsSyncFeatureEnabled())
return RecordAttemptStatus::kChromeSyncFeatureDisabled;
if (!sync_settings->GetSelectedOsTypes().Has(
syncer::UserSelectableOsType::kOsApps))
return RecordAttemptStatus::kOSAppSyncDisabled;
return RecordAttemptStatus::kAppSyncEnabled;
}
// Check the current state of App Sync in the settings. This is done by getting
// all currently fully initialized profiles and reading the sync settings from
// them.
MetricProvider::RecordAttemptStatus MetricProvider::GetAppSyncState() {
if (!profile_manager_)
return RecordAttemptStatus::kProfileManagerUnset;
std::vector<Profile*> profiles = profile_manager_->GetLoadedProfiles();
// Tracks the number of user profiles initialized on Chrome OS other than the
// Default profile.
int user_profile_count = 0;
for (Profile* profile : profiles) {
// The Default profile, lock screen app profile and lock screen profile are
// all not regular user profiles on Chrome OS. They always disable sync and
// we would skip them.
if (!ash::ProfileHelper::IsUserProfile(profile))
continue;
auto app_sync_state = AppSyncStateForUserProfile(profile);
if (app_sync_state != RecordAttemptStatus::kAppSyncEnabled)
return app_sync_state;
user_profile_count++;
}
if (user_profile_count == 0)
return RecordAttemptStatus::kNoLoadedProfile;
return RecordAttemptStatus::kAppSyncEnabled;
}
void MetricProvider::AddProfileToCache(
std::unique_ptr<SampledProfile> sampled_profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!recording_enabled_) {
base::UmaHistogramEnumeration(record_uma_histogram_,
RecordAttemptStatus::kRecordingDisabled);
return;
}
// For privacy reasons, Chrome can not collect Android app names that may be
// present in the perf data, unless the user consent to enabling App Sync.
// Therefore, if the user does not enable App Sync, we redact comm_md5_prefix
// in all COMM events of perf data proto, so these MD5 prefixes can not be
// used to recover Android app names. We perform the check on App Sync here
// because the procedure to get the user profile (from which sync settings can
// be obtained) must execute on the UI thread.
auto app_sync_state = GetAppSyncState();
base::UmaHistogramEnumeration(record_uma_histogram_, app_sync_state);
if (app_sync_state != RecordAttemptStatus::kAppSyncEnabled)
RedactCommMd5Prefixes(sampled_profile->mutable_perf_data());
// Add the device thermal state and cpu speed limit to the profile.
sampled_profile->set_thermal_state(ToProtoThermalStateEnum(thermal_state_));
sampled_profile->set_cpu_speed_limit_percent(cpu_speed_limit_percent_);
collector_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricCollector::AddCachedDataDelta,
base::Unretained(metric_collector_.get()),
sampled_profile->ByteSizeLong()));
cached_profile_data_.resize(cached_profile_data_.size() + 1);
cached_profile_data_.back().Swap(sampled_profile.get());
if (!cache_updated_callback_.is_null())
cache_updated_callback_.Run();
}
} // namespace metrics
|