File: profile_provider_unittest_main.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 (347 lines) | stat: -rw-r--r-- 12,867 bytes parent folder | download | duplicates (3)
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
// 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 <tuple>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/test_suite.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/browser/metrics/perf/collection_params.h"
#include "chrome/browser/metrics/perf/metric_provider.h"
#include "chrome/browser/metrics/perf/perf_events_collector.h"
#include "chrome/browser/metrics/perf/profile_provider_chromeos.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
#include "ui/aura/env.h"

namespace metrics {

const base::TimeDelta kPeriodicCollectionInterval = base::Hours(1);
const base::TimeDelta kMaxCollectionDelay = base::Seconds(1);
// Use a 2-sec collection duration.
const base::TimeDelta kCollectionDuration = base::Seconds(2);
// The timeout in waiting until collection done. 20 seconds is a safe value far
// beyond the collection duration used.
const base::TimeDelta kCollectionDoneTimeout = base::Seconds(20);

CollectionParams GetTestCollectionParams() {
  CollectionParams test_params;
  test_params.collection_duration = kCollectionDuration;
  test_params.resume_from_suspend.sampling_factor = 1;
  test_params.resume_from_suspend.max_collection_delay = kMaxCollectionDelay;
  test_params.restore_session.sampling_factor = 1;
  test_params.restore_session.max_collection_delay = kMaxCollectionDelay;
  test_params.periodic_interval = kPeriodicCollectionInterval;
  return test_params;
}

class TestPerfCollector : public PerfCollector {
 public:
  explicit TestPerfCollector(const CollectionParams& params) : PerfCollector() {
    // Override default collection params.
    collection_params() = params;
  }
  ~TestPerfCollector() override = default;

  using internal::MetricCollector::collection_params;
};

class TestMetricProvider : public MetricProvider {
 public:
  using MetricProvider::MetricProvider;
  ~TestMetricProvider() override = default;

  using MetricProvider::set_cache_updated_callback;
};

// Allows access to some private methods for testing.
class TestProfileProvider : public ProfileProvider {
 public:
  TestProfileProvider() : TestProfileProvider(GetTestCollectionParams()) {}

  explicit TestProfileProvider(const CollectionParams& test_params) {
    collectors_.clear();
    auto metric_provider = std::make_unique<TestMetricProvider>(
        std::make_unique<TestPerfCollector>(test_params), nullptr);
    metric_provider->set_cache_updated_callback(base::BindRepeating(
        &TestProfileProvider::OnProfileDone, base::Unretained(this)));

    collectors_.push_back(std::move(metric_provider));
  }

  TestProfileProvider(const TestProfileProvider&) = delete;
  TestProfileProvider& operator=(const TestProfileProvider&) = delete;

  void WaitUntilCollectionDone() {
    // Collection shouldn't be done when this method is called, or the test will
    // waste time in |run_loop_| for the duration of |timeout|.
    EXPECT_FALSE(collection_done());

    timeout_timer_.Start(FROM_HERE, kCollectionDoneTimeout,
                         base::BindLambdaForTesting([&]() {
                           // Collection is not done yet. Quit the run loop to
                           // fail the test.
                           run_loop_.Quit();
                         }));
    // The run loop returns when its Quit() is called either on collection done
    // or on timeout. Note that the second call of Quit() is a noop.
    run_loop_.Run();

    if (timeout_timer_.IsRunning())
      // Timer is still running: the run loop doesn't quit on timeout. Stop the
      // timer.
      timeout_timer_.Stop();
  }

  bool collection_done() { return collection_done_; }

  using ProfileProvider::collectors_;
  using ProfileProvider::OnJankStarted;
  using ProfileProvider::OnJankStopped;
  using ProfileProvider::OnSessionRestoreDone;
  using ProfileProvider::SuspendDone;

 private:
  void OnProfileDone() {
    collection_done_ = true;
    // Notify that profile collection is done. Quitting the run loop is
    // thread-safe.
    run_loop_.Quit();
  }

