1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/call_stacks/child_call_stack_profile_collector.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/functional/bind.h"
12 #include "base/run_loop.h"
13 #include "base/test/task_environment.h"
14 #include "base/time/time.h"
15 #include "components/metrics/public/mojom/call_stack_profile_collector.mojom.h"
16 #include "mojo/public/cpp/bindings/pending_receiver.h"
17 #include "mojo/public/cpp/bindings/receiver.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/metrics_proto/sampled_profile.pb.h"
20
21 namespace metrics {
22
23 class ChildCallStackProfileCollectorTest : public ::testing::Test {
24 protected:
25 struct ReceivedProfile {
26 base::TimeTicks start_timestamp;
27 mojom::ProfileType profile_type;
28 };
29
30 class Receiver : public mojom::CallStackProfileCollector {
31 public:
Receiver(mojo::PendingReceiver<mojom::CallStackProfileCollector> receiver)32 explicit Receiver(
33 mojo::PendingReceiver<mojom::CallStackProfileCollector> receiver)
34 : receiver_(this, std::move(receiver)) {}
35
36 Receiver(const Receiver&) = delete;
37 Receiver& operator=(const Receiver&) = delete;
38
~Receiver()39 ~Receiver() override {}
40
Collect(base::TimeTicks start_timestamp,mojom::ProfileType profile_type,mojom::SampledProfilePtr profile)41 void Collect(base::TimeTicks start_timestamp,
42 mojom::ProfileType profile_type,
43 mojom::SampledProfilePtr profile) override {
44 profiles_.push_back({start_timestamp, profile_type});
45 }
46
profiles()47 std::vector<ReceivedProfile>& profiles() { return profiles_; }
48
49 private:
50 mojo::Receiver<mojom::CallStackProfileCollector> receiver_;
51 std::vector<ReceivedProfile> profiles_;
52 };
53
ChildCallStackProfileCollectorTest()54 ChildCallStackProfileCollectorTest()
55 : receiver_impl_(std::make_unique<Receiver>(
56 collector_remote_.InitWithNewPipeAndPassReceiver())) {}
57
58 ChildCallStackProfileCollectorTest(
59 const ChildCallStackProfileCollectorTest&) = delete;
60 ChildCallStackProfileCollectorTest& operator=(
61 const ChildCallStackProfileCollectorTest&) = delete;
62
63 // Collects a profile and returns its start timestamp.
CollectProfile(SampledProfile::TriggerEvent trigger_event)64 base::TimeTicks CollectProfile(SampledProfile::TriggerEvent trigger_event) {
65 base::TimeTicks start_timestamp = task_environment_.NowTicks();
66 SampledProfile profile;
67 profile.set_trigger_event(trigger_event);
68 child_collector_.Collect(start_timestamp, std::move(profile));
69 return start_timestamp;
70 }
71
ExpectProfile(const ReceivedProfile & profile,base::TimeTicks expected_start_timestamp,mojom::ProfileType expected_profile_type)72 void ExpectProfile(const ReceivedProfile& profile,
73 base::TimeTicks expected_start_timestamp,
74 mojom::ProfileType expected_profile_type) {
75 EXPECT_EQ(expected_start_timestamp, profile.start_timestamp);
76 EXPECT_EQ(expected_profile_type, profile.profile_type);
77 }
78
ExpectProfile(const ChildCallStackProfileCollector::ProfileState & profile,base::TimeTicks expected_start_timestamp,mojom::ProfileType expected_profile_type)79 void ExpectProfile(
80 const ChildCallStackProfileCollector::ProfileState& profile,
81 base::TimeTicks expected_start_timestamp,
82 mojom::ProfileType expected_profile_type) {
83 EXPECT_EQ(expected_start_timestamp, profile.start_timestamp);
84 EXPECT_EQ(expected_profile_type, profile.profile_type);
85 }
86
profiles() const87 const std::vector<ChildCallStackProfileCollector::ProfileState>& profiles()
88 const {
89 return child_collector_.profiles_;
90 }
91
92 base::test::SingleThreadTaskEnvironment task_environment_{
93 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
94 mojo::PendingRemote<mojom::CallStackProfileCollector> collector_remote_;
95 std::unique_ptr<Receiver> receiver_impl_;
96 ChildCallStackProfileCollector child_collector_;
97 };
98
99 // Test the behavior when an interface is provided.
TEST_F(ChildCallStackProfileCollectorTest,InterfaceProvided)100 TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
101 EXPECT_EQ(0u, profiles().size());
102
103 // Add a profile before providing the interface.
104 base::TimeTicks start_timestamp =
105 CollectProfile(SampledProfile::PERIODIC_COLLECTION);
106 ASSERT_EQ(1u, profiles().size());
107 ExpectProfile(profiles()[0], start_timestamp, mojom::ProfileType::kCPU);
108
109 // Set the interface. The profiles should be passed to it.
110 child_collector_.SetParentProfileCollector(std::move(collector_remote_));
111 task_environment_.FastForwardBy(base::Milliseconds(1));
112 EXPECT_EQ(0u, profiles().size());
113 ASSERT_EQ(1u, receiver_impl_->profiles().size());
114 ExpectProfile(receiver_impl_->profiles()[0], start_timestamp,
115 mojom::ProfileType::kCPU);
116
117 // Add a profile after providing the interface. It should also be passed.
118 receiver_impl_->profiles().clear();
119 base::TimeTicks start_timestamp2 =
120 CollectProfile(SampledProfile::PERIODIC_COLLECTION);
121 task_environment_.FastForwardBy(base::Milliseconds(1));
122 EXPECT_EQ(0u, profiles().size());
123 ASSERT_EQ(1u, receiver_impl_->profiles().size());
124 ExpectProfile(receiver_impl_->profiles()[0], start_timestamp2,
125 mojom::ProfileType::kCPU);
126 }
127
TEST_F(ChildCallStackProfileCollectorTest,InterfaceNotProvided)128 TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
129 EXPECT_EQ(0u, profiles().size());
130
131 // Add a profile before providing a null interface.
132 base::TimeTicks start_timestamp =
133 CollectProfile(SampledProfile::PERIODIC_COLLECTION);
134 ASSERT_EQ(1u, profiles().size());
135 ExpectProfile(profiles()[0], start_timestamp, mojom::ProfileType::kCPU);
136
137 // Set the null interface. The profile should be flushed.
138 child_collector_.SetParentProfileCollector(mojo::NullRemote());
139 task_environment_.FastForwardBy(base::Milliseconds(1));
140 EXPECT_EQ(0u, profiles().size());
141 EXPECT_EQ(0u, receiver_impl_->profiles().size());
142
143 // Add a profile after providing a null interface. They should also be
144 // flushed.
145 CollectProfile(SampledProfile::PERIODIC_COLLECTION);
146 task_environment_.FastForwardBy(base::Milliseconds(1));
147 EXPECT_EQ(0u, profiles().size());
148 EXPECT_EQ(0u, receiver_impl_->profiles().size());
149 }
150
151 // Tests that the `profile_type` parameter is set correctly when heap profiles
152 // pass through the interface.
TEST_F(ChildCallStackProfileCollectorTest,HeapProfiles)153 TEST_F(ChildCallStackProfileCollectorTest, HeapProfiles) {
154 EXPECT_EQ(0u, profiles().size());
155
156 // Add a profile before providing the interface.
157 base::TimeTicks start_timestamp =
158 CollectProfile(SampledProfile::PERIODIC_HEAP_COLLECTION);
159 ASSERT_EQ(1u, profiles().size());
160 ExpectProfile(profiles()[0], start_timestamp, mojom::ProfileType::kHeap);
161
162 // Set the interface. The profile type should pass to it unchanged.
163 child_collector_.SetParentProfileCollector(std::move(collector_remote_));
164 task_environment_.FastForwardBy(base::Milliseconds(1));
165 ASSERT_EQ(1u, receiver_impl_->profiles().size());
166 ExpectProfile(receiver_impl_->profiles()[0], start_timestamp,
167 mojom::ProfileType::kHeap);
168 }
169
170 } // namespace metrics
171