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/proto/heap_graph_tracker.h"
20 #include "src/trace_processor/process_tracker.h"
21 #include "src/trace_processor/storage/trace_storage.h"
22 #include "src/trace_processor/trace_processor_context.h"
23
24 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
25
26 namespace perfetto {
27 namespace trace_processor {
28
29 namespace {
30
HeapGraphRootTypeToString(int32_t type)31 const char* HeapGraphRootTypeToString(int32_t type) {
32 switch (type) {
33 case protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN:
34 return "ROOT_UNKNOWN";
35 case protos::pbzero::HeapGraphRoot::ROOT_JNI_GLOBAL:
36 return "ROOT_JNI_GLOBAL";
37 case protos::pbzero::HeapGraphRoot::ROOT_JNI_LOCAL:
38 return "ROOT_JNI_LOCAL";
39 case protos::pbzero::HeapGraphRoot::ROOT_JAVA_FRAME:
40 return "ROOT_JAVA_FRAME";
41 case protos::pbzero::HeapGraphRoot::ROOT_NATIVE_STACK:
42 return "ROOT_NATIVE_STACK";
43 case protos::pbzero::HeapGraphRoot::ROOT_STICKY_CLASS:
44 return "ROOT_STICKY_CLASS";
45 case protos::pbzero::HeapGraphRoot::ROOT_THREAD_BLOCK:
46 return "ROOT_THREAD_BLOCK";
47 case protos::pbzero::HeapGraphRoot::ROOT_MONITOR_USED:
48 return "ROOT_MONITOR_USED";
49 case protos::pbzero::HeapGraphRoot::ROOT_THREAD_OBJECT:
50 return "ROOT_THREAD_OBJECT";
51 case protos::pbzero::HeapGraphRoot::ROOT_INTERNED_STRING:
52 return "ROOT_INTERNED_STRING";
53 case protos::pbzero::HeapGraphRoot::ROOT_FINALIZING:
54 return "ROOT_FINALIZING";
55 case protos::pbzero::HeapGraphRoot::ROOT_DEBUGGER:
56 return "ROOT_DEBUGGER";
57 case protos::pbzero::HeapGraphRoot::ROOT_REFERENCE_CLEANUP:
58 return "ROOT_REFERENCE_CLEANUP";
59 case protos::pbzero::HeapGraphRoot::ROOT_VM_INTERNAL:
60 return "ROOT_VM_INTERNAL";
61 case protos::pbzero::HeapGraphRoot::ROOT_JNI_MONITOR:
62 return "ROOT_JNI_MONITOR";
63 default:
64 return "ROOT_UNKNOWN";
65 }
66 }
67
68 // Iterate over a repeated field of varints, independent of whether it is
69 // packed or not.
70 template <int32_t field_no, typename T, typename F>
ForEachVarInt(const T & decoder,F fn)71 bool ForEachVarInt(const T& decoder, F fn) {
72 auto field = decoder.template at<field_no>();
73 bool parse_error = false;
74 if (field.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
75 // packed repeated
76 auto it = decoder.template GetPackedRepeated<
77 ::protozero::proto_utils::ProtoWireType::kVarInt, uint64_t>(
78 field_no, &parse_error);
79 for (; it; ++it)
80 fn(*it);
81 } else {
82 // non-packed repeated
83 auto it = decoder.template GetRepeated<uint64_t>(field_no);
84 for (; it; ++it)
85 fn(*it);
86 }
87 return parse_error;
88 }
89
90 } // namespace
91
92 using perfetto::protos::pbzero::TracePacket;
93
HeapGraphModule(TraceProcessorContext * context)94 HeapGraphModule::HeapGraphModule(TraceProcessorContext* context)
95 : context_(context) {
96 RegisterForField(TracePacket::kHeapGraphFieldNumber, context);
97 RegisterForField(TracePacket::kDeobfuscationMappingFieldNumber, context);
98 }
99
ParsePacket(const protos::pbzero::TracePacket::Decoder & decoder,const TimestampedTracePiece & ttp,uint32_t field_id)100 void HeapGraphModule::ParsePacket(
101 const protos::pbzero::TracePacket::Decoder& decoder,
102 const TimestampedTracePiece& ttp,
103 uint32_t field_id) {
104 switch (field_id) {
105 case TracePacket::kHeapGraphFieldNumber:
106 ParseHeapGraph(decoder.trusted_packet_sequence_id(), ttp.timestamp,
107 decoder.heap_graph());
108 return;
109 case TracePacket::kDeobfuscationMappingFieldNumber:
110 ParseDeobfuscationMapping(decoder.deobfuscation_mapping());
111 return;
112 }
113 }
114
ParseHeapGraph(uint32_t seq_id,int64_t ts,protozero::ConstBytes blob)115 void HeapGraphModule::ParseHeapGraph(uint32_t seq_id,
116 int64_t ts,
117 protozero::ConstBytes blob) {
118 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
119 protos::pbzero::HeapGraph::Decoder heap_graph(blob.data, blob.size);
120 UniquePid upid = context_->process_tracker->GetOrCreateProcess(
121 static_cast<uint32_t>(heap_graph.pid()));
122 heap_graph_tracker->SetPacketIndex(seq_id, heap_graph.index());
123 for (auto it = heap_graph.objects(); it; ++it) {
124 protos::pbzero::HeapGraphObject::Decoder object(*it);
125 HeapGraphTracker::SourceObject obj;
126 obj.object_id = object.id();
127 obj.self_size = object.self_size();
128 obj.type_id = object.type_id();
129
130 std::vector<uint64_t> field_ids;
131 std::vector<uint64_t> object_ids;
132
133 bool parse_error = ForEachVarInt<
134 protos::pbzero::HeapGraphObject::kReferenceFieldIdFieldNumber>(
135 object, [&field_ids](uint64_t value) { field_ids.push_back(value); });
136
137 if (!parse_error) {
138 parse_error = ForEachVarInt<
139 protos::pbzero::HeapGraphObject::kReferenceObjectIdFieldNumber>(
140 object,
141 [&object_ids](uint64_t value) { object_ids.push_back(value); });
142 }
143
144 if (parse_error) {
145 context_->storage->IncrementIndexedStats(
146 stats::heap_graph_malformed_packet, static_cast<int>(upid));
147 break;
148 }
149 if (field_ids.size() != object_ids.size()) {
150 context_->storage->IncrementIndexedStats(
151 stats::heap_graph_malformed_packet, static_cast<int>(upid));
152 continue;
153 }
154 for (size_t i = 0; i < field_ids.size(); ++i) {
155 HeapGraphTracker::SourceObject::Reference ref;
156 ref.field_name_id = field_ids[i];
157 ref.owned_object_id = object_ids[i];
158 obj.references.emplace_back(std::move(ref));
159 }
160 heap_graph_tracker->AddObject(seq_id, upid, ts, std::move(obj));
161 }
162 for (auto it = heap_graph.types(); it; ++it) {
163 protos::pbzero::HeapGraphType::Decoder entry(*it);
164 const char* str = reinterpret_cast<const char*>(entry.class_name().data);
165 auto str_view = base::StringView(str, entry.class_name().size);
166
167 heap_graph_tracker->AddInternedType(
168 seq_id, entry.id(), context_->storage->InternString(str_view),
169 entry.location_id());
170 }
171 for (auto it = heap_graph.type_names(); it; ++it) {
172 protos::pbzero::InternedString::Decoder entry(*it);
173 const char* str = reinterpret_cast<const char*>(entry.str().data);
174 auto str_view = base::StringView(str, entry.str().size);
175
176 heap_graph_tracker->AddInternedTypeName(
177 seq_id, entry.iid(), context_->storage->InternString(str_view));
178 }
179 for (auto it = heap_graph.field_names(); it; ++it) {
180 protos::pbzero::InternedString::Decoder entry(*it);
181 const char* str = reinterpret_cast<const char*>(entry.str().data);
182 auto str_view = base::StringView(str, entry.str().size);
183
184 heap_graph_tracker->AddInternedFieldName(seq_id, entry.iid(), str_view);
185 }
186 for (auto it = heap_graph.location_names(); it; ++it) {
187 protos::pbzero::InternedString::Decoder entry(*it);
188 const char* str = reinterpret_cast<const char*>(entry.str().data);
189 auto str_view = base::StringView(str, entry.str().size);
190
191 heap_graph_tracker->AddInternedLocationName(
192 seq_id, entry.iid(), context_->storage->InternString(str_view));
193 }
194 for (auto it = heap_graph.roots(); it; ++it) {
195 protos::pbzero::HeapGraphRoot::Decoder entry(*it);
196 const char* str = HeapGraphRootTypeToString(entry.root_type());
197 auto str_view = base::StringView(str);
198
199 HeapGraphTracker::SourceRoot src_root;
200 src_root.root_type = context_->storage->InternString(str_view);
201 bool parse_error =
202 ForEachVarInt<protos::pbzero::HeapGraphRoot::kObjectIdsFieldNumber>(
203 entry, [&src_root](uint64_t value) {
204 src_root.object_ids.emplace_back(value);
205 });
206 if (parse_error) {
207 context_->storage->IncrementIndexedStats(
208 stats::heap_graph_malformed_packet, static_cast<int>(upid));
209 break;
210 }
211 heap_graph_tracker->AddRoot(seq_id, upid, ts, std::move(src_root));
212 }
213 if (!heap_graph.continued()) {
214 heap_graph_tracker->FinalizeProfile(seq_id);
215 }
216 }
217
ParseDeobfuscationMapping(protozero::ConstBytes blob)218 void HeapGraphModule::ParseDeobfuscationMapping(protozero::ConstBytes blob) {
219 // TODO(fmayer): Support multiple profiles in the same trace.
220 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
221 protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
222 blob.data, blob.size);
223 for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
224 ++class_it) {
225 protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
226 auto obfuscated_class_name_id =
227 context_->storage->string_pool().GetId(cls.obfuscated_name());
228 if (!obfuscated_class_name_id) {
229 PERFETTO_DLOG("Class string %s not found",
230 cls.obfuscated_name().ToStdString().c_str());
231 } else {
232 const std::vector<int64_t>* cls_objects =
233 heap_graph_tracker->RowsForType(*obfuscated_class_name_id);
234
235 if (cls_objects) {
236 heap_graph_tracker->AddDeobfuscationMapping(
237 *obfuscated_class_name_id,
238 context_->storage->InternString(
239 base::StringView(cls.deobfuscated_name())));
240 for (int64_t row : *cls_objects) {
241 const StringPool::Id obfuscated_type_name =
242 context_->storage->mutable_heap_graph_object_table()
243 ->type_name()[static_cast<uint32_t>(row)];
244 context_->storage->mutable_heap_graph_object_table()
245 ->mutable_deobfuscated_type_name()
246 ->Set(static_cast<uint32_t>(row),
247 heap_graph_tracker->MaybeDeobfuscate(obfuscated_type_name));
248 }
249 } else {
250 PERFETTO_DLOG("Class %s not found",
251 cls.obfuscated_name().ToStdString().c_str());
252 }
253 }
254 for (auto member_it = cls.obfuscated_members(); member_it; ++member_it) {
255 protos::pbzero::ObfuscatedMember::Decoder member(*member_it);
256
257 std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
258 "." +
259 member.obfuscated_name().ToStdString();
260 std::string merged_deobfuscated;
261 std::string member_deobfuscated_name =
262 member.deobfuscated_name().ToStdString();
263 if (member_deobfuscated_name.find('.') == std::string::npos) {
264 // Name relative to class.
265 merged_deobfuscated = cls.deobfuscated_name().ToStdString() + "." +
266 member_deobfuscated_name;
267 } else {
268 // Fully qualified name.
269 merged_deobfuscated = std::move(member_deobfuscated_name);
270 }
271
272 auto obfuscated_field_name_id = context_->storage->string_pool().GetId(
273 base::StringView(merged_obfuscated));
274 if (!obfuscated_field_name_id) {
275 PERFETTO_DLOG("Field string %s not found", merged_obfuscated.c_str());
276 continue;
277 }
278
279 const std::vector<int64_t>* field_references =
280 heap_graph_tracker->RowsForField(*obfuscated_field_name_id);
281 if (field_references) {
282 auto interned_deobfuscated_name = context_->storage->InternString(
283 base::StringView(merged_deobfuscated));
284 for (int64_t row : *field_references) {
285 context_->storage->mutable_heap_graph_reference_table()
286 ->mutable_deobfuscated_field_name()
287 ->Set(static_cast<uint32_t>(row), interned_deobfuscated_name);
288 }
289 } else {
290 PERFETTO_DLOG("Field %s not found", merged_obfuscated.c_str());
291 }
292 }
293 }
294 }
295
NotifyEndOfFile()296 void HeapGraphModule::NotifyEndOfFile() {
297 auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
298 heap_graph_tracker->NotifyEndOfFile();
299 }
300
301 } // namespace trace_processor
302 } // namespace perfetto
303