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