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