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