Home | History | Annotate | Download | only in runtime
      1 /*
      2  * Copyright (C) 2008 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 "reference_table.h"
     18 
     19 #include "android-base/stringprintf.h"
     20 
     21 #include "base/mutex.h"
     22 #include "base/utils.h"
     23 #include "gc/allocation_record.h"
     24 #include "gc/heap.h"
     25 #include "indirect_reference_table.h"
     26 #include "mirror/array-inl.h"
     27 #include "mirror/array.h"
     28 #include "mirror/class-inl.h"
     29 #include "mirror/class.h"
     30 #include "mirror/object-inl.h"
     31 #include "mirror/string-inl.h"
     32 #include "runtime-inl.h"
     33 #include "thread.h"
     34 
     35 namespace art {
     36 
     37 using android::base::StringAppendF;
     38 using android::base::StringPrintf;
     39 
     40 ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max_size)
     41     : name_(name), max_size_(max_size) {
     42   CHECK_LE(initial_size, max_size);
     43   entries_.reserve(initial_size);
     44 }
     45 
     46 ReferenceTable::~ReferenceTable() {
     47 }
     48 
     49 void ReferenceTable::Add(ObjPtr<mirror::Object> obj) {
     50   DCHECK(obj != nullptr);
     51   VerifyObject(obj);
     52   if (entries_.size() >= max_size_) {
     53     LOG(FATAL) << "ReferenceTable '" << name_ << "' "
     54                << "overflowed (" << max_size_ << " entries)";
     55   }
     56   entries_.push_back(GcRoot<mirror::Object>(obj));
     57 }
     58 
     59 void ReferenceTable::Remove(ObjPtr<mirror::Object> obj) {
     60   // We iterate backwards on the assumption that references are LIFO.
     61   for (int i = entries_.size() - 1; i >= 0; --i) {
     62     ObjPtr<mirror::Object> entry = entries_[i].Read();
     63     if (entry == obj) {
     64       entries_.erase(entries_.begin() + i);
     65       return;
     66     }
     67   }
     68 }
     69 
     70 // If "obj" is an array, return the number of elements in the array.
     71 // Otherwise, return zero.
     72 static size_t GetElementCount(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
     73   // We assume the special cleared value isn't an array in the if statement below.
     74   DCHECK(!Runtime::Current()->GetClearedJniWeakGlobal()->IsArrayInstance());
     75   if (obj == nullptr || !obj->IsArrayInstance()) {
     76     return 0;
     77   }
     78   return obj->AsArray()->GetLength();
     79 }
     80 
     81 // Log an object with some additional info.
     82 //
     83 // Pass in the number of elements in the array (or 0 if this is not an
     84 // array object), and the number of additional objects that are identical
     85 // or equivalent to the original.
     86 static void DumpSummaryLine(std::ostream& os, ObjPtr<mirror::Object> obj, size_t element_count,
     87                             int identical, int equiv)
     88     REQUIRES_SHARED(Locks::mutator_lock_) {
     89   if (obj == nullptr) {
     90     os << "    null reference (count=" << equiv << ")\n";
     91     return;
     92   }
     93   if (Runtime::Current()->IsClearedJniWeakGlobal(obj)) {
     94     os << "    cleared jweak (count=" << equiv << ")\n";
     95     return;
     96   }
     97 
     98   std::string className(obj->PrettyTypeOf());
     99   if (obj->IsClass()) {
    100     // We're summarizing multiple instances, so using the exemplar
    101     // Class' type parameter here would be misleading.
    102     className = "java.lang.Class";
    103   }
    104   if (element_count != 0) {
    105     StringAppendF(&className, " (%zd elements)", element_count);
    106   }
    107 
    108   size_t total = identical + equiv + 1;
    109   std::string msg(StringPrintf("%5zd of %s", total, className.c_str()));
    110   if (identical + equiv != 0) {
    111     StringAppendF(&msg, " (%d unique instances)", equiv + 1);
    112   }
    113   os << "    " << msg << "\n";
    114 }
    115 
    116 size_t ReferenceTable::Size() const {
    117   return entries_.size();
    118 }
    119 
    120 void ReferenceTable::Dump(std::ostream& os) {
    121   os << name_ << " reference table dump:\n";
    122   Dump(os, entries_);
    123 }
    124 
    125 void ReferenceTable::Dump(std::ostream& os, Table& entries) {
    126   // Compare GC roots, first by class, then size, then address.
    127   struct GcRootComparator {
    128     bool operator()(GcRoot<mirror::Object> root1, GcRoot<mirror::Object> root2) const
    129       // TODO: enable analysis when analysis can work with the STL.
    130         NO_THREAD_SAFETY_ANALYSIS {
    131       Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
    132       // These GC roots are already forwarded in ReferenceTable::Dump. We sort by class since there
    133       // are no suspend points which can happen during the sorting process. This works since
    134       // we are guaranteed that the addresses of obj1, obj2, obj1->GetClass, obj2->GetClass wont
    135       // change during the sorting process. The classes are forwarded by ref->GetClass().
    136       ObjPtr<mirror::Object> obj1 = root1.Read<kWithoutReadBarrier>();
    137       ObjPtr<mirror::Object> obj2 = root2.Read<kWithoutReadBarrier>();
    138       DCHECK(obj1 != nullptr);
    139       DCHECK(obj2 != nullptr);
    140       Runtime* runtime = Runtime::Current();
    141       DCHECK(!runtime->IsClearedJniWeakGlobal(obj1));
    142       DCHECK(!runtime->IsClearedJniWeakGlobal(obj2));
    143       // Sort by class...
    144       if (obj1->GetClass() != obj2->GetClass()) {
    145         return obj1->GetClass() < obj2->GetClass();
    146       }
    147       // ...then by size...
    148       const size_t size1 = obj1->SizeOf();
    149       const size_t size2 = obj2->SizeOf();
    150       if (size1 != size2) {
    151         return size1 < size2;
    152       }
    153       // ...and finally by address.
    154       return obj1.Ptr() < obj2.Ptr();
    155     }
    156   };
    157 
    158   if (entries.empty()) {
    159     os << "  (empty)\n";
    160     return;
    161   }
    162 
    163   // Dump the most recent N entries.
    164   const size_t kLast = 10;
    165   size_t count = entries.size();
    166   int first = count - kLast;
    167   if (first < 0) {
    168     first = 0;
    169   }
    170   os << "  Last " << (count - first) << " entries (of " << count << "):\n";
    171   Runtime* runtime = Runtime::Current();
    172   for (int idx = count - 1; idx >= first; --idx) {
    173     ObjPtr<mirror::Object> ref = entries[idx].Read();
    174     if (ref == nullptr) {
    175       continue;
    176     }
    177     if (runtime->IsClearedJniWeakGlobal(ref)) {
    178       os << StringPrintf("    %5d: cleared jweak\n", idx);
    179       continue;
    180     }
    181     if (ref->GetClass() == nullptr) {
    182       // should only be possible right after a plain dvmMalloc().
    183       size_t size = ref->SizeOf();
    184       os << StringPrintf("    %5d: %p (raw) (%zd bytes)\n", idx, ref.Ptr(), size);
    185       continue;
    186     }
    187 
    188     std::string className(ref->PrettyTypeOf());
    189 
    190     std::string extras;
    191     size_t element_count = GetElementCount(ref);
    192     if (element_count != 0) {
    193       StringAppendF(&extras, " (%zd elements)", element_count);
    194     } else if (ref->GetClass()->IsStringClass()) {
    195       ObjPtr<mirror::String> s = ref->AsString();
    196       std::string utf8(s->ToModifiedUtf8());
    197       if (s->GetLength() <= 16) {
    198         StringAppendF(&extras, " \"%s\"", utf8.c_str());
    199       } else {
    200         StringAppendF(&extras, " \"%.16s... (%d chars)", utf8.c_str(), s->GetLength());
    201       }
    202     } else if (ref->IsReferenceInstance()) {
    203       ObjPtr<mirror::Object> referent = ref->AsReference()->GetReferent();
    204       if (referent == nullptr) {
    205         extras = " (referent is null)";
    206       } else {
    207         extras = StringPrintf(" (referent is a %s)", referent->PrettyTypeOf().c_str());
    208       }
    209     }
    210     os << StringPrintf("    %5d: ", idx) << ref << " " << className << extras << "\n";
    211     if (runtime->GetHeap()->IsAllocTrackingEnabled()) {
    212       MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
    213 
    214       gc::AllocRecordObjectMap* records = runtime->GetHeap()->GetAllocationRecords();
    215       DCHECK(records != nullptr);
    216       // It's annoying that this is a list. But this code should be very uncommon to be executed.
    217 
    218       auto print_stack = [&](ObjPtr<mirror::Object> to_print, const std::string& msg)
    219           REQUIRES_SHARED(Locks::mutator_lock_)
    220           REQUIRES(Locks::alloc_tracker_lock_) {
    221         for (auto it = records->Begin(), end = records->End(); it != end; ++it) {
    222           GcRoot<mirror::Object>& stack_for_object = it->first;
    223           gc::AllocRecord& record = it->second;
    224           if (stack_for_object.Read() == to_print.Ptr()) {
    225             os << "          " << msg << "\n";
    226             const gc::AllocRecordStackTrace* trace = record.GetStackTrace();
    227             size_t depth = trace->GetDepth();
    228             if (depth == 0) {
    229               os << "            (No managed frames)\n";
    230             } else {
    231               for (size_t i = 0; i < depth; ++i) {
    232                 const gc::AllocRecordStackTraceElement& frame = trace->GetStackElement(i);
    233                 os << "            ";
    234                 if (frame.GetMethod() == nullptr) {
    235                   os << "(missing method data)\n";
    236                   continue;
    237                 }
    238                 os << frame.GetMethod()->PrettyMethod(true)
    239                    << ":"
    240                    << frame.ComputeLineNumber()
    241                    << "\n";
    242               }
    243             }
    244             break;
    245           }
    246         }
    247       };
    248       // Print the stack trace of the ref.
    249       print_stack(ref, "Allocated at:");
    250 
    251       // If it's a reference, see if we have data about the referent.
    252       if (ref->IsReferenceInstance()) {
    253         ObjPtr<mirror::Object> referent = ref->AsReference()->GetReferent();
    254         if (referent != nullptr) {
    255           print_stack(referent, "Referent allocated at:");
    256         }
    257       }
    258     }
    259   }
    260 
    261   // Make a copy of the table and sort it, only adding non null and not cleared elements.
    262   Table sorted_entries;
    263   for (GcRoot<mirror::Object>& root : entries) {
    264     if (!root.IsNull() && !runtime->IsClearedJniWeakGlobal(root.Read())) {
    265       sorted_entries.push_back(root);
    266     }
    267   }
    268   if (sorted_entries.empty()) {
    269     return;
    270   }
    271   std::sort(sorted_entries.begin(), sorted_entries.end(), GcRootComparator());
    272 
    273   class SummaryElement {
    274    public:
    275     GcRoot<mirror::Object> root;
    276     size_t equiv;
    277     size_t identical;
    278 
    279     SummaryElement() : equiv(0), identical(0) {}
    280     SummaryElement(SummaryElement&& ref) {
    281       root = ref.root;
    282       equiv = ref.equiv;
    283       identical = ref.identical;
    284     }
    285     SummaryElement(const SummaryElement&) = default;
    286     SummaryElement& operator=(SummaryElement&&) = default;
    287 
    288     void Reset(GcRoot<mirror::Object>& _root) {
    289       root = _root;
    290       equiv = 0;
    291       identical = 0;
    292     }
    293   };
    294   std::vector<SummaryElement> sorted_summaries;
    295   {
    296     SummaryElement prev;
    297 
    298     for (GcRoot<mirror::Object>& root : sorted_entries) {
    299       ObjPtr<mirror::Object> current = root.Read<kWithoutReadBarrier>();
    300 
    301       if (UNLIKELY(prev.root.IsNull())) {
    302         prev.Reset(root);
    303         continue;
    304       }
    305 
    306       ObjPtr<mirror::Object> prevObj = prev.root.Read<kWithoutReadBarrier>();
    307       if (current == prevObj) {
    308         // Same reference, added more than once.
    309         ++prev.identical;
    310       } else if (current->GetClass() == prevObj->GetClass() &&
    311           GetElementCount(current) == GetElementCount(prevObj)) {
    312         // Same class / element count, different object.
    313         ++prev.equiv;
    314       } else {
    315         sorted_summaries.push_back(prev);
    316         prev.Reset(root);
    317       }
    318       prev.root = root;
    319     }
    320     sorted_summaries.push_back(prev);
    321 
    322     // Compare summary elements, first by combined count, then by identical (indicating leaks),
    323     // then by class (and size and address).
    324     struct SummaryElementComparator {
    325       GcRootComparator gc_root_cmp;
    326 
    327       bool operator()(SummaryElement& elem1, SummaryElement& elem2) const
    328           NO_THREAD_SAFETY_ANALYSIS {
    329         Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
    330 
    331         size_t count1 = elem1.equiv + elem1.identical;
    332         size_t count2 = elem2.equiv + elem2.identical;
    333         if (count1 != count2) {
    334           return count1 > count2;
    335         }
    336 
    337         if (elem1.identical != elem2.identical) {
    338           return elem1.identical > elem2.identical;
    339         }
    340 
    341         // Otherwise, compare the GC roots as before.
    342         return gc_root_cmp(elem1.root, elem2.root);
    343       }
    344     };
    345     std::sort(sorted_summaries.begin(), sorted_summaries.end(), SummaryElementComparator());
    346   }
    347 
    348   // Dump a summary of the whole table.
    349   os << "  Summary:\n";
    350   for (SummaryElement& elem : sorted_summaries) {
    351     ObjPtr<mirror::Object> elemObj = elem.root.Read<kWithoutReadBarrier>();
    352     DumpSummaryLine(os, elemObj, GetElementCount(elemObj), elem.identical, elem.equiv);
    353   }
    354 }
    355 
    356 void ReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
    357   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(visitor, root_info);
    358   for (GcRoot<mirror::Object>& root : entries_) {
    359     buffered_visitor.VisitRoot(root);
    360   }
    361 }
    362 
    363 }  // namespace art
    364