xref: /aosp_15_r20/external/perfetto/src/profiling/perf/event_config_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/profiling/perf/event_config.h"
18 
19 #include <linux/perf_event.h>
20 #include <stdint.h>
21 #include <time.h>
22 #include <optional>
23 
24 #include "perfetto/base/logging.h"
25 #include "test/gtest_and_gmock.h"
26 
27 #include "protos/perfetto/common/perf_events.gen.h"
28 #include "protos/perfetto/config/data_source_config.gen.h"
29 #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
30 
31 using ::testing::UnorderedElementsAreArray;
32 
33 namespace perfetto {
34 namespace profiling {
35 namespace {
36 
IsPowerOfTwo(size_t v)37 bool IsPowerOfTwo(size_t v) {
38   return (v != 0 && ((v & (v - 1)) == 0));
39 }
40 
CreateEventConfig(const protos::gen::PerfEventConfig & perf_cfg,EventConfig::tracepoint_id_fn_t tracepoint_id_lookup=[](const std::string &,const std::string &){})41 std::optional<EventConfig> CreateEventConfig(
42     const protos::gen::PerfEventConfig& perf_cfg,
43     EventConfig::tracepoint_id_fn_t tracepoint_id_lookup =
44         [](const std::string&, const std::string&) { return 0; }) {
45   protos::gen::DataSourceConfig ds_cfg;
46   ds_cfg.set_perf_event_config_raw(perf_cfg.SerializeAsString());
47   return EventConfig::Create(perf_cfg, ds_cfg,
48                              /*process_sharding=*/std::nullopt,
49                              tracepoint_id_lookup);
50 }
51 
TEST(EventConfigTest,AttrStructConstructed)52 TEST(EventConfigTest, AttrStructConstructed) {
53   protos::gen::PerfEventConfig cfg;
54   std::optional<EventConfig> event_config = CreateEventConfig(cfg);
55 
56   ASSERT_TRUE(event_config.has_value());
57   ASSERT_TRUE(event_config->perf_attr() != nullptr);
58 }
59 
TEST(EventConfigTest,RingBufferPagesValidated)60 TEST(EventConfigTest, RingBufferPagesValidated) {
61   {  // if unset, a default is used
62     protos::gen::PerfEventConfig cfg;
63     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
64 
65     ASSERT_TRUE(event_config.has_value());
66     ASSERT_GT(event_config->ring_buffer_pages(), 0u);
67     ASSERT_TRUE(IsPowerOfTwo(event_config->ring_buffer_pages()));
68   }
69   {  // power of two pages accepted
70     uint32_t num_pages = 128;
71     protos::gen::PerfEventConfig cfg;
72     cfg.set_ring_buffer_pages(num_pages);
73     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
74 
75     ASSERT_TRUE(event_config.has_value());
76     ASSERT_EQ(event_config->ring_buffer_pages(), num_pages);
77   }
78   {  // entire config rejected if not a power of two of pages
79     protos::gen::PerfEventConfig cfg;
80     cfg.set_ring_buffer_pages(7);
81     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
82 
83     ASSERT_FALSE(event_config.has_value());
84   }
85 }
86 
TEST(EventConfigTest,ReadTickPeriodDefaultedIfUnset)87 TEST(EventConfigTest, ReadTickPeriodDefaultedIfUnset) {
88   {  // if unset, a default is used
89     protos::gen::PerfEventConfig cfg;
90     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
91 
92     ASSERT_TRUE(event_config.has_value());
93     ASSERT_GT(event_config->read_tick_period_ms(), 0u);
94   }
95   {  // otherwise, given value used
96     uint32_t period_ms = 250;
97     protos::gen::PerfEventConfig cfg;
98     cfg.set_ring_buffer_read_period_ms(period_ms);
99     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
100 
101     ASSERT_TRUE(event_config.has_value());
102     ASSERT_EQ(event_config->read_tick_period_ms(), period_ms);
103   }
104 }
105 
TEST(EventConfigTest,RemotePeriodTimeoutDefaultedIfUnset)106 TEST(EventConfigTest, RemotePeriodTimeoutDefaultedIfUnset) {
107   {  // if unset, a default is used
108     protos::gen::PerfEventConfig cfg;
109     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
110 
111     ASSERT_TRUE(event_config.has_value());
112     ASSERT_GT(event_config->remote_descriptor_timeout_ms(), 0u);
113   }
114   {  // otherwise, given value used
115     uint32_t timeout_ms = 300;
116     protos::gen::PerfEventConfig cfg;
117     cfg.set_remote_descriptor_timeout_ms(timeout_ms);
118     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
119 
120     ASSERT_TRUE(event_config.has_value());
121     ASSERT_EQ(event_config->remote_descriptor_timeout_ms(), timeout_ms);
122   }
123 }
124 
TEST(EventConfigTest,SelectSamplingInterval)125 TEST(EventConfigTest, SelectSamplingInterval) {
126   {  // period:
127     protos::gen::PerfEventConfig cfg;
128     cfg.mutable_timebase()->set_period(100);
129     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
130 
131     ASSERT_TRUE(event_config.has_value());
132     EXPECT_FALSE(event_config->perf_attr()->freq);
133     EXPECT_EQ(event_config->perf_attr()->sample_period, 100u);
134   }
135   {  // frequency:
136     protos::gen::PerfEventConfig cfg;
137     cfg.mutable_timebase()->set_frequency(4000);
138     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
139 
140     ASSERT_TRUE(event_config.has_value());
141     EXPECT_TRUE(event_config->perf_attr()->freq);
142     EXPECT_EQ(event_config->perf_attr()->sample_freq, 4000u);
143   }
144   {  // legacy frequency field:
145     protos::gen::PerfEventConfig cfg;
146     cfg.set_sampling_frequency(5000);
147     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
148 
149     ASSERT_TRUE(event_config.has_value());
150     EXPECT_TRUE(event_config->perf_attr()->freq);
151     EXPECT_EQ(event_config->perf_attr()->sample_freq, 5000u);
152   }
153   {  // default is 10 Hz (implementation-defined)
154     protos::gen::PerfEventConfig cfg;
155     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
156 
157     ASSERT_TRUE(event_config.has_value());
158     EXPECT_TRUE(event_config->perf_attr()->freq);
159     EXPECT_EQ(event_config->perf_attr()->sample_freq, 10u);
160   }
161 }
162 
TEST(EventConfigTest,SelectTimebaseEvent)163 TEST(EventConfigTest, SelectTimebaseEvent) {
164   auto id_lookup = [](const std::string& group, const std::string& name) {
165     return (group == "sched" && name == "sched_switch") ? 42 : 0;
166   };
167 
168   {
169     protos::gen::PerfEventConfig cfg;
170     protos::gen::PerfEvents::Tracepoint* mutable_tracepoint =
171         cfg.mutable_timebase()->mutable_tracepoint();
172     mutable_tracepoint->set_name("sched:sched_switch");
173 
174     std::optional<EventConfig> event_config = CreateEventConfig(cfg, id_lookup);
175 
176     ASSERT_TRUE(event_config.has_value());
177     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_TRACEPOINT);
178     EXPECT_EQ(event_config->perf_attr()->config, 42u);
179   }
180   {  // default is the CPU timer:
181     protos::gen::PerfEventConfig cfg;
182     cfg.mutable_timebase()->set_frequency(1000);
183     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
184 
185     ASSERT_TRUE(event_config.has_value());
186     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_SOFTWARE);
187     EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_SW_CPU_CLOCK);
188   }
189 }
190 
TEST(EventConfigTest,ParseTargetfilter)191 TEST(EventConfigTest, ParseTargetfilter) {
192   {
193     protos::gen::PerfEventConfig cfg;
194     auto* mutable_scope = cfg.mutable_callstack_sampling()->mutable_scope();
195     mutable_scope->add_target_pid(42);
196     mutable_scope->add_target_cmdline("traced_probes");
197     mutable_scope->add_target_cmdline("traced");
198     mutable_scope->set_additional_cmdline_count(3);
199     mutable_scope->add_exclude_cmdline("heapprofd");
200 
201     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
202 
203     ASSERT_TRUE(event_config.has_value());
204     const auto& filter = event_config->filter();
205     EXPECT_THAT(filter.pids, UnorderedElementsAreArray({42}));
206     EXPECT_THAT(filter.cmdlines,
207                 UnorderedElementsAreArray({"traced_probes", "traced"}));
208     EXPECT_EQ(filter.additional_cmdline_count, 3u);
209     EXPECT_TRUE(filter.exclude_pids.empty());
210     EXPECT_THAT(filter.exclude_cmdlines,
211                 UnorderedElementsAreArray({"heapprofd"}));
212   }
213   {  // legacy:
214     protos::gen::PerfEventConfig cfg;
215     cfg.set_all_cpus(true);
216     cfg.add_target_pid(42);
217     cfg.add_target_cmdline("traced_probes");
218     cfg.add_target_cmdline("traced");
219     cfg.set_additional_cmdline_count(3);
220     cfg.add_exclude_cmdline("heapprofd");
221 
222     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
223 
224     ASSERT_TRUE(event_config.has_value());
225     const auto& filter = event_config->filter();
226     EXPECT_THAT(filter.pids, UnorderedElementsAreArray({42}));
227     EXPECT_THAT(filter.cmdlines,
228                 UnorderedElementsAreArray({"traced_probes", "traced"}));
229     EXPECT_EQ(filter.additional_cmdline_count, 3u);
230     EXPECT_TRUE(filter.exclude_pids.empty());
231     EXPECT_THAT(filter.exclude_cmdlines,
232                 UnorderedElementsAreArray({"heapprofd"}));
233   }
234 }
235 
TEST(EventConfigTest,CounterOnlyModeDetection)236 TEST(EventConfigTest, CounterOnlyModeDetection) {
237   {  // hardware counter:
238     protos::gen::PerfEventConfig cfg;
239     auto* mutable_timebase = cfg.mutable_timebase();
240     mutable_timebase->set_period(500);
241     mutable_timebase->set_counter(protos::gen::PerfEvents::HW_CPU_CYCLES);
242 
243     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
244 
245     ASSERT_TRUE(event_config.has_value());
246     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_HARDWARE);
247     EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_HW_CPU_CYCLES);
248     EXPECT_EQ(event_config->perf_attr()->sample_type &
249                   (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
250               0u);
251   }
252   {  // software counter:
253     protos::gen::PerfEventConfig cfg;
254     auto* mutable_timebase = cfg.mutable_timebase();
255     mutable_timebase->set_period(500);
256     mutable_timebase->set_counter(protos::gen::PerfEvents::SW_PAGE_FAULTS);
257 
258     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
259 
260     ASSERT_TRUE(event_config.has_value());
261     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_SOFTWARE);
262     EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_SW_PAGE_FAULTS);
263     EXPECT_EQ(event_config->perf_attr()->sample_type &
264                   (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
265               0u);
266   }
267 }
268 
TEST(EventConfigTest,CallstackSamplingModeDetection)269 TEST(EventConfigTest, CallstackSamplingModeDetection) {
270   {  // set-but-empty |callstack_sampling| field enables userspace callstacks
271     protos::gen::PerfEventConfig cfg;
272     cfg.mutable_callstack_sampling();  // set field
273 
274     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
275 
276     ASSERT_TRUE(event_config.has_value());
277     EXPECT_TRUE(event_config->sample_callstacks());
278     EXPECT_TRUE(event_config->user_frames());
279     EXPECT_FALSE(event_config->kernel_frames());
280     EXPECT_EQ(
281         event_config->perf_attr()->sample_type &
282             (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
283         static_cast<uint64_t>(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER));
284 
285     EXPECT_NE(event_config->perf_attr()->sample_regs_user, 0u);
286     EXPECT_NE(event_config->perf_attr()->sample_stack_user, 0u);
287   }
288   {  // kernel-only callstacks
289     protos::gen::PerfEventConfig cfg;
290     cfg.mutable_callstack_sampling()->set_kernel_frames(true);
291     cfg.mutable_callstack_sampling()->set_user_frames(
292         protos::gen::PerfEventConfig::UNWIND_SKIP);
293 
294     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
295 
296     ASSERT_TRUE(event_config.has_value());
297     EXPECT_TRUE(event_config->sample_callstacks());
298     EXPECT_FALSE(event_config->user_frames());
299     EXPECT_TRUE(event_config->kernel_frames());
300     EXPECT_EQ(event_config->perf_attr()->sample_type &
301                   (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
302               0u);
303     EXPECT_EQ(event_config->perf_attr()->sample_type & (PERF_SAMPLE_CALLCHAIN),
304               static_cast<uint64_t>(PERF_SAMPLE_CALLCHAIN));
305 
306     EXPECT_EQ(event_config->perf_attr()->sample_regs_user, 0u);
307     EXPECT_EQ(event_config->perf_attr()->sample_stack_user, 0u);
308 
309     EXPECT_NE(event_config->perf_attr()->exclude_callchain_user, 0u);
310   }
311 }
312 
TEST(EventConfigTest,EnableKernelFrames)313 TEST(EventConfigTest, EnableKernelFrames) {
314   {
315     protos::gen::PerfEventConfig cfg;
316     cfg.mutable_callstack_sampling()->set_kernel_frames(true);
317     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
318 
319     ASSERT_TRUE(event_config.has_value());
320     EXPECT_TRUE(event_config->kernel_frames());
321   }
322   {  // legacy config:
323     protos::gen::PerfEventConfig cfg;
324     cfg.set_all_cpus(true);  // used to detect compat mode
325     cfg.set_kernel_frames(true);
326     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
327 
328     ASSERT_TRUE(event_config.has_value());
329     EXPECT_TRUE(event_config->kernel_frames());
330   }
331   {  // default is false
332     protos::gen::PerfEventConfig cfg;
333     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
334 
335     ASSERT_TRUE(event_config.has_value());
336     EXPECT_FALSE(event_config->kernel_frames());
337   }
338 }
339 
TEST(EventConfigTest,TimestampClockId)340 TEST(EventConfigTest, TimestampClockId) {
341   {  // if unset, a default is used
342     protos::gen::PerfEventConfig cfg;
343     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
344 
345     ASSERT_TRUE(event_config.has_value());
346     EXPECT_TRUE(event_config->perf_attr()->use_clockid);
347     EXPECT_EQ(event_config->perf_attr()->clockid, CLOCK_MONOTONIC_RAW);
348   }
349   {  // explicit boottime
350     protos::gen::PerfEventConfig cfg;
351     cfg.mutable_timebase()->set_timestamp_clock(
352         protos::gen::PerfEvents::PERF_CLOCK_BOOTTIME);
353     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
354 
355     ASSERT_TRUE(event_config.has_value());
356     EXPECT_TRUE(event_config->perf_attr()->use_clockid);
357     EXPECT_EQ(event_config->perf_attr()->clockid, CLOCK_BOOTTIME);
358   }
359   {  // explicit monotonic
360     protos::gen::PerfEventConfig cfg;
361     cfg.mutable_timebase()->set_timestamp_clock(
362         protos::gen::PerfEvents::PERF_CLOCK_MONOTONIC);
363     std::optional<EventConfig> event_config = CreateEventConfig(cfg);
364 
365     ASSERT_TRUE(event_config.has_value());
366     EXPECT_TRUE(event_config->perf_attr()->use_clockid);
367     EXPECT_EQ(event_config->perf_attr()->clockid, CLOCK_MONOTONIC);
368   }
369 }
370 
TEST(EventConfigTest,GroupMultipleType)371 TEST(EventConfigTest, GroupMultipleType) {
372   protos::gen::PerfEventConfig cfg;
373   {
374     // timebase:
375     auto* mutable_timebase = cfg.mutable_timebase();
376     mutable_timebase->set_period(500);
377     mutable_timebase->set_counter(protos::gen::PerfEvents::HW_CPU_CYCLES);
378     mutable_timebase->set_name("timebase");
379 
380     // raw follower:
381     auto* raw_follower = cfg.add_followers();
382     raw_follower->set_name("raw");
383     auto* raw_event = raw_follower->mutable_raw_event();
384     raw_event->set_type(8);
385     raw_event->set_config(8);
386 
387     // HW counter follower:
388     auto* counter_follower = cfg.add_followers();
389     counter_follower->set_name("counter");
390     counter_follower->set_counter(
391         protos::gen::PerfEvents::HW_BRANCH_INSTRUCTIONS);
392 
393     // tracepoint follower:
394     auto* tracepoint_follower = cfg.add_followers();
395     tracepoint_follower->set_name("tracepoint");
396     auto* tracepoint_event = tracepoint_follower->mutable_tracepoint();
397     tracepoint_event->set_name("sched:sched_switch");
398   }
399 
400   auto id_lookup = [](const std::string& group, const std::string& name) {
401     return (group == "sched" && name == "sched_switch") ? 42 : 0;
402   };
403   std::optional<EventConfig> event_config = CreateEventConfig(cfg, id_lookup);
404 
405   ASSERT_TRUE(event_config.has_value());
406   EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_HARDWARE);
407   EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_HW_CPU_CYCLES);
408   EXPECT_EQ(event_config->perf_attr()->sample_type &
409                 (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
410             0u);
411   EXPECT_EQ(event_config->perf_attr()->read_format, PERF_FORMAT_GROUP);
412 
413   ASSERT_EQ(event_config->perf_attr_followers().size(), 3u);
414 
415   const auto& raw_event = event_config->perf_attr_followers().at(0);
416   EXPECT_EQ(raw_event.type, 8u);
417   EXPECT_EQ(raw_event.config, 8u);
418   EXPECT_TRUE(raw_event.sample_type & PERF_SAMPLE_READ);
419 
420   const auto& hw_counter = event_config->perf_attr_followers().at(1);
421   EXPECT_EQ(hw_counter.type, PERF_TYPE_HARDWARE);
422   EXPECT_EQ(hw_counter.config, PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
423   EXPECT_TRUE(hw_counter.sample_type & PERF_SAMPLE_READ);
424 
425   const auto& tracepoint = event_config->perf_attr_followers().at(2);
426   EXPECT_EQ(tracepoint.type, PERF_TYPE_TRACEPOINT);
427   EXPECT_EQ(tracepoint.config, 42u);
428   EXPECT_TRUE(tracepoint.sample_type & PERF_SAMPLE_READ);
429 }
430 
431 }  // namespace
432 }  // namespace profiling
433 }  // namespace perfetto
434