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
ComputeLineNumber() const30 int32_t AllocRecordStackTraceElement::ComputeLineNumber() const {
31 DCHECK(method_ != nullptr);
32 return method_->GetLineNumFromDexPC(dex_pc_);
33 }
34
GetClassDescriptor(std::string * storage) const35 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
SetProperties()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
~AllocRecordObjectMap()94 AllocRecordObjectMap::~AllocRecordObjectMap() {
95 Clear();
96 }
97
VisitRoots(RootVisitor * visitor)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
SweepClassObject(AllocRecord * record,IsMarkedVisitor * visitor)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
SweepAllocationRecords(IsMarkedVisitor * visitor)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
AllowNewAllocationRecords()170 void AllocRecordObjectMap::AllowNewAllocationRecords() {
171 CHECK(!kUseReadBarrier);
172 allow_new_record_ = true;
173 new_record_condition_.Broadcast(Thread::Current());
174 }
175
DisallowNewAllocationRecords()176 void AllocRecordObjectMap::DisallowNewAllocationRecords() {
177 CHECK(!kUseReadBarrier);
178 allow_new_record_ = false;
179 }
180
BroadcastForNewAllocationRecords()181 void AllocRecordObjectMap::BroadcastForNewAllocationRecords() {
182 CHECK(kUseReadBarrier);
183 new_record_condition_.Broadcast(Thread::Current());
184 }
185
186 class AllocRecordStackVisitor : public StackVisitor {
187 public:
AllocRecordStackVisitor(Thread * thread,size_t max_depth,AllocRecordStackTrace * trace_out)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.
VisitFrame()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
SetAllocTrackingEnabled(bool enable)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
RecordAllocation(Thread * self,mirror::Object ** obj,size_t byte_count)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
Clear()311 void AllocRecordObjectMap::Clear() {
312 entries_.clear();
313 }
314
AllocRecordObjectMap()315 AllocRecordObjectMap::AllocRecordObjectMap()
316 : new_record_condition_("New allocation record condition", *Locks::alloc_tracker_lock_) {}
317
318 } // namespace gc
319 } // namespace art
320