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