1 /*
2 * Copyright (C) 2021 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/trace_processor/importers/proto/perf_sample_tracker.h"
18
19 #include "perfetto/base/logging.h"
20 #include "src/trace_processor/importers/common/track_tracker.h"
21 #include "test/gtest_and_gmock.h"
22
23 #include "protos/perfetto/common/perf_events.gen.h"
24 #include "protos/perfetto/trace/profiling/profile_packet.gen.h"
25 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
26 #include "protos/perfetto/trace/trace_packet_defaults.gen.h"
27 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
28
29 namespace perfetto {
30 namespace trace_processor {
31 namespace {
32
33 class PerfSampleTrackerTest : public ::testing::Test {
34 public:
PerfSampleTrackerTest()35 PerfSampleTrackerTest() {
36 context.storage.reset(new TraceStorage());
37 context.track_tracker.reset(new TrackTracker(&context));
38 context.perf_sample_tracker.reset(new PerfSampleTracker(&context));
39 }
40
41 protected:
42 TraceProcessorContext context;
43 };
44
TEST_F(PerfSampleTrackerTest,PerCpuCounterTracks)45 TEST_F(PerfSampleTrackerTest, PerCpuCounterTracks) {
46 uint32_t seq_id = 42;
47 uint32_t cpu0 = 0;
48 uint32_t cpu1 = 1;
49
50 auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
51 seq_id, cpu0, /*nullable_defaults=*/nullptr);
52 auto stream2 = context.perf_sample_tracker->GetSamplingStreamInfo(
53 seq_id, cpu1, /*nullable_defaults=*/nullptr);
54
55 // same session, different counter tracks
56 EXPECT_EQ(stream.perf_session_id, stream2.perf_session_id);
57 EXPECT_NE(stream.timebase_track_id, stream2.timebase_track_id);
58
59 // re-querying one of the existing streams gives the same ids
60 auto stream3 = context.perf_sample_tracker->GetSamplingStreamInfo(
61 seq_id, cpu1, /*nullable_defaults=*/nullptr);
62
63 EXPECT_EQ(stream2.perf_session_id, stream3.perf_session_id);
64 EXPECT_EQ(stream2.timebase_track_id, stream3.timebase_track_id);
65 }
66
TEST_F(PerfSampleTrackerTest,TimebaseTrackName_Counter)67 TEST_F(PerfSampleTrackerTest, TimebaseTrackName_Counter) {
68 uint32_t seq_id = 42;
69 uint32_t cpu0 = 0;
70
71 protos::gen::TracePacketDefaults defaults;
72 auto* perf_defaults = defaults.mutable_perf_sample_defaults();
73 perf_defaults->mutable_timebase()->set_frequency(100);
74 perf_defaults->mutable_timebase()->set_counter(
75 protos::gen::PerfEvents::SW_PAGE_FAULTS);
76 auto defaults_pb = defaults.SerializeAsString();
77 protos::pbzero::TracePacketDefaults::Decoder defaults_decoder(defaults_pb);
78
79 auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
80 seq_id, cpu0, &defaults_decoder);
81
82 TrackId track_id = stream.timebase_track_id;
83 const auto& track_table = context.storage->perf_counter_track_table();
84 auto row_id = track_table.id().IndexOf(track_id);
85
86 // track exists and looks sensible
87 ASSERT_TRUE(row_id.has_value());
88 EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
89 EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
90 EXPECT_TRUE(track_table.is_timebase()[*row_id]);
91
92 // Name derived from the timebase.
93 std::string track_name =
94 context.storage->GetString(track_table.name()[*row_id]).ToStdString();
95 ASSERT_EQ(track_name, "page-faults");
96 }
97
TEST_F(PerfSampleTrackerTest,TimebaseTrackName_Tracepoint)98 TEST_F(PerfSampleTrackerTest, TimebaseTrackName_Tracepoint) {
99 uint32_t seq_id = 42;
100 uint32_t cpu0 = 0;
101
102 protos::gen::TracePacketDefaults defaults;
103 auto* perf_defaults = defaults.mutable_perf_sample_defaults();
104 perf_defaults->mutable_timebase()->set_frequency(100);
105 perf_defaults->mutable_timebase()->mutable_tracepoint()->set_name(
106 "sched:sched_switch");
107 auto defaults_pb = defaults.SerializeAsString();
108 protos::pbzero::TracePacketDefaults::Decoder defaults_decoder(defaults_pb);
109
110 auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
111 seq_id, cpu0, &defaults_decoder);
112
113 TrackId track_id = stream.timebase_track_id;
114 const auto& track_table = context.storage->perf_counter_track_table();
115 auto row_id = track_table.id().IndexOf(track_id);
116
117 // track exists and looks sensible
118 ASSERT_TRUE(row_id.has_value());
119 EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
120 EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
121 EXPECT_TRUE(track_table.is_timebase()[*row_id]);
122
123 // Name derived from the timebase.
124 std::string track_name =
125 context.storage->GetString(track_table.name()[*row_id]).ToStdString();
126 ASSERT_EQ(track_name, "sched:sched_switch");
127 }
128
TEST_F(PerfSampleTrackerTest,UnknownCounterTreatedAsCpuClock)129 TEST_F(PerfSampleTrackerTest, UnknownCounterTreatedAsCpuClock) {
130 uint32_t seq_id = 42;
131 uint32_t cpu0 = 0;
132
133 auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
134 seq_id, cpu0, /*nullable_defaults=*/nullptr);
135
136 TrackId track_id = stream.timebase_track_id;
137 const auto& track_table = context.storage->perf_counter_track_table();
138 auto row_id = track_table.id().IndexOf(track_id);
139
140 // track exists and looks sensible
141 ASSERT_TRUE(row_id.has_value());
142 EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
143 EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
144 EXPECT_TRUE(track_table.is_timebase()[*row_id]);
145
146 // If the trace doesn't have a PerfSampleDefaults describing the timebase
147 // counter, we assume cpu-clock.
148 std::string track_name =
149 context.storage->GetString(track_table.name()[*row_id]).ToStdString();
150 ASSERT_EQ(track_name, "cpu-clock");
151 }
152
153 // Like TimebaseTrackName_Counter, but with a config supplying an explicit name
154 // for the counter.
TEST_F(PerfSampleTrackerTest,TimebaseTrackName_ConfigSuppliedName)155 TEST_F(PerfSampleTrackerTest, TimebaseTrackName_ConfigSuppliedName) {
156 uint32_t seq_id = 42;
157 uint32_t cpu0 = 0;
158
159 protos::gen::TracePacketDefaults defaults;
160 auto* perf_defaults = defaults.mutable_perf_sample_defaults();
161 perf_defaults->mutable_timebase()->set_name("test-name");
162 perf_defaults->mutable_timebase()->set_frequency(100);
163 perf_defaults->mutable_timebase()->set_counter(
164 protos::gen::PerfEvents::SW_PAGE_FAULTS);
165 auto defaults_pb = defaults.SerializeAsString();
166 protos::pbzero::TracePacketDefaults::Decoder defaults_decoder(defaults_pb);
167
168 auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
169 seq_id, cpu0, &defaults_decoder);
170
171 TrackId track_id = stream.timebase_track_id;
172 const auto& track_table = context.storage->perf_counter_track_table();
173 auto row_id = track_table.id().IndexOf(track_id);
174
175 // track exists and looks sensible
176 ASSERT_TRUE(row_id.has_value());
177 EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
178 EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
179 EXPECT_TRUE(track_table.is_timebase()[*row_id]);
180
181 // Using the config-supplied name for the track.
182 std::string track_name =
183 context.storage->GetString(track_table.name()[*row_id]).ToStdString();
184 ASSERT_EQ(track_name, "test-name");
185 }
186
TEST_F(PerfSampleTrackerTest,ProcessShardingStatsEntries)187 TEST_F(PerfSampleTrackerTest, ProcessShardingStatsEntries) {
188 uint32_t cpu0 = 0;
189 uint32_t cpu1 = 1;
190
191 protos::gen::TracePacketDefaults defaults;
192 auto* perf_defaults = defaults.mutable_perf_sample_defaults();
193 perf_defaults->mutable_timebase()->set_frequency(100);
194 perf_defaults->mutable_timebase()->set_counter(
195 protos::gen::PerfEvents::SW_PAGE_FAULTS);
196 // shard 7/8
197 perf_defaults->set_process_shard_count(8u);
198 perf_defaults->set_chosen_process_shard(7u);
199 auto defaults_pb = defaults.SerializeAsString();
200 protos::pbzero::TracePacketDefaults::Decoder defaults_decoder(defaults_pb);
201
202 // Two per-cpu lookups for first sequence
203 auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
204 /*seq_id=*/42, cpu0, &defaults_decoder);
205 context.perf_sample_tracker->GetSamplingStreamInfo(
206 /*seq_id=*/42, cpu1, &defaults_decoder);
207
208 // Second sequence
209 auto stream2 = context.perf_sample_tracker->GetSamplingStreamInfo(
210 /*seq_id=*/100, cpu0, &defaults_decoder);
211 context.perf_sample_tracker->GetSamplingStreamInfo(
212 /*seq_id=*/100, cpu1, &defaults_decoder);
213
214 EXPECT_NE(stream.perf_session_id, stream2.perf_session_id);
215
216 std::optional<int64_t> shard_count = context.storage->GetIndexedStats(
217 stats::perf_process_shard_count,
218 static_cast<int>(stream.perf_session_id));
219 std::optional<int64_t> chosen_shard = context.storage->GetIndexedStats(
220 stats::perf_chosen_process_shard,
221 static_cast<int>(stream.perf_session_id));
222
223 ASSERT_TRUE(shard_count.has_value());
224 EXPECT_EQ(shard_count.value(), 8);
225 ASSERT_TRUE(chosen_shard.has_value());
226 EXPECT_EQ(chosen_shard.value(), 7);
227
228 std::optional<int64_t> shard_count2 = context.storage->GetIndexedStats(
229 stats::perf_process_shard_count,
230 static_cast<int>(stream.perf_session_id));
231 std::optional<int64_t> chosen_shard2 = context.storage->GetIndexedStats(
232 stats::perf_chosen_process_shard,
233 static_cast<int>(stream.perf_session_id));
234
235 ASSERT_TRUE(shard_count2.has_value());
236 EXPECT_EQ(shard_count2.value(), 8);
237 ASSERT_TRUE(chosen_shard2.has_value());
238 EXPECT_EQ(chosen_shard2.value(), 7);
239 }
240
241 } // namespace
242 } // namespace trace_processor
243 } // namespace perfetto
244