• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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