xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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 <cstddef>
20 #include <cstdint>
21 #include <memory>
22 #include <optional>
23 #include <utility>
24 
25 #include "perfetto/ext/base/string_view.h"
26 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
27 #include "src/trace_processor/containers/string_pool.h"
28 #include "src/trace_processor/importers/common/process_tracker.h"
29 #include "src/trace_processor/storage/trace_storage.h"
30 #include "src/trace_processor/tables/profiler_tables_py.h"
31 #include "src/trace_processor/util/profiler_util.h"
32 #include "test/gtest_and_gmock.h"
33 
34 namespace perfetto {
35 namespace trace_processor {
36 namespace {
37 
38 using ::testing::UnorderedElementsAre;
39 
TEST(HeapGraphTrackerTest,PackageFromLocationApp)40 TEST(HeapGraphTrackerTest, PackageFromLocationApp) {
41   std::unique_ptr<TraceStorage> storage(new TraceStorage());
42 
43   const char data_app_path[] =
44       "/data/app/org.perfetto.test-6XfQhnaSkFwGK0sYL9is0G==/base.apk";
45   EXPECT_EQ(PackageFromLocation(storage.get(), data_app_path),
46             "org.perfetto.test");
47 
48   const char with_extra_dir[] =
49       "/data/app/~~ASDFGH1234QWerT==/"
50       "com.perfetto.test-MNBVCX7890SDTst6==/test.apk";
51   EXPECT_EQ(PackageFromLocation(storage.get(), with_extra_dir),
52             "com.perfetto.test");
53 
54   const char odex[] =
55       "/data/app/com.google.android.apps.wellbeing-"
56       "qfQCaB4uJ7P0OPpZQqOu0Q==/oat/arm64/base.odex";
57   EXPECT_EQ(PackageFromLocation(storage.get(), odex),
58             "com.google.android.apps.wellbeing");
59 
60   const char inmem_dex[] =
61       "[anon:dalvik-classes.dex extracted in memory from "
62       "/data/app/~~uUgHYtbjPNr2VFa3byIF4Q==/"
63       "com.perfetto.example-aC94wTfXRC60l2HJU5YvjQ==/base.apk]";
64   EXPECT_EQ(PackageFromLocation(storage.get(), inmem_dex),
65             "com.perfetto.example");
66 }
67 
TEST(HeapGraphTrackerTest,PopulateNativeSize)68 TEST(HeapGraphTrackerTest, PopulateNativeSize) {
69   constexpr uint64_t kSeqId = 1;
70   constexpr UniquePid kPid = 1;
71   constexpr int64_t kTimestamp = 1;
72 
73   TraceProcessorContext context;
74   context.storage = std::make_shared<TraceStorage>();
75   context.process_tracker = std::make_unique<ProcessTracker>(&context);
76   context.process_tracker->GetOrCreateProcess(kPid);
77 
78   HeapGraphTracker tracker(context.storage.get());
79 
80   constexpr uint64_t kLocation = 0;
81   tracker.AddInternedLocationName(kSeqId, kLocation,
82                                   context.storage->InternString("location"));
83 
84   enum Fields : uint64_t { kReferent = 1, kThunk, kThis0, kNext };
85 
86   tracker.AddInternedFieldName(kSeqId, kReferent,
87                                "java.lang.ref.Reference.referent");
88   tracker.AddInternedFieldName(kSeqId, kThunk, "sun.misc.Cleaner.thunk");
89   tracker.AddInternedFieldName(
90       kSeqId, kThis0,
91       "libcore.util.NativeAllocationRegistry$CleanerThunk.this$0");
92   tracker.AddInternedFieldName(kSeqId, kNext, "sun.misc.Cleaner.next");
93 
94   enum Types : uint64_t {
95     kTypeBitmap = 1,
96     kTypeCleaner,
97     kTypeCleanerThunk,
98     kTypeNativeAllocationRegistry,
99   };
100 
101   tracker.AddInternedType(
102       kSeqId, kTypeBitmap,
103       context.storage->InternString("android.graphics.Bitmap"), kLocation,
104       /*object_size=*/0,
105       /*field_name_ids=*/{}, /*superclass_id=*/0,
106       /*classloader_id=*/0, /*no_fields=*/false,
107       protos::pbzero::HeapGraphType::KIND_NORMAL);
108 
109   tracker.AddInternedType(kSeqId, kTypeCleaner,
110                           context.storage->InternString("sun.misc.Cleaner"),
111                           kLocation, /*object_size=*/0,
112                           /*field_name_ids=*/{kReferent, kThunk, kNext},
113                           /*superclass_id=*/0,
114                           /*classloader_id=*/0, /*no_fields=*/false,
115                           protos::pbzero::HeapGraphType::KIND_NORMAL);
116 
117   tracker.AddInternedType(
118       kSeqId, kTypeCleanerThunk,
119       context.storage->InternString(
120           "libcore.util.NativeAllocationRegistry$CleanerThunk"),
121       kLocation, /*object_size=*/0,
122       /*field_name_ids=*/{kThis0}, /*superclass_id=*/0,
123       /*classloader_id=*/0, /*no_fields=*/false,
124       protos::pbzero::HeapGraphType::KIND_NORMAL);
125 
126   tracker.AddInternedType(
127       kSeqId, kTypeNativeAllocationRegistry,
128       context.storage->InternString("libcore.util.NativeAllocationRegistry"),
129       kLocation, /*object_size=*/0,
130       /*field_name_ids=*/{}, /*superclass_id=*/0,
131       /*classloader_id=*/0, /*no_fields=*/false,
132       protos::pbzero::HeapGraphType::KIND_NORMAL);
133 
134   enum Objects : uint64_t {
135     kObjBitmap = 1,
136     kObjCleaner,
137     kObjThunk,
138     kObjNativeAllocationRegistry,
139   };
140 
141   {
142     HeapGraphTracker::SourceObject obj;
143     obj.object_id = kObjBitmap;
144     obj.type_id = kTypeBitmap;
145 
146     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
147   }
148 
149   {
150     HeapGraphTracker::SourceObject obj;
151     obj.object_id = kObjCleaner;
152     obj.type_id = kTypeCleaner;
153     obj.referred_objects = {kObjBitmap, kObjThunk, 0};
154 
155     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
156   }
157 
158   {
159     HeapGraphTracker::SourceObject obj;
160     obj.object_id = kObjThunk;
161     obj.type_id = kTypeCleanerThunk;
162     obj.referred_objects = {kObjNativeAllocationRegistry};
163 
164     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
165   }
166 
167   {
168     HeapGraphTracker::SourceObject obj;
169     obj.object_id = kObjNativeAllocationRegistry;
170     obj.type_id = kTypeNativeAllocationRegistry;
171 
172     // NativeAllocationRegistry.size least significant bit is used to encode the
173     // source of the allocation (1: malloc, 0: other).
174     obj.native_allocation_registry_size = 24242 | 1;
175 
176     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
177   }
178 
179   tracker.FinalizeProfile(kSeqId);
180 
181   const auto& objs_table = context.storage->heap_graph_object_table();
182   const auto& class_table = context.storage->heap_graph_class_table();
183   size_t count_bitmaps = 0;
184   for (auto it = objs_table.IterateRows(); it; ++it) {
185     auto class_row = class_table.FindById(it.type_id());
186     ASSERT_TRUE(class_row.has_value());
187     if (context.storage->string_pool().Get(class_row->name()) ==
188         "android.graphics.Bitmap") {
189       EXPECT_EQ(it.native_size(), 24242);
190       count_bitmaps++;
191     } else {
192       EXPECT_EQ(it.native_size(), 0)
193           << context.storage->string_pool().Get(class_row->name()).c_str()
194           << " has non zero native_size";
195     }
196   }
197   EXPECT_EQ(count_bitmaps, 1u);
198 }
199 
TEST(HeapGraphTrackerTest,BuildFlamegraph)200 TEST(HeapGraphTrackerTest, BuildFlamegraph) {
201   //           4@A 5@B
202   //             \ /
203   //         2@Y 3@Y
204   //           \ /
205   //           1@X
206 
207   constexpr uint64_t kSeqId = 1;
208   constexpr UniquePid kPid = 1;
209   constexpr int64_t kTimestamp = 1;
210 
211   TraceProcessorContext context;
212   context.storage.reset(new TraceStorage());
213   context.process_tracker.reset(new ProcessTracker(&context));
214   context.process_tracker->GetOrCreateProcess(kPid);
215 
216   HeapGraphTracker tracker(context.storage.get());
217 
218   constexpr uint64_t kField = 1;
219   constexpr uint64_t kLocation = 0;
220 
221   constexpr uint64_t kX = 1;
222   constexpr uint64_t kY = 2;
223   constexpr uint64_t kA = 3;
224   constexpr uint64_t kB = 4;
225 
226   auto field = base::StringView("foo");
227   StringPool::Id x = context.storage->InternString("X");
228   StringPool::Id y = context.storage->InternString("Y");
229   StringPool::Id a = context.storage->InternString("A");
230   StringPool::Id b = context.storage->InternString("B");
231 
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                           protos::pbzero::HeapGraphType::KIND_NORMAL);
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                           protos::pbzero::HeapGraphType::KIND_NORMAL);
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                           protos::pbzero::HeapGraphType::KIND_NORMAL);
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                           protos::pbzero::HeapGraphType::KIND_NORMAL);
252   {
253     HeapGraphTracker::SourceObject obj;
254     obj.object_id = 1;
255     obj.self_size = 1;
256     obj.type_id = kX;
257     obj.field_name_ids = {kField, kField};
258     obj.referred_objects = {2, 3};
259 
260     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
261   }
262 
263   {
264     HeapGraphTracker::SourceObject obj;
265     obj.object_id = 2;
266     obj.self_size = 2;
267     obj.type_id = kY;
268     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
269   }
270 
271   {
272     HeapGraphTracker::SourceObject obj;
273     obj.object_id = 3;
274     obj.self_size = 3;
275     obj.type_id = kY;
276     obj.field_name_ids = {kField, kField};
277     obj.referred_objects = {4, 5};
278 
279     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
280   }
281 
282   {
283     HeapGraphTracker::SourceObject obj;
284     obj.object_id = 4;
285     obj.self_size = 4;
286     obj.type_id = kA;
287     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
288   }
289 
290   {
291     HeapGraphTracker::SourceObject obj;
292     obj.object_id = 5;
293     obj.self_size = 5;
294     obj.type_id = kB;
295     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
296   }
297 
298   HeapGraphTracker::SourceRoot root;
299   root.root_type = protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN;
300   root.object_ids.emplace_back(1);
301   tracker.AddRoot(kSeqId, kPid, kTimestamp, root);
302 
303   tracker.FinalizeProfile(kSeqId);
304   std::unique_ptr<tables::ExperimentalFlamegraphTable> flame =
305       tracker.BuildFlamegraph(kPid, kTimestamp);
306   ASSERT_NE(flame, nullptr);
307 
308   auto cumulative_sizes = flame->cumulative_size().ToVectorForTesting();
309   EXPECT_THAT(cumulative_sizes, UnorderedElementsAre(15, 4, 14, 5));
310 
311   auto cumulative_counts = flame->cumulative_count().ToVectorForTesting();
312   EXPECT_THAT(cumulative_counts, UnorderedElementsAre(5, 4, 1, 1));
313 
314   auto sizes = flame->size().ToVectorForTesting();
315   EXPECT_THAT(sizes, UnorderedElementsAre(1, 5, 4, 5));
316 
317   auto counts = flame->count().ToVectorForTesting();
318   EXPECT_THAT(counts, UnorderedElementsAre(1, 2, 1, 1));
319 }
320 
TEST(HeapGraphTrackerTest,BuildFlamegraphWeakReferences)321 TEST(HeapGraphTrackerTest, BuildFlamegraphWeakReferences) {
322   // Regression test for http://b.corp.google.com/issues/302662734:
323   // For weak (and other) references, we should not follow the
324   // `java.lang.ref.Reference.referent` field, but we should follow other
325   // fields.
326   //
327   //                                   2@A 4@B
328   //  (java.lang.ref.Reference.referent) \ / (X.other)
329   //                                     1@X (extends WeakReference)
330 
331   constexpr uint64_t kSeqId = 1;
332   constexpr UniquePid kPid = 1;
333   constexpr int64_t kTimestamp = 1;
334 
335   TraceProcessorContext context;
336   context.storage.reset(new TraceStorage());
337   context.process_tracker.reset(new ProcessTracker(&context));
338   context.process_tracker->GetOrCreateProcess(kPid);
339 
340   HeapGraphTracker tracker(context.storage.get());
341 
342   constexpr uint64_t kLocation = 0;
343 
344   base::StringView referent_field =
345       base::StringView("java.lang.ref.Reference.referent");
346   constexpr uint64_t kReferentField = 1;
347   base::StringView other_field = base::StringView("X.other");
348   constexpr uint64_t kOtherField = 2;
349 
350   constexpr uint64_t kX = 1;
351   StringPool::Id x = context.storage->InternString("X");
352   constexpr uint64_t kA = 2;
353   StringPool::Id a = context.storage->InternString("A");
354   constexpr uint64_t kB = 4;
355   StringPool::Id b = context.storage->InternString("B");
356   constexpr uint64_t kWeakRef = 5;
357   StringPool::Id weak_ref = context.storage->InternString("WeakReference");
358 
359   tracker.AddInternedFieldName(kSeqId, kReferentField, referent_field);
360   tracker.AddInternedFieldName(kSeqId, kOtherField, other_field);
361 
362   tracker.AddInternedLocationName(kSeqId, kLocation,
363                                   context.storage->InternString("location"));
364 
365   tracker.AddInternedType(kSeqId, kWeakRef, weak_ref, kLocation,
366                           /*object_size=*/0,
367                           /*field_name_ids=*/{kReferentField},
368                           /*superclass_id=*/0,
369                           /*classloader_id=*/0, /*no_fields=*/false,
370                           protos::pbzero::HeapGraphType::KIND_WEAK_REFERENCE);
371   tracker.AddInternedType(kSeqId, kX, x, kLocation,
372                           /*object_size=*/0,
373                           /*field_name_ids=*/{kOtherField},
374                           /*superclass_id=*/kWeakRef,
375                           /*classloader_id=*/0, /*no_fields=*/false,
376                           protos::pbzero::HeapGraphType::KIND_WEAK_REFERENCE);
377   tracker.AddInternedType(kSeqId, kA, a, kLocation, /*object_size=*/0,
378                           /*field_name_ids=*/{}, /*superclass_id=*/0,
379                           /*classloader_id=*/0, /*no_fields=*/false,
380                           protos::pbzero::HeapGraphType::KIND_NORMAL);
381   tracker.AddInternedType(kSeqId, kB, b, kLocation, /*object_size=*/0,
382                           /*field_name_ids=*/{}, /*superclass_id=*/0,
383                           /*classloader_id=*/0, /*no_fields=*/false,
384                           protos::pbzero::HeapGraphType::KIND_NORMAL);
385   {
386     HeapGraphTracker::SourceObject obj;
387     obj.object_id = 1;
388     obj.self_size = 1;
389     obj.type_id = kX;
390     obj.referred_objects = {/*X.other*/ 4,
391                             /*java.lang.ref.Reference.referent*/ 2};
392     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
393   }
394 
395   {
396     HeapGraphTracker::SourceObject obj;
397     obj.object_id = 2;
398     obj.self_size = 2;
399     obj.type_id = kA;
400     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
401   }
402 
403   {
404     HeapGraphTracker::SourceObject obj;
405     obj.object_id = 4;
406     obj.self_size = 4;
407     obj.type_id = kB;
408     tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
409   }
410 
411   HeapGraphTracker::SourceRoot root;
412   root.root_type = protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN;
413   root.object_ids.emplace_back(1);
414   tracker.AddRoot(kSeqId, kPid, kTimestamp, root);
415 
416   tracker.FinalizeProfile(kSeqId);
417   std::unique_ptr<tables::ExperimentalFlamegraphTable> flame =
418       tracker.BuildFlamegraph(kPid, kTimestamp);
419   ASSERT_NE(flame, nullptr);
420 
421   auto cumulative_sizes = flame->cumulative_size().ToVectorForTesting();
422   EXPECT_THAT(cumulative_sizes, UnorderedElementsAre(4, 4 + 1));
423 
424   auto cumulative_counts = flame->cumulative_count().ToVectorForTesting();
425   EXPECT_THAT(cumulative_counts, UnorderedElementsAre(1, 1 + 1));
426 
427   auto sizes = flame->size().ToVectorForTesting();
428   EXPECT_THAT(sizes, UnorderedElementsAre(1, 4));
429 
430   auto counts = flame->count().ToVectorForTesting();
431   EXPECT_THAT(counts, UnorderedElementsAre(1, 1));
432 }
433 
434 constexpr char kArray[] = "X[]";
435 constexpr char kDoubleArray[] = "X[][]";
436 constexpr char kNoArray[] = "X";
437 constexpr char kLongNoArray[] = "ABCDE";
438 constexpr char kStaticClassNoArray[] = "java.lang.Class<abc>";
439 constexpr char kStaticClassArray[] = "java.lang.Class<abc[]>";
440 
TEST(HeapGraphTrackerTest,NormalizeTypeName)441 TEST(HeapGraphTrackerTest, NormalizeTypeName) {
442   // sizeof(...) - 1 below to get rid of the null-byte.
443   EXPECT_EQ(NormalizeTypeName(base::StringView(kArray, sizeof(kArray) - 1))
444                 .ToStdString(),
445             "X");
446   EXPECT_EQ(NormalizeTypeName(
447                 base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1))
448                 .ToStdString(),
449             "X");
450   EXPECT_EQ(NormalizeTypeName(base::StringView(kNoArray, sizeof(kNoArray) - 1))
451                 .ToStdString(),
452             "X");
453   EXPECT_EQ(NormalizeTypeName(
454                 base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1))
455                 .ToStdString(),
456             "ABCDE");
457   EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassNoArray,
458                                                sizeof(kStaticClassNoArray) - 1))
459                 .ToStdString(),
460             "abc");
461   EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassArray,
462                                                sizeof(kStaticClassArray) - 1))
463                 .ToStdString(),
464             "abc");
465 }
466 
TEST(HeapGraphTrackerTest,NumberOfArray)467 TEST(HeapGraphTrackerTest, NumberOfArray) {
468   // sizeof(...) - 1 below to get rid of the null-byte.
469   EXPECT_EQ(NumberOfArrays(base::StringView(kArray, sizeof(kArray) - 1)), 1u);
470   EXPECT_EQ(
471       NumberOfArrays(base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1)),
472       2u);
473   EXPECT_EQ(NumberOfArrays(base::StringView(kNoArray, sizeof(kNoArray) - 1)),
474             0u);
475   EXPECT_EQ(
476       NumberOfArrays(base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1)),
477       0u);
478 }
479 
480 }  // namespace
481 }  // namespace trace_processor
482 }  // namespace perfetto
483