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