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 "src/trace_processor/importers/proto/profiler_util.h"
20
21 #include "perfetto/base/logging.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 TraceStorage storage;
32 EXPECT_EQ(
33 PackageFromLocation(&storage,
34 "/data/app/~~ASDFGH1234QWerT==/"
35 "com.twitter.android-MNBVCX7890SDTst6==/test.apk"),
36 "com.twitter.android");
37 EXPECT_EQ(PackageFromLocation(
38 &storage,
39 "/data/app/com.google.android.webview-6XfQhnaSkFwGK0sYL9is0G==/"
40 "base.apk"),
41 "com.google.android.webview");
42 EXPECT_EQ(PackageFromLocation(&storage,
43 "/data/app/"
44 "com.google.android.apps.wellbeing-"
45 "qfQCaB4uJ7P0OPpZQqOu0Q==/oat/arm64/base.odex"),
46 "com.google.android.apps.wellbeing");
47 }
48
TEST(HeapGraphTrackerTest,BuildFlamegraph)49 TEST(HeapGraphTrackerTest, BuildFlamegraph) {
50 // 4@A 5@B
51 // \ /
52 // 2@Y 3@Y
53 // \ /
54 // 1@X
55
56 constexpr uint64_t kSeqId = 1;
57 constexpr UniquePid kPid = 1;
58 constexpr int64_t kTimestamp = 1;
59
60 TraceProcessorContext context;
61 context.storage.reset(new TraceStorage());
62
63 HeapGraphTracker tracker(&context);
64
65 constexpr uint64_t kField = 1;
66 constexpr uint64_t kLocation = 0;
67
68 constexpr uint64_t kX = 1;
69 constexpr uint64_t kY = 2;
70 constexpr uint64_t kA = 3;
71 constexpr uint64_t kB = 4;
72 constexpr uint64_t kWeakRef = 5;
73
74 base::StringView field = base::StringView("foo");
75 StringPool::Id x = context.storage->InternString("X");
76 StringPool::Id y = context.storage->InternString("Y");
77 StringPool::Id a = context.storage->InternString("A");
78 StringPool::Id b = context.storage->InternString("B");
79 StringPool::Id weak_ref = context.storage->InternString("WeakReference");
80
81 StringPool::Id normal_kind = context.storage->InternString("KIND_NORMAL");
82 StringPool::Id weak_ref_kind =
83 context.storage->InternString("KIND_WEAK_REFERENCE");
84 tracker.AddInternedFieldName(kSeqId, kField, field);
85
86 tracker.AddInternedLocationName(kSeqId, kLocation,
87 context.storage->InternString("location"));
88 tracker.AddInternedType(kSeqId, kX, x, kLocation, /*object_size=*/0,
89 /*field_name_ids=*/{}, /*superclass_id=*/0,
90 /*classloader_id=*/0, /*no_fields=*/false,
91 /*kind=*/normal_kind);
92 tracker.AddInternedType(kSeqId, kY, y, kLocation, /*object_size=*/0,
93 /*field_name_ids=*/{}, /*superclass_id=*/0,
94 /*classloader_id=*/0, /*no_fields=*/false,
95 /*kind=*/normal_kind);
96 tracker.AddInternedType(kSeqId, kA, a, kLocation, /*object_size=*/0,
97 /*field_name_ids=*/{}, /*superclass_id=*/0,
98 /*classloader_id=*/0, /*no_fields=*/false,
99 /*kind=*/normal_kind);
100 tracker.AddInternedType(kSeqId, kB, b, kLocation, /*object_size=*/0,
101 /*field_name_ids=*/{}, /*superclass_id=*/0,
102 /*classloader_id=*/0, /*no_fields=*/false,
103 /*kind=*/normal_kind);
104 tracker.AddInternedType(kSeqId, kWeakRef, weak_ref, kLocation,
105 /*object_size=*/0,
106 /*field_name_ids=*/{}, /*superclass_id=*/0,
107 /*classloader_id=*/0, /*no_fields=*/false,
108 /*kind=*/weak_ref_kind);
109 {
110 HeapGraphTracker::SourceObject obj;
111 obj.object_id = 999;
112 obj.self_size = 999;
113 obj.type_id = kWeakRef;
114 obj.field_name_ids = {kField};
115 obj.referred_objects = {5};
116
117 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
118 }
119
120 {
121 HeapGraphTracker::SourceObject obj;
122 obj.object_id = 1;
123 obj.self_size = 1;
124 obj.type_id = kX;
125 obj.field_name_ids = {kField, kField};
126 obj.referred_objects = {2, 3};
127
128 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
129 }
130
131 {
132 HeapGraphTracker::SourceObject obj;
133 obj.object_id = 2;
134 obj.self_size = 2;
135 obj.type_id = kY;
136 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
137 }
138
139 {
140 HeapGraphTracker::SourceObject obj;
141 obj.object_id = 3;
142 obj.self_size = 3;
143 obj.type_id = kY;
144 obj.field_name_ids = {kField, kField};
145 obj.referred_objects = {4, 5};
146
147 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
148 }
149
150 {
151 HeapGraphTracker::SourceObject obj;
152 obj.object_id = 4;
153 obj.self_size = 4;
154 obj.type_id = kA;
155 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
156 }
157
158 {
159 HeapGraphTracker::SourceObject obj;
160 obj.object_id = 5;
161 obj.self_size = 5;
162 obj.type_id = kB;
163 tracker.AddObject(kSeqId, kPid, kTimestamp, std::move(obj));
164 }
165
166 HeapGraphTracker::SourceRoot root;
167 root.root_type = context.storage->InternString("ROOT");
168 root.object_ids.emplace_back(1);
169 root.object_ids.emplace_back(999);
170 tracker.AddRoot(kSeqId, kPid, kTimestamp, root);
171
172 tracker.FinalizeProfile(kSeqId);
173 std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> flame =
174 tracker.BuildFlamegraph(kPid, kTimestamp);
175 ASSERT_NE(flame, nullptr);
176
177 auto cumulative_sizes = flame->cumulative_size().ToVectorForTesting();
178 EXPECT_THAT(cumulative_sizes, UnorderedElementsAre(15, 4, 14, 5, 999));
179
180 auto cumulative_counts = flame->cumulative_count().ToVectorForTesting();
181 EXPECT_THAT(cumulative_counts, UnorderedElementsAre(5, 4, 1, 1, 1));
182
183 auto sizes = flame->size().ToVectorForTesting();
184 EXPECT_THAT(sizes, UnorderedElementsAre(1, 5, 4, 5, 999));
185
186 auto counts = flame->count().ToVectorForTesting();
187 EXPECT_THAT(counts, UnorderedElementsAre(1, 2, 1, 1, 1));
188 }
189
190 static const char kArray[] = "X[]";
191 static const char kDoubleArray[] = "X[][]";
192 static const char kNoArray[] = "X";
193 static const char kLongNoArray[] = "ABCDE";
194 static const char kStaticClassNoArray[] = "java.lang.Class<abc>";
195 static const char kStaticClassArray[] = "java.lang.Class<abc[]>";
196
TEST(HeapGraphTrackerTest,NormalizeTypeName)197 TEST(HeapGraphTrackerTest, NormalizeTypeName) {
198 // sizeof(...) - 1 below to get rid of the null-byte.
199 EXPECT_EQ(NormalizeTypeName(base::StringView(kArray, sizeof(kArray) - 1))
200 .ToStdString(),
201 "X");
202 EXPECT_EQ(NormalizeTypeName(
203 base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1))
204 .ToStdString(),
205 "X");
206 EXPECT_EQ(NormalizeTypeName(base::StringView(kNoArray, sizeof(kNoArray) - 1))
207 .ToStdString(),
208 "X");
209 EXPECT_EQ(NormalizeTypeName(
210 base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1))
211 .ToStdString(),
212 "ABCDE");
213 EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassNoArray,
214 sizeof(kStaticClassNoArray) - 1))
215 .ToStdString(),
216 "abc");
217 EXPECT_EQ(NormalizeTypeName(base::StringView(kStaticClassArray,
218 sizeof(kStaticClassArray) - 1))
219 .ToStdString(),
220 "abc");
221 }
222
TEST(HeapGraphTrackerTest,NumberOfArray)223 TEST(HeapGraphTrackerTest, NumberOfArray) {
224 // sizeof(...) - 1 below to get rid of the null-byte.
225 EXPECT_EQ(NumberOfArrays(base::StringView(kArray, sizeof(kArray) - 1)), 1u);
226 EXPECT_EQ(
227 NumberOfArrays(base::StringView(kDoubleArray, sizeof(kDoubleArray) - 1)),
228 2u);
229 EXPECT_EQ(NumberOfArrays(base::StringView(kNoArray, sizeof(kNoArray) - 1)),
230 0u);
231 EXPECT_EQ(
232 NumberOfArrays(base::StringView(kLongNoArray, sizeof(kLongNoArray) - 1)),
233 0u);
234 }
235
236 } // namespace
237 } // namespace trace_processor
238 } // namespace perfetto
239