• 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_tracker.h"
18 
19 #include <optional>
20 
21 #include "perfetto/base/flat_set.h"
22 #include "perfetto/ext/base/string_splitter.h"
23 #include "perfetto/ext/base/string_utils.h"
24 #include "src/trace_processor/importers/proto/profiler_util.h"
25 #include "src/trace_processor/tables/profiler_tables_py.h"
26 
27 namespace perfetto {
28 namespace trace_processor {
29 
30 namespace {
31 
32 using ClassTable = tables::HeapGraphClassTable;
33 using ObjectTable = tables::HeapGraphObjectTable;
34 using ReferenceTable = tables::HeapGraphReferenceTable;
35 
36 // Iterates all the references owned by the object `id`.
37 //
38 // Calls bool(*fn)(ObjectTable::RowReference) with the each row
39 // from the `storage.heap_graph_reference()` table associated to the |object|.
40 // When `fn` returns false (or when there are no more rows owned by |object|),
41 // stops the iteration.
42 template <typename F>
ForReferenceSet(TraceStorage * storage,ObjectTable::ConstRowReference object,F fn)43 void ForReferenceSet(TraceStorage* storage,
44                      ObjectTable::ConstRowReference object,
45                      F fn) {
46   std::optional<uint32_t> reference_set_id = object.reference_set_id();
47   if (!reference_set_id)
48     return;
49 
50   auto* ref = storage->mutable_heap_graph_reference_table();
51   auto it =
52       ref->FilterToIterator({ref->reference_set_id().eq(*reference_set_id)});
53 
54   for (; it; ++it) {
55     if (!fn(it.row_reference()))
56       break;
57   }
58 }
59 
GetChildren(TraceStorage * storage,ObjectTable::RowReference object)60 base::FlatSet<ObjectTable::Id> GetChildren(TraceStorage* storage,
61                                            ObjectTable::RowReference object) {
62   auto cls_row_ref =
63       *storage->heap_graph_class_table().FindById(object.type_id());
64 
65   StringId kind = cls_row_ref.kind();
66   std::optional<StringId> weakref_kind =
67       storage->string_pool().GetId("KIND_WEAK_REFERENCE");
68   std::optional<StringId> softref_kind =
69       storage->string_pool().GetId("KIND_SOFT_REFERENCE");
70   std::optional<StringId> finalizerref_kind =
71       storage->string_pool().GetId("KIND_FINALIZER_REFERENCE");
72   std::optional<StringId> phantomref_kind =
73       storage->string_pool().GetId("KIND_PHANTOM_REFERENCE");
74 
75   if ((weakref_kind && kind == *weakref_kind) ||
76       (softref_kind && kind == *softref_kind) ||
77       (finalizerref_kind && kind == *finalizerref_kind) ||
78       (phantomref_kind && kind == *phantomref_kind)) {
79     // Do not follow weak / soft / finalizer / phantom references.
80     return {};
81   }
82 
83   base::FlatSet<ObjectTable::Id> children;
84   ForReferenceSet(storage, object,
85                   [object, &children](ReferenceTable::RowReference ref) {
86                     PERFETTO_CHECK(ref.owner_id() == object.id());
87                     auto opt_owned = ref.owned_id();
88                     if (opt_owned) {
89                       children.insert(*opt_owned);
90                     }
91                     return true;
92                   });
93   return children;
94 }
95 
96 struct ClassDescriptor {
97   StringId name;
98   std::optional<StringId> location;
99 
operator <perfetto::trace_processor::__anon4f189da70111::ClassDescriptor100   bool operator<(const ClassDescriptor& other) const {
101     return std::tie(name, location) < std::tie(other.name, other.location);
102   }
103 };
104 
GetClassDescriptor(const TraceStorage & storage,ObjectTable::Id obj_id)105 ClassDescriptor GetClassDescriptor(const TraceStorage& storage,
106                                    ObjectTable::Id obj_id) {
107   auto obj_row_ref = *storage.heap_graph_object_table().FindById(obj_id);
108   auto type_row_ref =
109       *storage.heap_graph_class_table().FindById(obj_row_ref.type_id());
110   return {type_row_ref.name(), type_row_ref.location()};
111 }
112 
GetReferredObj(const TraceStorage & storage,uint32_t ref_set_id,const std::string & field_name)113 std::optional<ObjectTable::Id> GetReferredObj(const TraceStorage& storage,
114                                               uint32_t ref_set_id,
115                                               const std::string& field_name) {
116   const auto& refs_tbl = storage.heap_graph_reference_table();
117 
118   auto refs_it = refs_tbl.FilterToIterator(
119       {refs_tbl.reference_set_id().eq(ref_set_id),
120        refs_tbl.field_name().eq(NullTermStringView(field_name))});
121   if (!refs_it) {
122     return std::nullopt;
123   }
124   return refs_it.owned_id();
125 }
126 
127 // Maps from normalized class name and location, to superclass.
128 std::map<ClassDescriptor, ClassDescriptor>
BuildSuperclassMap(UniquePid upid,int64_t ts,TraceStorage * storage)129 BuildSuperclassMap(UniquePid upid, int64_t ts, TraceStorage* storage) {
130   std::map<ClassDescriptor, ClassDescriptor> superclass_map;
131 
132   // Resolve superclasses by iterating heap graph objects and identifying the
133   // superClass field.
134   const auto& objects_tbl = storage->heap_graph_object_table();
135   auto obj_it = objects_tbl.FilterToIterator(
136       {objects_tbl.upid().eq(upid), objects_tbl.graph_sample_ts().eq(ts)});
137   for (; obj_it; ++obj_it) {
138     auto obj_id = obj_it.id();
139     auto class_descriptor = GetClassDescriptor(*storage, obj_id);
140     auto normalized =
141         GetNormalizedType(storage->GetString(class_descriptor.name));
142     // superClass ptrs are stored on the static class objects
143     // ignore arrays (as they are generated objects)
144     if (!normalized.is_static_class || normalized.number_of_arrays > 0)
145       continue;
146 
147     auto opt_ref_set_id = obj_it.reference_set_id();
148     if (!opt_ref_set_id)
149       continue;
150     auto super_obj_id =
151         GetReferredObj(*storage, *opt_ref_set_id, "java.lang.Class.superClass");
152     if (!super_obj_id) {
153       // This is expected to be missing for Object and primitive types
154       continue;
155     }
156 
157     // Lookup the super obj type id
158     auto super_class_descriptor = GetClassDescriptor(*storage, *super_obj_id);
159     auto super_class_name =
160         NormalizeTypeName(storage->GetString(super_class_descriptor.name));
161     StringId super_class_id = storage->InternString(super_class_name);
162     StringId class_id = storage->InternString(normalized.name);
163     superclass_map[{class_id, class_descriptor.location}] = {
164         super_class_id, super_class_descriptor.location};
165   }
166   return superclass_map;
167 }
168 
169 // Extract the size from `nar_size`, which is the value of a
170 // libcore.util.NativeAllocationRegistry.size field: it encodes the size, but
171 // uses the least significant bit to represent the source of the allocation.
GetSizeFromNativeAllocationRegistry(int64_t nar_size)172 int64_t GetSizeFromNativeAllocationRegistry(int64_t nar_size) {
173   constexpr uint64_t kIsMalloced = 1;
174   return static_cast<int64_t>(static_cast<uint64_t>(nar_size) & ~kIsMalloced);
175 }
176 
177 }  // namespace
178 
MarkRoot(TraceStorage * storage,ObjectTable::RowReference row_ref,StringId type)179 void MarkRoot(TraceStorage* storage,
180               ObjectTable::RowReference row_ref,
181               StringId type) {
182   row_ref.set_root_type(type);
183 
184   // DFS to mark reachability for all children
185   std::vector<ObjectTable::RowReference> stack({row_ref});
186   while (!stack.empty()) {
187     ObjectTable::RowReference cur_node = stack.back();
188     stack.pop_back();
189 
190     if (cur_node.reachable())
191       continue;
192     cur_node.set_reachable(true);
193 
194     for (ObjectTable::Id child_node : GetChildren(storage, cur_node)) {
195       auto child_ref =
196           *storage->mutable_heap_graph_object_table()->FindById(child_node);
197       stack.push_back(child_ref);
198     }
199   }
200 }
201 
UpdateShortestPaths(TraceStorage * storage,ObjectTable::RowReference row_ref)202 void UpdateShortestPaths(TraceStorage* storage,
203                          ObjectTable::RowReference row_ref) {
204   // Calculate shortest distance to a GC root.
205   std::deque<std::pair<int32_t, ObjectTable::RowReference>> reachable_nodes{
206       {0, row_ref}};
207   while (!reachable_nodes.empty()) {
208     auto pair = reachable_nodes.front();
209 
210     int32_t distance = pair.first;
211     ObjectTable::RowReference cur_row_ref = pair.second;
212 
213     reachable_nodes.pop_front();
214     int32_t cur_distance = cur_row_ref.root_distance();
215     if (cur_distance == -1 || cur_distance > distance) {
216       cur_row_ref.set_root_distance(distance);
217 
218       for (ObjectTable::Id child_node : GetChildren(storage, cur_row_ref)) {
219         auto child_row_ref =
220             *storage->mutable_heap_graph_object_table()->FindById(child_node);
221         int32_t child_distance = child_row_ref.root_distance();
222         if (child_distance == -1 || child_distance > distance + 1)
223           reachable_nodes.emplace_back(distance + 1, child_row_ref);
224       }
225     }
226   }
227 }
228 
GetStaticClassTypeName(base::StringView type)229 std::optional<base::StringView> GetStaticClassTypeName(base::StringView type) {
230   static const base::StringView kJavaClassTemplate("java.lang.Class<");
231   if (!type.empty() && type.at(type.size() - 1) == '>' &&
232       type.substr(0, kJavaClassTemplate.size()) == kJavaClassTemplate) {
233     return type.substr(kJavaClassTemplate.size(),
234                        type.size() - kJavaClassTemplate.size() - 1);
235   }
236   return {};
237 }
238 
NumberOfArrays(base::StringView type)239 size_t NumberOfArrays(base::StringView type) {
240   if (type.size() < 2)
241     return 0;
242 
243   size_t arrays = 0;
244   while (type.size() >= 2 * (arrays + 1) &&
245          memcmp(type.end() - 2 * (arrays + 1), "[]", 2) == 0) {
246     arrays++;
247   }
248   return arrays;
249 }
250 
GetNormalizedType(base::StringView type)251 NormalizedType GetNormalizedType(base::StringView type) {
252   auto static_class_type_name = GetStaticClassTypeName(type);
253   if (static_class_type_name.has_value()) {
254     type = static_class_type_name.value();
255   }
256   size_t number_of_arrays = NumberOfArrays(type);
257   return {base::StringView(type.data(), type.size() - number_of_arrays * 2),
258           static_class_type_name.has_value(), number_of_arrays};
259 }
260 
NormalizeTypeName(base::StringView type)261 base::StringView NormalizeTypeName(base::StringView type) {
262   return GetNormalizedType(type).name;
263 }
264 
DenormalizeTypeName(NormalizedType normalized,base::StringView deobfuscated_type_name)265 std::string DenormalizeTypeName(NormalizedType normalized,
266                                 base::StringView deobfuscated_type_name) {
267   std::string result = deobfuscated_type_name.ToStdString();
268   for (size_t i = 0; i < normalized.number_of_arrays; ++i) {
269     result += "[]";
270   }
271   if (normalized.is_static_class) {
272     result = "java.lang.Class<" + result + ">";
273   }
274   return result;
275 }
276 
HeapGraphTracker(TraceStorage * storage)277 HeapGraphTracker::HeapGraphTracker(TraceStorage* storage)
278     : storage_(storage),
279       cleaner_thunk_str_id_(storage_->InternString("sun.misc.Cleaner.thunk")),
280       referent_str_id_(
281           storage_->InternString("java.lang.ref.Reference.referent")),
282       cleaner_thunk_this0_str_id_(storage_->InternString(
283           "libcore.util.NativeAllocationRegistry$CleanerThunk.this$0")),
284       native_size_str_id_(
285           storage_->InternString("libcore.util.NativeAllocationRegistry.size")),
286       cleaner_next_str_id_(storage_->InternString("sun.misc.Cleaner.next")) {}
287 
GetOrCreateSequence(uint32_t seq_id)288 HeapGraphTracker::SequenceState& HeapGraphTracker::GetOrCreateSequence(
289     uint32_t seq_id) {
290   return sequence_state_[seq_id];
291 }
292 
SetPidAndTimestamp(SequenceState * sequence_state,UniquePid upid,int64_t ts)293 bool HeapGraphTracker::SetPidAndTimestamp(SequenceState* sequence_state,
294                                           UniquePid upid,
295                                           int64_t ts) {
296   if (sequence_state->current_upid != 0 &&
297       sequence_state->current_upid != upid) {
298     storage_->IncrementStats(stats::heap_graph_non_finalized_graph);
299     return false;
300   }
301   if (sequence_state->current_ts != 0 && sequence_state->current_ts != ts) {
302     storage_->IncrementStats(stats::heap_graph_non_finalized_graph);
303     return false;
304   }
305   sequence_state->current_upid = upid;
306   sequence_state->current_ts = ts;
307   return true;
308 }
309 
GetOrInsertObject(SequenceState * sequence_state,uint64_t object_id)310 ObjectTable::RowReference HeapGraphTracker::GetOrInsertObject(
311     SequenceState* sequence_state,
312     uint64_t object_id) {
313   auto object_table = storage_->mutable_heap_graph_object_table();
314   auto* ptr = sequence_state->object_id_to_db_row.Find(object_id);
315   if (!ptr) {
316     auto id_and_row = object_table->Insert({sequence_state->current_upid,
317                                             sequence_state->current_ts,
318                                             -1,
319                                             0,
320                                             /*reference_set_id=*/std::nullopt,
321                                             /*reachable=*/0,
322                                             {},
323                                             /*root_type=*/std::nullopt,
324                                             /*root_distance*/ -1});
325     bool inserted;
326     std::tie(ptr, inserted) = sequence_state->object_id_to_db_row.Insert(
327         object_id, id_and_row.row_number);
328   }
329   return ptr->ToRowReference(object_table);
330 }
331 
GetOrInsertType(SequenceState * sequence_state,uint64_t type_id)332 ClassTable::RowReference HeapGraphTracker::GetOrInsertType(
333     SequenceState* sequence_state,
334     uint64_t type_id) {
335   auto class_table = storage_->mutable_heap_graph_class_table();
336   auto* ptr = sequence_state->type_id_to_db_row.Find(type_id);
337   if (!ptr) {
338     auto id_and_row =
339         class_table->Insert({StringId(), std::nullopt, std::nullopt});
340     bool inserted;
341     std::tie(ptr, inserted) = sequence_state->type_id_to_db_row.Insert(
342         type_id, id_and_row.row_number);
343   }
344   return ptr->ToRowReference(class_table);
345 }
346 
AddObject(uint32_t seq_id,UniquePid upid,int64_t ts,SourceObject obj)347 void HeapGraphTracker::AddObject(uint32_t seq_id,
348                                  UniquePid upid,
349                                  int64_t ts,
350                                  SourceObject obj) {
351   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
352 
353   if (!SetPidAndTimestamp(&sequence_state, upid, ts))
354     return;
355 
356   sequence_state.last_object_id = obj.object_id;
357 
358   ObjectTable::RowReference owner_row_ref =
359       GetOrInsertObject(&sequence_state, obj.object_id);
360   ClassTable::RowReference type_row_ref =
361       GetOrInsertType(&sequence_state, obj.type_id);
362 
363   ClassTable::Id type_id = type_row_ref.id();
364 
365   owner_row_ref.set_self_size(static_cast<int64_t>(obj.self_size));
366   owner_row_ref.set_type_id(type_id);
367 
368   if (obj.self_size == 0) {
369     sequence_state.deferred_size_objects_for_type_[type_id].push_back(
370         owner_row_ref.ToRowNumber());
371   }
372 
373   uint32_t reference_set_id =
374       storage_->heap_graph_reference_table().row_count();
375   bool any_references = false;
376 
377   ObjectTable::Id owner_id = owner_row_ref.id();
378   for (size_t i = 0; i < obj.referred_objects.size(); ++i) {
379     uint64_t owned_object_id = obj.referred_objects[i];
380     // This is true for unset reference fields.
381     std::optional<ObjectTable::RowReference> owned_row_ref;
382     if (owned_object_id != 0)
383       owned_row_ref = GetOrInsertObject(&sequence_state, owned_object_id);
384 
385     auto ref_id_and_row =
386         storage_->mutable_heap_graph_reference_table()->Insert(
387             {reference_set_id,
388              owner_id,
389              owned_row_ref ? std::make_optional(owned_row_ref->id())
390                            : std::nullopt,
391              {},
392              {},
393              /*deobfuscated_field_name=*/std::nullopt});
394     if (!obj.field_name_ids.empty()) {
395       sequence_state.references_for_field_name_id[obj.field_name_ids[i]]
396           .push_back(ref_id_and_row.row_number);
397     }
398     any_references = true;
399   }
400   if (any_references) {
401     owner_row_ref.set_reference_set_id(reference_set_id);
402     if (obj.field_name_ids.empty()) {
403       sequence_state.deferred_reference_objects_for_type_[type_id].push_back(
404           owner_row_ref.ToRowNumber());
405     }
406   }
407 
408   if (obj.native_allocation_registry_size.has_value()) {
409     sequence_state.nar_size_by_obj_id[owner_id] =
410         *obj.native_allocation_registry_size;
411   }
412 }
413 
AddRoot(uint32_t seq_id,UniquePid upid,int64_t ts,SourceRoot root)414 void HeapGraphTracker::AddRoot(uint32_t seq_id,
415                                UniquePid upid,
416                                int64_t ts,
417                                SourceRoot root) {
418   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
419   if (!SetPidAndTimestamp(&sequence_state, upid, ts))
420     return;
421 
422   sequence_state.current_roots.emplace_back(std::move(root));
423 }
424 
AddInternedLocationName(uint32_t seq_id,uint64_t intern_id,StringId strid)425 void HeapGraphTracker::AddInternedLocationName(uint32_t seq_id,
426                                                uint64_t intern_id,
427                                                StringId strid) {
428   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
429   sequence_state.interned_location_names.emplace(intern_id, strid);
430 }
431 
AddInternedType(uint32_t seq_id,uint64_t intern_id,StringId strid,std::optional<uint64_t> location_id,uint64_t object_size,std::vector<uint64_t> field_name_ids,uint64_t superclass_id,uint64_t classloader_id,bool no_fields,StringId kind)432 void HeapGraphTracker::AddInternedType(uint32_t seq_id,
433                                        uint64_t intern_id,
434                                        StringId strid,
435                                        std::optional<uint64_t> location_id,
436                                        uint64_t object_size,
437                                        std::vector<uint64_t> field_name_ids,
438                                        uint64_t superclass_id,
439                                        uint64_t classloader_id,
440                                        bool no_fields,
441                                        StringId kind) {
442   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
443   sequence_state.interned_types[intern_id].name = strid;
444   sequence_state.interned_types[intern_id].location_id = location_id;
445   sequence_state.interned_types[intern_id].object_size = object_size;
446   sequence_state.interned_types[intern_id].field_name_ids =
447       std::move(field_name_ids);
448   sequence_state.interned_types[intern_id].superclass_id = superclass_id;
449   sequence_state.interned_types[intern_id].classloader_id = classloader_id;
450   sequence_state.interned_types[intern_id].no_fields = no_fields;
451   sequence_state.interned_types[intern_id].kind = kind;
452 }
453 
AddInternedFieldName(uint32_t seq_id,uint64_t intern_id,base::StringView str)454 void HeapGraphTracker::AddInternedFieldName(uint32_t seq_id,
455                                             uint64_t intern_id,
456                                             base::StringView str) {
457   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
458   size_t space = str.find(' ');
459   base::StringView type;
460   if (space != base::StringView::npos) {
461     type = str.substr(0, space);
462     str = str.substr(space + 1);
463   }
464   StringId field_name = storage_->InternString(str);
465   StringId type_name = storage_->InternString(type);
466 
467   sequence_state.interned_fields.Insert(intern_id,
468                                         InternedField{field_name, type_name});
469 
470   auto it = sequence_state.references_for_field_name_id.find(intern_id);
471   if (it != sequence_state.references_for_field_name_id.end()) {
472     auto hgr = storage_->mutable_heap_graph_reference_table();
473     for (ReferenceTable::RowNumber reference_row_num : it->second) {
474       auto row_ref = reference_row_num.ToRowReference(hgr);
475       row_ref.set_field_name(field_name);
476       row_ref.set_field_type_name(type_name);
477       field_to_rows_[field_name].emplace_back(reference_row_num);
478     }
479   }
480 }
481 
SetPacketIndex(uint32_t seq_id,uint64_t index)482 void HeapGraphTracker::SetPacketIndex(uint32_t seq_id, uint64_t index) {
483   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
484   bool dropped_packet = false;
485   // perfetto_hprof starts counting at index = 0.
486   if (!sequence_state.prev_index && index != 0) {
487     dropped_packet = true;
488   }
489 
490   if (sequence_state.prev_index && *sequence_state.prev_index + 1 != index) {
491     dropped_packet = true;
492   }
493 
494   if (dropped_packet) {
495     sequence_state.truncated = true;
496     if (sequence_state.prev_index) {
497       PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
498                     *sequence_state.prev_index, index);
499     } else {
500       PERFETTO_ELOG("Invalid first packet index %" PRIu64 " (!= 0)", index);
501     }
502 
503     storage_->IncrementIndexedStats(
504         stats::heap_graph_missing_packet,
505         static_cast<int>(sequence_state.current_upid));
506   }
507   sequence_state.prev_index = index;
508 }
509 
510 // This only works on Android S+ traces. We need to have ingested the whole
511 // profile before calling this function (e.g. in FinalizeProfile).
GetSuperClass(SequenceState * sequence_state,const InternedType * current_type)512 HeapGraphTracker::InternedType* HeapGraphTracker::GetSuperClass(
513     SequenceState* sequence_state,
514     const InternedType* current_type) {
515   if (current_type->superclass_id) {
516     auto it = sequence_state->interned_types.find(current_type->superclass_id);
517     if (it != sequence_state->interned_types.end())
518       return &it->second;
519   }
520   storage_->IncrementIndexedStats(
521       stats::heap_graph_malformed_packet,
522       static_cast<int>(sequence_state->current_upid));
523   return nullptr;
524 }
525 
FinalizeProfile(uint32_t seq_id)526 void HeapGraphTracker::FinalizeProfile(uint32_t seq_id) {
527   SequenceState& sequence_state = GetOrCreateSequence(seq_id);
528   if (sequence_state.truncated) {
529     truncated_graphs_.emplace(
530         std::make_pair(sequence_state.current_upid, sequence_state.current_ts));
531   }
532 
533   // We do this in FinalizeProfile because the interned_location_names get
534   // written at the end of the dump.
535   for (const auto& p : sequence_state.interned_types) {
536     uint64_t id = p.first;
537     const InternedType& interned_type = p.second;
538     std::optional<StringId> location_name;
539     if (interned_type.location_id) {
540       auto it = sequence_state.interned_location_names.find(
541           *interned_type.location_id);
542       if (it == sequence_state.interned_location_names.end()) {
543         storage_->IncrementIndexedStats(
544             stats::heap_graph_invalid_string_id,
545             static_cast<int>(sequence_state.current_upid));
546       } else {
547         location_name = it->second;
548       }
549     }
550     ClassTable::RowReference type_row_ref =
551         GetOrInsertType(&sequence_state, id);
552     ClassTable::Id type_id = type_row_ref.id();
553 
554     auto sz_obj_it =
555         sequence_state.deferred_size_objects_for_type_.find(type_id);
556     if (sz_obj_it != sequence_state.deferred_size_objects_for_type_.end()) {
557       auto* hgo = storage_->mutable_heap_graph_object_table();
558       for (ObjectTable::RowNumber obj_row_num : sz_obj_it->second) {
559         auto obj_row_ref = obj_row_num.ToRowReference(hgo);
560         obj_row_ref.set_self_size(
561             static_cast<int64_t>(interned_type.object_size));
562       }
563       sequence_state.deferred_size_objects_for_type_.erase(sz_obj_it);
564     }
565 
566     auto ref_obj_it =
567         sequence_state.deferred_reference_objects_for_type_.find(type_id);
568     if (ref_obj_it !=
569         sequence_state.deferred_reference_objects_for_type_.end()) {
570       for (ObjectTable::RowNumber obj_row_number : ref_obj_it->second) {
571         auto obj_row_ref = obj_row_number.ToRowReference(
572             storage_->mutable_heap_graph_object_table());
573         const InternedType* current_type = &interned_type;
574         if (interned_type.no_fields) {
575           continue;
576         }
577         size_t field_offset_in_cls = 0;
578         ForReferenceSet(
579             storage_, obj_row_ref,
580             [this, &current_type, &sequence_state,
581              &field_offset_in_cls](ReferenceTable::RowReference ref) {
582               while (current_type && field_offset_in_cls >=
583                                          current_type->field_name_ids.size()) {
584                 size_t prev_type_size = current_type->field_name_ids.size();
585                 current_type = GetSuperClass(&sequence_state, current_type);
586                 field_offset_in_cls -= prev_type_size;
587               }
588 
589               if (!current_type) {
590                 return false;
591               }
592 
593               uint64_t field_id =
594                   current_type->field_name_ids[field_offset_in_cls++];
595               auto* ptr = sequence_state.interned_fields.Find(field_id);
596               if (!ptr) {
597                 PERFETTO_DLOG("Invalid field id.");
598                 storage_->IncrementIndexedStats(
599                     stats::heap_graph_malformed_packet,
600                     static_cast<int>(sequence_state.current_upid));
601                 return true;
602               }
603               const InternedField& field = *ptr;
604               ref.set_field_name(field.name);
605               ref.set_field_type_name(field.type_name);
606               field_to_rows_[field.name].emplace_back(ref.ToRowNumber());
607               return true;
608             });
609       }
610       sequence_state.deferred_reference_objects_for_type_.erase(ref_obj_it);
611     }
612 
613     type_row_ref.set_name(interned_type.name);
614     if (interned_type.classloader_id) {
615       auto classloader_object_ref =
616           GetOrInsertObject(&sequence_state, interned_type.classloader_id);
617       type_row_ref.set_classloader_id(classloader_object_ref.id().value);
618     }
619     if (location_name)
620       type_row_ref.set_location(*location_name);
621     type_row_ref.set_kind(interned_type.kind);
622 
623     base::StringView normalized_type =
624         NormalizeTypeName(storage_->GetString(interned_type.name));
625 
626     std::optional<StringId> class_package;
627     if (location_name) {
628       std::optional<std::string> package_name =
629           PackageFromLocation(storage_, storage_->GetString(*location_name));
630       if (package_name) {
631         class_package = storage_->InternString(base::StringView(*package_name));
632       }
633     }
634     if (!class_package) {
635       auto app_id = storage_->process_table()
636                         .android_appid()[sequence_state.current_upid];
637       if (app_id) {
638         auto pkg_row = storage_->package_list_table().uid().IndexOf(*app_id);
639         if (pkg_row) {
640           class_package =
641               storage_->package_list_table().package_name()[*pkg_row];
642         }
643       }
644     }
645 
646     class_to_rows_[std::make_pair(class_package,
647                                   storage_->InternString(normalized_type))]
648         .emplace_back(type_row_ref.ToRowNumber());
649   }
650 
651   if (!sequence_state.deferred_size_objects_for_type_.empty() ||
652       !sequence_state.deferred_reference_objects_for_type_.empty()) {
653     storage_->IncrementIndexedStats(
654         stats::heap_graph_malformed_packet,
655         static_cast<int>(sequence_state.current_upid));
656   }
657 
658   for (const SourceRoot& root : sequence_state.current_roots) {
659     for (uint64_t obj_id : root.object_ids) {
660       auto ptr = sequence_state.object_id_to_db_row.Find(obj_id);
661       // This can only happen for an invalid type string id, which is already
662       // reported as an error. Silently continue here.
663       if (!ptr)
664         continue;
665 
666       ObjectTable::RowReference row_ref =
667           ptr->ToRowReference(storage_->mutable_heap_graph_object_table());
668       auto it_and_success = roots_[std::make_pair(sequence_state.current_upid,
669                                                   sequence_state.current_ts)]
670                                 .emplace(*ptr);
671       if (it_and_success.second)
672         MarkRoot(storage_, row_ref, root.root_type);
673     }
674   }
675 
676   PopulateSuperClasses(sequence_state);
677   PopulateNativeSize(sequence_state);
678   sequence_state_.erase(seq_id);
679 }
680 
GetReferenceByFieldName(ObjectTable::Id obj,StringId field)681 std::optional<ObjectTable::Id> HeapGraphTracker::GetReferenceByFieldName(
682     ObjectTable::Id obj,
683     StringId field) {
684   std::optional<ObjectTable::Id> referred;
685   auto obj_row_ref = *storage_->heap_graph_object_table().FindById(obj);
686   ForReferenceSet(storage_, obj_row_ref,
687                   [&](ReferenceTable::RowReference ref) -> bool {
688                     if (ref.field_name() == field) {
689                       referred = ref.owned_id();
690                       return false;
691                     }
692                     return true;
693                   });
694   return referred;
695 }
696 
PopulateNativeSize(const SequenceState & seq)697 void HeapGraphTracker::PopulateNativeSize(const SequenceState& seq) {
698   //             +-------------------------------+  .referent   +--------+
699   //             |       sun.misc.Cleaner        | -----------> | Object |
700   //             +-------------------------------+              +--------+
701   //                |
702   //                | .thunk
703   //                v
704   // +----------------------------------------------------+
705   // | libcore.util.NativeAllocationRegistry$CleanerThunk |
706   // +----------------------------------------------------+
707   //   |
708   //   | .this$0
709   //   v
710   // +----------------------------------------------------+
711   // |       libcore.util.NativeAllocationRegistry        |
712   // |                       .size                        |
713   // +----------------------------------------------------+
714   //
715   // `.size` should be attributed as the native size of Object
716 
717   const auto& class_tbl = storage_->heap_graph_class_table();
718   auto& objects_tbl = *storage_->mutable_heap_graph_object_table();
719 
720   struct Cleaner {
721     ObjectTable::Id referent;
722     ObjectTable::Id thunk;
723   };
724   std::vector<Cleaner> cleaners;
725 
726   auto class_it =
727       class_tbl.FilterToIterator({class_tbl.name().eq("sun.misc.Cleaner")});
728   for (; class_it; ++class_it) {
729     auto class_id = class_it.id();
730     auto obj_it = objects_tbl.FilterToIterator(
731         {objects_tbl.type_id().eq(class_id.value),
732          objects_tbl.upid().eq(seq.current_upid),
733          objects_tbl.graph_sample_ts().eq(seq.current_ts)});
734     for (; obj_it; ++obj_it) {
735       ObjectTable::Id cleaner_obj_id = obj_it.id();
736       std::optional<ObjectTable::Id> referent_id =
737           GetReferenceByFieldName(cleaner_obj_id, referent_str_id_);
738       std::optional<ObjectTable::Id> thunk_id =
739           GetReferenceByFieldName(cleaner_obj_id, cleaner_thunk_str_id_);
740 
741       if (!referent_id || !thunk_id) {
742         continue;
743       }
744 
745       std::optional<ObjectTable::Id> next_id =
746           GetReferenceByFieldName(cleaner_obj_id, cleaner_next_str_id_);
747       if (next_id.has_value() && *next_id == cleaner_obj_id) {
748         // sun.misc.Cleaner.next points to the sun.misc.Cleaner: this means
749         // that the sun.misc.Cleaner.clean() has already been called. Skip this.
750         continue;
751       }
752       cleaners.push_back(Cleaner{*referent_id, *thunk_id});
753     }
754   }
755 
756   for (const auto& cleaner : cleaners) {
757     std::optional<ObjectTable::Id> this0 =
758         GetReferenceByFieldName(cleaner.thunk, cleaner_thunk_this0_str_id_);
759     if (!this0) {
760       continue;
761     }
762 
763     auto nar_size_it = seq.nar_size_by_obj_id.find(*this0);
764     if (nar_size_it == seq.nar_size_by_obj_id.end()) {
765       continue;
766     }
767 
768     int64_t native_size =
769         GetSizeFromNativeAllocationRegistry(nar_size_it->second);
770     auto referent_row_ref = *objects_tbl.FindById(cleaner.referent);
771     int64_t total_native_size = referent_row_ref.native_size() + native_size;
772     referent_row_ref.set_native_size(total_native_size);
773   }
774 }
775 
776 // TODO(fmayer): For Android S+ traces, use the superclass_id from the trace.
PopulateSuperClasses(const SequenceState & seq)777 void HeapGraphTracker::PopulateSuperClasses(const SequenceState& seq) {
778   // Maps from normalized class name and location, to superclass.
779   std::map<ClassDescriptor, ClassDescriptor> superclass_map =
780       BuildSuperclassMap(seq.current_upid, seq.current_ts, storage_);
781 
782   auto* classes_tbl = storage_->mutable_heap_graph_class_table();
783   std::map<ClassDescriptor, ClassTable::Id> class_to_id;
784   for (uint32_t idx = 0; idx < classes_tbl->row_count(); ++idx) {
785     class_to_id[{classes_tbl->name()[idx], classes_tbl->location()[idx]}] =
786         classes_tbl->id()[idx];
787   }
788 
789   // Iterate through the classes table and annotate with superclasses.
790   // We iterate all rows on the classes table (even though the superclass
791   // mapping was generated on the current sequence) - if we cannot identify
792   // a superclass we will just skip.
793   for (uint32_t idx = 0; idx < classes_tbl->row_count(); ++idx) {
794     auto name = storage_->GetString(classes_tbl->name()[idx]);
795     auto location = classes_tbl->location()[idx];
796     auto normalized = GetNormalizedType(name);
797     if (normalized.is_static_class || normalized.number_of_arrays > 0)
798       continue;
799 
800     StringId class_name_id = storage_->InternString(normalized.name);
801     auto map_it = superclass_map.find({class_name_id, location});
802     if (map_it == superclass_map.end()) {
803       continue;
804     }
805 
806     // Find the row for the superclass id
807     auto superclass_it = class_to_id.find(map_it->second);
808     if (superclass_it == class_to_id.end()) {
809       // This can happen for traces was captured before the patch to
810       // explicitly emit interned types (meaning classes without live
811       // instances would not appear here).
812       continue;
813     }
814     auto superclass_id = superclass_it->second;
815     // Mutate the superclass column
816     classes_tbl->mutable_superclass_id()->Set(idx, superclass_id);
817   }
818 }
819 
FindPathFromRoot(TraceStorage * storage,ObjectTable::RowReference row_ref,PathFromRoot * path)820 void FindPathFromRoot(TraceStorage* storage,
821                       ObjectTable::RowReference row_ref,
822                       PathFromRoot* path) {
823   // We have long retention chains (e.g. from LinkedList). If we use the stack
824   // here, we risk running out of stack space. This is why we use a vector to
825   // simulate the stack.
826   struct StackElem {
827     ObjectTable::RowReference node;  // Node in the original graph.
828     size_t parent_id;                // id of parent node in the result tree.
829     size_t i;        // Index of the next child of this node to handle.
830     uint32_t depth;  // Depth in the resulting tree
831                      // (including artificial root).
832     std::vector<ObjectTable::Id> children;
833   };
834 
835   std::vector<StackElem> stack{{row_ref, PathFromRoot::kRoot, 0, 0, {}}};
836 
837   while (!stack.empty()) {
838     ObjectTable::RowReference object_row_ref = stack.back().node;
839 
840     size_t parent_id = stack.back().parent_id;
841     uint32_t depth = stack.back().depth;
842     size_t& i = stack.back().i;
843     std::vector<ObjectTable::Id>& children = stack.back().children;
844 
845     ClassTable::Id type_id = object_row_ref.type_id();
846 
847     auto type_row_ref = *storage->heap_graph_class_table().FindById(type_id);
848     std::optional<StringId> opt_class_name_id =
849         type_row_ref.deobfuscated_name();
850     if (!opt_class_name_id) {
851       opt_class_name_id = type_row_ref.name();
852     }
853     PERFETTO_CHECK(opt_class_name_id);
854     StringId class_name_id = *opt_class_name_id;
855     std::optional<StringId> root_type = object_row_ref.root_type();
856     if (root_type) {
857       class_name_id = storage->InternString(base::StringView(
858           storage->GetString(class_name_id).ToStdString() + " [" +
859           storage->GetString(*root_type).ToStdString() + "]"));
860     }
861     auto it = path->nodes[parent_id].children.find(class_name_id);
862     if (it == path->nodes[parent_id].children.end()) {
863       size_t path_id = path->nodes.size();
864       path->nodes.emplace_back(PathFromRoot::Node{});
865       std::tie(it, std::ignore) =
866           path->nodes[parent_id].children.emplace(class_name_id, path_id);
867       path->nodes.back().class_name_id = class_name_id;
868       path->nodes.back().depth = depth;
869       path->nodes.back().parent_id = parent_id;
870     }
871     size_t path_id = it->second;
872     PathFromRoot::Node* output_tree_node = &path->nodes[path_id];
873 
874     if (i == 0) {
875       // This is the first time we are looking at this node, so add its
876       // size to the relevant node in the resulting tree.
877       output_tree_node->size += object_row_ref.self_size();
878       output_tree_node->count++;
879       base::FlatSet<ObjectTable::Id> children_set =
880           GetChildren(storage, object_row_ref);
881       children.assign(children_set.begin(), children_set.end());
882       PERFETTO_CHECK(children.size() == children_set.size());
883 
884       if (object_row_ref.native_size()) {
885         StringId native_class_name_id = storage->InternString(
886             base::StringView(std::string("[native] ") +
887                              storage->GetString(class_name_id).ToStdString()));
888         std::map<StringId, size_t>::iterator native_it;
889         bool inserted_new_node;
890         std::tie(native_it, inserted_new_node) =
891             path->nodes[path_id].children.insert({native_class_name_id, 0});
892         if (inserted_new_node) {
893           native_it->second = path->nodes.size();
894           path->nodes.emplace_back(PathFromRoot::Node{});
895 
896           path->nodes.back().class_name_id = native_class_name_id;
897           path->nodes.back().depth = depth + 1;
898           path->nodes.back().parent_id = path_id;
899         }
900         PathFromRoot::Node* new_output_tree_node =
901             &path->nodes[native_it->second];
902 
903         new_output_tree_node->size += object_row_ref.native_size();
904         new_output_tree_node->count++;
905       }
906     }
907 
908     // We have already handled this node and just need to get its i-th child.
909     if (!children.empty()) {
910       PERFETTO_CHECK(i < children.size());
911       ObjectTable::Id child = children[i];
912       auto child_row_ref =
913           *storage->mutable_heap_graph_object_table()->FindById(child);
914       if (++i == children.size())
915         stack.pop_back();
916 
917       int32_t child_distance = child_row_ref.root_distance();
918       int32_t n_distance = object_row_ref.root_distance();
919       PERFETTO_CHECK(n_distance >= 0);
920       PERFETTO_CHECK(child_distance >= 0);
921 
922       bool visited = path->visited.count(child);
923 
924       if (child_distance == n_distance + 1 && !visited) {
925         path->visited.emplace(child);
926         stack.emplace_back(StackElem{child_row_ref, path_id, 0, depth + 1, {}});
927       }
928     } else {
929       stack.pop_back();
930     }
931   }
932 }
933 
934 std::unique_ptr<tables::ExperimentalFlamegraphNodesTable>
BuildFlamegraph(const int64_t current_ts,const UniquePid current_upid)935 HeapGraphTracker::BuildFlamegraph(const int64_t current_ts,
936                                   const UniquePid current_upid) {
937   auto profile_type = storage_->InternString("graph");
938   auto java_mapping = storage_->InternString("JAVA");
939 
940   std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> tbl(
941       new tables::ExperimentalFlamegraphNodesTable(
942           storage_->mutable_string_pool()));
943 
944   auto it = roots_.find(std::make_pair(current_upid, current_ts));
945   if (it == roots_.end()) {
946     // TODO(fmayer): This should not be within the flame graph but some marker
947     // in the UI.
948     if (IsTruncated(current_upid, current_ts)) {
949       tables::ExperimentalFlamegraphNodesTable::Row alloc_row{};
950       alloc_row.ts = current_ts;
951       alloc_row.upid = current_upid;
952       alloc_row.profile_type = profile_type;
953       alloc_row.depth = 0;
954       alloc_row.name = storage_->InternString(
955           "ERROR: INCOMPLETE GRAPH (try increasing buffer size)");
956       alloc_row.map_name = java_mapping;
957       alloc_row.count = 1;
958       alloc_row.cumulative_count = 1;
959       alloc_row.size = 1;
960       alloc_row.cumulative_size = 1;
961       alloc_row.parent_id = std::nullopt;
962       tbl->Insert(alloc_row);
963       return tbl;
964     }
965     // We haven't seen this graph, so we should raise an error.
966     return nullptr;
967   }
968 
969   const std::set<ObjectTable::RowNumber>& roots = it->second;
970   auto* object_table = storage_->mutable_heap_graph_object_table();
971 
972   // First pass to calculate shortest paths
973   for (ObjectTable::RowNumber root : roots) {
974     UpdateShortestPaths(storage_, root.ToRowReference(object_table));
975   }
976   PathFromRoot init_path;
977   for (ObjectTable::RowNumber root : roots) {
978     FindPathFromRoot(storage_, root.ToRowReference(object_table), &init_path);
979   }
980 
981   std::vector<int64_t> node_to_cumulative_size(init_path.nodes.size());
982   std::vector<int64_t> node_to_cumulative_count(init_path.nodes.size());
983   // i > 0 is to skip the artifical root node.
984   for (size_t i = init_path.nodes.size() - 1; i > 0; --i) {
985     const PathFromRoot::Node& node = init_path.nodes[i];
986 
987     node_to_cumulative_size[i] += node.size;
988     node_to_cumulative_count[i] += node.count;
989     node_to_cumulative_size[node.parent_id] += node_to_cumulative_size[i];
990     node_to_cumulative_count[node.parent_id] += node_to_cumulative_count[i];
991   }
992 
993   std::vector<FlamegraphId> node_to_id(init_path.nodes.size());
994   // i = 1 is to skip the artifical root node.
995   for (size_t i = 1; i < init_path.nodes.size(); ++i) {
996     const PathFromRoot::Node& node = init_path.nodes[i];
997     PERFETTO_CHECK(node.parent_id < i);
998     std::optional<FlamegraphId> parent_id;
999     if (node.parent_id != 0)
1000       parent_id = node_to_id[node.parent_id];
1001     const uint32_t depth = node.depth;
1002 
1003     tables::ExperimentalFlamegraphNodesTable::Row alloc_row{};
1004     alloc_row.ts = current_ts;
1005     alloc_row.upid = current_upid;
1006     alloc_row.profile_type = profile_type;
1007     alloc_row.depth = depth;
1008     alloc_row.name = node.class_name_id;
1009     alloc_row.map_name = java_mapping;
1010     alloc_row.count = static_cast<int64_t>(node.count);
1011     alloc_row.cumulative_count =
1012         static_cast<int64_t>(node_to_cumulative_count[i]);
1013     alloc_row.size = static_cast<int64_t>(node.size);
1014     alloc_row.cumulative_size =
1015         static_cast<int64_t>(node_to_cumulative_size[i]);
1016     alloc_row.parent_id = parent_id;
1017     node_to_id[i] = tbl->Insert(alloc_row).id;
1018   }
1019   return tbl;
1020 }
1021 
FinalizeAllProfiles()1022 void HeapGraphTracker::FinalizeAllProfiles() {
1023   if (!sequence_state_.empty()) {
1024     storage_->IncrementStats(stats::heap_graph_non_finalized_graph);
1025     // There might still be valuable data even though the trace is truncated.
1026     while (!sequence_state_.empty()) {
1027       FinalizeProfile(sequence_state_.begin()->first);
1028     }
1029   }
1030 }
1031 
IsTruncated(UniquePid upid,int64_t ts)1032 bool HeapGraphTracker::IsTruncated(UniquePid upid, int64_t ts) {
1033   // The graph was finalized but was missing packets.
1034   if (truncated_graphs_.find(std::make_pair(upid, ts)) !=
1035       truncated_graphs_.end()) {
1036     return true;
1037   }
1038 
1039   // Or the graph was never finalized, so is missing packets at the end.
1040   for (const auto& p : sequence_state_) {
1041     const SequenceState& sequence_state = p.second;
1042     if (sequence_state.current_upid == upid &&
1043         sequence_state.current_ts == ts) {
1044       return true;
1045     }
1046   }
1047   return false;
1048 }
1049 
1050 HeapGraphTracker::~HeapGraphTracker() = default;
1051 
1052 }  // namespace trace_processor
1053 }  // namespace perfetto
1054