File: metric_provider.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (287 lines) | stat: -rw-r--r-- 11,760 bytes parent folder | download | duplicates (6)
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