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