1 /*
2  * Copyright (C) 2011 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 "intern_table.h"
18 
19 #include <memory>
20 
21 #include "gc_root-inl.h"
22 #include "gc/collector/garbage_collector.h"
23 #include "gc/space/image_space.h"
24 #include "gc/weak_root_state.h"
25 #include "image-inl.h"
26 #include "mirror/dex_cache-inl.h"
27 #include "mirror/object_array-inl.h"
28 #include "mirror/object-inl.h"
29 #include "mirror/string-inl.h"
30 #include "thread.h"
31 #include "utf.h"
32 
33 namespace art {
34 
InternTable()35 InternTable::InternTable()
36     : log_new_roots_(false),
37       weak_intern_condition_("New intern condition", *Locks::intern_table_lock_),
38       weak_root_state_(gc::kWeakRootStateNormal) {
39 }
40 
Size() const41 size_t InternTable::Size() const {
42   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
43   return strong_interns_.Size() + weak_interns_.Size();
44 }
45 
StrongSize() const46 size_t InternTable::StrongSize() const {
47   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
48   return strong_interns_.Size();
49 }
50 
WeakSize() const51 size_t InternTable::WeakSize() const {
52   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
53   return weak_interns_.Size();
54 }
55 
DumpForSigQuit(std::ostream & os) const56 void InternTable::DumpForSigQuit(std::ostream& os) const {
57   os << "Intern table: " << StrongSize() << " strong; " << WeakSize() << " weak\n";
58 }
59 
VisitRoots(RootVisitor * visitor,VisitRootFlags flags)60 void InternTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
61   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
62   if ((flags & kVisitRootFlagAllRoots) != 0) {
63     strong_interns_.VisitRoots(visitor);
64   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
65     for (auto& root : new_strong_intern_roots_) {
66       ObjPtr<mirror::String> old_ref = root.Read<kWithoutReadBarrier>();
67       root.VisitRoot(visitor, RootInfo(kRootInternedString));
68       ObjPtr<mirror::String> new_ref = root.Read<kWithoutReadBarrier>();
69       if (new_ref != old_ref) {
70         // The GC moved a root in the log. Need to search the strong interns and update the
71         // corresponding object. This is slow, but luckily for us, this may only happen with a
72         // concurrent moving GC.
73         strong_interns_.Remove(old_ref);
74         strong_interns_.Insert(new_ref);
75       }
76     }
77   }
78   if ((flags & kVisitRootFlagClearRootLog) != 0) {
79     new_strong_intern_roots_.clear();
80   }
81   if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
82     log_new_roots_ = true;
83   } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
84     log_new_roots_ = false;
85   }
86   // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
87 }
88 
LookupWeak(Thread * self,ObjPtr<mirror::String> s)89 ObjPtr<mirror::String> InternTable::LookupWeak(Thread* self, ObjPtr<mirror::String> s) {
90   MutexLock mu(self, *Locks::intern_table_lock_);
91   return LookupWeakLocked(s);
92 }
93 
LookupStrong(Thread * self,ObjPtr<mirror::String> s)94 ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self, ObjPtr<mirror::String> s) {
95   MutexLock mu(self, *Locks::intern_table_lock_);
96   return LookupStrongLocked(s);
97 }
98 
LookupStrong(Thread * self,uint32_t utf16_length,const char * utf8_data)99 ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self,
100                                           uint32_t utf16_length,
101                                           const char* utf8_data) {
102   DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data));
103   Utf8String string(utf16_length,
104                     utf8_data,
105                     ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length));
106   MutexLock mu(self, *Locks::intern_table_lock_);
107   return strong_interns_.Find(string);
108 }
109 
LookupWeakLocked(ObjPtr<mirror::String> s)110 ObjPtr<mirror::String> InternTable::LookupWeakLocked(ObjPtr<mirror::String> s) {
111   return weak_interns_.Find(s);
112 }
113 
LookupStrongLocked(ObjPtr<mirror::String> s)114 ObjPtr<mirror::String> InternTable::LookupStrongLocked(ObjPtr<mirror::String> s) {
115   return strong_interns_.Find(s);
116 }
117 
AddNewTable()118 void InternTable::AddNewTable() {
119   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
120   weak_interns_.AddNewTable();
121   strong_interns_.AddNewTable();
122 }
123 
InsertStrong(ObjPtr<mirror::String> s)124 ObjPtr<mirror::String> InternTable::InsertStrong(ObjPtr<mirror::String> s) {
125   Runtime* runtime = Runtime::Current();
126   if (runtime->IsActiveTransaction()) {
127     runtime->RecordStrongStringInsertion(s);
128   }
129   if (log_new_roots_) {
130     new_strong_intern_roots_.push_back(GcRoot<mirror::String>(s));
131   }
132   strong_interns_.Insert(s);
133   return s;
134 }
135 
InsertWeak(ObjPtr<mirror::String> s)136 ObjPtr<mirror::String> InternTable::InsertWeak(ObjPtr<mirror::String> s) {
137   Runtime* runtime = Runtime::Current();
138   if (runtime->IsActiveTransaction()) {
139     runtime->RecordWeakStringInsertion(s);
140   }
141   weak_interns_.Insert(s);
142   return s;
143 }
144 
RemoveStrong(ObjPtr<mirror::String> s)145 void InternTable::RemoveStrong(ObjPtr<mirror::String> s) {
146   strong_interns_.Remove(s);
147 }
148 
RemoveWeak(ObjPtr<mirror::String> s)149 void InternTable::RemoveWeak(ObjPtr<mirror::String> s) {
150   Runtime* runtime = Runtime::Current();
151   if (runtime->IsActiveTransaction()) {
152     runtime->RecordWeakStringRemoval(s);
153   }
154   weak_interns_.Remove(s);
155 }
156 
157 // Insert/remove methods used to undo changes made during an aborted transaction.
InsertStrongFromTransaction(ObjPtr<mirror::String> s)158 ObjPtr<mirror::String> InternTable::InsertStrongFromTransaction(ObjPtr<mirror::String> s) {
159   DCHECK(!Runtime::Current()->IsActiveTransaction());
160   return InsertStrong(s);
161 }
162 
InsertWeakFromTransaction(ObjPtr<mirror::String> s)163 ObjPtr<mirror::String> InternTable::InsertWeakFromTransaction(ObjPtr<mirror::String> s) {
164   DCHECK(!Runtime::Current()->IsActiveTransaction());
165   return InsertWeak(s);
166 }
167 
RemoveStrongFromTransaction(ObjPtr<mirror::String> s)168 void InternTable::RemoveStrongFromTransaction(ObjPtr<mirror::String> s) {
169   DCHECK(!Runtime::Current()->IsActiveTransaction());
170   RemoveStrong(s);
171 }
172 
RemoveWeakFromTransaction(ObjPtr<mirror::String> s)173 void InternTable::RemoveWeakFromTransaction(ObjPtr<mirror::String> s) {
174   DCHECK(!Runtime::Current()->IsActiveTransaction());
175   RemoveWeak(s);
176 }
177 
AddImagesStringsToTable(const std::vector<gc::space::ImageSpace * > & image_spaces)178 void InternTable::AddImagesStringsToTable(const std::vector<gc::space::ImageSpace*>& image_spaces) {
179   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
180   for (gc::space::ImageSpace* image_space : image_spaces) {
181     const ImageHeader* const header = &image_space->GetImageHeader();
182     // Check if we have the interned strings section.
183     const ImageSection& section = header->GetImageSection(ImageHeader::kSectionInternedStrings);
184     if (section.Size() > 0) {
185       AddTableFromMemoryLocked(image_space->Begin() + section.Offset());
186     }
187   }
188 }
189 
BroadcastForNewInterns()190 void InternTable::BroadcastForNewInterns() {
191   Thread* self = Thread::Current();
192   MutexLock mu(self, *Locks::intern_table_lock_);
193   weak_intern_condition_.Broadcast(self);
194 }
195 
WaitUntilAccessible(Thread * self)196 void InternTable::WaitUntilAccessible(Thread* self) {
197   Locks::intern_table_lock_->ExclusiveUnlock(self);
198   {
199     ScopedThreadSuspension sts(self, kWaitingWeakGcRootRead);
200     MutexLock mu(self, *Locks::intern_table_lock_);
201     while ((!kUseReadBarrier && weak_root_state_ == gc::kWeakRootStateNoReadsOrWrites) ||
202            (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
203       weak_intern_condition_.Wait(self);
204     }
205   }
206   Locks::intern_table_lock_->ExclusiveLock(self);
207 }
208 
Insert(ObjPtr<mirror::String> s,bool is_strong,bool holding_locks)209 ObjPtr<mirror::String> InternTable::Insert(ObjPtr<mirror::String> s,
210                                            bool is_strong,
211                                            bool holding_locks) {
212   if (s == nullptr) {
213     return nullptr;
214   }
215   Thread* const self = Thread::Current();
216   MutexLock mu(self, *Locks::intern_table_lock_);
217   if (kDebugLocking && !holding_locks) {
218     Locks::mutator_lock_->AssertSharedHeld(self);
219     CHECK_EQ(2u, self->NumberOfHeldMutexes()) << "may only safely hold the mutator lock";
220   }
221   while (true) {
222     if (holding_locks) {
223       if (!kUseReadBarrier) {
224         CHECK_EQ(weak_root_state_, gc::kWeakRootStateNormal);
225       } else {
226         CHECK(self->GetWeakRefAccessEnabled());
227       }
228     }
229     // Check the strong table for a match.
230     ObjPtr<mirror::String> strong = LookupStrongLocked(s);
231     if (strong != nullptr) {
232       return strong;
233     }
234     if ((!kUseReadBarrier && weak_root_state_ != gc::kWeakRootStateNoReadsOrWrites) ||
235         (kUseReadBarrier && self->GetWeakRefAccessEnabled())) {
236       break;
237     }
238     // weak_root_state_ is set to gc::kWeakRootStateNoReadsOrWrites in the GC pause but is only
239     // cleared after SweepSystemWeaks has completed. This is why we need to wait until it is
240     // cleared.
241     CHECK(!holding_locks);
242     StackHandleScope<1> hs(self);
243     auto h = hs.NewHandleWrapper(&s);
244     WaitUntilAccessible(self);
245   }
246   if (!kUseReadBarrier) {
247     CHECK_EQ(weak_root_state_, gc::kWeakRootStateNormal);
248   } else {
249     CHECK(self->GetWeakRefAccessEnabled());
250   }
251   // There is no match in the strong table, check the weak table.
252   ObjPtr<mirror::String> weak = LookupWeakLocked(s);
253   if (weak != nullptr) {
254     if (is_strong) {
255       // A match was found in the weak table. Promote to the strong table.
256       RemoveWeak(weak);
257       return InsertStrong(weak);
258     }
259     return weak;
260   }
261   // No match in the strong table or the weak table. Insert into the strong / weak table.
262   return is_strong ? InsertStrong(s) : InsertWeak(s);
263 }
264 
InternStrong(int32_t utf16_length,const char * utf8_data)265 ObjPtr<mirror::String> InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
266   DCHECK(utf8_data != nullptr);
267   Thread* self = Thread::Current();
268   // Try to avoid allocation.
269   ObjPtr<mirror::String> s = LookupStrong(self, utf16_length, utf8_data);
270   if (s != nullptr) {
271     return s;
272   }
273   return InternStrong(mirror::String::AllocFromModifiedUtf8(
274       self, utf16_length, utf8_data));
275 }
276 
InternStrong(const char * utf8_data)277 ObjPtr<mirror::String> InternTable::InternStrong(const char* utf8_data) {
278   DCHECK(utf8_data != nullptr);
279   return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data));
280 }
281 
InternStrongImageString(ObjPtr<mirror::String> s)282 ObjPtr<mirror::String> InternTable::InternStrongImageString(ObjPtr<mirror::String> s) {
283   // May be holding the heap bitmap lock.
284   return Insert(s, true, true);
285 }
286 
InternStrong(ObjPtr<mirror::String> s)287 ObjPtr<mirror::String> InternTable::InternStrong(ObjPtr<mirror::String> s) {
288   return Insert(s, true, false);
289 }
290 
InternWeak(ObjPtr<mirror::String> s)291 ObjPtr<mirror::String> InternTable::InternWeak(ObjPtr<mirror::String> s) {
292   return Insert(s, false, false);
293 }
294 
ContainsWeak(ObjPtr<mirror::String> s)295 bool InternTable::ContainsWeak(ObjPtr<mirror::String> s) {
296   return LookupWeak(Thread::Current(), s) == s;
297 }
298 
SweepInternTableWeaks(IsMarkedVisitor * visitor)299 void InternTable::SweepInternTableWeaks(IsMarkedVisitor* visitor) {
300   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
301   weak_interns_.SweepWeaks(visitor);
302 }
303 
AddTableFromMemory(const uint8_t * ptr)304 size_t InternTable::AddTableFromMemory(const uint8_t* ptr) {
305   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
306   return AddTableFromMemoryLocked(ptr);
307 }
308 
AddTableFromMemoryLocked(const uint8_t * ptr)309 size_t InternTable::AddTableFromMemoryLocked(const uint8_t* ptr) {
310   return strong_interns_.AddTableFromMemory(ptr);
311 }
312 
WriteToMemory(uint8_t * ptr)313 size_t InternTable::WriteToMemory(uint8_t* ptr) {
314   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
315   return strong_interns_.WriteToMemory(ptr);
316 }
317 
operator ()(const GcRoot<mirror::String> & root) const318 std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) const {
319   if (kIsDebugBuild) {
320     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
321   }
322   // An additional cast to prevent undesired sign extension.
323   return static_cast<size_t>(
324       static_cast<uint32_t>(root.Read<kWithoutReadBarrier>()->GetHashCode()));
325 }
326 
operator ()(const GcRoot<mirror::String> & a,const GcRoot<mirror::String> & b) const327 bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
328                                                const GcRoot<mirror::String>& b) const {
329   if (kIsDebugBuild) {
330     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
331   }
332   return a.Read<kWithoutReadBarrier>()->Equals(b.Read<kWithoutReadBarrier>());
333 }
334 
operator ()(const GcRoot<mirror::String> & a,const Utf8String & b) const335 bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
336                                                const Utf8String& b) const {
337   if (kIsDebugBuild) {
338     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
339   }
340   ObjPtr<mirror::String> a_string = a.Read<kWithoutReadBarrier>();
341   uint32_t a_length = static_cast<uint32_t>(a_string->GetLength());
342   if (a_length != b.GetUtf16Length()) {
343     return false;
344   }
345   if (a_string->IsCompressed()) {
346     size_t b_byte_count = strlen(b.GetUtf8Data());
347     size_t b_utf8_length = CountModifiedUtf8Chars(b.GetUtf8Data(), b_byte_count);
348     // Modified UTF-8 single byte character range is 0x01 .. 0x7f
349     // The string compression occurs on regular ASCII with same exact range,
350     // not on extended ASCII which up to 0xff
351     const bool is_b_regular_ascii = (b_byte_count == b_utf8_length);
352     if (is_b_regular_ascii) {
353       return memcmp(b.GetUtf8Data(),
354                     a_string->GetValueCompressed(), a_length * sizeof(uint8_t)) == 0;
355     } else {
356       return false;
357     }
358   } else {
359     const uint16_t* a_value = a_string->GetValue();
360     return CompareModifiedUtf8ToUtf16AsCodePointValues(b.GetUtf8Data(), a_value, a_length) == 0;
361   }
362 }
363 
AddTableFromMemory(const uint8_t * ptr)364 size_t InternTable::Table::AddTableFromMemory(const uint8_t* ptr) {
365   size_t read_count = 0;
366   UnorderedSet set(ptr, /*make copy*/false, &read_count);
367   if (set.Empty()) {
368     // Avoid inserting empty sets.
369     return read_count;
370   }
371   // TODO: Disable this for app images if app images have intern tables.
372   static constexpr bool kCheckDuplicates = true;
373   if (kCheckDuplicates) {
374     for (GcRoot<mirror::String>& string : set) {
375       CHECK(Find(string.Read()) == nullptr) << "Already found " << string.Read()->ToModifiedUtf8();
376     }
377   }
378   // Insert at the front since we add new interns into the back.
379   tables_.insert(tables_.begin(), std::move(set));
380   return read_count;
381 }
382 
WriteToMemory(uint8_t * ptr)383 size_t InternTable::Table::WriteToMemory(uint8_t* ptr) {
384   if (tables_.empty()) {
385     return 0;
386   }
387   UnorderedSet* table_to_write;
388   UnorderedSet combined;
389   if (tables_.size() > 1) {
390     table_to_write = &combined;
391     for (UnorderedSet& table : tables_) {
392       for (GcRoot<mirror::String>& string : table) {
393         combined.Insert(string);
394       }
395     }
396   } else {
397     table_to_write = &tables_.back();
398   }
399   return table_to_write->WriteToMemory(ptr);
400 }
401 
Remove(ObjPtr<mirror::String> s)402 void InternTable::Table::Remove(ObjPtr<mirror::String> s) {
403   for (UnorderedSet& table : tables_) {
404     auto it = table.Find(GcRoot<mirror::String>(s));
405     if (it != table.end()) {
406       table.Erase(it);
407       return;
408     }
409   }
410   LOG(FATAL) << "Attempting to remove non-interned string " << s->ToModifiedUtf8();
411 }
412 
Find(ObjPtr<mirror::String> s)413 ObjPtr<mirror::String> InternTable::Table::Find(ObjPtr<mirror::String> s) {
414   Locks::intern_table_lock_->AssertHeld(Thread::Current());
415   for (UnorderedSet& table : tables_) {
416     auto it = table.Find(GcRoot<mirror::String>(s));
417     if (it != table.end()) {
418       return it->Read();
419     }
420   }
421   return nullptr;
422 }
423 
Find(const Utf8String & string)424 ObjPtr<mirror::String> InternTable::Table::Find(const Utf8String& string) {
425   Locks::intern_table_lock_->AssertHeld(Thread::Current());
426   for (UnorderedSet& table : tables_) {
427     auto it = table.Find(string);
428     if (it != table.end()) {
429       return it->Read();
430     }
431   }
432   return nullptr;
433 }
434 
AddNewTable()435 void InternTable::Table::AddNewTable() {
436   tables_.push_back(UnorderedSet());
437 }
438 
Insert(ObjPtr<mirror::String> s)439 void InternTable::Table::Insert(ObjPtr<mirror::String> s) {
440   // Always insert the last table, the image tables are before and we avoid inserting into these
441   // to prevent dirty pages.
442   DCHECK(!tables_.empty());
443   tables_.back().Insert(GcRoot<mirror::String>(s));
444 }
445 
VisitRoots(RootVisitor * visitor)446 void InternTable::Table::VisitRoots(RootVisitor* visitor) {
447   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
448       visitor, RootInfo(kRootInternedString));
449   for (UnorderedSet& table : tables_) {
450     for (auto& intern : table) {
451       buffered_visitor.VisitRoot(intern);
452     }
453   }
454 }
455 
SweepWeaks(IsMarkedVisitor * visitor)456 void InternTable::Table::SweepWeaks(IsMarkedVisitor* visitor) {
457   for (UnorderedSet& table : tables_) {
458     SweepWeaks(&table, visitor);
459   }
460 }
461 
SweepWeaks(UnorderedSet * set,IsMarkedVisitor * visitor)462 void InternTable::Table::SweepWeaks(UnorderedSet* set, IsMarkedVisitor* visitor) {
463   for (auto it = set->begin(), end = set->end(); it != end;) {
464     // This does not need a read barrier because this is called by GC.
465     mirror::Object* object = it->Read<kWithoutReadBarrier>();
466     mirror::Object* new_object = visitor->IsMarked(object);
467     if (new_object == nullptr) {
468       it = set->Erase(it);
469     } else {
470       *it = GcRoot<mirror::String>(new_object->AsString());
471       ++it;
472     }
473   }
474 }
475 
Size() const476 size_t InternTable::Table::Size() const {
477   return std::accumulate(tables_.begin(),
478                          tables_.end(),
479                          0U,
480                          [](size_t sum, const UnorderedSet& set) {
481                            return sum + set.Size();
482                          });
483 }
484 
ChangeWeakRootState(gc::WeakRootState new_state)485 void InternTable::ChangeWeakRootState(gc::WeakRootState new_state) {
486   MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
487   ChangeWeakRootStateLocked(new_state);
488 }
489 
ChangeWeakRootStateLocked(gc::WeakRootState new_state)490 void InternTable::ChangeWeakRootStateLocked(gc::WeakRootState new_state) {
491   CHECK(!kUseReadBarrier);
492   weak_root_state_ = new_state;
493   if (new_state != gc::kWeakRootStateNoReadsOrWrites) {
494     weak_intern_condition_.Broadcast(Thread::Current());
495   }
496 }
497 
Table()498 InternTable::Table::Table() {
499   Runtime* const runtime = Runtime::Current();
500   // Initial table.
501   tables_.push_back(UnorderedSet());
502   tables_.back().SetLoadFactor(runtime->GetHashTableMinLoadFactor(),
503                                runtime->GetHashTableMaxLoadFactor());
504 }
505 
506 }  // namespace art
507