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