1 /*
2 * Copyright (C) 2008 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 "reference_table.h"
18
19 #include "android-base/stringprintf.h"
20
21 #include "base/mutex.h"
22 #include "indirect_reference_table.h"
23 #include "mirror/array.h"
24 #include "mirror/array-inl.h"
25 #include "mirror/class.h"
26 #include "mirror/class-inl.h"
27 #include "mirror/object-inl.h"
28 #include "mirror/string-inl.h"
29 #include "runtime-inl.h"
30 #include "thread.h"
31 #include "utils.h"
32
33 namespace art {
34
35 using android::base::StringAppendF;
36 using android::base::StringPrintf;
37
ReferenceTable(const char * name,size_t initial_size,size_t max_size)38 ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max_size)
39 : name_(name), max_size_(max_size) {
40 CHECK_LE(initial_size, max_size);
41 entries_.reserve(initial_size);
42 }
43
~ReferenceTable()44 ReferenceTable::~ReferenceTable() {
45 }
46
Add(ObjPtr<mirror::Object> obj)47 void ReferenceTable::Add(ObjPtr<mirror::Object> obj) {
48 DCHECK(obj != nullptr);
49 VerifyObject(obj);
50 if (entries_.size() >= max_size_) {
51 LOG(FATAL) << "ReferenceTable '" << name_ << "' "
52 << "overflowed (" << max_size_ << " entries)";
53 }
54 entries_.push_back(GcRoot<mirror::Object>(obj));
55 }
56
Remove(ObjPtr<mirror::Object> obj)57 void ReferenceTable::Remove(ObjPtr<mirror::Object> obj) {
58 // We iterate backwards on the assumption that references are LIFO.
59 for (int i = entries_.size() - 1; i >= 0; --i) {
60 ObjPtr<mirror::Object> entry = entries_[i].Read();
61 if (entry == obj) {
62 entries_.erase(entries_.begin() + i);
63 return;
64 }
65 }
66 }
67
68 // If "obj" is an array, return the number of elements in the array.
69 // Otherwise, return zero.
GetElementCount(ObjPtr<mirror::Object> obj)70 static size_t GetElementCount(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
71 // We assume the special cleared value isn't an array in the if statement below.
72 DCHECK(!Runtime::Current()->GetClearedJniWeakGlobal()->IsArrayInstance());
73 if (obj == nullptr || !obj->IsArrayInstance()) {
74 return 0;
75 }
76 return obj->AsArray()->GetLength();
77 }
78
79 // Log an object with some additional info.
80 //
81 // Pass in the number of elements in the array (or 0 if this is not an
82 // array object), and the number of additional objects that are identical
83 // or equivalent to the original.
DumpSummaryLine(std::ostream & os,ObjPtr<mirror::Object> obj,size_t element_count,int identical,int equiv)84 static void DumpSummaryLine(std::ostream& os, ObjPtr<mirror::Object> obj, size_t element_count,
85 int identical, int equiv)
86 REQUIRES_SHARED(Locks::mutator_lock_) {
87 if (obj == nullptr) {
88 os << " null reference (count=" << equiv << ")\n";
89 return;
90 }
91 if (Runtime::Current()->IsClearedJniWeakGlobal(obj)) {
92 os << " cleared jweak (count=" << equiv << ")\n";
93 return;
94 }
95
96 std::string className(obj->PrettyTypeOf());
97 if (obj->IsClass()) {
98 // We're summarizing multiple instances, so using the exemplar
99 // Class' type parameter here would be misleading.
100 className = "java.lang.Class";
101 }
102 if (element_count != 0) {
103 StringAppendF(&className, " (%zd elements)", element_count);
104 }
105
106 size_t total = identical + equiv + 1;
107 std::string msg(StringPrintf("%5zd of %s", total, className.c_str()));
108 if (identical + equiv != 0) {
109 StringAppendF(&msg, " (%d unique instances)", equiv + 1);
110 }
111 os << " " << msg << "\n";
112 }
113
Size() const114 size_t ReferenceTable::Size() const {
115 return entries_.size();
116 }
117
Dump(std::ostream & os)118 void ReferenceTable::Dump(std::ostream& os) {
119 os << name_ << " reference table dump:\n";
120 Dump(os, entries_);
121 }
122
Dump(std::ostream & os,Table & entries)123 void ReferenceTable::Dump(std::ostream& os, Table& entries) {
124 // Compare GC roots, first by class, then size, then address.
125 struct GcRootComparator {
126 bool operator()(GcRoot<mirror::Object> root1, GcRoot<mirror::Object> root2) const
127 // TODO: enable analysis when analysis can work with the STL.
128 NO_THREAD_SAFETY_ANALYSIS {
129 Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
130 // These GC roots are already forwarded in ReferenceTable::Dump. We sort by class since there
131 // are no suspend points which can happen during the sorting process. This works since
132 // we are guaranteed that the addresses of obj1, obj2, obj1->GetClass, obj2->GetClass wont
133 // change during the sorting process. The classes are forwarded by ref->GetClass().
134 ObjPtr<mirror::Object> obj1 = root1.Read<kWithoutReadBarrier>();
135 ObjPtr<mirror::Object> obj2 = root2.Read<kWithoutReadBarrier>();
136 DCHECK(obj1 != nullptr);
137 DCHECK(obj2 != nullptr);
138 Runtime* runtime = Runtime::Current();
139 DCHECK(!runtime->IsClearedJniWeakGlobal(obj1));
140 DCHECK(!runtime->IsClearedJniWeakGlobal(obj2));
141 // Sort by class...
142 if (obj1->GetClass() != obj2->GetClass()) {
143 return obj1->GetClass() < obj2->GetClass();
144 }
145 // ...then by size...
146 const size_t size1 = obj1->SizeOf();
147 const size_t size2 = obj2->SizeOf();
148 if (size1 != size2) {
149 return size1 < size2;
150 }
151 // ...and finally by address.
152 return obj1.Ptr() < obj2.Ptr();
153 }
154 };
155
156 if (entries.empty()) {
157 os << " (empty)\n";
158 return;
159 }
160
161 // Dump the most recent N entries.
162 const size_t kLast = 10;
163 size_t count = entries.size();
164 int first = count - kLast;
165 if (first < 0) {
166 first = 0;
167 }
168 os << " Last " << (count - first) << " entries (of " << count << "):\n";
169 Runtime* runtime = Runtime::Current();
170 for (int idx = count - 1; idx >= first; --idx) {
171 ObjPtr<mirror::Object> ref = entries[idx].Read();
172 if (ref == nullptr) {
173 continue;
174 }
175 if (runtime->IsClearedJniWeakGlobal(ref)) {
176 os << StringPrintf(" %5d: cleared jweak\n", idx);
177 continue;
178 }
179 if (ref->GetClass() == nullptr) {
180 // should only be possible right after a plain dvmMalloc().
181 size_t size = ref->SizeOf();
182 os << StringPrintf(" %5d: %p (raw) (%zd bytes)\n", idx, ref.Ptr(), size);
183 continue;
184 }
185
186 std::string className(ref->PrettyTypeOf());
187
188 std::string extras;
189 size_t element_count = GetElementCount(ref);
190 if (element_count != 0) {
191 StringAppendF(&extras, " (%zd elements)", element_count);
192 } else if (ref->GetClass()->IsStringClass()) {
193 ObjPtr<mirror::String> s = ref->AsString();
194 std::string utf8(s->ToModifiedUtf8());
195 if (s->GetLength() <= 16) {
196 StringAppendF(&extras, " \"%s\"", utf8.c_str());
197 } else {
198 StringAppendF(&extras, " \"%.16s... (%d chars)", utf8.c_str(), s->GetLength());
199 }
200 } else if (ref->IsReferenceInstance()) {
201 ObjPtr<mirror::Object> referent = ref->AsReference()->GetReferent();
202 if (referent == nullptr) {
203 extras = " (referent is null)";
204 } else {
205 extras = StringPrintf(" (referent is a %s)", referent->PrettyTypeOf().c_str());
206 }
207 }
208 os << StringPrintf(" %5d: ", idx) << ref << " " << className << extras << "\n";
209 }
210
211 // Make a copy of the table and sort it, only adding non null and not cleared elements.
212 Table sorted_entries;
213 for (GcRoot<mirror::Object>& root : entries) {
214 if (!root.IsNull() && !runtime->IsClearedJniWeakGlobal(root.Read())) {
215 sorted_entries.push_back(root);
216 }
217 }
218 if (sorted_entries.empty()) {
219 return;
220 }
221 std::sort(sorted_entries.begin(), sorted_entries.end(), GcRootComparator());
222
223 class SummaryElement {
224 public:
225 GcRoot<mirror::Object> root;
226 size_t equiv;
227 size_t identical;
228
229 SummaryElement() : equiv(0), identical(0) {}
230 SummaryElement(SummaryElement&& ref) {
231 root = ref.root;
232 equiv = ref.equiv;
233 identical = ref.identical;
234 }
235 SummaryElement(const SummaryElement&) = default;
236 SummaryElement& operator=(SummaryElement&&) = default;
237
238 void Reset(GcRoot<mirror::Object>& _root) {
239 root = _root;
240 equiv = 0;
241 identical = 0;
242 }
243 };
244 std::vector<SummaryElement> sorted_summaries;
245 {
246 SummaryElement prev;
247
248 for (GcRoot<mirror::Object>& root : sorted_entries) {
249 ObjPtr<mirror::Object> current = root.Read<kWithoutReadBarrier>();
250
251 if (UNLIKELY(prev.root.IsNull())) {
252 prev.Reset(root);
253 continue;
254 }
255
256 ObjPtr<mirror::Object> prevObj = prev.root.Read<kWithoutReadBarrier>();
257 if (current == prevObj) {
258 // Same reference, added more than once.
259 ++prev.identical;
260 } else if (current->GetClass() == prevObj->GetClass() &&
261 GetElementCount(current) == GetElementCount(prevObj)) {
262 // Same class / element count, different object.
263 ++prev.equiv;
264 } else {
265 sorted_summaries.push_back(prev);
266 prev.Reset(root);
267 }
268 prev.root = root;
269 }
270 sorted_summaries.push_back(prev);
271
272 // Compare summary elements, first by combined count, then by identical (indicating leaks),
273 // then by class (and size and address).
274 struct SummaryElementComparator {
275 GcRootComparator gc_root_cmp;
276
277 bool operator()(SummaryElement& elem1, SummaryElement& elem2) const
278 NO_THREAD_SAFETY_ANALYSIS {
279 Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
280
281 size_t count1 = elem1.equiv + elem1.identical;
282 size_t count2 = elem2.equiv + elem2.identical;
283 if (count1 != count2) {
284 return count1 > count2;
285 }
286
287 if (elem1.identical != elem2.identical) {
288 return elem1.identical > elem2.identical;
289 }
290
291 // Otherwise, compare the GC roots as before.
292 return gc_root_cmp(elem1.root, elem2.root);
293 }
294 };
295 std::sort(sorted_summaries.begin(), sorted_summaries.end(), SummaryElementComparator());
296 }
297
298 // Dump a summary of the whole table.
299 os << " Summary:\n";
300 for (SummaryElement& elem : sorted_summaries) {
301 ObjPtr<mirror::Object> elemObj = elem.root.Read<kWithoutReadBarrier>();
302 DumpSummaryLine(os, elemObj, GetElementCount(elemObj), elem.identical, elem.equiv);
303 }
304 }
305
VisitRoots(RootVisitor * visitor,const RootInfo & root_info)306 void ReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
307 BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(visitor, root_info);
308 for (GcRoot<mirror::Object>& root : entries_) {
309 buffered_visitor.VisitRoot(root);
310 }
311 }
312
313 } // namespace art
314