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