Home | History | Annotate | Download | only in gc
      1 /*
      2  * Copyright (C) 2015 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 "allocation_record.h"
     18 
     19 #include "art_method-inl.h"
     20 #include "base/enums.h"
     21 #include "base/logging.h"  // For VLOG
     22 #include "base/stl_util.h"
     23 #include "obj_ptr-inl.h"
     24 #include "object_callbacks.h"
     25 #include "stack.h"
     26 
     27 #ifdef ART_TARGET_ANDROID
     28 #include "cutils/properties.h"
     29 #endif
     30 
     31 namespace art {
     32 namespace gc {
     33 
     34 int32_t AllocRecordStackTraceElement::ComputeLineNumber() const {
     35   DCHECK(method_ != nullptr);
     36   return method_->GetLineNumFromDexPC(dex_pc_);
     37 }
     38 
     39 const char* AllocRecord::GetClassDescriptor(std::string* storage) const {
     40   // klass_ could contain null only if we implement class unloading.
     41   return klass_.IsNull() ? "null" : klass_.Read()->GetDescriptor(storage);
     42 }
     43 
     44 void AllocRecordObjectMap::SetProperties() {
     45 #ifdef ART_TARGET_ANDROID
     46   // Check whether there's a system property overriding the max number of records.
     47   const char* propertyName = "dalvik.vm.allocTrackerMax";
     48   char allocMaxString[PROPERTY_VALUE_MAX];
     49   if (property_get(propertyName, allocMaxString, "") > 0) {
     50     char* end;
     51     size_t value = strtoul(allocMaxString, &end, 10);
     52     if (*end != '\0') {
     53       LOG(ERROR) << "Ignoring  " << propertyName << " '" << allocMaxString
     54                  << "' --- invalid";
     55     } else {
     56       alloc_record_max_ = value;
     57       if (recent_record_max_ > value) {
     58         recent_record_max_ = value;
     59       }
     60     }
     61   }
     62   // Check whether there's a system property overriding the number of recent records.
     63   propertyName = "dalvik.vm.recentAllocMax";
     64   char recentAllocMaxString[PROPERTY_VALUE_MAX];
     65   if (property_get(propertyName, recentAllocMaxString, "") > 0) {
     66     char* end;
     67     size_t value = strtoul(recentAllocMaxString, &end, 10);
     68     if (*end != '\0') {
     69       LOG(ERROR) << "Ignoring  " << propertyName << " '" << recentAllocMaxString
     70                  << "' --- invalid";
     71     } else if (value > alloc_record_max_) {
     72       LOG(ERROR) << "Ignoring  " << propertyName << " '" << recentAllocMaxString
     73                  << "' --- should be less than " << alloc_record_max_;
     74     } else {
     75       recent_record_max_ = value;
     76     }
     77   }
     78   // Check whether there's a system property overriding the max depth of stack trace.
     79   propertyName = "debug.allocTracker.stackDepth";
     80   char stackDepthString[PROPERTY_VALUE_MAX];
     81   if (property_get(propertyName, stackDepthString, "") > 0) {
     82     char* end;
     83     size_t value = strtoul(stackDepthString, &end, 10);
     84     if (*end != '\0') {
     85       LOG(ERROR) << "Ignoring  " << propertyName << " '" << stackDepthString
     86                  << "' --- invalid";
     87     } else if (value > kMaxSupportedStackDepth) {
     88       LOG(WARNING) << propertyName << " '" << stackDepthString << "' too large, using "
     89                    << kMaxSupportedStackDepth;
     90       max_stack_depth_ = kMaxSupportedStackDepth;
     91     } else {
     92       max_stack_depth_ = value;
     93     }
     94   }
     95 #endif  // ART_TARGET_ANDROID
     96 }
     97 
     98 AllocRecordObjectMap::~AllocRecordObjectMap() {
     99   Clear();
    100 }
    101 
    102 void AllocRecordObjectMap::VisitRoots(RootVisitor* visitor) {
    103   CHECK_LE(recent_record_max_, alloc_record_max_);
    104   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(visitor, RootInfo(kRootDebugger));
    105   size_t count = recent_record_max_;
    106   // Only visit the last recent_record_max_ number of allocation records in entries_ and mark the
    107   // klass_ fields as strong roots.
    108   for (auto it = entries_.rbegin(), end = entries_.rend(); it != end; ++it) {
    109     AllocRecord& record = it->second;
    110     if (count > 0) {
    111       buffered_visitor.VisitRootIfNonNull(record.GetClassGcRoot());
    112       --count;
    113     }
    114     // Visit all of the stack frames to make sure no methods in the stack traces get unloaded by
    115     // class unloading.
    116     for (size_t i = 0, depth = record.GetDepth(); i < depth; ++i) {
    117       const AllocRecordStackTraceElement& element = record.StackElement(i);
    118       DCHECK(element.GetMethod() != nullptr);
    119       element.GetMethod()->VisitRoots(buffered_visitor, kRuntimePointerSize);
    120     }
    121   }
    122 }
    123 
    124 static inline void SweepClassObject(AllocRecord* record, IsMarkedVisitor* visitor)
    125     REQUIRES_SHARED(Locks::mutator_lock_)
    126     REQUIRES(Locks::alloc_tracker_lock_) {
    127   GcRoot<mirror::Class>& klass = record->GetClassGcRoot();
    128   // This does not need a read barrier because this is called by GC.
    129   mirror::Object* old_object = klass.Read<kWithoutReadBarrier>();
    130   if (old_object != nullptr) {
    131     // The class object can become null if we implement class unloading.
    132     // In that case we might still want to keep the class name string (not implemented).
    133     mirror::Object* new_object = visitor->IsMarked(old_object);
    134     DCHECK(new_object != nullptr);
    135     if (UNLIKELY(old_object != new_object)) {
    136       klass = GcRoot<mirror::Class>(new_object->AsClass());
    137     }
    138   }
    139 }
    140 
    141 void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedVisitor* visitor) {
    142   VLOG(heap) << "Start SweepAllocationRecords()";
    143   size_t count_deleted = 0, count_moved = 0, count = 0;
    144   // Only the first (size - recent_record_max_) number of records can be deleted.
    145   const size_t delete_bound = std::max(entries_.size(), recent_record_max_) - recent_record_max_;
    146   for (auto it = entries_.begin(), end = entries_.end(); it != end;) {
    147     ++count;
    148     // This does not need a read barrier because this is called by GC.
    149     mirror::Object* old_object = it->first.Read<kWithoutReadBarrier>();
    150     AllocRecord& record = it->second;
    151     mirror::Object* new_object = old_object == nullptr ? nullptr : visitor->IsMarked(old_object);
    152     if (new_object == nullptr) {
    153       if (count > delete_bound) {
    154         it->first = GcRoot<mirror::Object>(nullptr);
    155         SweepClassObject(&record, visitor);
    156         ++it;
    157       } else {
    158         it = entries_.erase(it);
    159         ++count_deleted;
    160       }
    161     } else {
    162       if (old_object != new_object) {
    163         it->first = GcRoot<mirror::Object>(new_object);
    164         ++count_moved;
    165       }
    166       SweepClassObject(&record, visitor);
    167       ++it;
    168     }
    169   }
    170   VLOG(heap) << "Deleted " << count_deleted << " allocation records";
    171   VLOG(heap) << "Updated " << count_moved << " allocation records";
    172 }
    173 
    174 void AllocRecordObjectMap::AllowNewAllocationRecords() {
    175   CHECK(!kUseReadBarrier);
    176   allow_new_record_ = true;
    177   new_record_condition_.Broadcast(Thread::Current());
    178 }
    179 
    180 void AllocRecordObjectMap::DisallowNewAllocationRecords() {
    181   CHECK(!kUseReadBarrier);
    182   allow_new_record_ = false;
    183 }
    184 
    185 void AllocRecordObjectMap::BroadcastForNewAllocationRecords() {
    186   new_record_condition_.Broadcast(Thread::Current());
    187 }
    188 
    189 class AllocRecordStackVisitor : public StackVisitor {
    190  public:
    191   AllocRecordStackVisitor(Thread* thread, size_t max_depth, AllocRecordStackTrace* trace_out)
    192       REQUIRES_SHARED(Locks::mutator_lock_)
    193       : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
    194         max_depth_(max_depth),
    195         trace_(trace_out) {}
    196 
    197   // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
    198   // annotalysis.
    199   bool VisitFrame() OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
    200     if (trace_->GetDepth() >= max_depth_) {
    201       return false;
    202     }
    203     ArtMethod* m = GetMethod();
    204     // m may be null if we have inlined methods of unresolved classes. b/27858645
    205     if (m != nullptr && !m->IsRuntimeMethod()) {
    206       m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize);
    207       trace_->AddStackElement(AllocRecordStackTraceElement(m, GetDexPc()));
    208     }
    209     return true;
    210   }
    211 
    212  private:
    213   const size_t max_depth_;
    214   AllocRecordStackTrace* const trace_;
    215 };
    216 
    217 void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
    218   Thread* self = Thread::Current();
    219   Heap* heap = Runtime::Current()->GetHeap();
    220   if (enable) {
    221     {
    222       MutexLock mu(self, *Locks::alloc_tracker_lock_);
    223       if (heap->IsAllocTrackingEnabled()) {
    224         return;  // Already enabled, bail.
    225       }
    226       AllocRecordObjectMap* records = heap->GetAllocationRecords();
    227       if (records == nullptr) {
    228         records = new AllocRecordObjectMap;
    229         heap->SetAllocationRecords(records);
    230       }
    231       CHECK(records != nullptr);
    232       records->SetProperties();
    233       std::string self_name;
    234       self->GetThreadName(self_name);
    235       if (self_name == "JDWP") {
    236         records->alloc_ddm_thread_id_ = self->GetTid();
    237       }
    238       size_t sz = sizeof(AllocRecordStackTraceElement) * records->max_stack_depth_ +
    239                   sizeof(AllocRecord) + sizeof(AllocRecordStackTrace);
    240       LOG(INFO) << "Enabling alloc tracker (" << records->alloc_record_max_ << " entries of "
    241                 << records->max_stack_depth_ << " frames, taking up to "
    242                 << PrettySize(sz * records->alloc_record_max_) << ")";
    243     }
    244     Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
    245     {
    246       MutexLock mu(self, *Locks::alloc_tracker_lock_);
    247       heap->SetAllocTrackingEnabled(true);
    248     }
    249   } else {
    250     // Delete outside of the critical section to avoid possible lock violations like the runtime
    251     // shutdown lock.
    252     {
    253       MutexLock mu(self, *Locks::alloc_tracker_lock_);
    254       if (!heap->IsAllocTrackingEnabled()) {
    255         return;  // Already disabled, bail.
    256       }
    257       heap->SetAllocTrackingEnabled(false);
    258       LOG(INFO) << "Disabling alloc tracker";
    259       AllocRecordObjectMap* records = heap->GetAllocationRecords();
    260       records->Clear();
    261     }
    262     // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
    263     Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
    264   }
    265 }
    266 
    267 void AllocRecordObjectMap::RecordAllocation(Thread* self,
    268                                             ObjPtr<mirror::Object>* obj,
    269                                             size_t byte_count) {
    270   // Get stack trace outside of lock in case there are allocations during the stack walk.
    271   // b/27858645.
    272   AllocRecordStackTrace trace;
    273   AllocRecordStackVisitor visitor(self, max_stack_depth_, /*out*/ &trace);
    274   {
    275     StackHandleScope<1> hs(self);
    276     auto obj_wrapper = hs.NewHandleWrapper(obj);
    277     visitor.WalkStack();
    278   }
    279 
    280   MutexLock mu(self, *Locks::alloc_tracker_lock_);
    281   Heap* const heap = Runtime::Current()->GetHeap();
    282   if (!heap->IsAllocTrackingEnabled()) {
    283     // In the process of shutting down recording, bail.
    284     return;
    285   }
    286 
    287   // Do not record for DDM thread.
    288   if (alloc_ddm_thread_id_ == self->GetTid()) {
    289     return;
    290   }
    291 
    292   // Wait for GC's sweeping to complete and allow new records.
    293   while (UNLIKELY((!kUseReadBarrier && !allow_new_record_) ||
    294                   (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
    295     // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
    296     // presence of threads blocking for weak ref access.
    297     self->CheckEmptyCheckpointFromWeakRefAccess(Locks::alloc_tracker_lock_);
    298     new_record_condition_.WaitHoldingLocks(self);
    299   }
    300 
    301   if (!heap->IsAllocTrackingEnabled()) {
    302     // Return if the allocation tracking has been disabled while waiting for system weak access
    303     // above.
    304     return;
    305   }
    306 
    307   DCHECK_LE(Size(), alloc_record_max_);
    308 
    309   // Erase extra unfilled elements.
    310   trace.SetTid(self->GetTid());
    311 
    312   // Add the record.
    313   Put(obj->Ptr(), AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace)));
    314   DCHECK_LE(Size(), alloc_record_max_);
    315 }
    316 
    317 void AllocRecordObjectMap::Clear() {
    318   entries_.clear();
    319 }
    320 
    321 AllocRecordObjectMap::AllocRecordObjectMap()
    322     : new_record_condition_("New allocation record condition", *Locks::alloc_tracker_lock_) {}
    323 
    324 }  // namespace gc
    325 }  // namespace art
    326