• 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       ParseDeobfuscationMapping(decoder.deobfuscation_mapping());
142       return;
143   }
144 }
145 
ParseHeapGraph(uint32_t seq_id,int64_t ts,protozero::ConstBytes blob)146 void HeapGraphModule::ParseHeapGraph(uint32_t seq_id,
147                                      int64_t ts,
148                                      protozero::ConstBytes blob) {
149   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
150   protos::pbzero::HeapGraph::Decoder heap_graph(blob.data, blob.size);
151   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
152       static_cast<uint32_t>(heap_graph.pid()));
153   heap_graph_tracker->SetPacketIndex(seq_id, heap_graph.index());
154   for (auto it = heap_graph.objects(); it; ++it) {
155     protos::pbzero::HeapGraphObject::Decoder object(*it);
156     HeapGraphTracker::SourceObject obj;
157     if (object.id_delta()) {
158       obj.object_id =
159           heap_graph_tracker->GetLastObjectId(seq_id) + object.id_delta();
160     } else {
161       obj.object_id = object.id();
162     }
163     obj.self_size = object.self_size();
164     obj.type_id = object.type_id();
165 
166     uint64_t base_obj_id = object.reference_field_id_base();
167 
168     // In S+ traces, this field will not be set for normal instances. It will be
169     // set in the corresponding HeapGraphType instead. It will still be set for
170     // class objects.
171     //
172     // grep-friendly: reference_field_id
173     bool parse_error = ForEachVarInt<
174         protos::pbzero::HeapGraphObject::kReferenceFieldIdFieldNumber>(
175         object,
176         [&obj](uint64_t value) { obj.field_name_ids.push_back(value); });
177 
178     if (!parse_error) {
179       // grep-friendly: reference_object_id
180       parse_error = ForEachVarInt<
181           protos::pbzero::HeapGraphObject::kReferenceObjectIdFieldNumber>(
182           object, [&obj, base_obj_id](uint64_t value) {
183             if (value)
184               value += base_obj_id;
185             obj.referred_objects.push_back(value);
186           });
187     }
188 
189     if (parse_error) {
190       context_->storage->IncrementIndexedStats(
191           stats::heap_graph_malformed_packet, static_cast<int>(upid));
192       break;
193     }
194     if (!obj.field_name_ids.empty() &&
195         (obj.field_name_ids.size() != obj.referred_objects.size())) {
196       context_->storage->IncrementIndexedStats(
197           stats::heap_graph_malformed_packet, static_cast<int>(upid));
198       continue;
199     }
200     heap_graph_tracker->AddObject(seq_id, upid, ts, std::move(obj));
201   }
202   for (auto it = heap_graph.types(); it; ++it) {
203     std::vector<uint64_t> field_name_ids;
204     protos::pbzero::HeapGraphType::Decoder entry(*it);
205     const char* str = reinterpret_cast<const char*>(entry.class_name().data);
206     auto str_view = base::StringView(str, entry.class_name().size);
207 
208     // grep-friendly: reference_field_id
209     bool parse_error = ForEachVarInt<
210         protos::pbzero::HeapGraphType::kReferenceFieldIdFieldNumber>(
211         entry,
212         [&field_name_ids](uint64_t value) { field_name_ids.push_back(value); });
213 
214     if (parse_error) {
215       context_->storage->IncrementIndexedStats(
216           stats::heap_graph_malformed_packet, static_cast<int>(upid));
217       continue;
218     }
219 
220     bool no_fields =
221         entry.kind() == protos::pbzero::HeapGraphType::KIND_NOREFERENCES ||
222         entry.kind() == protos::pbzero::HeapGraphType::KIND_ARRAY ||
223         entry.kind() == protos::pbzero::HeapGraphType::KIND_STRING;
224 
225     StringPool::Id kind = context_->storage->InternString(
226         HeapGraphTypeKindToString(entry.kind()));
227     base::Optional<uint64_t> location_id;
228     if (entry.has_location_id())
229       location_id = entry.location_id();
230 
231     heap_graph_tracker->AddInternedType(
232         seq_id, entry.id(), context_->storage->InternString(str_view),
233         location_id, entry.object_size(), std::move(field_name_ids),
234         entry.superclass_id(), entry.classloader_id(), no_fields, kind);
235   }
236   for (auto it = heap_graph.field_names(); it; ++it) {
237     protos::pbzero::InternedString::Decoder entry(*it);
238     const char* str = reinterpret_cast<const char*>(entry.str().data);
239     auto str_view = base::StringView(str, entry.str().size);
240 
241     heap_graph_tracker->AddInternedFieldName(seq_id, entry.iid(), str_view);
242   }
243   for (auto it = heap_graph.location_names(); it; ++it) {
244     protos::pbzero::InternedString::Decoder entry(*it);
245     const char* str = reinterpret_cast<const char*>(entry.str().data);
246     auto str_view = base::StringView(str, entry.str().size);
247 
248     heap_graph_tracker->AddInternedLocationName(
249         seq_id, entry.iid(), context_->storage->InternString(str_view));
250   }
251   for (auto it = heap_graph.roots(); it; ++it) {
252     protos::pbzero::HeapGraphRoot::Decoder entry(*it);
253     const char* str = HeapGraphRootTypeToString(entry.root_type());
254     auto str_view = base::StringView(str);
255 
256     HeapGraphTracker::SourceRoot src_root;
257     src_root.root_type = context_->storage->InternString(str_view);
258     // grep-friendly: object_ids
259     bool parse_error =
260         ForEachVarInt<protos::pbzero::HeapGraphRoot::kObjectIdsFieldNumber>(
261             entry, [&src_root](uint64_t value) {
262               src_root.object_ids.emplace_back(value);
263             });
264     if (parse_error) {
265       context_->storage->IncrementIndexedStats(
266           stats::heap_graph_malformed_packet, static_cast<int>(upid));
267       break;
268     }
269     heap_graph_tracker->AddRoot(seq_id, upid, ts, std::move(src_root));
270   }
271   if (!heap_graph.continued()) {
272     heap_graph_tracker->FinalizeProfile(seq_id);
273   }
274 }
275 
DeobfuscateClass(base::Optional<StringPool::Id> package_name_id,StringPool::Id obfuscated_class_name_id,const protos::pbzero::ObfuscatedClass::Decoder & cls)276 void HeapGraphModule::DeobfuscateClass(
277     base::Optional<StringPool::Id> package_name_id,
278     StringPool::Id obfuscated_class_name_id,
279     const protos::pbzero::ObfuscatedClass::Decoder& cls) {
280   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
281   const std::vector<tables::HeapGraphClassTable::Id>* cls_objects =
282       heap_graph_tracker->RowsForType(package_name_id,
283                                       obfuscated_class_name_id);
284 
285   if (cls_objects) {
286     for (tables::HeapGraphClassTable::Id id : *cls_objects) {
287       uint32_t row =
288           *context_->storage->heap_graph_class_table().id().IndexOf(id);
289       const StringPool::Id obfuscated_type_name_id =
290           context_->storage->heap_graph_class_table().name()[row];
291       const base::StringView obfuscated_type_name =
292           context_->storage->GetString(obfuscated_type_name_id);
293       NormalizedType normalized_type = GetNormalizedType(obfuscated_type_name);
294       std::string deobfuscated_type_name =
295           DenormalizeTypeName(normalized_type, cls.deobfuscated_name());
296       StringPool::Id deobfuscated_type_name_id =
297           context_->storage->InternString(
298               base::StringView(deobfuscated_type_name));
299       context_->storage->mutable_heap_graph_class_table()
300           ->mutable_deobfuscated_name()
301           ->Set(row, deobfuscated_type_name_id);
302     }
303   } else {
304     PERFETTO_DLOG("Class %s not found",
305                   cls.obfuscated_name().ToStdString().c_str());
306   }
307 }
308 
ParseDeobfuscationMapping(protozero::ConstBytes blob)309 void HeapGraphModule::ParseDeobfuscationMapping(protozero::ConstBytes blob) {
310   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
311   protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
312       blob.data, blob.size);
313   base::Optional<StringPool::Id> package_name_id;
314   if (deobfuscation_mapping.package_name().size > 0) {
315     package_name_id = context_->storage->string_pool().GetId(
316         deobfuscation_mapping.package_name());
317   }
318 
319   for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
320        ++class_it) {
321     protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
322     auto obfuscated_class_name_id =
323         context_->storage->string_pool().GetId(cls.obfuscated_name());
324     if (!obfuscated_class_name_id) {
325       PERFETTO_DLOG("Class string %s not found",
326                     cls.obfuscated_name().ToStdString().c_str());
327     } else {
328       // TODO(b/153552977): Remove this work-around for legacy traces.
329       // For traces without location information, deobfuscate all matching
330       // classes.
331       DeobfuscateClass(base::nullopt, *obfuscated_class_name_id, cls);
332       if (package_name_id) {
333         DeobfuscateClass(package_name_id, *obfuscated_class_name_id, cls);
334       }
335     }
336     for (auto member_it = cls.obfuscated_members(); member_it; ++member_it) {
337       protos::pbzero::ObfuscatedMember::Decoder member(*member_it);
338 
339       std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
340                                       "." +
341                                       member.obfuscated_name().ToStdString();
342       std::string merged_deobfuscated =
343           FullyQualifiedDeobfuscatedName(cls, member);
344 
345       auto obfuscated_field_name_id = context_->storage->string_pool().GetId(
346           base::StringView(merged_obfuscated));
347       if (!obfuscated_field_name_id) {
348         PERFETTO_DLOG("Field string %s not found", merged_obfuscated.c_str());
349         continue;
350       }
351 
352       const std::vector<int64_t>* field_references =
353           heap_graph_tracker->RowsForField(*obfuscated_field_name_id);
354       if (field_references) {
355         auto interned_deobfuscated_name = context_->storage->InternString(
356             base::StringView(merged_deobfuscated));
357         for (int64_t row : *field_references) {
358           context_->storage->mutable_heap_graph_reference_table()
359               ->mutable_deobfuscated_field_name()
360               ->Set(static_cast<uint32_t>(row), interned_deobfuscated_name);
361         }
362       } else {
363         PERFETTO_DLOG("Field %s not found", merged_obfuscated.c_str());
364       }
365     }
366   }
367 }
368 
NotifyEndOfFile()369 void HeapGraphModule::NotifyEndOfFile() {
370   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
371   heap_graph_tracker->NotifyEndOfFile();
372 }
373 
374 }  // namespace trace_processor
375 }  // namespace perfetto
376