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
108 // Even though the field is named reference_field_id_base, it has always
109 // been used as a base for reference_object_id.
110 uint64_t base_obj_id = object.reference_field_id_base();
111
112 // In S+ traces, this field will not be set for normal instances. It will be
113 // set in the corresponding HeapGraphType instead. It will still be set for
114 // class objects.
115 //
116 // grep-friendly: reference_field_id
117 bool parse_error = ForEachVarInt<
118 protos::pbzero::HeapGraphObject::kReferenceFieldIdFieldNumber>(
119 object,
120 [&obj](uint64_t value) { obj.field_name_ids.push_back(value); });
121
122 if (!parse_error) {
123 // grep-friendly: reference_object_id
124 parse_error = ForEachVarInt<
125 protos::pbzero::HeapGraphObject::kReferenceObjectIdFieldNumber>(
126 object, [&obj, base_obj_id](uint64_t value) {
127 if (value)
128 value += base_obj_id;
129 obj.referred_objects.push_back(value);
130 });
131 }
132
133 if (object.has_native_allocation_registry_size_field()) {
134 obj.native_allocation_registry_size =
135 object.native_allocation_registry_size_field();
136 }
137
138 if (parse_error) {
139 context_->storage->IncrementIndexedStats(
140 stats::heap_graph_malformed_packet, static_cast<int>(upid));
141 break;
142 }
143 if (!obj.field_name_ids.empty() &&
144 (obj.field_name_ids.size() != obj.referred_objects.size())) {
145 context_->storage->IncrementIndexedStats(
146 stats::heap_graph_malformed_packet, static_cast<int>(upid));
147 continue;
148 }
149 heap_graph_tracker->AddObject(seq_id, upid, ts, std::move(obj));
150 }
151 for (auto it = heap_graph.types(); it; ++it) {
152 std::vector<uint64_t> field_name_ids;
153 protos::pbzero::HeapGraphType::Decoder entry(*it);
154 const char* str = reinterpret_cast<const char*>(entry.class_name().data);
155 auto str_view = base::StringView(str, entry.class_name().size);
156
157 // grep-friendly: reference_field_id
158 bool parse_error = ForEachVarInt<
159 protos::pbzero::HeapGraphType::kReferenceFieldIdFieldNumber>(
160 entry,
161 [&field_name_ids](uint64_t value) { field_name_ids.push_back(value); });
162
163 if (parse_error) {
164 context_->storage->IncrementIndexedStats(
165 stats::heap_graph_malformed_packet, static_cast<int>(upid));
166 continue;
167 }
168
169 bool no_fields =
170 entry.kind() == protos::pbzero::HeapGraphType::KIND_NOREFERENCES ||
171 entry.kind() == protos::pbzero::HeapGraphType::KIND_ARRAY ||
172 entry.kind() == protos::pbzero::HeapGraphType::KIND_STRING;
173
174 protos::pbzero::HeapGraphType::Kind kind =
175 protos::pbzero::HeapGraphType::KIND_UNKNOWN;
176 if (protos::pbzero::HeapGraphType_Kind_MIN <= entry.kind() &&
177 entry.kind() <= protos::pbzero::HeapGraphType_Kind_MAX) {
178 kind = protos::pbzero::HeapGraphType::Kind(entry.kind());
179 }
180
181 std::optional<uint64_t> location_id;
182 if (entry.has_location_id())
183 location_id = entry.location_id();
184
185 heap_graph_tracker->AddInternedType(
186 seq_id, entry.id(), context_->storage->InternString(str_view),
187 location_id, entry.object_size(), std::move(field_name_ids),
188 entry.superclass_id(), entry.classloader_id(), no_fields, kind);
189 }
190 for (auto it = heap_graph.field_names(); it; ++it) {
191 protos::pbzero::InternedString::Decoder entry(*it);
192 const char* str = reinterpret_cast<const char*>(entry.str().data);
193 auto str_view = base::StringView(str, entry.str().size);
194
195 heap_graph_tracker->AddInternedFieldName(seq_id, entry.iid(), str_view);
196 }
197 for (auto it = heap_graph.location_names(); it; ++it) {
198 protos::pbzero::InternedString::Decoder entry(*it);
199 const char* str = reinterpret_cast<const char*>(entry.str().data);
200 auto str_view = base::StringView(str, entry.str().size);
201
202 heap_graph_tracker->AddInternedLocationName(
203 seq_id, entry.iid(), context_->storage->InternString(str_view));
204 }
205 for (auto it = heap_graph.roots(); it; ++it) {
206 protos::pbzero::HeapGraphRoot::Decoder entry(*it);
207
208 HeapGraphTracker::SourceRoot src_root;
209 if (protos::pbzero::HeapGraphRoot_Type_MIN <= entry.root_type() &&
210 entry.root_type() <= protos::pbzero::HeapGraphRoot_Type_MAX) {
211 src_root.root_type =
212 protos::pbzero::HeapGraphRoot::Type(entry.root_type());
213 } else {
214 src_root.root_type = protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN;
215 }
216 // grep-friendly: object_ids
217 bool parse_error =
218 ForEachVarInt<protos::pbzero::HeapGraphRoot::kObjectIdsFieldNumber>(
219 entry, [&src_root](uint64_t value) {
220 src_root.object_ids.emplace_back(value);
221 });
222 if (parse_error) {
223 context_->storage->IncrementIndexedStats(
224 stats::heap_graph_malformed_packet, static_cast<int>(upid));
225 break;
226 }
227 heap_graph_tracker->AddRoot(seq_id, upid, ts, std::move(src_root));
228 }
229 if (!heap_graph.continued()) {
230 heap_graph_tracker->FinalizeProfile(seq_id);
231 }
232 }
233
DeobfuscateClass(std::optional<StringId> package_name_id,StringId obfuscated_class_name_id,const protos::pbzero::ObfuscatedClass::Decoder & cls)234 void HeapGraphModule::DeobfuscateClass(
235 std::optional<StringId> package_name_id,
236 StringId obfuscated_class_name_id,
237 const protos::pbzero::ObfuscatedClass::Decoder& cls) {
238 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
239 const std::vector<ClassTable::RowNumber>* cls_objects =
240 heap_graph_tracker->RowsForType(package_name_id,
241 obfuscated_class_name_id);
242 if (cls_objects) {
243 auto* class_table = context_->storage->mutable_heap_graph_class_table();
244 for (ClassTable::RowNumber class_row_num : *cls_objects) {
245 auto class_ref = class_row_num.ToRowReference(class_table);
246 const StringId obfuscated_type_name_id = class_ref.name();
247 const base::StringView obfuscated_type_name =
248 context_->storage->GetString(obfuscated_type_name_id);
249 NormalizedType normalized_type = GetNormalizedType(obfuscated_type_name);
250 std::string deobfuscated_type_name =
251 DenormalizeTypeName(normalized_type, cls.deobfuscated_name());
252 StringId deobfuscated_type_name_id = context_->storage->InternString(
253 base::StringView(deobfuscated_type_name));
254 class_ref.set_deobfuscated_name(deobfuscated_type_name_id);
255 }
256 } else {
257 PERFETTO_DLOG("Class %s not found",
258 cls.obfuscated_name().ToStdString().c_str());
259 }
260 }
261
ParseDeobfuscationMapping(protozero::ConstBytes blob)262 void HeapGraphModule::ParseDeobfuscationMapping(protozero::ConstBytes blob) {
263 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
264 protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
265 blob.data, blob.size);
266 std::optional<StringId> package_name_id;
267 if (deobfuscation_mapping.package_name().size > 0) {
268 package_name_id = context_->storage->string_pool().GetId(
269 deobfuscation_mapping.package_name());
270 }
271
272 auto* reference_table =
273 context_->storage->mutable_heap_graph_reference_table();
274 for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
275 ++class_it) {
276 protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
277 auto obfuscated_class_name_id =
278 context_->storage->string_pool().GetId(cls.obfuscated_name());
279 if (!obfuscated_class_name_id) {
280 PERFETTO_DLOG("Class string %s not found",
281 cls.obfuscated_name().ToStdString().c_str());
282 } else {
283 // TODO(b/153552977): Remove this work-around for legacy traces.
284 // For traces without location information, deobfuscate all matching
285 // classes.
286 DeobfuscateClass(std::nullopt, *obfuscated_class_name_id, cls);
287 if (package_name_id) {
288 DeobfuscateClass(package_name_id, *obfuscated_class_name_id, cls);
289 }
290 }
291 for (auto member_it = cls.obfuscated_members(); member_it; ++member_it) {
292 protos::pbzero::ObfuscatedMember::Decoder member(*member_it);
293
294 std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
295 "." +
296 member.obfuscated_name().ToStdString();
297 std::string merged_deobfuscated =
298 FullyQualifiedDeobfuscatedName(cls, member);
299
300 auto obfuscated_field_name_id = context_->storage->string_pool().GetId(
301 base::StringView(merged_obfuscated));
302 if (!obfuscated_field_name_id) {
303 PERFETTO_DLOG("Field string %s not found", merged_obfuscated.c_str());
304 continue;
305 }
306
307 const std::vector<ReferenceTable::RowNumber>* field_references =
308 heap_graph_tracker->RowsForField(*obfuscated_field_name_id);
309 if (field_references) {
310 auto interned_deobfuscated_name = context_->storage->InternString(
311 base::StringView(merged_deobfuscated));
312 for (ReferenceTable::RowNumber row_number : *field_references) {
313 auto row_ref = row_number.ToRowReference(reference_table);
314 row_ref.set_deobfuscated_field_name(interned_deobfuscated_name);
315 }
316 } else {
317 PERFETTO_DLOG("Field %s not found", merged_obfuscated.c_str());
318 }
319 }
320 }
321 }
322
NotifyEndOfFile()323 void HeapGraphModule::NotifyEndOfFile() {
324 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
325 heap_graph_tracker->FinalizeAllProfiles();
326 }
327
328 } // namespace trace_processor
329 } // namespace perfetto
330