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