• 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/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