1 /*
2 * Copyright (C) 2019 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_module.h"
18
19 #include "src/trace_processor/importers/common/process_tracker.h"
20 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
21 #include "src/trace_processor/importers/proto/profiler_util.h"
22 #include "src/trace_processor/storage/trace_storage.h"
23 #include "src/trace_processor/types/trace_processor_context.h"
24
25 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
26 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
27
28 namespace perfetto {
29 namespace trace_processor {
30
31 namespace {
32
HeapGraphRootTypeToString(int32_t type)33 const char* HeapGraphRootTypeToString(int32_t type) {
34 switch (type) {
35 case protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN:
36 return "ROOT_UNKNOWN";
37 case protos::pbzero::HeapGraphRoot::ROOT_JNI_GLOBAL:
38 return "ROOT_JNI_GLOBAL";
39 case protos::pbzero::HeapGraphRoot::ROOT_JNI_LOCAL:
40 return "ROOT_JNI_LOCAL";
41 case protos::pbzero::HeapGraphRoot::ROOT_JAVA_FRAME:
42 return "ROOT_JAVA_FRAME";
43 case protos::pbzero::HeapGraphRoot::ROOT_NATIVE_STACK:
44 return "ROOT_NATIVE_STACK";
45 case protos::pbzero::HeapGraphRoot::ROOT_STICKY_CLASS:
46 return "ROOT_STICKY_CLASS";
47 case protos::pbzero::HeapGraphRoot::ROOT_THREAD_BLOCK:
48 return "ROOT_THREAD_BLOCK";
49 case protos::pbzero::HeapGraphRoot::ROOT_MONITOR_USED:
50 return "ROOT_MONITOR_USED";
51 case protos::pbzero::HeapGraphRoot::ROOT_THREAD_OBJECT:
52 return "ROOT_THREAD_OBJECT";
53 case protos::pbzero::HeapGraphRoot::ROOT_INTERNED_STRING:
54 return "ROOT_INTERNED_STRING";
55 case protos::pbzero::HeapGraphRoot::ROOT_FINALIZING:
56 return "ROOT_FINALIZING";
57 case protos::pbzero::HeapGraphRoot::ROOT_DEBUGGER:
58 return "ROOT_DEBUGGER";
59 case protos::pbzero::HeapGraphRoot::ROOT_REFERENCE_CLEANUP:
60 return "ROOT_REFERENCE_CLEANUP";
61 case protos::pbzero::HeapGraphRoot::ROOT_VM_INTERNAL:
62 return "ROOT_VM_INTERNAL";
63 case protos::pbzero::HeapGraphRoot::ROOT_JNI_MONITOR:
64 return "ROOT_JNI_MONITOR";
65 default:
66 return "ROOT_UNKNOWN";
67 }
68 }
69
HeapGraphTypeKindToString(int32_t type)70 const char* HeapGraphTypeKindToString(int32_t type) {
71 switch (type) {
72 case protos::pbzero::HeapGraphType::KIND_NORMAL:
73 return "KIND_NORMAL";
74 case protos::pbzero::HeapGraphType::KIND_NOREFERENCES:
75 return "KIND_NOREFERENCES";
76 case protos::pbzero::HeapGraphType::KIND_STRING:
77 return "KIND_STRING";
78 case protos::pbzero::HeapGraphType::KIND_ARRAY:
79 return "KIND_ARRAY";
80 case protos::pbzero::HeapGraphType::KIND_CLASS:
81 return "KIND_CLASS";
82 case protos::pbzero::HeapGraphType::KIND_CLASSLOADER:
83 return "KIND_CLASSLOADER";
84 case protos::pbzero::HeapGraphType::KIND_DEXCACHE:
85 return "KIND_DEXCACHE";
86 case protos::pbzero::HeapGraphType::KIND_SOFT_REFERENCE:
87 return "KIND_SOFT_REFERENCE";
88 case protos::pbzero::HeapGraphType::KIND_WEAK_REFERENCE:
89 return "KIND_WEAK_REFERENCE";
90 case protos::pbzero::HeapGraphType::KIND_FINALIZER_REFERENCE:
91 return "KIND_FINALIZER_REFERENCE";
92 case protos::pbzero::HeapGraphType::KIND_PHANTOM_REFERENCE:
93 return "KIND_PHANTOM_REFERENCE";
94 default:
95 return "KIND_UNKNOWN";
96 }
97 }
98
99 // Iterate over a repeated field of varints, independent of whether it is
100 // packed or not.
101 template <int32_t field_no, typename T, typename F>
ForEachVarInt(const T & decoder,F fn)102 bool ForEachVarInt(const T& decoder, F fn) {
103 auto field = decoder.template at<field_no>();
104 bool parse_error = false;
105 if (field.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
106 // packed repeated
107 auto it = decoder.template GetPackedRepeated<
108 ::protozero::proto_utils::ProtoWireType::kVarInt, uint64_t>(
109 field_no, &parse_error);
110 for (; it; ++it)
111 fn(*it);
112 } else {
113 // non-packed repeated
114 auto it = decoder.template GetRepeated<uint64_t>(field_no);
115 for (; it; ++it)
116 fn(*it);
117 }
118 return parse_error;
119 }
120
121 } // namespace
122
123 using perfetto::protos::pbzero::TracePacket;
124
HeapGraphModule(TraceProcessorContext * context)125 HeapGraphModule::HeapGraphModule(TraceProcessorContext* context)
126 : context_(context) {
127 RegisterForField(TracePacket::kHeapGraphFieldNumber, context);
128 RegisterForField(TracePacket::kDeobfuscationMappingFieldNumber, context);
129 }
130
ParsePacket(const protos::pbzero::TracePacket::Decoder & decoder,const TimestampedTracePiece & ttp,uint32_t field_id)131 void HeapGraphModule::ParsePacket(
132 const protos::pbzero::TracePacket::Decoder& decoder,
133 const TimestampedTracePiece& ttp,
134 uint32_t field_id) {
135 switch (field_id) {
136 case TracePacket::kHeapGraphFieldNumber:
137 ParseHeapGraph(decoder.trusted_packet_sequence_id(), ttp.timestamp,
138 decoder.heap_graph());
139 return;
140 case TracePacket::kDeobfuscationMappingFieldNumber:
141 HeapGraphTracker::GetOrCreate(context_)->FinalizeAllProfiles();
142 ParseDeobfuscationMapping(decoder.deobfuscation_mapping());
143 return;
144 }
145 }
146
ParseHeapGraph(uint32_t seq_id,int64_t ts,protozero::ConstBytes blob)147 void HeapGraphModule::ParseHeapGraph(uint32_t seq_id,
148 int64_t ts,
149 protozero::ConstBytes blob) {
150 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
151 protos::pbzero::HeapGraph::Decoder heap_graph(blob.data, blob.size);
152 UniquePid upid = context_->process_tracker->GetOrCreateProcess(
153 static_cast<uint32_t>(heap_graph.pid()));
154 heap_graph_tracker->SetPacketIndex(seq_id, heap_graph.index());
155 for (auto it = heap_graph.objects(); it; ++it) {
156 protos::pbzero::HeapGraphObject::Decoder object(*it);
157 HeapGraphTracker::SourceObject obj;
158 if (object.id_delta()) {
159 obj.object_id =
160 heap_graph_tracker->GetLastObjectId(seq_id) + object.id_delta();
161 } else {
162 obj.object_id = object.id();
163 }
164 obj.self_size = object.self_size();
165 obj.type_id = object.type_id();
166
167 // Even though the field is named reference_field_id_base, it has always
168 // been used as a base for reference_object_id.
169 uint64_t base_obj_id = object.reference_field_id_base();
170
171 // In S+ traces, this field will not be set for normal instances. It will be
172 // set in the corresponding HeapGraphType instead. It will still be set for
173 // class objects.
174 //
175 // grep-friendly: reference_field_id
176 bool parse_error = ForEachVarInt<
177 protos::pbzero::HeapGraphObject::kReferenceFieldIdFieldNumber>(
178 object,
179 [&obj](uint64_t value) { obj.field_name_ids.push_back(value); });
180
181 if (!parse_error) {
182 // grep-friendly: reference_object_id
183 parse_error = ForEachVarInt<
184 protos::pbzero::HeapGraphObject::kReferenceObjectIdFieldNumber>(
185 object, [&obj, base_obj_id](uint64_t value) {
186 if (value)
187 value += base_obj_id;
188 obj.referred_objects.push_back(value);
189 });
190 }
191
192 if (object.has_native_allocation_registry_size_field()) {
193 obj.native_allocation_registry_size =
194 object.native_allocation_registry_size_field();
195 }
196
197 if (parse_error) {
198 context_->storage->IncrementIndexedStats(
199 stats::heap_graph_malformed_packet, static_cast<int>(upid));
200 break;
201 }
202 if (!obj.field_name_ids.empty() &&
203 (obj.field_name_ids.size() != obj.referred_objects.size())) {
204 context_->storage->IncrementIndexedStats(
205 stats::heap_graph_malformed_packet, static_cast<int>(upid));
206 continue;
207 }
208 heap_graph_tracker->AddObject(seq_id, upid, ts, std::move(obj));
209 }
210 for (auto it = heap_graph.types(); it; ++it) {
211 std::vector<uint64_t> field_name_ids;
212 protos::pbzero::HeapGraphType::Decoder entry(*it);
213 const char* str = reinterpret_cast<const char*>(entry.class_name().data);
214 auto str_view = base::StringView(str, entry.class_name().size);
215
216 // grep-friendly: reference_field_id
217 bool parse_error = ForEachVarInt<
218 protos::pbzero::HeapGraphType::kReferenceFieldIdFieldNumber>(
219 entry,
220 [&field_name_ids](uint64_t value) { field_name_ids.push_back(value); });
221
222 if (parse_error) {
223 context_->storage->IncrementIndexedStats(
224 stats::heap_graph_malformed_packet, static_cast<int>(upid));
225 continue;
226 }
227
228 bool no_fields =
229 entry.kind() == protos::pbzero::HeapGraphType::KIND_NOREFERENCES ||
230 entry.kind() == protos::pbzero::HeapGraphType::KIND_ARRAY ||
231 entry.kind() == protos::pbzero::HeapGraphType::KIND_STRING;
232
233 StringPool::Id kind = context_->storage->InternString(
234 HeapGraphTypeKindToString(entry.kind()));
235 base::Optional<uint64_t> location_id;
236 if (entry.has_location_id())
237 location_id = entry.location_id();
238
239 heap_graph_tracker->AddInternedType(
240 seq_id, entry.id(), context_->storage->InternString(str_view),
241 location_id, entry.object_size(), std::move(field_name_ids),
242 entry.superclass_id(), entry.classloader_id(), no_fields, kind);
243 }
244 for (auto it = heap_graph.field_names(); it; ++it) {
245 protos::pbzero::InternedString::Decoder entry(*it);
246 const char* str = reinterpret_cast<const char*>(entry.str().data);
247 auto str_view = base::StringView(str, entry.str().size);
248
249 heap_graph_tracker->AddInternedFieldName(seq_id, entry.iid(), str_view);
250 }
251 for (auto it = heap_graph.location_names(); it; ++it) {
252 protos::pbzero::InternedString::Decoder entry(*it);
253 const char* str = reinterpret_cast<const char*>(entry.str().data);
254 auto str_view = base::StringView(str, entry.str().size);
255
256 heap_graph_tracker->AddInternedLocationName(
257 seq_id, entry.iid(), context_->storage->InternString(str_view));
258 }
259 for (auto it = heap_graph.roots(); it; ++it) {
260 protos::pbzero::HeapGraphRoot::Decoder entry(*it);
261 const char* str = HeapGraphRootTypeToString(entry.root_type());
262 auto str_view = base::StringView(str);
263
264 HeapGraphTracker::SourceRoot src_root;
265 src_root.root_type = context_->storage->InternString(str_view);
266 // grep-friendly: object_ids
267 bool parse_error =
268 ForEachVarInt<protos::pbzero::HeapGraphRoot::kObjectIdsFieldNumber>(
269 entry, [&src_root](uint64_t value) {
270 src_root.object_ids.emplace_back(value);
271 });
272 if (parse_error) {
273 context_->storage->IncrementIndexedStats(
274 stats::heap_graph_malformed_packet, static_cast<int>(upid));
275 break;
276 }
277 heap_graph_tracker->AddRoot(seq_id, upid, ts, std::move(src_root));
278 }
279 if (!heap_graph.continued()) {
280 heap_graph_tracker->FinalizeProfile(seq_id);
281 }
282 }
283
DeobfuscateClass(base::Optional<StringPool::Id> package_name_id,StringPool::Id obfuscated_class_name_id,const protos::pbzero::ObfuscatedClass::Decoder & cls)284 void HeapGraphModule::DeobfuscateClass(
285 base::Optional<StringPool::Id> package_name_id,
286 StringPool::Id obfuscated_class_name_id,
287 const protos::pbzero::ObfuscatedClass::Decoder& cls) {
288 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
289 const std::vector<tables::HeapGraphClassTable::Id>* cls_objects =
290 heap_graph_tracker->RowsForType(package_name_id,
291 obfuscated_class_name_id);
292 if (cls_objects) {
293 for (tables::HeapGraphClassTable::Id id : *cls_objects) {
294 uint32_t row =
295 *context_->storage->heap_graph_class_table().id().IndexOf(id);
296 const StringPool::Id obfuscated_type_name_id =
297 context_->storage->heap_graph_class_table().name()[row];
298 const base::StringView obfuscated_type_name =
299 context_->storage->GetString(obfuscated_type_name_id);
300 NormalizedType normalized_type = GetNormalizedType(obfuscated_type_name);
301 std::string deobfuscated_type_name =
302 DenormalizeTypeName(normalized_type, cls.deobfuscated_name());
303 StringPool::Id deobfuscated_type_name_id =
304 context_->storage->InternString(
305 base::StringView(deobfuscated_type_name));
306 context_->storage->mutable_heap_graph_class_table()
307 ->mutable_deobfuscated_name()
308 ->Set(row, deobfuscated_type_name_id);
309 }
310 } else {
311 PERFETTO_DLOG("Class %s not found",
312 cls.obfuscated_name().ToStdString().c_str());
313 }
314 }
315
ParseDeobfuscationMapping(protozero::ConstBytes blob)316 void HeapGraphModule::ParseDeobfuscationMapping(protozero::ConstBytes blob) {
317 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
318 protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
319 blob.data, blob.size);
320 base::Optional<StringPool::Id> package_name_id;
321 if (deobfuscation_mapping.package_name().size > 0) {
322 package_name_id = context_->storage->string_pool().GetId(
323 deobfuscation_mapping.package_name());
324 }
325
326 for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
327 ++class_it) {
328 protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
329 auto obfuscated_class_name_id =
330 context_->storage->string_pool().GetId(cls.obfuscated_name());
331 if (!obfuscated_class_name_id) {
332 PERFETTO_DLOG("Class string %s not found",
333 cls.obfuscated_name().ToStdString().c_str());
334 } else {
335 // TODO(b/153552977): Remove this work-around for legacy traces.
336 // For traces without location information, deobfuscate all matching
337 // classes.
338 DeobfuscateClass(base::nullopt, *obfuscated_class_name_id, cls);
339 if (package_name_id) {
340 DeobfuscateClass(package_name_id, *obfuscated_class_name_id, cls);
341 }
342 }
343 for (auto member_it = cls.obfuscated_members(); member_it; ++member_it) {
344 protos::pbzero::ObfuscatedMember::Decoder member(*member_it);
345
346 std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
347 "." +
348 member.obfuscated_name().ToStdString();
349 std::string merged_deobfuscated =
350 FullyQualifiedDeobfuscatedName(cls, member);
351
352 auto obfuscated_field_name_id = context_->storage->string_pool().GetId(
353 base::StringView(merged_obfuscated));
354 if (!obfuscated_field_name_id) {
355 PERFETTO_DLOG("Field string %s not found", merged_obfuscated.c_str());
356 continue;
357 }
358
359 const std::vector<int64_t>* field_references =
360 heap_graph_tracker->RowsForField(*obfuscated_field_name_id);
361 if (field_references) {
362 auto interned_deobfuscated_name = context_->storage->InternString(
363 base::StringView(merged_deobfuscated));
364 for (int64_t row : *field_references) {
365 context_->storage->mutable_heap_graph_reference_table()
366 ->mutable_deobfuscated_field_name()
367 ->Set(static_cast<uint32_t>(row), interned_deobfuscated_name);
368 }
369 } else {
370 PERFETTO_DLOG("Field %s not found", merged_obfuscated.c_str());
371 }
372 }
373 }
374 }
375
NotifyEndOfFile()376 void HeapGraphModule::NotifyEndOfFile() {
377 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
378 heap_graph_tracker->FinalizeAllProfiles();
379 }
380
381 } // namespace trace_processor
382 } // namespace perfetto
383