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
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/optional.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
AsDataSourceConfig(const protos::gen::PerfEventConfig & perf_cfg)41 static DataSourceConfig AsDataSourceConfig(
42 const protos::gen::PerfEventConfig& perf_cfg) {
43 protos::gen::DataSourceConfig ds_cfg;
44 ds_cfg.set_perf_event_config_raw(perf_cfg.SerializeAsString());
45 return ds_cfg;
46 }
47
TEST(EventConfigTest,AttrStructConstructed)48 TEST(EventConfigTest, AttrStructConstructed) {
49 protos::gen::PerfEventConfig cfg;
50 base::Optional<EventConfig> event_config =
51 EventConfig::Create(AsDataSourceConfig(cfg));
52
53 ASSERT_TRUE(event_config.has_value());
54 ASSERT_TRUE(event_config->perf_attr() != nullptr);
55 }
56
TEST(EventConfigTest,RingBufferPagesValidated)57 TEST(EventConfigTest, RingBufferPagesValidated) {
58 { // if unset, a default is used
59 protos::gen::PerfEventConfig cfg;
60 base::Optional<EventConfig> event_config =
61 EventConfig::Create(AsDataSourceConfig(cfg));
62
63 ASSERT_TRUE(event_config.has_value());
64 ASSERT_GT(event_config->ring_buffer_pages(), 0u);
65 ASSERT_TRUE(IsPowerOfTwo(event_config->ring_buffer_pages()));
66 }
67 { // power of two pages accepted
68 uint32_t num_pages = 128;
69 protos::gen::PerfEventConfig cfg;
70 cfg.set_ring_buffer_pages(num_pages);
71 base::Optional<EventConfig> event_config =
72 EventConfig::Create(AsDataSourceConfig(cfg));
73
74 ASSERT_TRUE(event_config.has_value());
75 ASSERT_EQ(event_config->ring_buffer_pages(), num_pages);
76 }
77 { // entire config rejected if not a power of two of pages
78 protos::gen::PerfEventConfig cfg;
79 cfg.set_ring_buffer_pages(7);
80 base::Optional<EventConfig> event_config =
81 EventConfig::Create(AsDataSourceConfig(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 base::Optional<EventConfig> event_config =
91 EventConfig::Create(AsDataSourceConfig(cfg));
92
93 ASSERT_TRUE(event_config.has_value());
94 ASSERT_GT(event_config->read_tick_period_ms(), 0u);
95 }
96 { // otherwise, given value used
97 uint32_t period_ms = 250;
98 protos::gen::PerfEventConfig cfg;
99 cfg.set_ring_buffer_read_period_ms(period_ms);
100 base::Optional<EventConfig> event_config =
101 EventConfig::Create(AsDataSourceConfig(cfg));
102
103 ASSERT_TRUE(event_config.has_value());
104 ASSERT_EQ(event_config->read_tick_period_ms(), period_ms);
105 }
106 }
107
TEST(EventConfigTest,RemotePeriodTimeoutDefaultedIfUnset)108 TEST(EventConfigTest, RemotePeriodTimeoutDefaultedIfUnset) {
109 { // if unset, a default is used
110 protos::gen::PerfEventConfig cfg;
111 base::Optional<EventConfig> event_config =
112 EventConfig::Create(AsDataSourceConfig(cfg));
113
114 ASSERT_TRUE(event_config.has_value());
115 ASSERT_GT(event_config->remote_descriptor_timeout_ms(), 0u);
116 }
117 { // otherwise, given value used
118 uint32_t timeout_ms = 300;
119 protos::gen::PerfEventConfig cfg;
120 cfg.set_remote_descriptor_timeout_ms(timeout_ms);
121 base::Optional<EventConfig> event_config =
122 EventConfig::Create(AsDataSourceConfig(cfg));
123
124 ASSERT_TRUE(event_config.has_value());
125 ASSERT_EQ(event_config->remote_descriptor_timeout_ms(), timeout_ms);
126 }
127 }
128
TEST(EventConfigTest,EnableKernelFrames)129 TEST(EventConfigTest, EnableKernelFrames) {
130 {
131 protos::gen::PerfEventConfig cfg;
132 cfg.mutable_callstack_sampling()->set_kernel_frames(true);
133 base::Optional<EventConfig> event_config =
134 EventConfig::Create(AsDataSourceConfig(cfg));
135
136 ASSERT_TRUE(event_config.has_value());
137 EXPECT_TRUE(event_config->kernel_frames());
138 }
139 { // legacy config:
140 protos::gen::PerfEventConfig cfg;
141 cfg.set_all_cpus(true); // used to detect compat mode
142 cfg.set_kernel_frames(true);
143 base::Optional<EventConfig> event_config =
144 EventConfig::Create(AsDataSourceConfig(cfg));
145
146 ASSERT_TRUE(event_config.has_value());
147 EXPECT_TRUE(event_config->kernel_frames());
148 }
149 { // default is false
150 protos::gen::PerfEventConfig cfg;
151 base::Optional<EventConfig> event_config =
152 EventConfig::Create(AsDataSourceConfig(cfg));
153
154 ASSERT_TRUE(event_config.has_value());
155 EXPECT_FALSE(event_config->kernel_frames());
156 }
157 }
158
TEST(EventConfigTest,SelectSamplingInterval)159 TEST(EventConfigTest, SelectSamplingInterval) {
160 { // period:
161 protos::gen::PerfEventConfig cfg;
162 cfg.mutable_timebase()->set_period(100);
163 base::Optional<EventConfig> event_config =
164 EventConfig::Create(AsDataSourceConfig(cfg));
165
166 ASSERT_TRUE(event_config.has_value());
167 EXPECT_FALSE(event_config->perf_attr()->freq);
168 EXPECT_EQ(event_config->perf_attr()->sample_period, 100u);
169 }
170 { // frequency:
171 protos::gen::PerfEventConfig cfg;
172 cfg.mutable_timebase()->set_frequency(4000);
173 base::Optional<EventConfig> event_config =
174 EventConfig::Create(AsDataSourceConfig(cfg));
175
176 ASSERT_TRUE(event_config.has_value());
177 EXPECT_TRUE(event_config->perf_attr()->freq);
178 EXPECT_EQ(event_config->perf_attr()->sample_freq, 4000u);
179 }
180 { // legacy frequency field:
181 protos::gen::PerfEventConfig cfg;
182 cfg.set_sampling_frequency(5000);
183 base::Optional<EventConfig> event_config =
184 EventConfig::Create(AsDataSourceConfig(cfg));
185
186 ASSERT_TRUE(event_config.has_value());
187 EXPECT_TRUE(event_config->perf_attr()->freq);
188 EXPECT_EQ(event_config->perf_attr()->sample_freq, 5000u);
189 }
190 { // default is 10 Hz (implementation-defined)
191 protos::gen::PerfEventConfig cfg;
192 base::Optional<EventConfig> event_config =
193 EventConfig::Create(AsDataSourceConfig(cfg));
194
195 ASSERT_TRUE(event_config.has_value());
196 EXPECT_TRUE(event_config->perf_attr()->freq);
197 EXPECT_EQ(event_config->perf_attr()->sample_freq, 10u);
198 }
199 }
200
TEST(EventConfigTest,SelectTimebaseEvent)201 TEST(EventConfigTest, SelectTimebaseEvent) {
202 auto id_lookup = [](const std::string& group, const std::string& name) {
203 return (group == "sched" && name == "sched_switch") ? 42 : 0;
204 };
205
206 {
207 protos::gen::PerfEventConfig cfg;
208 protos::gen::PerfEvents::Tracepoint* mutable_tracepoint =
209 cfg.mutable_timebase()->mutable_tracepoint();
210 mutable_tracepoint->set_name("sched:sched_switch");
211
212 base::Optional<EventConfig> event_config =
213 EventConfig::Create(AsDataSourceConfig(cfg), id_lookup);
214
215 ASSERT_TRUE(event_config.has_value());
216 EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_TRACEPOINT);
217 EXPECT_EQ(event_config->perf_attr()->config, 42u);
218 }
219 { // default is the CPU timer:
220 protos::gen::PerfEventConfig cfg;
221 cfg.mutable_timebase()->set_frequency(1000);
222 base::Optional<EventConfig> event_config =
223 EventConfig::Create(AsDataSourceConfig(cfg));
224
225 ASSERT_TRUE(event_config.has_value());
226 EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_SOFTWARE);
227 EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_SW_CPU_CLOCK);
228 }
229 }
230
TEST(EventConfigTest,ParseTargetfilter)231 TEST(EventConfigTest, ParseTargetfilter) {
232 {
233 protos::gen::PerfEventConfig cfg;
234 auto* mutable_scope = cfg.mutable_callstack_sampling()->mutable_scope();
235 mutable_scope->add_target_pid(42);
236 mutable_scope->add_target_cmdline("traced_probes");
237 mutable_scope->add_target_cmdline("traced");
238 mutable_scope->set_additional_cmdline_count(3);
239 mutable_scope->add_exclude_cmdline("heapprofd");
240
241 base::Optional<EventConfig> event_config =
242 EventConfig::Create(AsDataSourceConfig(cfg));
243
244 ASSERT_TRUE(event_config.has_value());
245 const auto& filter = event_config->filter();
246 EXPECT_THAT(filter.pids, UnorderedElementsAreArray({42}));
247 EXPECT_THAT(filter.cmdlines,
248 UnorderedElementsAreArray({"traced_probes", "traced"}));
249 EXPECT_EQ(filter.additional_cmdline_count, 3u);
250 EXPECT_TRUE(filter.exclude_pids.empty());
251 EXPECT_THAT(filter.exclude_cmdlines,
252 UnorderedElementsAreArray({"heapprofd"}));
253 }
254 { // legacy:
255 protos::gen::PerfEventConfig cfg;
256 cfg.set_all_cpus(true);
257 cfg.add_target_pid(42);
258 cfg.add_target_cmdline("traced_probes");
259 cfg.add_target_cmdline("traced");
260 cfg.set_additional_cmdline_count(3);
261 cfg.add_exclude_cmdline("heapprofd");
262
263 base::Optional<EventConfig> event_config =
264 EventConfig::Create(AsDataSourceConfig(cfg));
265
266 ASSERT_TRUE(event_config.has_value());
267 const auto& filter = event_config->filter();
268 EXPECT_THAT(filter.pids, UnorderedElementsAreArray({42}));
269 EXPECT_THAT(filter.cmdlines,
270 UnorderedElementsAreArray({"traced_probes", "traced"}));
271 EXPECT_EQ(filter.additional_cmdline_count, 3u);
272 EXPECT_TRUE(filter.exclude_pids.empty());
273 EXPECT_THAT(filter.exclude_cmdlines,
274 UnorderedElementsAreArray({"heapprofd"}));
275 }
276 }
277
TEST(EventConfigTest,CounterOnlyModeDetection)278 TEST(EventConfigTest, CounterOnlyModeDetection) {
279 { // hardware counter:
280 protos::gen::PerfEventConfig cfg;
281 auto* mutable_timebase = cfg.mutable_timebase();
282 mutable_timebase->set_period(500);
283 mutable_timebase->set_counter(protos::gen::PerfEvents::HW_CPU_CYCLES);
284
285 base::Optional<EventConfig> event_config =
286 EventConfig::Create(AsDataSourceConfig(cfg));
287
288 ASSERT_TRUE(event_config.has_value());
289 EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_HARDWARE);
290 EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_HW_CPU_CYCLES);
291 EXPECT_EQ(event_config->perf_attr()->sample_type &
292 (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
293 0u);
294 }
295 { // software counter:
296 protos::gen::PerfEventConfig cfg;
297 auto* mutable_timebase = cfg.mutable_timebase();
298 mutable_timebase->set_period(500);
299 mutable_timebase->set_counter(protos::gen::PerfEvents::SW_PAGE_FAULTS);
300
301 base::Optional<EventConfig> event_config =
302 EventConfig::Create(AsDataSourceConfig(cfg));
303
304 ASSERT_TRUE(event_config.has_value());
305 EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_SOFTWARE);
306 EXPECT_EQ(event_config->perf_attr()->config, PERF_COUNT_SW_PAGE_FAULTS);
307 EXPECT_EQ(event_config->perf_attr()->sample_type &
308 (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
309 0u);
310 }
311 }
312
TEST(EventConfigTest,CallstackSamplingModeDetection)313 TEST(EventConfigTest, CallstackSamplingModeDetection) {
314 { // set-but-empty |callstack_sampling| field enables callstacks
315 protos::gen::PerfEventConfig cfg;
316 cfg.mutable_callstack_sampling(); // set field
317
318 base::Optional<EventConfig> event_config =
319 EventConfig::Create(AsDataSourceConfig(cfg));
320
321 ASSERT_TRUE(event_config.has_value());
322 EXPECT_EQ(
323 event_config->perf_attr()->sample_type &
324 (PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER),
325 static_cast<uint64_t>(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER));
326
327 EXPECT_NE(event_config->perf_attr()->sample_regs_user, 0u);
328 EXPECT_NE(event_config->perf_attr()->sample_stack_user, 0u);
329 }
330 }
331
TEST(EventConfigTest,TimestampClockId)332 TEST(EventConfigTest, TimestampClockId) {
333 { // if unset, a default is used
334 protos::gen::PerfEventConfig cfg;
335 base::Optional<EventConfig> event_config =
336 EventConfig::Create(AsDataSourceConfig(cfg));
337
338 ASSERT_TRUE(event_config.has_value());
339 EXPECT_TRUE(event_config->perf_attr()->use_clockid);
340 EXPECT_EQ(event_config->perf_attr()->clockid, CLOCK_MONOTONIC_RAW);
341 }
342 { // explicit boottime
343 protos::gen::PerfEventConfig cfg;
344 cfg.mutable_timebase()->set_timestamp_clock(
345 protos::gen::PerfEvents::PERF_CLOCK_BOOTTIME);
346 base::Optional<EventConfig> event_config =
347 EventConfig::Create(AsDataSourceConfig(cfg));
348
349 ASSERT_TRUE(event_config.has_value());
350 EXPECT_TRUE(event_config->perf_attr()->use_clockid);
351 EXPECT_EQ(event_config->perf_attr()->clockid, CLOCK_BOOTTIME);
352 }
353 { // explicit monotonic
354 protos::gen::PerfEventConfig cfg;
355 cfg.mutable_timebase()->set_timestamp_clock(
356 protos::gen::PerfEvents::PERF_CLOCK_MONOTONIC);
357 base::Optional<EventConfig> event_config =
358 EventConfig::Create(AsDataSourceConfig(cfg));
359
360 ASSERT_TRUE(event_config.has_value());
361 EXPECT_TRUE(event_config->perf_attr()->use_clockid);
362 EXPECT_EQ(event_config->perf_attr()->clockid, CLOCK_MONOTONIC);
363 }
364 }
365
366 } // namespace
367 } // namespace profiling
368 } // namespace perfetto
369