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/trace_processor/importers/proto/profile_packet_sequence_state.h"
18
19 #include <memory>
20
21 #include "src/trace_processor/importers/common/mapping_tracker.h"
22 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
23 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
24 #include "src/trace_processor/types/trace_processor_context.h"
25 #include "test/gtest_and_gmock.h"
26
27 namespace perfetto {
28 namespace trace_processor {
29 namespace {
30
31 struct Packet {
32 uint32_t mapping_name_id;
33 uint32_t build_id;
34 uint32_t frame_name_id;
35 uint32_t mapping_id;
36 uint32_t frame_id;
37 };
38
39 constexpr Packet kFirstPacket{1, 2, 3, 1, 1};
40 constexpr Packet kSecondPacket{3, 2, 1, 2, 2};
41
42 constexpr auto kMappingExactOffset = 123;
43 constexpr auto kMappingStartOffset = 1231;
44 constexpr auto kMappingStart = 234;
45 constexpr auto kMappingEnd = 345;
46 constexpr auto kMappingLoadBias = 456;
47
48 // heapprofd on Android Q has large callstack ideas, explicitly test large
49 // values.
50 constexpr auto kCallstackId = 1ull << 34;
51
52 static constexpr auto kFrameRelPc = 567;
53 static constexpr char kBuildIDName[] = "[build id]";
54 static constexpr char kBuildIDHexName[] = "5b6275696c642069645d";
55
56 using ::testing::ElementsAre;
57
58 class HeapProfileTrackerDupTest : public ::testing::Test {
59 public:
HeapProfileTrackerDupTest()60 HeapProfileTrackerDupTest() {
61 context.storage.reset(new TraceStorage());
62 context.mapping_tracker.reset(new MappingTracker(&context));
63 context.stack_profile_tracker.reset(new StackProfileTracker(&context));
64 sequence_state = PacketSequenceStateGeneration::CreateFirst(&context);
65
66 mapping_name = context.storage->InternString("[mapping]");
67 fully_qualified_mapping_name = context.storage->InternString("/[mapping]");
68 build = context.storage->InternString(kBuildIDName);
69 frame_name = context.storage->InternString("[frame]");
70 }
71
72 protected:
profile_packet_sequence_state()73 ProfilePacketSequenceState& profile_packet_sequence_state() {
74 return *sequence_state->GetCustomState<ProfilePacketSequenceState>();
75 }
InsertMapping(const Packet & packet)76 void InsertMapping(const Packet& packet) {
77 profile_packet_sequence_state().AddString(packet.mapping_name_id,
78 "[mapping]");
79
80 profile_packet_sequence_state().AddString(packet.build_id, kBuildIDName);
81
82 ProfilePacketSequenceState::SourceMapping first_frame;
83 first_frame.build_id = packet.build_id;
84 first_frame.exact_offset = kMappingExactOffset;
85 first_frame.start_offset = kMappingStartOffset;
86 first_frame.start = kMappingStart;
87 first_frame.end = kMappingEnd;
88 first_frame.load_bias = kMappingLoadBias;
89 first_frame.name_ids = {packet.mapping_name_id};
90
91 profile_packet_sequence_state().AddMapping(packet.mapping_id, first_frame);
92 }
93
InsertFrame(const Packet & packet)94 void InsertFrame(const Packet& packet) {
95 InsertMapping(packet);
96 profile_packet_sequence_state().AddString(packet.frame_name_id, "[frame]");
97
98 ProfilePacketSequenceState::SourceFrame first_frame;
99 first_frame.name_id = packet.frame_name_id;
100 first_frame.mapping_id = packet.mapping_id;
101 first_frame.rel_pc = kFrameRelPc;
102
103 profile_packet_sequence_state().AddFrame(packet.frame_id, first_frame);
104 }
105
InsertCallsite(const Packet & packet)106 void InsertCallsite(const Packet& packet) {
107 InsertFrame(packet);
108
109 ProfilePacketSequenceState::SourceCallstack first_callsite = {
110 packet.frame_id, packet.frame_id};
111 profile_packet_sequence_state().AddCallstack(kCallstackId, first_callsite);
112 }
113
114 StringId mapping_name;
115 StringId fully_qualified_mapping_name;
116 StringId build;
117 StringId frame_name;
118 TraceProcessorContext context;
119 RefPtr<PacketSequenceStateGeneration> sequence_state;
120 };
121
122 // Insert the same mapping from two different packets, with different strings
123 // interned, and assert we only store one.
TEST_F(HeapProfileTrackerDupTest,Mapping)124 TEST_F(HeapProfileTrackerDupTest, Mapping) {
125 InsertMapping(kFirstPacket);
126 profile_packet_sequence_state().FinalizeProfile();
127 InsertMapping(kSecondPacket);
128 profile_packet_sequence_state().FinalizeProfile();
129
130 EXPECT_THAT(context.storage->stack_profile_mapping_table().build_id()[0],
131 context.storage->InternString({kBuildIDHexName}));
132 EXPECT_THAT(context.storage->stack_profile_mapping_table().exact_offset()[0],
133 kMappingExactOffset);
134 EXPECT_THAT(context.storage->stack_profile_mapping_table().start_offset()[0],
135 kMappingStartOffset);
136 EXPECT_THAT(context.storage->stack_profile_mapping_table().start()[0],
137 kMappingStart);
138 EXPECT_THAT(context.storage->stack_profile_mapping_table().end()[0],
139 kMappingEnd);
140 EXPECT_THAT(context.storage->stack_profile_mapping_table().load_bias()[0],
141 kMappingLoadBias);
142 EXPECT_THAT(context.storage->stack_profile_mapping_table().name()[0],
143 fully_qualified_mapping_name);
144 }
145
146 // Insert the same mapping from two different packets, with different strings
147 // interned, and assert we only store one.
TEST_F(HeapProfileTrackerDupTest,Frame)148 TEST_F(HeapProfileTrackerDupTest, Frame) {
149 InsertFrame(kFirstPacket);
150 profile_packet_sequence_state().FinalizeProfile();
151 InsertFrame(kSecondPacket);
152 profile_packet_sequence_state().FinalizeProfile();
153
154 const auto& frames = context.storage->stack_profile_frame_table();
155 EXPECT_THAT(frames.name()[0], frame_name);
156 EXPECT_THAT(frames.mapping()[0], MappingId{0});
157 EXPECT_THAT(frames.rel_pc()[0], kFrameRelPc);
158 }
159
160 // Insert the same callstack from two different packets, assert it is only
161 // stored once.
TEST_F(HeapProfileTrackerDupTest,Callstack)162 TEST_F(HeapProfileTrackerDupTest, Callstack) {
163 InsertCallsite(kFirstPacket);
164 profile_packet_sequence_state().FinalizeProfile();
165 InsertCallsite(kSecondPacket);
166 profile_packet_sequence_state().FinalizeProfile();
167
168 const auto& callsite_table = context.storage->stack_profile_callsite_table();
169 const auto& depth = callsite_table.depth();
170 const auto& parent_id = callsite_table.parent_id();
171 const auto& frame_id = callsite_table.frame_id();
172
173 EXPECT_EQ(depth[0], 0u);
174 EXPECT_EQ(depth[1], 1u);
175
176 EXPECT_EQ(parent_id[0], std::nullopt);
177 EXPECT_EQ(parent_id[1], CallsiteId{0});
178
179 EXPECT_EQ(frame_id[0], FrameId{0});
180 EXPECT_EQ(frame_id[1], FrameId{0});
181 }
182
FindCallstack(const TraceStorage & storage,int64_t depth,std::optional<CallsiteId> parent,FrameId frame_id)183 std::optional<CallsiteId> FindCallstack(const TraceStorage& storage,
184 int64_t depth,
185 std::optional<CallsiteId> parent,
186 FrameId frame_id) {
187 const auto& callsites = storage.stack_profile_callsite_table();
188 for (uint32_t i = 0; i < callsites.row_count(); ++i) {
189 if (callsites.depth()[i] == depth && callsites.parent_id()[i] == parent &&
190 callsites.frame_id()[i] == frame_id) {
191 return callsites.id()[i];
192 }
193 }
194 return std::nullopt;
195 }
196
TEST(HeapProfileTrackerTest,SourceMappingPath)197 TEST(HeapProfileTrackerTest, SourceMappingPath) {
198 TraceProcessorContext context;
199 context.storage.reset(new TraceStorage());
200 context.mapping_tracker.reset(new MappingTracker(&context));
201 context.stack_profile_tracker.reset(new StackProfileTracker(&context));
202 auto state = PacketSequenceStateGeneration::CreateFirst(&context);
203 ProfilePacketSequenceState& ppss =
204 *state->GetCustomState<ProfilePacketSequenceState>();
205
206 constexpr auto kBuildId = 1u;
207 constexpr auto kMappingNameId1 = 2u;
208 constexpr auto kMappingNameId2 = 3u;
209
210 ppss.AddString(kBuildId, "buildid");
211 ppss.AddString(kMappingNameId1, "foo");
212 ppss.AddString(kMappingNameId2, "bar");
213
214 ProfilePacketSequenceState::SourceMapping mapping;
215 mapping.build_id = kBuildId;
216 mapping.exact_offset = 1;
217 mapping.start_offset = 1;
218 mapping.start = 2;
219 mapping.end = 3;
220 mapping.load_bias = 0;
221 mapping.name_ids = {kMappingNameId1, kMappingNameId2};
222 ppss.AddMapping(0, mapping);
223 ppss.CommitAllocations();
224 auto foo_bar_id = context.storage->string_pool().GetId("/foo/bar");
225 ASSERT_NE(foo_bar_id, std::nullopt);
226 EXPECT_THAT(context.storage->stack_profile_mapping_table().name()[0],
227 *foo_bar_id);
228 }
229
230 // Insert multiple mappings, frames and callstacks and check result.
TEST(HeapProfileTrackerTest,Functional)231 TEST(HeapProfileTrackerTest, Functional) {
232 TraceProcessorContext context;
233 context.storage.reset(new TraceStorage());
234 context.mapping_tracker.reset(new MappingTracker(&context));
235 context.stack_profile_tracker.reset(new StackProfileTracker(&context));
236
237 auto state = PacketSequenceStateGeneration::CreateFirst(&context);
238 ProfilePacketSequenceState& ppss =
239 *state->GetCustomState<ProfilePacketSequenceState>();
240
241 uint32_t next_string_intern_id = 1;
242
243 const std::string build_ids[] = {"build1", "build2", "build3"};
244 uint32_t build_id_ids[base::ArraySize(build_ids)];
245 for (size_t i = 0; i < base::ArraySize(build_ids); ++i)
246 build_id_ids[i] = next_string_intern_id++;
247
248 const std::string mapping_names[] = {"map1", "map2", "map3"};
249 uint32_t mapping_name_ids[base::ArraySize(mapping_names)];
250 for (size_t i = 0; i < base::ArraySize(mapping_names); ++i)
251 mapping_name_ids[i] = next_string_intern_id++;
252
253 ProfilePacketSequenceState::SourceMapping
254 mappings[base::ArraySize(mapping_names)] = {};
255 mappings[0].build_id = build_id_ids[0];
256 mappings[0].exact_offset = 1;
257 mappings[0].start_offset = 1;
258 mappings[0].start = 2;
259 mappings[0].end = 3;
260 mappings[0].load_bias = 0;
261 mappings[0].name_ids = {mapping_name_ids[0], mapping_name_ids[1]};
262
263 mappings[1].build_id = build_id_ids[1];
264 mappings[1].exact_offset = 1;
265 mappings[1].start_offset = 1;
266 mappings[1].start = 2;
267 mappings[1].end = 3;
268 mappings[1].load_bias = 1;
269 mappings[1].name_ids = {mapping_name_ids[1]};
270
271 mappings[2].build_id = build_id_ids[2];
272 mappings[2].exact_offset = 1;
273 mappings[2].start_offset = 1;
274 mappings[2].start = 2;
275 mappings[2].end = 3;
276 mappings[2].load_bias = 2;
277 mappings[2].name_ids = {mapping_name_ids[2]};
278
279 const std::string function_names[] = {"fun1", "fun2", "fun3", "fun4"};
280 uint32_t function_name_ids[base::ArraySize(function_names)];
281 for (size_t i = 0; i < base::ArraySize(function_names); ++i)
282 function_name_ids[i] = next_string_intern_id++;
283
284 ProfilePacketSequenceState::SourceFrame
285 frames[base::ArraySize(function_names)];
286 frames[0].name_id = function_name_ids[0];
287 frames[0].mapping_id = 0;
288 frames[0].rel_pc = 123;
289
290 frames[1].name_id = function_name_ids[1];
291 frames[1].mapping_id = 0;
292 frames[1].rel_pc = 123;
293
294 frames[2].name_id = function_name_ids[2];
295 frames[2].mapping_id = 1;
296 frames[2].rel_pc = 123;
297
298 frames[3].name_id = function_name_ids[3];
299 frames[3].mapping_id = 2;
300 frames[3].rel_pc = 123;
301
302 ProfilePacketSequenceState::SourceCallstack callstacks[3];
303 callstacks[0] = {2, 1, 0};
304 callstacks[1] = {2, 1, 0, 1, 0};
305 callstacks[2] = {0, 2, 0, 1, 2};
306
307 for (size_t i = 0; i < base::ArraySize(build_ids); ++i) {
308 auto interned = base::StringView(build_ids[i].data(), build_ids[i].size());
309 ppss.AddString(build_id_ids[i], interned);
310 }
311 for (size_t i = 0; i < base::ArraySize(mapping_names); ++i) {
312 auto interned =
313 base::StringView(mapping_names[i].data(), mapping_names[i].size());
314 ppss.AddString(mapping_name_ids[i], interned);
315 }
316 for (size_t i = 0; i < base::ArraySize(function_names); ++i) {
317 auto interned =
318 base::StringView(function_names[i].data(), function_names[i].size());
319 ppss.AddString(function_name_ids[i], interned);
320 }
321
322 for (uint32_t i = 0; i < base::ArraySize(mappings); ++i)
323 ppss.AddMapping(i, mappings[i]);
324 for (uint32_t i = 0; i < base::ArraySize(frames); ++i)
325 ppss.AddFrame(i, frames[i]);
326 for (uint32_t i = 0; i < base::ArraySize(callstacks); ++i)
327 ppss.AddCallstack(i, callstacks[i]);
328
329 ppss.CommitAllocations();
330
331 for (size_t i = 0; i < base::ArraySize(callstacks); ++i) {
332 std::optional<CallsiteId> parent;
333 const ProfilePacketSequenceState::SourceCallstack& callstack =
334 callstacks[i];
335 for (size_t depth = 0; depth < callstack.size(); ++depth) {
336 auto frame_id = ppss.GetDatabaseFrameIdForTesting(callstack[depth]);
337 std::optional<CallsiteId> self = FindCallstack(
338 *context.storage, static_cast<int64_t>(depth), parent, frame_id);
339 ASSERT_TRUE(self.has_value());
340 parent = self;
341 }
342 }
343
344 ppss.FinalizeProfile();
345 }
346
347 } // namespace
348 } // namespace trace_processor
349 } // namespace perfetto
350