  base::OneShotTimer timeout_timer_;
  base::RunLoop run_loop_;
  bool collection_done_ = false;
};

// This test doesn't mock any class used indirectly by ProfileProvider to make
// real collections from debugd.
class ProfileProviderRealCollectionTest : public testing::Test {
 public:
  ProfileProviderRealCollectionTest() = default;

  ProfileProviderRealCollectionTest(const ProfileProviderRealCollectionTest&) =
      delete;
  ProfileProviderRealCollectionTest& operator=(
      const ProfileProviderRealCollectionTest&) = delete;

  void SetUp() override {
    ash::DBusThreadManager::Initialize();
    // ProfileProvider requires ash::LoginState and
    // chromeos::PowerManagerClient to be initialized.
    chromeos::PowerManagerClient::InitializeFake();
    ash::LoginState::Initialize();

    // The constructor of ProfileProvider uses g_browser_process thus requiring
    // it to be not null, so initialize it here.
    TestingBrowserProcess::CreateInstance();

    std::map<std::string, std::string> field_trial_params;
    // Only "cycles" event is supported.
    field_trial_params.insert(std::make_pair(
        "PerfCommand::default::0", "50 -- record -a -e cycles -c 1000003"));
    field_trial_params.insert(std::make_pair(
        "PerfCommand::default::1", "50 -- record -a -e cycles -g -c 4000037"));
    ASSERT_TRUE(base::AssociateFieldTrialParams(
        "ChromeOSWideProfilingCollection", "group_name", field_trial_params));
    field_trial_ = base::FieldTrialList::CreateFieldTrial(
        "ChromeOSWideProfilingCollection", "group_name");
    ASSERT_TRUE(field_trial_.get());

    // JankMonitor requires aura::Env to be initialized.
    aura_env_ = aura::Env::CreateInstance();
    profile_provider_ = std::make_unique<TestProfileProvider>();
    profile_provider_->Init();

    // Set user state as logged in. This activates periodic collection, but
    // other triggers like SUSPEND_DONE take precedence.
    ash::LoginState::Get()->SetLoggedInState(
        ash::LoginState::LOGGED_IN_ACTIVE,
        ash::LoginState::LOGGED_IN_USER_REGULAR);

    // Finishes Init() on the dedicated sequence.
    task_environment_.RunUntilIdle();

    StartSpinningCPU();
  }

  void TearDown() override {
    StopSpinningCPU();

    profile_provider_.reset();
    aura_env_.reset();
    TestingBrowserProcess::DeleteInstance();
    ash::LoginState::Shutdown();
    chromeos::PowerManagerClient::Shutdown();
    ash::DBusThreadManager::Shutdown();
    variations::testing::ClearAllVariationParams();
  }

  void AssertProfileData(SampledProfile::TriggerEvent trigger_event) {
    // Log extra information on assertion failure.
    absl::Cleanup scoped_log_error = [] {
      // Collection failed: log the failure in the UMA histogram.
      auto* histogram =
          base::StatisticsRecorder::FindHistogram("ChromeOS.CWP.CollectPerf");
      if (!histogram) {
        LOG(WARNING) << "Profile collection failed without "
                        "ChromeOS.CWP.CollectPerf histogram data";
        return;
      }

      std::string histogram_ascii;
      histogram->WriteAscii(&histogram_ascii);
      LOG(ERROR) << "Profile collection result: " << histogram_ascii;
    };

    std::vector<SampledProfile> stored_profiles;
    ASSERT_TRUE(profile_provider_->GetSampledProfiles(&stored_profiles));

    auto& profile = stored_profiles[0];
    EXPECT_EQ(trigger_event, profile.trigger_event());

    ASSERT_TRUE(profile.has_perf_data());

    // Collection succeeded: don't output the error log.
    std::move(scoped_log_error).Cancel();
  }

