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