• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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