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