blob: a3853634285f360a1a468083b61fd7255799b571 [file] [log] [blame]
Man Cao8c2ff642015-05-27 17:25:30 -07001/*
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 HAVE_ANDROID_OS
24#include "cutils/properties.h"
25#endif
26
27namespace art {
28namespace gc {
29
30int32_t AllocRecordStackTraceElement::ComputeLineNumber() const {
31 DCHECK(method_ != nullptr);
32 return method_->GetLineNumFromDexPC(dex_pc_);
33}
34
35void AllocRecordObjectMap::SetProperties() {
36#ifdef HAVE_ANDROID_OS
37 // Check whether there's a system property overriding the max number of records.
38 const char* propertyName = "dalvik.vm.allocTrackerMax";
39 char allocMaxString[PROPERTY_VALUE_MAX];
40 if (property_get(propertyName, allocMaxString, "") > 0) {
41 char* end;
42 size_t value = strtoul(allocMaxString, &end, 10);
43 if (*end != '\0') {
44 LOG(ERROR) << "Ignoring " << propertyName << " '" << allocMaxString
45 << "' --- invalid";
46 } else {
47 alloc_record_max_ = value;
48 }
49 }
50 // Check whether there's a system property overriding the max depth of stack trace.
51 propertyName = "dalvik.vm.allocStackDepth";
52 char stackDepthString[PROPERTY_VALUE_MAX];
53 if (property_get(propertyName, stackDepthString, "") > 0) {
54 char* end;
55 size_t value = strtoul(stackDepthString, &end, 10);
56 if (*end != '\0') {
57 LOG(ERROR) << "Ignoring " << propertyName << " '" << stackDepthString
58 << "' --- invalid";
59 } else {
60 max_stack_depth_ = value;
61 }
62 }
63#endif
64}
65
66AllocRecordObjectMap::~AllocRecordObjectMap() {
67 STLDeleteValues(&entries_);
68}
69
70void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedCallback* callback, void* arg) {
71 VLOG(heap) << "Start SweepAllocationRecords()";
72 size_t count_deleted = 0, count_moved = 0;
73 for (auto it = entries_.begin(), end = entries_.end(); it != end;) {
74 // This does not need a read barrier because this is called by GC.
75 mirror::Object* old_object = it->first.Read<kWithoutReadBarrier>();
76 AllocRecord* record = it->second;
77 mirror::Object* new_object = callback(old_object, arg);
78 if (new_object == nullptr) {
79 delete record;
80 it = entries_.erase(it);
81 ++count_deleted;
82 } else {
83 if (old_object != new_object) {
84 it->first = GcRoot<mirror::Object>(new_object);
85 ++count_moved;
86 }
87 ++it;
88 }
89 }
90 VLOG(heap) << "Deleted " << count_deleted << " allocation records";
91 VLOG(heap) << "Updated " << count_moved << " allocation records";
92}
93
94struct AllocRecordStackVisitor : public StackVisitor {
95 AllocRecordStackVisitor(Thread* thread, AllocRecordStackTrace* trace_in, size_t max)
96 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
97 : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
98 trace(trace_in),
99 depth(0),
100 max_depth(max) {}
101
102 // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
103 // annotalysis.
104 bool VisitFrame() OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
105 if (depth >= max_depth) {
106 return false;
107 }
108 ArtMethod* m = GetMethod();
109 if (!m->IsRuntimeMethod()) {
110 trace->SetStackElementAt(depth, m, GetDexPc());
111 ++depth;
112 }
113 return true;
114 }
115
116 ~AllocRecordStackVisitor() {
117 trace->SetDepth(depth);
118 }
119
120 AllocRecordStackTrace* trace;
121 size_t depth;
122 const size_t max_depth;
123};
124
125void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
126 Thread* self = Thread::Current();
127 Heap* heap = Runtime::Current()->GetHeap();
128 if (enable) {
129 {
130 MutexLock mu(self, *Locks::alloc_tracker_lock_);
131 if (heap->IsAllocTrackingEnabled()) {
132 return; // Already enabled, bail.
133 }
134 AllocRecordObjectMap* records = new AllocRecordObjectMap();
135 CHECK(records != nullptr);
136 records->SetProperties();
137 std::string self_name;
138 self->GetThreadName(self_name);
139 if (self_name == "JDWP") {
140 records->alloc_ddm_thread_id_ = self->GetTid();
141 }
142 size_t sz = sizeof(AllocRecordStackTraceElement) * records->max_stack_depth_ +
143 sizeof(AllocRecord) + sizeof(AllocRecordStackTrace);
144 LOG(INFO) << "Enabling alloc tracker (" << records->alloc_record_max_ << " entries of "
145 << records->max_stack_depth_ << " frames, taking up to "
146 << PrettySize(sz * records->alloc_record_max_) << ")";
147 heap->SetAllocationRecords(records);
148 heap->SetAllocTrackingEnabled(true);
149 }
150 Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
151 } else {
152 {
153 MutexLock mu(self, *Locks::alloc_tracker_lock_);
154 if (!heap->IsAllocTrackingEnabled()) {
155 return; // Already disabled, bail.
156 }
157 heap->SetAllocTrackingEnabled(false);
158 LOG(INFO) << "Disabling alloc tracker";
159 heap->SetAllocationRecords(nullptr);
160 }
161 // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
162 Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
163 }
164}
165
166void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, size_t byte_count) {
167 MutexLock mu(self, *Locks::alloc_tracker_lock_);
168 Heap* heap = Runtime::Current()->GetHeap();
169 if (!heap->IsAllocTrackingEnabled()) {
170 // In the process of shutting down recording, bail.
171 return;
172 }
173
174 AllocRecordObjectMap* records = heap->GetAllocationRecords();
175 DCHECK(records != nullptr);
176
177 // Do not record for DDM thread
178 if (records->alloc_ddm_thread_id_ == self->GetTid()) {
179 return;
180 }
181
182 DCHECK_LE(records->Size(), records->alloc_record_max_);
183
184 // Remove oldest record.
185 if (records->Size() == records->alloc_record_max_) {
186 records->RemoveOldest();
187 }
188
189 // Get stack trace.
190 const size_t max_depth = records->max_stack_depth_;
191 AllocRecordStackTrace* trace = new AllocRecordStackTrace(self->GetTid(), max_depth);
192 // add scope to make "visitor" destroyed promptly, in order to set the trace->depth_
193 {
194 AllocRecordStackVisitor visitor(self, trace, max_depth);
195 visitor.WalkStack();
196 }
197
198 // Fill in the basics.
199 AllocRecord* record = new AllocRecord(byte_count, trace);
200
201 records->Put(obj, record);
202 DCHECK_LE(records->Size(), records->alloc_record_max_);
203}
204
205} // namespace gc
206} // namespace art