• 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/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