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