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