 protected:
  // Spins the CPU to move forward the CPU cycles counter and makes sure the
  // perf session always has samples to collect.
  void StartSpinningCPU() {
    spin_cpu_ = true;
    spin_cpu_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner({});
    static constexpr auto spin_duration = base::Milliseconds(1);
    static constexpr auto sleep_duration = base::Milliseconds(9);
    spin_cpu_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(
                       [](ProfileProviderRealCollectionTest* self) {
                         // Spin the CPU nicely: spin for 1 ms per 10 ms so that
                         // we don't take more than 10% of a core.
                         while (self->spin_cpu_) {
                           auto start = base::Time::Now();
                           while (base::Time::Now() - start < spin_duration) {
                           }
                           base::PlatformThread::Sleep(sleep_duration);
                         }
                         // Signal that this task is exiting and won't touch
                         // |this| anymore.
                         self->spin_cpu_done_.Signal();
                       },
                       base::Unretained(this)));
  }

  void StopSpinningCPU() {
    spin_cpu_ = false;

    // Wait until the current sequence is signaled that the CPU spinning task
    // has finished execution so it doesn't use any data member of |this|.
    if (!spin_cpu_done_.IsSignaled())
      spin_cpu_done_.Wait();

    spin_cpu_task_runner_ = nullptr;
  }

  // |task_environment_| must be the first member (or at least before
  // any member that cares about tasks) to be initialized first and destroyed
  // last.
  content::BrowserTaskEnvironment task_environment_;

  scoped_refptr<base::FieldTrial> field_trial_;

  scoped_refptr<base::SequencedTaskRunner> spin_cpu_task_runner_;
  std::atomic_bool spin_cpu_{false};
  base::WaitableEvent spin_cpu_done_;

  std::unique_ptr<aura::Env> aura_env_;
  std::unique_ptr<TestProfileProvider> profile_provider_;
};

// Flaky on chromeos: crbug.com/1184119
TEST_F(ProfileProviderRealCollectionTest, SuspendDone) {
  // Trigger a resume from suspend.
  profile_provider_->SuspendDone(base::Minutes(10));

  profile_provider_->WaitUntilCollectionDone();
  EXPECT_TRUE(profile_provider_->collection_done());

  AssertProfileData(SampledProfile::RESUME_FROM_SUSPEND);
}

TEST_F(ProfileProviderRealCollectionTest, SessionRestoreDone) {
  // Restored 10 tabs.
  profile_provider_->OnSessionRestoreDone(nullptr, 10);

  profile_provider_->WaitUntilCollectionDone();
  EXPECT_TRUE(profile_provider_->collection_done());

  AssertProfileData(SampledProfile::RESTORE_SESSION);
}

TEST_F(ProfileProviderRealCollectionTest, OnJankStarted) {
  // Trigger a resume from suspend.
  profile_provider_->OnJankStarted();

  profile_provider_->WaitUntilCollectionDone();
  EXPECT_TRUE(profile_provider_->collection_done());

  AssertProfileData(SampledProfile::JANKY_TASK);
}

TEST_F(ProfileProviderRealCollectionTest, OnJankStopped) {
  // Override the default collection duration.
  auto test_params_override = GetTestCollectionParams();
  auto full_collection_duration = kCollectionDuration * 2;
  test_params_override.collection_duration = full_collection_duration;

  // Reinitialize |profile_provider_| with the override.
  profile_provider_ =
      std::make_unique<TestProfileProvider>(test_params_override);
  profile_provider_->Init();

  profile_provider_->OnJankStarted();

  // Call ProfileProvider::OnJankStopped() halfway through the collection
  // duration.
  base::OneShotTimer stop_timer;
  base::RunLoop run_loop;
  // The jank lasts for 0.75*(collection duration). We'd like to stop the
  // collection before the full duration elapses.
  stop_timer.Start(FROM_HERE, full_collection_duration * 3 / 4,
                   base::BindLambdaForTesting([&]() {
                     profile_provider_->OnJankStopped();
                     run_loop.Quit();
                   }));
  run_loop.Run();
  // |run_loop| quits only by |stop_timer| so the timer shouldn't be running.
  EXPECT_FALSE(stop_timer.IsRunning());

  profile_provider_->WaitUntilCollectionDone();
  EXPECT_TRUE(profile_provider_->collection_done());

  AssertProfileData(SampledProfile::JANKY_TASK);
}

}  // namespace metrics

int main(int argc, char* argv[]) {
  base::CommandLine::Init(argc, argv);
  return base::RunUnitTestsUsingBaseTestSuite(argc, argv);
}