ART: Prioritize reference table dump
Sort the reference table summary dump by highest count, prioritizing
repeated types and instances. This will help with triaging leaks.
Add a test.
Bug: 32857749
Test: m test-art-host-gtest-reference_table_test
Change-Id: I7e61881b5badf9ac2b6b72333f31437ab498caee
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 16ed7fb..1c975a4 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -215,33 +215,87 @@
}
std::sort(sorted_entries.begin(), sorted_entries.end(), GcRootComparator());
+ class SummaryElement {
+ public:
+ GcRoot<mirror::Object> root;
+ size_t equiv;
+ size_t identical;
+
+ SummaryElement() : equiv(0), identical(0) {}
+ SummaryElement(SummaryElement&& ref) {
+ root = ref.root;
+ equiv = ref.equiv;
+ identical = ref.identical;
+ }
+ SummaryElement(const SummaryElement&) = default;
+ SummaryElement& operator=(SummaryElement&&) = default;
+
+ void Reset(GcRoot<mirror::Object>& _root) {
+ root = _root;
+ equiv = 0;
+ identical = 0;
+ }
+ };
+ std::vector<SummaryElement> sorted_summaries;
+ {
+ SummaryElement prev;
+
+ for (GcRoot<mirror::Object>& root : sorted_entries) {
+ ObjPtr<mirror::Object> current = root.Read<kWithoutReadBarrier>();
+
+ if (UNLIKELY(prev.root.IsNull())) {
+ prev.Reset(root);
+ continue;
+ }
+
+ ObjPtr<mirror::Object> prevObj = prev.root.Read<kWithoutReadBarrier>();
+ if (current == prevObj) {
+ // Same reference, added more than once.
+ ++prev.identical;
+ } else if (current->GetClass() == prevObj->GetClass() &&
+ GetElementCount(current) == GetElementCount(prevObj)) {
+ // Same class / element count, different object.
+ ++prev.equiv;
+ } else {
+ sorted_summaries.push_back(prev);
+ prev.Reset(root);
+ }
+ prev.root = root;
+ }
+ sorted_summaries.push_back(prev);
+
+ // Compare summary elements, first by combined count, then by identical (indicating leaks),
+ // then by class (and size and address).
+ struct SummaryElementComparator {
+ GcRootComparator gc_root_cmp;
+
+ bool operator()(SummaryElement& elem1, SummaryElement& elem2) const
+ NO_THREAD_SAFETY_ANALYSIS {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+
+ size_t count1 = elem1.equiv + elem1.identical;
+ size_t count2 = elem2.equiv + elem2.identical;
+ if (count1 != count2) {
+ return count1 > count2;
+ }
+
+ if (elem1.identical != elem2.identical) {
+ return elem1.identical > elem2.identical;
+ }
+
+ // Otherwise, compare the GC roots as before.
+ return gc_root_cmp(elem1.root, elem2.root);
+ }
+ };
+ std::sort(sorted_summaries.begin(), sorted_summaries.end(), SummaryElementComparator());
+ }
+
// Dump a summary of the whole table.
os << " Summary:\n";
- size_t equiv = 0;
- size_t identical = 0;
- ObjPtr<mirror::Object> prev = nullptr;
- for (GcRoot<mirror::Object>& root : sorted_entries) {
- ObjPtr<mirror::Object> current = root.Read<kWithoutReadBarrier>();
- if (prev != nullptr) {
- const size_t element_count = GetElementCount(prev);
- if (current == prev) {
- // Same reference, added more than once.
- ++identical;
- } else if (current->GetClass() == prev->GetClass() &&
- GetElementCount(current) == element_count) {
- // Same class / element count, different object.
- ++equiv;
- } else {
- // Different class.
- DumpSummaryLine(os, prev, element_count, identical, equiv);
- equiv = 0;
- identical = 0;
- }
- }
- prev = current;
+ for (SummaryElement& elem : sorted_summaries) {
+ ObjPtr<mirror::Object> elemObj = elem.root.Read<kWithoutReadBarrier>();
+ DumpSummaryLine(os, elemObj, GetElementCount(elemObj), elem.identical, elem.equiv);
}
- // Handle the last entry.
- DumpSummaryLine(os, prev, GetElementCount(prev), identical, equiv);
}
void ReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {