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