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