1 /*
2 * Copyright (C) 2020 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_graph_tracker.h"
18
19 #include "perfetto/base/logging.h"
20 #include "src/trace_processor/importers/common/process_tracker.h"
21 #include "src/trace_processor/importers/proto/profiler_util.h"
22 #include "test/gtest_and_gmock.h"
23
24 namespace perfetto {
25 namespace trace_processor {
26 namespace {
27
28 using ::testing::UnorderedElementsAre;
29
TEST(HeapGraphTrackerTest,PackageFromLocationApp)30 TEST(HeapGraphTrackerTest, PackageFromLocationApp) {
31 std::unique_ptr<TraceStorage> storage(new TraceStorage());
32
33 const char data_app_path[] =
34 "/data/app/org.perfetto.test-6XfQhnaSkFwGK0sYL9is0G==/base.apk";
35 EXPECT_EQ(PackageFromLocation(storage.get(), data_app_path),
36 "org.perfetto.test");
37
38 const char with_extra_dir[] =
39 "/data/app/~~ASDFGH1234QWerT==/"
40 "com.perfetto.test-MNBVCX7890SDTst6==/test.apk";
41 EXPECT_EQ(PackageFromLocation(storage.get(), with_extra_dir),
42 "com.perfetto.test");
43
44 const char odex[] =
45 "/data/app/com.google.android.apps.wellbeing-"
46 "qfQCaB4uJ7P0OPpZQqOu0Q==/oat/arm64/base.odex";
47 EXPECT_EQ(PackageFromLocation(storage.get(), odex),
48 "com.google.android.apps.wellbeing");
49
50 const char inmem_dex[] =
51 "[anon:dalvik-classes.dex extracted in memory from "
52 "/data/app/~~uUgHYtbjPNr2VFa3byIF4Q==/"
53 "com.perfetto.example-aC94wTfXRC60l2HJU5YvjQ==/base.apk]";
54 EXPECT_EQ(PackageFromLocation(storage.get(), inmem_dex),
55 "com.perfetto.example");
56 }
57
TEST(HeapGraphTrackerTest,PopulateNativeSize)58 TEST(HeapGraphTrackerTest, PopulateNativeSize) {
59 constexpr uint64_t kSeqId = 1;
60 constexpr UniquePid kPid = 1;
61 constexpr int64_t kTimestamp = 1;
62
63 TraceProcessorContext context;
64 context.storage.reset(new TraceStorage());
65 context.process_tracker.reset(new ProcessTracker(&context));
66 context.process_tracker->GetOrCreateProcess(kPid);
67
68 HeapGraphTracker tracker(context.storage.get());
69
70 StringPool::Id normal_kind = context.storage->InternString("KIND_NORMAL");
71
72 constexpr uint64_t kLocation = 0;
73 tracker.AddInternedLocationName(kSeqId, kLocation,
74 context.storage->InternString("location"));
75
76 enum Fields : uint64_t { kReferent = 1, kThunk, kThis0, kNext };
77
78 tracker.AddInternedFieldName(kSeqId, kReferent,
79 "java.lang.ref.Reference.referent");
80 tracker.AddInternedFieldName(kSeqId, kThunk, "sun.misc.Cleaner.thunk");
81 tracker.AddInternedFieldName(
82 kSeqId, kThis0,
83 "libcore.util.NativeAllocationRegistry$CleanerThunk.this$0");
84 tracker.AddInternedFieldName(kSeqId, kNext, "sun.misc.Cleaner.next");
85
86 enum Types : uint64_t {
87 kTypeBitmap = 1,
88 kTypeCleaner,
89 kTypeCleanerThunk,
90 kTypeNativeAllocationRegistry,
91 };
92
93 tracker.AddInternedType(
94 kSeqId, kTypeBitmap,
95 context.storage->InternString("android.graphics.Bitmap"), kLocation,
96 /*object_size=*/0,
97 /*reference_field_name_ids=*/{}, /*superclass_id=*/0,
98 /*classloader_id=*/0, /*no_reference_fields=*/false,
99 /*kind=*/normal_kind);
100
101 tracker.AddInternedType(
102 kSeqId, kTypeCleaner, context.storage->InternString("sun.misc.Cleaner"),
103 kLocation, /*object_size=*/0,
104 /*reference_field_name_ids=*/{kReferent, kThunk, kNext},
105 /*superclass_id=*/0,
106 /*classloader_id=*/0, /*no_reference_fields=*/false,
107 /*kind=*/normal_kind);
108
109 tracker.AddInternedType(
110 kSeqId, kTypeCleanerThunk,
111 context.storage->InternString(
112 "libcore.util.NativeAllocationRegistry$CleanerThunk"),
113 kLocation, /*object_size=*/0,
114 /*reference_field_name_ids=*/{kThis0}, /*superclass_id=*/0,
115 /*classloader_id=*/0, /*no_reference_fields=*/false,
116 /*kind=*/normal_kind);
117
118 tracker.AddInternedType(
119 kSeqId, kTypeNativeAllocationRegistry,
120 context.storage->InternString("libcore.util.NativeAllocationRegistry"),
121 kLocation, /*object_size=*/0,
122 /*reference_field_name_ids=*/{}, /*superclass_id=*/0,
123 /*classloader_id=*/0, /*no_reference_fields=*/false,
124 /*kind=*/normal_kind);
125
126 enum Objects : uint64_t {
127 kObjBitmap = 1,
128 kObjCleaner,
129 kObjThunk,
130 kObjNativeAllocationRegistry,
131 };
132
133 {
134 HeapGraphTracker::SourceObject obj;
135 obj.object_id = kObjBitmap;
136 obj.type_id = kTypeBitmap;
137
138 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
139 }
140
141 {
142 HeapGraphTracker::SourceObject obj;
143 obj.object_id = kObjCleaner;
144 obj.type_id = kTypeCleaner;
145 obj.referred_objects = {kObjBitmap, kObjThunk, 0};
146
147 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
148 }
149
150 {
151 HeapGraphTracker::SourceObject obj;
152 obj.object_id = kObjThunk;
153 obj.type_id = kTypeCleanerThunk;
154 obj.referred_objects = {kObjNativeAllocationRegistry};
155
156 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
157 }
158
159 {
160 HeapGraphTracker::SourceObject obj;
161 obj.object_id = kObjNativeAllocationRegistry;
162 obj.type_id = kTypeNativeAllocationRegistry;
163
164 // NativeAllocationRegistry.size least significant bit is used to encode the
165 // source of the allocation (1: malloc, 0: other).
166 obj.native_allocation_registry_size = 24242 | 1;
167
168 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
169 }
170
171 tracker.FinalizeProfile(kSeqId);
172
173 const auto& objs_table = context.storage->heap_graph_object_table();
174 const auto& class_table = context.storage->heap_graph_class_table();
175 size_t count_bitmaps = 0;
176 for (uint32_t obj_row = 0; obj_row < objs_table.row_count(); ++obj_row) {
177 std::optional<uint32_t> class_row =
178 class_table.id().IndexOf(objs_table.type_id()[obj_row]);
179 ASSERT_TRUE(class_row.has_value());
180 if (context.storage->string_pool().Get(class_table.name()[*class_row]) ==
181 "android.graphics.Bitmap") {
182 EXPECT_EQ(objs_table.native_size()[obj_row], 24242);
183 count_bitmaps++;
184 } else {
185 EXPECT_EQ(objs_table.native_size()[obj_row], 0)
186 << context.storage->string_pool()
187 .Get(class_table.name()[*class_row])
188 .c_str()
189 << " has non zero native_size";
190 }
191 }
192 EXPECT_EQ(count_bitmaps, 1u);
193 }
194
TEST(HeapGraphTrackerTest,BuildFlamegraph)195 TEST(HeapGraphTrackerTest, BuildFlamegraph) {
196 // 4@A 5@B
197 // \ /
198 // 2@Y 3@Y
199 // \ /
200 // 1@X
201
202 constexpr uint64_t kSeqId = 1;
203 constexpr UniquePid kPid = 1;
204 constexpr int64_t kTimestamp = 1;
205
206 TraceProcessorContext context;
207 context.storage.reset(new TraceStorage());
208 context.process_tracker.reset(new ProcessTracker(&context));
209 context.process_tracker->GetOrCreateProcess(kPid);
210
211 HeapGraphTracker tracker(context.storage.get());
212
213 constexpr uint64_t kField = 1;
214 constexpr uint64_t kLocation = 0;
215
216 constexpr uint64_t kX = 1;
217 constexpr uint64_t kY = 2;
218 constexpr uint64_t kA = 3;
219 constexpr uint64_t kB = 4;
220 constexpr uint64_t kWeakRef = 5;
221
222 base::StringView field = base::StringView("foo");
223 StringPool::Id x = context.storage->InternString("X");
224 StringPool::Id y = context.storage->InternString("Y");
225 StringPool::Id a = context.storage->InternString("A");
226 StringPool::Id b = context.storage->InternString("B");
227 StringPool::Id weak_ref = context.storage->InternString("WeakReference");
228
229 StringPool::Id normal_kind = context.storage->InternString("KIND_NORMAL");
230 StringPool::Id weak_ref_kind =
231 context.storage->InternString("KIND_WEAK_REFERENCE");
232 tracker.AddInternedFieldName(kSeqId, kField, field);
233
234 tracker.AddInternedLocationName(kSeqId, kLocation,
235 context.storage->InternString("location"));
236 tracker.AddInternedType(kSeqId, kX, x, kLocation, /*object_size=*/0,
237 /*field_name_ids=*/{}, /*superclass_id=*/0,
238 /*classloader_id=*/0, /*no_fields=*/false,
239 /*kind=*/normal_kind);
240 tracker.AddInternedType(kSeqId, kY, y, kLocation, /*object_size=*/0,
241 /*field_name_ids=*/{}, /*superclass_id=*/0,
242 /*classloader_id=*/0, /*no_fields=*/false,
243 /*kind=*/normal_kind);
244 tracker.AddInternedType(kSeqId, kA, a, kLocation, /*object_size=*/0,
245 /*field_name_ids=*/{}, /*superclass_id=*/0,
246 /*classloader_id=*/0, /*no_fields=*/false,
247 /*kind=*/normal_kind);
248 tracker.AddInternedType(kSeqId, kB, b, kLocation, /*object_size=*/0,
249 /*field_name_ids=*/{}, /*superclass_id=*/0,
250 /*classloader_id=*/0, /*no_fields=*/false,
251 /*kind=*/normal_kind);
252 tracker.AddInternedType(kSeqId, kWeakRef, weak_ref, kLocation,
253 /*object_size=*/0,
254 /*field_name_ids=*/{}, /*superclass_id=*/0,
255 /*classloader_id=*/0, /*no_fields=*/false,
256 /*kind=*/weak_ref_kind);
257 {
258 HeapGraphTracker::SourceObject obj;
259 obj.object_id = 999;
260 obj.self_size = 999;
261 obj.type_id = kWeakRef;
262 obj.field_name_ids = {kField};
263 obj.referred_objects = {5};
264
265 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
266 }
267
268 {
269 HeapGraphTracker::SourceObject obj;
270 obj.object_id = 1;
271 obj.self_size = 1;
272 obj.type_id = kX;
273 obj.field_name_ids = {kField, kField};
274 obj.referred_objects = {2, 3};
275
276 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
277 }
278
279 {
280 HeapGraphTracker::SourceObject obj;
281 obj.object_id = 2;
282 obj.self_size = 2;
283 obj.type_id = kY;
284 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
285 }
286
287 {
288 HeapGraphTracker::SourceObject obj;
289 obj.object_id = 3;
290 obj.self_size = 3;
291 obj.type_id = kY;
292 obj.field_name_ids = {kField, kField};
293 obj.referred_objects = {4, 5};
294
295 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
296 }
297
298 {
299 HeapGraphTracker::SourceObject obj;
300 obj.object_id = 4;
301 obj.self_size = 4;
302 obj.type_id = kA;
303 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
304 }
305
306 {
307 HeapGraphTracker::SourceObject obj;
308 obj.object_id = 5;
309 obj.self_size = 5;
310 obj.type_id = kB;
311 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
312 }
313
314 HeapGraphTracker::SourceRoot root;
315 root.root_type = context.storage->InternString("ROOT");
316 root.object_ids.emplace_back(1);
317 root.object_ids.emplace_back(999);
318 tracker.AddRoot(kSeqId, kPid, kTimestamp, root);
319
320 tracker.FinalizeProfile(kSeqId);
321 std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> flame =
322 tracker.BuildFlamegraph(kPid, kTimestamp);
323 ASSERT_NE(flame, nullptr);
324
325 auto cumulative_sizes = flame->cumulative_size().ToVectorForTesting();
326 EXPECT_THAT(cumulative_sizes, UnorderedElementsAre(15, 4, 14, 5, 999));
327
328 auto cumulative_counts = flame->cumulative_count().ToVectorForTesting();
329 EXPECT_THAT(cumulative_counts, UnorderedElementsAre(5, 4, 1, 1, 1));
330
331 auto sizes = flame->size().ToVectorForTesting();
332 EXPECT_THAT(sizes, UnorderedElementsAre(1, 5, 4, 5, 999));
333
334 auto counts = flame->count().ToVectorForTesting();
335 EXPECT_THAT(counts, UnorderedElementsAre(1, 2, 1, 1, 1));
336 }
337
338 static const char kArray[] = "X[]";
339 static const char kDoubleArray[] = "X[][]";
340 static const char kNoArray[] = "X";
341 static const char kLongNoArray[] = "ABCDE";
342 static const char kStaticClassNoArray[] = "java.lang.Class<abc>";
343 static const char kStaticClassArray[] = "java.lang.Class<abc[]>";
344
TEST(HeapGraphTrackerTest,NormalizeTypeName)345 TEST(HeapGraphTrackerTest, NormalizeTypeName) {
346 // sizeof(...) - 1 below to get rid of the null-byte.
347 EXPECT_EQ(NormalizeTypeName(base::StringView(kArray, sizeof(kArray) - 1))
348 .ToStdString(),
349 "X");
350 EXPECT_EQ(NormalizeTypeName(
351 base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1))
352 .ToStdString(),
353 "X");
354 EXPECT_EQ(NormalizeTypeName(base::StringView(kNoArray, sizeof(kNoArray) - 1))
355 .ToStdString(),
356 "X");
357 EXPECT_EQ(NormalizeTypeName(
358 base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1))
359 .ToStdString(),
360 "ABCDE");
361 EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassNoArray,
362 sizeof(kStaticClassNoArray) - 1))
363 .ToStdString(),
364 "abc");
365 EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassArray,
366 sizeof(kStaticClassArray) - 1))
367 .ToStdString(),
368 "abc");
369 }
370
TEST(HeapGraphTrackerTest,NumberOfArray)371 TEST(HeapGraphTrackerTest, NumberOfArray) {
372 // sizeof(...) - 1 below to get rid of the null-byte.
373 EXPECT_EQ(NumberOfArrays(base::StringView(kArray, sizeof(kArray) - 1)), 1u);
374 EXPECT_EQ(
375 NumberOfArrays(base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1)),
376 2u);
377 EXPECT_EQ(NumberOfArrays(base::StringView(kNoArray, sizeof(kNoArray) - 1)),
378 0u);
379 EXPECT_EQ(
380 NumberOfArrays(base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1)),
381 0u);
382 }
383
384 } // namespace
385 } // namespace trace_processor
386 } // namespace perfetto
387