blob: 3f0c5d50565dec47f6f64e779a5220601d6d63ad [file] [log] [blame]
Elliott Hughes11e45072011-08-16 17:40:46 -07001/*
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 "object.h"
20
21namespace art {
22
23ReferenceTable::ReferenceTable(const char* name,
24 size_t initial_size, size_t max_size)
25 : name_(name), max_size_(max_size) {
26 CHECK_LE(initial_size, max_size);
27 entries_.reserve(initial_size);
28}
29
30void ReferenceTable::Add(Object* obj) {
31 DCHECK(obj != NULL);
32 if (entries_.size() == max_size_) {
33 LOG(FATAL) << "ReferenceTable '" << name_ << "' "
34 << "overflowed (" << max_size_ << " entries)";
35 }
36 entries_.push_back(obj);
37}
38
39void ReferenceTable::Remove(Object* obj) {
40 // We iterate backwards on the assumption that references are LIFO.
41 for (int i = entries_.size() - 1; i >= 0; --i) {
42 if (entries_[i] == obj) {
43 entries_.erase(entries_.begin() + i);
44 return;
45 }
46 }
47}
48
49/*
50 * If "obj" is an array, return the number of elements in the array.
51 * Otherwise, return zero.
52 */
53size_t GetElementCount(const Object* obj) {
54 if (obj == NULL || obj == kClearedJniWeakGlobal || !obj->IsArray()) {
55 return 0;
56 }
57 return obj->AsArray()->GetLength();
58}
59
60struct ObjectComparator {
61 bool operator()(Object* obj1, Object* obj2){
62 // Ensure null references and cleared jweaks appear at the end.
63 if (obj1 == NULL) {
64 return true;
65 } else if (obj2 == NULL) {
66 return false;
67 }
68 if (obj1 == kClearedJniWeakGlobal) {
69 return true;
70 } else if (obj2 == kClearedJniWeakGlobal) {
71 return false;
72 }
73
74 // Sort by class...
75 if (obj1->GetClass() != obj2->GetClass()) {
76 return reinterpret_cast<uintptr_t>(obj1->GetClass()) <
77 reinterpret_cast<uintptr_t>(obj2->GetClass());
78 } else {
79 // ...then by size...
80 size_t count1 = obj1->SizeOf();
81 size_t count2 = obj2->SizeOf();
82 if (count1 != count2) {
83 return count1 < count2;
84 } else {
85 // ...and finally by address.
86 return reinterpret_cast<uintptr_t>(obj1) <
87 reinterpret_cast<uintptr_t>(obj2);
88 }
89 }
90 }
91};
92
93/*
94 * Log an object with some additional info.
95 *
96 * Pass in the number of elements in the array (or 0 if this is not an
97 * array object), and the number of additional objects that are identical
98 * or equivalent to the original.
99 */
100void LogSummaryLine(const Object* obj, size_t elems, int identical, int equiv) {
101 if (obj == NULL) {
102 LOG(WARNING) << " NULL reference (count=" << equiv << ")";
103 return;
104 }
105 if (obj == kClearedJniWeakGlobal) {
106 LOG(WARNING) << " cleared jweak (count=" << equiv << ")";
107 return;
108 }
109
110 std::string className(PrettyType(obj));
111 if (obj->IsClass()) {
112 // We're summarizing multiple instances, so using the exemplar
113 // Class' type parameter here would be misleading.
114 className = "java.lang.Class";
115 }
116 if (elems != 0) {
117 StringAppendF(&className, " (%zd elements)", elems);
118 }
119
120 size_t total = identical + equiv + 1;
121 std::string msg(StringPrintf("%5d of %s", total, className.c_str()));
122 if (identical + equiv != 0) {
123 StringAppendF(&msg, " (%d unique instances)", equiv + 1);
124 }
125 LOG(WARNING) << " " << msg;
126}
127
128size_t ReferenceTable::Size() const {
129 return entries_.size();
130}
131
132/*
133 * Dump a summary of an array of references to the log file.
134 *
135 * This is used to dump the contents of ReferenceTable and IndirectRefTable
136 * structs.
137 */
138void ReferenceTable::Dump() const {
139 LOG(WARNING) << name_ << " reference table dump:";
140
141 if (entries_.empty()) {
142 LOG(WARNING) << " (empty)";
143 return;
144 }
145
146 // Dump the most recent N entries.
147 const size_t kLast = 10;
148 size_t count = entries_.size();
149 int first = count - kLast;
150 if (first < 0) {
151 first = 0;
152 }
153 LOG(WARNING) << " Last " << (count - first) << " entries (of " << count << "):";
154 for (int idx = count - 1; idx >= first; --idx) {
155 const Object* ref = entries_[idx];
156 if (ref == NULL) {
157 continue;
158 }
159 if (ref == kClearedJniWeakGlobal) {
160 LOG(WARNING) << StringPrintf(" %5d: cleared jweak", idx);
161 continue;
162 }
163 if (ref->GetClass() == NULL) {
164 // should only be possible right after a plain dvmMalloc().
165 size_t size = ref->SizeOf();
166 LOG(WARNING) << StringPrintf(" %5d: %p (raw) (%zd bytes)", idx, ref, size);
167 continue;
168 }
169
170 std::string className(PrettyType(ref));
171
172 std::string extras;
173 size_t elems = GetElementCount(ref);
174 if (elems != 0) {
175 StringAppendF(&extras, " (%zd elements)", elems);
176 }
177#if 0
178 // TODO: support dumping string data.
179 else if (ref->GetClass() == gDvm.classJavaLangString) {
180 const StringObject* str = reinterpret_cast<const StringObject*>(ref);
181 extras += " \"";
182 size_t count = 0;
183 char* s = dvmCreateCstrFromString(str);
184 char* p = s;
185 for (; *p && count < 16; ++p, ++count) {
186 extras += *p;
187 }
188 if (*p == 0) {
189 extras += "\"";
190 } else {
191 StringAppendF(&extras, "... (%d chars)", str->length());
192 }
193 free(s);
194 }
195#endif
196 LOG(WARNING) << StringPrintf(" %5d: ", idx) << ref << " " << className << extras;
197 }
198
199 // Make a copy of the table and sort it.
200 std::vector<Object*> sorted_entries(entries_.begin(), entries_.end());
201 std::sort(sorted_entries.begin(), sorted_entries.end(), ObjectComparator());
202
203 // Remove any uninteresting stuff from the list. The sort moved them all to the end.
204 while (!sorted_entries.empty() && sorted_entries.back() == NULL) {
205 sorted_entries.pop_back();
206 }
207 while (!sorted_entries.empty() && sorted_entries.back() == kClearedJniWeakGlobal) {
208 sorted_entries.pop_back();
209 }
210 if (sorted_entries.empty()) {
211 return;
212 }
213
214 // Dump a summary of the whole table.
215 LOG(WARNING) << " Summary:";
216 size_t equiv = 0;
217 size_t identical = 0;
218 for (size_t idx = 1; idx < count; idx++) {
219 Object* prev = sorted_entries[idx-1];
220 Object* current = sorted_entries[idx];
221 size_t elems = GetElementCount(prev);
222 if (current == prev) {
223 // Same reference, added more than once.
224 identical++;
225 } else if (current->GetClass() == prev->GetClass() && GetElementCount(current) == elems) {
226 // Same class / element count, different object.
227 equiv++;
228 } else {
229 // Different class.
230 LogSummaryLine(prev, elems, identical, equiv);
231 equiv = identical = 0;
232 }
233 }
234 // Handle the last entry.
235 LogSummaryLine(sorted_entries.back(), GetElementCount(sorted_entries.back()), identical, equiv);
236}
237
238} // namespace art