1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include "local_reference_table-inl.h"
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include "base/bit_utils.h"
20*795d594fSAndroid Build Coastguard Worker #include "base/casts.h"
21*795d594fSAndroid Build Coastguard Worker #include "base/globals.h"
22*795d594fSAndroid Build Coastguard Worker #include "base/mutator_locked_dumpable.h"
23*795d594fSAndroid Build Coastguard Worker #include "base/systrace.h"
24*795d594fSAndroid Build Coastguard Worker #include "base/utils.h"
25*795d594fSAndroid Build Coastguard Worker #include "indirect_reference_table.h"
26*795d594fSAndroid Build Coastguard Worker #include "jni/java_vm_ext.h"
27*795d594fSAndroid Build Coastguard Worker #include "jni/jni_internal.h"
28*795d594fSAndroid Build Coastguard Worker #include "mirror/object-inl.h"
29*795d594fSAndroid Build Coastguard Worker #include "nth_caller_visitor.h"
30*795d594fSAndroid Build Coastguard Worker #include "reference_table.h"
31*795d594fSAndroid Build Coastguard Worker #include "runtime-inl.h"
32*795d594fSAndroid Build Coastguard Worker #include "scoped_thread_state_change-inl.h"
33*795d594fSAndroid Build Coastguard Worker #include "thread.h"
34*795d594fSAndroid Build Coastguard Worker
35*795d594fSAndroid Build Coastguard Worker #include <cstdlib>
36*795d594fSAndroid Build Coastguard Worker
37*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
38*795d594fSAndroid Build Coastguard Worker namespace jni {
39*795d594fSAndroid Build Coastguard Worker
40*795d594fSAndroid Build Coastguard Worker static constexpr bool kDumpStackOnNonLocalReference = false;
41*795d594fSAndroid Build Coastguard Worker
42*795d594fSAndroid Build Coastguard Worker // Mmap an "indirect ref table region. Table_bytes is a multiple of a page size.
NewLRTMap(size_t table_bytes,std::string * error_msg)43*795d594fSAndroid Build Coastguard Worker static inline MemMap NewLRTMap(size_t table_bytes, std::string* error_msg) {
44*795d594fSAndroid Build Coastguard Worker return MemMap::MapAnonymous("local ref table",
45*795d594fSAndroid Build Coastguard Worker table_bytes,
46*795d594fSAndroid Build Coastguard Worker PROT_READ | PROT_WRITE,
47*795d594fSAndroid Build Coastguard Worker /*low_4gb=*/ false,
48*795d594fSAndroid Build Coastguard Worker error_msg);
49*795d594fSAndroid Build Coastguard Worker }
50*795d594fSAndroid Build Coastguard Worker
SmallLrtAllocator()51*795d594fSAndroid Build Coastguard Worker SmallLrtAllocator::SmallLrtAllocator()
52*795d594fSAndroid Build Coastguard Worker : free_lists_(num_lrt_slots_, nullptr),
53*795d594fSAndroid Build Coastguard Worker shared_lrt_maps_(),
54*795d594fSAndroid Build Coastguard Worker lock_("Small LRT allocator lock", LockLevel::kGenericBottomLock) {
55*795d594fSAndroid Build Coastguard Worker }
56*795d594fSAndroid Build Coastguard Worker
GetIndex(size_t size)57*795d594fSAndroid Build Coastguard Worker inline size_t SmallLrtAllocator::GetIndex(size_t size) {
58*795d594fSAndroid Build Coastguard Worker DCHECK_GE(size, kSmallLrtEntries);
59*795d594fSAndroid Build Coastguard Worker DCHECK_LT(size, gPageSize / sizeof(LrtEntry));
60*795d594fSAndroid Build Coastguard Worker DCHECK(IsPowerOfTwo(size));
61*795d594fSAndroid Build Coastguard Worker size_t index = WhichPowerOf2(size / kSmallLrtEntries);
62*795d594fSAndroid Build Coastguard Worker DCHECK_LT(index, num_lrt_slots_);
63*795d594fSAndroid Build Coastguard Worker return index;
64*795d594fSAndroid Build Coastguard Worker }
65*795d594fSAndroid Build Coastguard Worker
Allocate(size_t size,std::string * error_msg)66*795d594fSAndroid Build Coastguard Worker LrtEntry* SmallLrtAllocator::Allocate(size_t size, std::string* error_msg) {
67*795d594fSAndroid Build Coastguard Worker size_t index = GetIndex(size);
68*795d594fSAndroid Build Coastguard Worker MutexLock lock(Thread::Current(), lock_);
69*795d594fSAndroid Build Coastguard Worker size_t fill_from = index;
70*795d594fSAndroid Build Coastguard Worker while (fill_from != num_lrt_slots_ && free_lists_[fill_from] == nullptr) {
71*795d594fSAndroid Build Coastguard Worker ++fill_from;
72*795d594fSAndroid Build Coastguard Worker }
73*795d594fSAndroid Build Coastguard Worker void* result = nullptr;
74*795d594fSAndroid Build Coastguard Worker if (fill_from != num_lrt_slots_) {
75*795d594fSAndroid Build Coastguard Worker // We found a slot with enough memory.
76*795d594fSAndroid Build Coastguard Worker result = free_lists_[fill_from];
77*795d594fSAndroid Build Coastguard Worker free_lists_[fill_from] = *reinterpret_cast<void**>(result);
78*795d594fSAndroid Build Coastguard Worker } else {
79*795d594fSAndroid Build Coastguard Worker // We need to allocate a new page and split it into smaller pieces.
80*795d594fSAndroid Build Coastguard Worker MemMap map = NewLRTMap(gPageSize, error_msg);
81*795d594fSAndroid Build Coastguard Worker if (!map.IsValid()) {
82*795d594fSAndroid Build Coastguard Worker return nullptr;
83*795d594fSAndroid Build Coastguard Worker }
84*795d594fSAndroid Build Coastguard Worker result = map.Begin();
85*795d594fSAndroid Build Coastguard Worker shared_lrt_maps_.emplace_back(std::move(map));
86*795d594fSAndroid Build Coastguard Worker }
87*795d594fSAndroid Build Coastguard Worker while (fill_from != index) {
88*795d594fSAndroid Build Coastguard Worker --fill_from;
89*795d594fSAndroid Build Coastguard Worker // Store the second half of the current buffer in appropriate free list slot.
90*795d594fSAndroid Build Coastguard Worker void* mid = reinterpret_cast<uint8_t*>(result) + (kInitialLrtBytes << fill_from);
91*795d594fSAndroid Build Coastguard Worker DCHECK(free_lists_[fill_from] == nullptr);
92*795d594fSAndroid Build Coastguard Worker *reinterpret_cast<void**>(mid) = nullptr;
93*795d594fSAndroid Build Coastguard Worker free_lists_[fill_from] = mid;
94*795d594fSAndroid Build Coastguard Worker }
95*795d594fSAndroid Build Coastguard Worker // Clear the memory we return to the caller.
96*795d594fSAndroid Build Coastguard Worker std::memset(result, 0, kInitialLrtBytes << index);
97*795d594fSAndroid Build Coastguard Worker return reinterpret_cast<LrtEntry*>(result);
98*795d594fSAndroid Build Coastguard Worker }
99*795d594fSAndroid Build Coastguard Worker
Deallocate(LrtEntry * unneeded,size_t size)100*795d594fSAndroid Build Coastguard Worker void SmallLrtAllocator::Deallocate(LrtEntry* unneeded, size_t size) {
101*795d594fSAndroid Build Coastguard Worker size_t index = GetIndex(size);
102*795d594fSAndroid Build Coastguard Worker MutexLock lock(Thread::Current(), lock_);
103*795d594fSAndroid Build Coastguard Worker while (index < num_lrt_slots_) {
104*795d594fSAndroid Build Coastguard Worker // Check if we can merge this free block with another block with the same size.
105*795d594fSAndroid Build Coastguard Worker void** other = reinterpret_cast<void**>(
106*795d594fSAndroid Build Coastguard Worker reinterpret_cast<uintptr_t>(unneeded) ^ (kInitialLrtBytes << index));
107*795d594fSAndroid Build Coastguard Worker void** before = &free_lists_[index];
108*795d594fSAndroid Build Coastguard Worker if (index + 1u == num_lrt_slots_ && *before == other && *other == nullptr) {
109*795d594fSAndroid Build Coastguard Worker // Do not unmap the page if we do not have other free blocks with index `num_lrt_slots_ - 1`.
110*795d594fSAndroid Build Coastguard Worker // (Keep at least one free block to avoid a situation where creating and destroying a single
111*795d594fSAndroid Build Coastguard Worker // thread with no local references would map and unmap a page in the `SmallLrtAllocator`.)
112*795d594fSAndroid Build Coastguard Worker break;
113*795d594fSAndroid Build Coastguard Worker }
114*795d594fSAndroid Build Coastguard Worker while (*before != nullptr && *before != other) {
115*795d594fSAndroid Build Coastguard Worker before = reinterpret_cast<void**>(*before);
116*795d594fSAndroid Build Coastguard Worker }
117*795d594fSAndroid Build Coastguard Worker if (*before == nullptr) {
118*795d594fSAndroid Build Coastguard Worker break;
119*795d594fSAndroid Build Coastguard Worker }
120*795d594fSAndroid Build Coastguard Worker // Remove `other` from the free list and merge it with the `unneeded` block.
121*795d594fSAndroid Build Coastguard Worker DCHECK(*before == other);
122*795d594fSAndroid Build Coastguard Worker *before = *reinterpret_cast<void**>(other);
123*795d594fSAndroid Build Coastguard Worker ++index;
124*795d594fSAndroid Build Coastguard Worker unneeded = reinterpret_cast<LrtEntry*>(
125*795d594fSAndroid Build Coastguard Worker reinterpret_cast<uintptr_t>(unneeded) & reinterpret_cast<uintptr_t>(other));
126*795d594fSAndroid Build Coastguard Worker }
127*795d594fSAndroid Build Coastguard Worker if (index == num_lrt_slots_) {
128*795d594fSAndroid Build Coastguard Worker // Free the entire page.
129*795d594fSAndroid Build Coastguard Worker DCHECK(free_lists_[num_lrt_slots_ - 1u] != nullptr);
130*795d594fSAndroid Build Coastguard Worker auto match = [=](MemMap& map) { return unneeded == reinterpret_cast<LrtEntry*>(map.Begin()); };
131*795d594fSAndroid Build Coastguard Worker auto it = std::find_if(shared_lrt_maps_.begin(), shared_lrt_maps_.end(), match);
132*795d594fSAndroid Build Coastguard Worker DCHECK(it != shared_lrt_maps_.end());
133*795d594fSAndroid Build Coastguard Worker shared_lrt_maps_.erase(it);
134*795d594fSAndroid Build Coastguard Worker DCHECK(!shared_lrt_maps_.empty());
135*795d594fSAndroid Build Coastguard Worker return;
136*795d594fSAndroid Build Coastguard Worker }
137*795d594fSAndroid Build Coastguard Worker *reinterpret_cast<void**>(unneeded) = free_lists_[index];
138*795d594fSAndroid Build Coastguard Worker free_lists_[index] = unneeded;
139*795d594fSAndroid Build Coastguard Worker }
140*795d594fSAndroid Build Coastguard Worker
LocalReferenceTable(bool check_jni)141*795d594fSAndroid Build Coastguard Worker LocalReferenceTable::LocalReferenceTable(bool check_jni)
142*795d594fSAndroid Build Coastguard Worker : previous_state_(kLRTFirstSegment),
143*795d594fSAndroid Build Coastguard Worker segment_state_(kLRTFirstSegment),
144*795d594fSAndroid Build Coastguard Worker max_entries_(0u),
145*795d594fSAndroid Build Coastguard Worker free_entries_list_(
146*795d594fSAndroid Build Coastguard Worker FirstFreeField::Update(kFreeListEnd, check_jni ? 1u << kFlagCheckJni : 0u)),
147*795d594fSAndroid Build Coastguard Worker small_table_(nullptr),
148*795d594fSAndroid Build Coastguard Worker tables_(),
149*795d594fSAndroid Build Coastguard Worker table_mem_maps_() {
150*795d594fSAndroid Build Coastguard Worker }
151*795d594fSAndroid Build Coastguard Worker
SetCheckJniEnabled(bool enabled)152*795d594fSAndroid Build Coastguard Worker void LocalReferenceTable::SetCheckJniEnabled(bool enabled) {
153*795d594fSAndroid Build Coastguard Worker free_entries_list_ =
154*795d594fSAndroid Build Coastguard Worker (free_entries_list_ & ~(1u << kFlagCheckJni)) | (enabled ? 1u << kFlagCheckJni : 0u);
155*795d594fSAndroid Build Coastguard Worker }
156*795d594fSAndroid Build Coastguard Worker
Initialize(size_t max_count,std::string * error_msg)157*795d594fSAndroid Build Coastguard Worker bool LocalReferenceTable::Initialize(size_t max_count, std::string* error_msg) {
158*795d594fSAndroid Build Coastguard Worker CHECK(error_msg != nullptr);
159*795d594fSAndroid Build Coastguard Worker
160*795d594fSAndroid Build Coastguard Worker // Overflow and maximum check.
161*795d594fSAndroid Build Coastguard Worker CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(LrtEntry));
162*795d594fSAndroid Build Coastguard Worker if (IsCheckJniEnabled()) {
163*795d594fSAndroid Build Coastguard Worker CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(LrtEntry) / kCheckJniEntriesPerReference);
164*795d594fSAndroid Build Coastguard Worker max_count *= kCheckJniEntriesPerReference;
165*795d594fSAndroid Build Coastguard Worker }
166*795d594fSAndroid Build Coastguard Worker
167*795d594fSAndroid Build Coastguard Worker SmallLrtAllocator* small_lrt_allocator = Runtime::Current()->GetSmallLrtAllocator();
168*795d594fSAndroid Build Coastguard Worker LrtEntry* first_table = small_lrt_allocator->Allocate(kSmallLrtEntries, error_msg);
169*795d594fSAndroid Build Coastguard Worker if (first_table == nullptr) {
170*795d594fSAndroid Build Coastguard Worker DCHECK(!error_msg->empty());
171*795d594fSAndroid Build Coastguard Worker return false;
172*795d594fSAndroid Build Coastguard Worker }
173*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(first_table, kCheckJniEntriesPerReference * sizeof(LrtEntry));
174*795d594fSAndroid Build Coastguard Worker small_table_ = first_table;
175*795d594fSAndroid Build Coastguard Worker max_entries_ = kSmallLrtEntries;
176*795d594fSAndroid Build Coastguard Worker return (max_count <= kSmallLrtEntries) || Resize(max_count, error_msg);
177*795d594fSAndroid Build Coastguard Worker }
178*795d594fSAndroid Build Coastguard Worker
~LocalReferenceTable()179*795d594fSAndroid Build Coastguard Worker LocalReferenceTable::~LocalReferenceTable() {
180*795d594fSAndroid Build Coastguard Worker SmallLrtAllocator* small_lrt_allocator =
181*795d594fSAndroid Build Coastguard Worker max_entries_ != 0u ? Runtime::Current()->GetSmallLrtAllocator() : nullptr;
182*795d594fSAndroid Build Coastguard Worker if (small_table_ != nullptr) {
183*795d594fSAndroid Build Coastguard Worker small_lrt_allocator->Deallocate(small_table_, kSmallLrtEntries);
184*795d594fSAndroid Build Coastguard Worker DCHECK(tables_.empty());
185*795d594fSAndroid Build Coastguard Worker } else {
186*795d594fSAndroid Build Coastguard Worker size_t num_small_tables = std::min(tables_.size(), MaxSmallTables());
187*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i != num_small_tables; ++i) {
188*795d594fSAndroid Build Coastguard Worker small_lrt_allocator->Deallocate(tables_[i], GetTableSize(i));
189*795d594fSAndroid Build Coastguard Worker }
190*795d594fSAndroid Build Coastguard Worker }
191*795d594fSAndroid Build Coastguard Worker }
192*795d594fSAndroid Build Coastguard Worker
Resize(size_t new_size,std::string * error_msg)193*795d594fSAndroid Build Coastguard Worker bool LocalReferenceTable::Resize(size_t new_size, std::string* error_msg) {
194*795d594fSAndroid Build Coastguard Worker DCHECK_GE(max_entries_, kSmallLrtEntries);
195*795d594fSAndroid Build Coastguard Worker DCHECK(IsPowerOfTwo(max_entries_));
196*795d594fSAndroid Build Coastguard Worker DCHECK_GT(new_size, max_entries_);
197*795d594fSAndroid Build Coastguard Worker DCHECK_LE(new_size, kMaxTableSizeInBytes / sizeof(LrtEntry));
198*795d594fSAndroid Build Coastguard Worker size_t required_size = RoundUpToPowerOfTwo(new_size);
199*795d594fSAndroid Build Coastguard Worker size_t num_required_tables = NumTablesForSize(required_size);
200*795d594fSAndroid Build Coastguard Worker DCHECK_GE(num_required_tables, 2u);
201*795d594fSAndroid Build Coastguard Worker // Delay moving the `small_table_` to `tables_` until after the next table allocation succeeds.
202*795d594fSAndroid Build Coastguard Worker size_t num_tables = (small_table_ != nullptr) ? 1u : tables_.size();
203*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(num_tables, NumTablesForSize(max_entries_));
204*795d594fSAndroid Build Coastguard Worker for (; num_tables != num_required_tables; ++num_tables) {
205*795d594fSAndroid Build Coastguard Worker size_t new_table_size = GetTableSize(num_tables);
206*795d594fSAndroid Build Coastguard Worker if (num_tables < MaxSmallTables()) {
207*795d594fSAndroid Build Coastguard Worker SmallLrtAllocator* small_lrt_allocator = Runtime::Current()->GetSmallLrtAllocator();
208*795d594fSAndroid Build Coastguard Worker LrtEntry* new_table = small_lrt_allocator->Allocate(new_table_size, error_msg);
209*795d594fSAndroid Build Coastguard Worker if (new_table == nullptr) {
210*795d594fSAndroid Build Coastguard Worker DCHECK(!error_msg->empty());
211*795d594fSAndroid Build Coastguard Worker return false;
212*795d594fSAndroid Build Coastguard Worker }
213*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(new_table, kCheckJniEntriesPerReference * sizeof(LrtEntry));
214*795d594fSAndroid Build Coastguard Worker tables_.push_back(new_table);
215*795d594fSAndroid Build Coastguard Worker } else {
216*795d594fSAndroid Build Coastguard Worker MemMap new_map = NewLRTMap(new_table_size * sizeof(LrtEntry), error_msg);
217*795d594fSAndroid Build Coastguard Worker if (!new_map.IsValid()) {
218*795d594fSAndroid Build Coastguard Worker DCHECK(!error_msg->empty());
219*795d594fSAndroid Build Coastguard Worker return false;
220*795d594fSAndroid Build Coastguard Worker }
221*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(new_map.Begin(), kCheckJniEntriesPerReference * sizeof(LrtEntry));
222*795d594fSAndroid Build Coastguard Worker tables_.push_back(reinterpret_cast<LrtEntry*>(new_map.Begin()));
223*795d594fSAndroid Build Coastguard Worker table_mem_maps_.push_back(std::move(new_map));
224*795d594fSAndroid Build Coastguard Worker }
225*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(num_tables == 1u, small_table_ != nullptr);
226*795d594fSAndroid Build Coastguard Worker if (num_tables == 1u) {
227*795d594fSAndroid Build Coastguard Worker tables_.insert(tables_.begin(), small_table_);
228*795d594fSAndroid Build Coastguard Worker small_table_ = nullptr;
229*795d594fSAndroid Build Coastguard Worker }
230*795d594fSAndroid Build Coastguard Worker // Record the new available capacity after each successful allocation.
231*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(max_entries_, new_table_size);
232*795d594fSAndroid Build Coastguard Worker max_entries_ = 2u * new_table_size;
233*795d594fSAndroid Build Coastguard Worker }
234*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(num_required_tables, tables_.size());
235*795d594fSAndroid Build Coastguard Worker return true;
236*795d594fSAndroid Build Coastguard Worker }
237*795d594fSAndroid Build Coastguard Worker
238*795d594fSAndroid Build Coastguard Worker template <typename EntryGetter>
PrunePoppedFreeEntries(EntryGetter && get_entry)239*795d594fSAndroid Build Coastguard Worker inline void LocalReferenceTable::PrunePoppedFreeEntries(EntryGetter&& get_entry) {
240*795d594fSAndroid Build Coastguard Worker const uint32_t top_index = segment_state_.top_index;
241*795d594fSAndroid Build Coastguard Worker uint32_t free_entries_list = free_entries_list_;
242*795d594fSAndroid Build Coastguard Worker uint32_t free_entry_index = FirstFreeField::Decode(free_entries_list);
243*795d594fSAndroid Build Coastguard Worker DCHECK_NE(free_entry_index, kFreeListEnd);
244*795d594fSAndroid Build Coastguard Worker DCHECK_GE(free_entry_index, top_index);
245*795d594fSAndroid Build Coastguard Worker do {
246*795d594fSAndroid Build Coastguard Worker free_entry_index = get_entry(free_entry_index)->GetNextFree();
247*795d594fSAndroid Build Coastguard Worker } while (free_entry_index != kFreeListEnd && free_entry_index >= top_index);
248*795d594fSAndroid Build Coastguard Worker free_entries_list_ = FirstFreeField::Update(free_entry_index, free_entries_list);
249*795d594fSAndroid Build Coastguard Worker }
250*795d594fSAndroid Build Coastguard Worker
IncrementSerialNumber(LrtEntry * serial_number_entry)251*795d594fSAndroid Build Coastguard Worker inline uint32_t LocalReferenceTable::IncrementSerialNumber(LrtEntry* serial_number_entry) {
252*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(serial_number_entry, GetCheckJniSerialNumberEntry(serial_number_entry));
253*795d594fSAndroid Build Coastguard Worker // The old serial number can be 0 if it was not used before. It can also be bits from the
254*795d594fSAndroid Build Coastguard Worker // representation of an object reference, or a link to the next free entry written in this
255*795d594fSAndroid Build Coastguard Worker // slot before enabling the CheckJNI. (Some gtests repeatedly enable and disable CheckJNI.)
256*795d594fSAndroid Build Coastguard Worker uint32_t old_serial_number =
257*795d594fSAndroid Build Coastguard Worker serial_number_entry->GetSerialNumberUnchecked() % kCheckJniEntriesPerReference;
258*795d594fSAndroid Build Coastguard Worker uint32_t new_serial_number =
259*795d594fSAndroid Build Coastguard Worker (old_serial_number + 1u) != kCheckJniEntriesPerReference ? old_serial_number + 1u : 1u;
260*795d594fSAndroid Build Coastguard Worker DCHECK(IsValidSerialNumber(new_serial_number));
261*795d594fSAndroid Build Coastguard Worker serial_number_entry->SetSerialNumber(new_serial_number);
262*795d594fSAndroid Build Coastguard Worker return new_serial_number;
263*795d594fSAndroid Build Coastguard Worker }
264*795d594fSAndroid Build Coastguard Worker
Add(ObjPtr<mirror::Object> obj,std::string * error_msg)265*795d594fSAndroid Build Coastguard Worker IndirectRef LocalReferenceTable::Add(ObjPtr<mirror::Object> obj, std::string* error_msg) {
266*795d594fSAndroid Build Coastguard Worker if (kDebugLRT) {
267*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "+++ Add: previous_state=" << previous_state_.top_index
268*795d594fSAndroid Build Coastguard Worker << " top_index=" << segment_state_.top_index;
269*795d594fSAndroid Build Coastguard Worker }
270*795d594fSAndroid Build Coastguard Worker
271*795d594fSAndroid Build Coastguard Worker DCHECK(obj != nullptr);
272*795d594fSAndroid Build Coastguard Worker VerifyObject(obj);
273*795d594fSAndroid Build Coastguard Worker
274*795d594fSAndroid Build Coastguard Worker DCHECK_LE(previous_state_.top_index, segment_state_.top_index);
275*795d594fSAndroid Build Coastguard Worker DCHECK(max_entries_ == kSmallLrtEntries ? small_table_ != nullptr : !tables_.empty());
276*795d594fSAndroid Build Coastguard Worker
277*795d594fSAndroid Build Coastguard Worker auto store_obj = [obj, this](LrtEntry* free_entry, const char* tag)
278*795d594fSAndroid Build Coastguard Worker REQUIRES_SHARED(Locks::mutator_lock_) {
279*795d594fSAndroid Build Coastguard Worker free_entry->SetReference(obj);
280*795d594fSAndroid Build Coastguard Worker IndirectRef result = ToIndirectRef(free_entry);
281*795d594fSAndroid Build Coastguard Worker if (kDebugLRT) {
282*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "+++ " << tag << ": added at index " << GetReferenceEntryIndex(result)
283*795d594fSAndroid Build Coastguard Worker << ", top=" << segment_state_.top_index;
284*795d594fSAndroid Build Coastguard Worker }
285*795d594fSAndroid Build Coastguard Worker return result;
286*795d594fSAndroid Build Coastguard Worker };
287*795d594fSAndroid Build Coastguard Worker
288*795d594fSAndroid Build Coastguard Worker // Fast-path for small table with CheckJNI disabled.
289*795d594fSAndroid Build Coastguard Worker uint32_t top_index = segment_state_.top_index;
290*795d594fSAndroid Build Coastguard Worker LrtEntry* const small_table = small_table_;
291*795d594fSAndroid Build Coastguard Worker if (LIKELY(small_table != nullptr)) {
292*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(max_entries_, kSmallLrtEntries);
293*795d594fSAndroid Build Coastguard Worker DCHECK_LE(segment_state_.top_index, kSmallLrtEntries);
294*795d594fSAndroid Build Coastguard Worker auto get_entry = [small_table](uint32_t index) ALWAYS_INLINE {
295*795d594fSAndroid Build Coastguard Worker DCHECK_LT(index, kSmallLrtEntries);
296*795d594fSAndroid Build Coastguard Worker return &small_table[index];
297*795d594fSAndroid Build Coastguard Worker };
298*795d594fSAndroid Build Coastguard Worker if (LIKELY(free_entries_list_ == kEmptyFreeListAndCheckJniDisabled)) {
299*795d594fSAndroid Build Coastguard Worker if (LIKELY(top_index != kSmallLrtEntries)) {
300*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = get_entry(top_index);
301*795d594fSAndroid Build Coastguard Worker segment_state_.top_index = top_index + 1u;
302*795d594fSAndroid Build Coastguard Worker return store_obj(free_entry, "small_table/empty-free-list");
303*795d594fSAndroid Build Coastguard Worker }
304*795d594fSAndroid Build Coastguard Worker } else if (LIKELY(!IsCheckJniEnabled())) {
305*795d594fSAndroid Build Coastguard Worker uint32_t first_free_index = GetFirstFreeIndex();
306*795d594fSAndroid Build Coastguard Worker DCHECK_NE(first_free_index, kFreeListEnd);
307*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(first_free_index >= top_index)) {
308*795d594fSAndroid Build Coastguard Worker PrunePoppedFreeEntries(get_entry);
309*795d594fSAndroid Build Coastguard Worker first_free_index = GetFirstFreeIndex();
310*795d594fSAndroid Build Coastguard Worker }
311*795d594fSAndroid Build Coastguard Worker if (first_free_index != kFreeListEnd && first_free_index >= previous_state_.top_index) {
312*795d594fSAndroid Build Coastguard Worker DCHECK_LT(first_free_index, segment_state_.top_index); // Popped entries pruned above.
313*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = get_entry(first_free_index);
314*795d594fSAndroid Build Coastguard Worker // Use the `free_entry` only if it was created with CheckJNI disabled.
315*795d594fSAndroid Build Coastguard Worker LrtEntry* serial_number_entry = GetCheckJniSerialNumberEntry(free_entry);
316*795d594fSAndroid Build Coastguard Worker if (!serial_number_entry->IsSerialNumber()) {
317*795d594fSAndroid Build Coastguard Worker free_entries_list_ = FirstFreeField::Update(free_entry->GetNextFree(), 0u);
318*795d594fSAndroid Build Coastguard Worker return store_obj(free_entry, "small_table/reuse-empty-slot");
319*795d594fSAndroid Build Coastguard Worker }
320*795d594fSAndroid Build Coastguard Worker }
321*795d594fSAndroid Build Coastguard Worker if (top_index != kSmallLrtEntries) {
322*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = get_entry(top_index);
323*795d594fSAndroid Build Coastguard Worker segment_state_.top_index = top_index + 1u;
324*795d594fSAndroid Build Coastguard Worker return store_obj(free_entry, "small_table/pruned-free-list");
325*795d594fSAndroid Build Coastguard Worker }
326*795d594fSAndroid Build Coastguard Worker }
327*795d594fSAndroid Build Coastguard Worker }
328*795d594fSAndroid Build Coastguard Worker DCHECK(IsCheckJniEnabled() || small_table == nullptr || top_index == kSmallLrtEntries);
329*795d594fSAndroid Build Coastguard Worker
330*795d594fSAndroid Build Coastguard Worker // Process free list: prune, reuse free entry or pad for CheckJNI.
331*795d594fSAndroid Build Coastguard Worker uint32_t first_free_index = GetFirstFreeIndex();
332*795d594fSAndroid Build Coastguard Worker if (first_free_index != kFreeListEnd && first_free_index >= top_index) {
333*795d594fSAndroid Build Coastguard Worker PrunePoppedFreeEntries([&](size_t index) { return GetEntry(index); });
334*795d594fSAndroid Build Coastguard Worker first_free_index = GetFirstFreeIndex();
335*795d594fSAndroid Build Coastguard Worker }
336*795d594fSAndroid Build Coastguard Worker if (first_free_index != kFreeListEnd && first_free_index >= previous_state_.top_index) {
337*795d594fSAndroid Build Coastguard Worker // Reuse the free entry if it was created with the same CheckJNI setting.
338*795d594fSAndroid Build Coastguard Worker DCHECK_LT(first_free_index, top_index); // Popped entries have been pruned above.
339*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = GetEntry(first_free_index);
340*795d594fSAndroid Build Coastguard Worker LrtEntry* serial_number_entry = GetCheckJniSerialNumberEntry(free_entry);
341*795d594fSAndroid Build Coastguard Worker if (serial_number_entry->IsSerialNumber() == IsCheckJniEnabled()) {
342*795d594fSAndroid Build Coastguard Worker free_entries_list_ = FirstFreeField::Update(free_entry->GetNextFree(), free_entries_list_);
343*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(IsCheckJniEnabled())) {
344*795d594fSAndroid Build Coastguard Worker DCHECK_NE(free_entry, serial_number_entry);
345*795d594fSAndroid Build Coastguard Worker uint32_t serial_number = IncrementSerialNumber(serial_number_entry);
346*795d594fSAndroid Build Coastguard Worker free_entry = serial_number_entry + serial_number;
347*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(
348*795d594fSAndroid Build Coastguard Worker free_entry,
349*795d594fSAndroid Build Coastguard Worker GetEntry(RoundDown(first_free_index, kCheckJniEntriesPerReference) + serial_number));
350*795d594fSAndroid Build Coastguard Worker }
351*795d594fSAndroid Build Coastguard Worker return store_obj(free_entry, "reuse-empty-slot");
352*795d594fSAndroid Build Coastguard Worker }
353*795d594fSAndroid Build Coastguard Worker }
354*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(IsCheckJniEnabled()) && !IsAligned<kCheckJniEntriesPerReference>(top_index)) {
355*795d594fSAndroid Build Coastguard Worker // Add non-CheckJNI holes up to the next serial number entry.
356*795d594fSAndroid Build Coastguard Worker for (; !IsAligned<kCheckJniEntriesPerReference>(top_index); ++top_index) {
357*795d594fSAndroid Build Coastguard Worker GetEntry(top_index)->SetNextFree(first_free_index);
358*795d594fSAndroid Build Coastguard Worker first_free_index = top_index;
359*795d594fSAndroid Build Coastguard Worker }
360*795d594fSAndroid Build Coastguard Worker free_entries_list_ = FirstFreeField::Update(first_free_index, 1u << kFlagCheckJni);
361*795d594fSAndroid Build Coastguard Worker segment_state_.top_index = top_index;
362*795d594fSAndroid Build Coastguard Worker }
363*795d594fSAndroid Build Coastguard Worker
364*795d594fSAndroid Build Coastguard Worker // Resize (double the space) if needed.
365*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(top_index == max_entries_)) {
366*795d594fSAndroid Build Coastguard Worker static_assert(IsPowerOfTwo(kMaxTableSizeInBytes));
367*795d594fSAndroid Build Coastguard Worker static_assert(IsPowerOfTwo(sizeof(LrtEntry)));
368*795d594fSAndroid Build Coastguard Worker DCHECK(IsPowerOfTwo(max_entries_));
369*795d594fSAndroid Build Coastguard Worker if (kMaxTableSizeInBytes == max_entries_ * sizeof(LrtEntry)) {
370*795d594fSAndroid Build Coastguard Worker std::ostringstream oss;
371*795d594fSAndroid Build Coastguard Worker oss << "JNI ERROR (app bug): " << kLocal << " table overflow "
372*795d594fSAndroid Build Coastguard Worker << "(max=" << max_entries_ << ")" << std::endl
373*795d594fSAndroid Build Coastguard Worker << MutatorLockedDumpable<LocalReferenceTable>(*this)
374*795d594fSAndroid Build Coastguard Worker << " Resizing failed: Cannot resize over the maximum permitted size.";
375*795d594fSAndroid Build Coastguard Worker *error_msg = oss.str();
376*795d594fSAndroid Build Coastguard Worker return nullptr;
377*795d594fSAndroid Build Coastguard Worker }
378*795d594fSAndroid Build Coastguard Worker
379*795d594fSAndroid Build Coastguard Worker std::string inner_error_msg;
380*795d594fSAndroid Build Coastguard Worker if (!Resize(max_entries_ * 2u, &inner_error_msg)) {
381*795d594fSAndroid Build Coastguard Worker std::ostringstream oss;
382*795d594fSAndroid Build Coastguard Worker oss << "JNI ERROR (app bug): " << kLocal << " table overflow "
383*795d594fSAndroid Build Coastguard Worker << "(max=" << max_entries_ << ")" << std::endl
384*795d594fSAndroid Build Coastguard Worker << MutatorLockedDumpable<LocalReferenceTable>(*this)
385*795d594fSAndroid Build Coastguard Worker << " Resizing failed: " << inner_error_msg;
386*795d594fSAndroid Build Coastguard Worker *error_msg = oss.str();
387*795d594fSAndroid Build Coastguard Worker return nullptr;
388*795d594fSAndroid Build Coastguard Worker }
389*795d594fSAndroid Build Coastguard Worker }
390*795d594fSAndroid Build Coastguard Worker
391*795d594fSAndroid Build Coastguard Worker // Use the next entry.
392*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(IsCheckJniEnabled())) {
393*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(top_index, kCheckJniEntriesPerReference);
394*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(previous_state_.top_index, kCheckJniEntriesPerReference);
395*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(max_entries_, kCheckJniEntriesPerReference);
396*795d594fSAndroid Build Coastguard Worker LrtEntry* serial_number_entry = GetEntry(top_index);
397*795d594fSAndroid Build Coastguard Worker uint32_t serial_number = IncrementSerialNumber(serial_number_entry);
398*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = serial_number_entry + serial_number;
399*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(free_entry, GetEntry(top_index + serial_number));
400*795d594fSAndroid Build Coastguard Worker segment_state_.top_index = top_index + kCheckJniEntriesPerReference;
401*795d594fSAndroid Build Coastguard Worker return store_obj(free_entry, "slow-path/check-jni");
402*795d594fSAndroid Build Coastguard Worker }
403*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = GetEntry(top_index);
404*795d594fSAndroid Build Coastguard Worker segment_state_.top_index = top_index + 1u;
405*795d594fSAndroid Build Coastguard Worker return store_obj(free_entry, "slow-path");
406*795d594fSAndroid Build Coastguard Worker }
407*795d594fSAndroid Build Coastguard Worker
408*795d594fSAndroid Build Coastguard Worker // Removes an object.
409*795d594fSAndroid Build Coastguard Worker //
410*795d594fSAndroid Build Coastguard Worker // This method is not called when a local frame is popped; this is only used
411*795d594fSAndroid Build Coastguard Worker // for explicit single removals.
412*795d594fSAndroid Build Coastguard Worker //
413*795d594fSAndroid Build Coastguard Worker // If the entry is not at the top, we just add it to the free entry list.
414*795d594fSAndroid Build Coastguard Worker // If the entry is at the top, we pop it from the top and check if there are
415*795d594fSAndroid Build Coastguard Worker // free entries under it to remove in order to reduce the size of the table.
416*795d594fSAndroid Build Coastguard Worker //
417*795d594fSAndroid Build Coastguard Worker // Returns "false" if nothing was removed.
Remove(IndirectRef iref)418*795d594fSAndroid Build Coastguard Worker bool LocalReferenceTable::Remove(IndirectRef iref) {
419*795d594fSAndroid Build Coastguard Worker if (kDebugLRT) {
420*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "+++ Remove: previous_state=" << previous_state_.top_index
421*795d594fSAndroid Build Coastguard Worker << " top_index=" << segment_state_.top_index;
422*795d594fSAndroid Build Coastguard Worker }
423*795d594fSAndroid Build Coastguard Worker
424*795d594fSAndroid Build Coastguard Worker IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(iref);
425*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(kind != kLocal)) {
426*795d594fSAndroid Build Coastguard Worker Thread* self = Thread::Current();
427*795d594fSAndroid Build Coastguard Worker if (kind == kJniTransition) {
428*795d594fSAndroid Build Coastguard Worker if (self->IsJniTransitionReference(reinterpret_cast<jobject>(iref))) {
429*795d594fSAndroid Build Coastguard Worker // Transition references count as local but they cannot be deleted.
430*795d594fSAndroid Build Coastguard Worker // TODO: They could actually be cleared on the stack, except for the `jclass`
431*795d594fSAndroid Build Coastguard Worker // reference for static methods that points to the method's declaring class.
432*795d594fSAndroid Build Coastguard Worker JNIEnvExt* env = self->GetJniEnv();
433*795d594fSAndroid Build Coastguard Worker DCHECK(env != nullptr);
434*795d594fSAndroid Build Coastguard Worker if (env->IsCheckJniEnabled()) {
435*795d594fSAndroid Build Coastguard Worker const char* msg = kDumpStackOnNonLocalReference
436*795d594fSAndroid Build Coastguard Worker ? "Attempt to remove non-JNI local reference, dumping thread"
437*795d594fSAndroid Build Coastguard Worker : "Attempt to remove non-JNI local reference";
438*795d594fSAndroid Build Coastguard Worker LOG(WARNING) << msg;
439*795d594fSAndroid Build Coastguard Worker if (kDumpStackOnNonLocalReference) {
440*795d594fSAndroid Build Coastguard Worker self->Dump(LOG_STREAM(WARNING));
441*795d594fSAndroid Build Coastguard Worker }
442*795d594fSAndroid Build Coastguard Worker }
443*795d594fSAndroid Build Coastguard Worker return true;
444*795d594fSAndroid Build Coastguard Worker }
445*795d594fSAndroid Build Coastguard Worker }
446*795d594fSAndroid Build Coastguard Worker if (kDumpStackOnNonLocalReference && IsCheckJniEnabled()) {
447*795d594fSAndroid Build Coastguard Worker // Log the error message and stack. Repeat the message as FATAL later.
448*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Attempt to delete " << kind
449*795d594fSAndroid Build Coastguard Worker << " reference as local JNI reference, dumping stack";
450*795d594fSAndroid Build Coastguard Worker self->Dump(LOG_STREAM(ERROR));
451*795d594fSAndroid Build Coastguard Worker }
452*795d594fSAndroid Build Coastguard Worker LOG(IsCheckJniEnabled() ? ERROR : FATAL)
453*795d594fSAndroid Build Coastguard Worker << "Attempt to delete " << kind << " reference as local JNI reference";
454*795d594fSAndroid Build Coastguard Worker return false;
455*795d594fSAndroid Build Coastguard Worker }
456*795d594fSAndroid Build Coastguard Worker
457*795d594fSAndroid Build Coastguard Worker DCHECK_LE(previous_state_.top_index, segment_state_.top_index);
458*795d594fSAndroid Build Coastguard Worker DCHECK(max_entries_ == kSmallLrtEntries ? small_table_ != nullptr : !tables_.empty());
459*795d594fSAndroid Build Coastguard Worker DCheckValidReference(iref);
460*795d594fSAndroid Build Coastguard Worker
461*795d594fSAndroid Build Coastguard Worker LrtEntry* entry = ToLrtEntry(iref);
462*795d594fSAndroid Build Coastguard Worker uint32_t entry_index = GetReferenceEntryIndex(iref);
463*795d594fSAndroid Build Coastguard Worker uint32_t top_index = segment_state_.top_index;
464*795d594fSAndroid Build Coastguard Worker const uint32_t bottom_index = previous_state_.top_index;
465*795d594fSAndroid Build Coastguard Worker
466*795d594fSAndroid Build Coastguard Worker if (entry_index < bottom_index) {
467*795d594fSAndroid Build Coastguard Worker // Wrong segment.
468*795d594fSAndroid Build Coastguard Worker LOG(WARNING) << "Attempt to remove index outside index area (" << entry_index
469*795d594fSAndroid Build Coastguard Worker << " vs " << bottom_index << "-" << top_index << ")";
470*795d594fSAndroid Build Coastguard Worker return false;
471*795d594fSAndroid Build Coastguard Worker }
472*795d594fSAndroid Build Coastguard Worker
473*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(IsCheckJniEnabled())) {
474*795d594fSAndroid Build Coastguard Worker // Ignore invalid references. CheckJNI should have aborted before passing this reference
475*795d594fSAndroid Build Coastguard Worker // to `LocalReferenceTable::Remove()` but gtests intercept the abort and proceed anyway.
476*795d594fSAndroid Build Coastguard Worker std::string error_msg;
477*795d594fSAndroid Build Coastguard Worker if (!IsValidReference(iref, &error_msg)) {
478*795d594fSAndroid Build Coastguard Worker LOG(WARNING) << "Attempt to remove invalid reference: " << error_msg;
479*795d594fSAndroid Build Coastguard Worker return false;
480*795d594fSAndroid Build Coastguard Worker }
481*795d594fSAndroid Build Coastguard Worker }
482*795d594fSAndroid Build Coastguard Worker DCHECK_LT(entry_index, top_index);
483*795d594fSAndroid Build Coastguard Worker
484*795d594fSAndroid Build Coastguard Worker // Workaround for double `DeleteLocalRef` bug. b/298297411
485*795d594fSAndroid Build Coastguard Worker if (entry->IsFree()) {
486*795d594fSAndroid Build Coastguard Worker // In debug build or with CheckJNI enabled, we would have detected this above.
487*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "App error: `DeleteLocalRef()` on already deleted local ref. b/298297411";
488*795d594fSAndroid Build Coastguard Worker return false;
489*795d594fSAndroid Build Coastguard Worker }
490*795d594fSAndroid Build Coastguard Worker
491*795d594fSAndroid Build Coastguard Worker // Prune the free entry list if a segment with holes was popped before the `Remove()` call.
492*795d594fSAndroid Build Coastguard Worker uint32_t first_free_index = GetFirstFreeIndex();
493*795d594fSAndroid Build Coastguard Worker if (first_free_index != kFreeListEnd && first_free_index >= top_index) {
494*795d594fSAndroid Build Coastguard Worker PrunePoppedFreeEntries([&](size_t index) { return GetEntry(index); });
495*795d594fSAndroid Build Coastguard Worker }
496*795d594fSAndroid Build Coastguard Worker
497*795d594fSAndroid Build Coastguard Worker // Check if we're removing the top entry (created with any CheckJNI setting).
498*795d594fSAndroid Build Coastguard Worker bool is_top_entry = false;
499*795d594fSAndroid Build Coastguard Worker uint32_t prune_end = entry_index;
500*795d594fSAndroid Build Coastguard Worker if (GetCheckJniSerialNumberEntry(entry)->IsSerialNumber()) {
501*795d594fSAndroid Build Coastguard Worker LrtEntry* serial_number_entry = GetCheckJniSerialNumberEntry(entry);
502*795d594fSAndroid Build Coastguard Worker uint32_t serial_number = dchecked_integral_cast<uint32_t>(entry - serial_number_entry);
503*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(serial_number, serial_number_entry->GetSerialNumber());
504*795d594fSAndroid Build Coastguard Worker prune_end = entry_index - serial_number;
505*795d594fSAndroid Build Coastguard Worker is_top_entry = (prune_end == top_index - kCheckJniEntriesPerReference);
506*795d594fSAndroid Build Coastguard Worker } else {
507*795d594fSAndroid Build Coastguard Worker is_top_entry = (entry_index == top_index - 1u);
508*795d594fSAndroid Build Coastguard Worker }
509*795d594fSAndroid Build Coastguard Worker if (is_top_entry) {
510*795d594fSAndroid Build Coastguard Worker // Top-most entry. Scan up and consume holes created with the current CheckJNI setting.
511*795d594fSAndroid Build Coastguard Worker constexpr uint32_t kDeadLocalValue = 0xdead10c0;
512*795d594fSAndroid Build Coastguard Worker entry->SetReference(reinterpret_cast32<mirror::Object*>(kDeadLocalValue));
513*795d594fSAndroid Build Coastguard Worker
514*795d594fSAndroid Build Coastguard Worker // TODO: Maybe we should not prune free entries from the top of the segment
515*795d594fSAndroid Build Coastguard Worker // because it has quadratic worst-case complexity. We could still prune while
516*795d594fSAndroid Build Coastguard Worker // the first free list entry is at the top.
517*795d594fSAndroid Build Coastguard Worker uint32_t prune_start = prune_end;
518*795d594fSAndroid Build Coastguard Worker size_t prune_count;
519*795d594fSAndroid Build Coastguard Worker auto find_prune_range = [&](size_t chunk_size, auto is_prev_entry_free) {
520*795d594fSAndroid Build Coastguard Worker while (prune_start > bottom_index && is_prev_entry_free(prune_start)) {
521*795d594fSAndroid Build Coastguard Worker prune_start -= chunk_size;
522*795d594fSAndroid Build Coastguard Worker }
523*795d594fSAndroid Build Coastguard Worker prune_count = (prune_end - prune_start) / chunk_size;
524*795d594fSAndroid Build Coastguard Worker };
525*795d594fSAndroid Build Coastguard Worker
526*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(IsCheckJniEnabled())) {
527*795d594fSAndroid Build Coastguard Worker auto is_prev_entry_free = [&](size_t index) {
528*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(index, kCheckJniEntriesPerReference);
529*795d594fSAndroid Build Coastguard Worker LrtEntry* serial_number_entry = GetEntry(index - kCheckJniEntriesPerReference);
530*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(serial_number_entry, kCheckJniEntriesPerReference * sizeof(LrtEntry));
531*795d594fSAndroid Build Coastguard Worker if (!serial_number_entry->IsSerialNumber()) {
532*795d594fSAndroid Build Coastguard Worker return false;
533*795d594fSAndroid Build Coastguard Worker }
534*795d594fSAndroid Build Coastguard Worker uint32_t serial_number = serial_number_entry->GetSerialNumber();
535*795d594fSAndroid Build Coastguard Worker DCHECK(IsValidSerialNumber(serial_number));
536*795d594fSAndroid Build Coastguard Worker LrtEntry* entry = serial_number_entry + serial_number;
537*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(entry, GetEntry(prune_start - kCheckJniEntriesPerReference + serial_number));
538*795d594fSAndroid Build Coastguard Worker return entry->IsFree();
539*795d594fSAndroid Build Coastguard Worker };
540*795d594fSAndroid Build Coastguard Worker find_prune_range(kCheckJniEntriesPerReference, is_prev_entry_free);
541*795d594fSAndroid Build Coastguard Worker } else {
542*795d594fSAndroid Build Coastguard Worker auto is_prev_entry_free = [&](size_t index) {
543*795d594fSAndroid Build Coastguard Worker LrtEntry* entry = GetEntry(index - 1u);
544*795d594fSAndroid Build Coastguard Worker return entry->IsFree() && !GetCheckJniSerialNumberEntry(entry)->IsSerialNumber();
545*795d594fSAndroid Build Coastguard Worker };
546*795d594fSAndroid Build Coastguard Worker find_prune_range(1u, is_prev_entry_free);
547*795d594fSAndroid Build Coastguard Worker }
548*795d594fSAndroid Build Coastguard Worker
549*795d594fSAndroid Build Coastguard Worker if (prune_count != 0u) {
550*795d594fSAndroid Build Coastguard Worker // Remove pruned entries from the free list.
551*795d594fSAndroid Build Coastguard Worker size_t remaining = prune_count;
552*795d594fSAndroid Build Coastguard Worker uint32_t free_index = GetFirstFreeIndex();
553*795d594fSAndroid Build Coastguard Worker while (remaining != 0u && free_index >= prune_start) {
554*795d594fSAndroid Build Coastguard Worker DCHECK_NE(free_index, kFreeListEnd);
555*795d594fSAndroid Build Coastguard Worker LrtEntry* pruned_entry = GetEntry(free_index);
556*795d594fSAndroid Build Coastguard Worker free_index = pruned_entry->GetNextFree();
557*795d594fSAndroid Build Coastguard Worker pruned_entry->SetReference(reinterpret_cast32<mirror::Object*>(kDeadLocalValue));
558*795d594fSAndroid Build Coastguard Worker --remaining;
559*795d594fSAndroid Build Coastguard Worker }
560*795d594fSAndroid Build Coastguard Worker free_entries_list_ = FirstFreeField::Update(free_index, free_entries_list_);
561*795d594fSAndroid Build Coastguard Worker while (remaining != 0u) {
562*795d594fSAndroid Build Coastguard Worker DCHECK_NE(free_index, kFreeListEnd);
563*795d594fSAndroid Build Coastguard Worker DCHECK_LT(free_index, prune_start);
564*795d594fSAndroid Build Coastguard Worker DCHECK_GE(free_index, bottom_index);
565*795d594fSAndroid Build Coastguard Worker LrtEntry* free_entry = GetEntry(free_index);
566*795d594fSAndroid Build Coastguard Worker while (free_entry->GetNextFree() < prune_start) {
567*795d594fSAndroid Build Coastguard Worker free_index = free_entry->GetNextFree();
568*795d594fSAndroid Build Coastguard Worker DCHECK_GE(free_index, bottom_index);
569*795d594fSAndroid Build Coastguard Worker free_entry = GetEntry(free_index);
570*795d594fSAndroid Build Coastguard Worker }
571*795d594fSAndroid Build Coastguard Worker LrtEntry* pruned_entry = GetEntry(free_entry->GetNextFree());
572*795d594fSAndroid Build Coastguard Worker free_entry->SetNextFree(pruned_entry->GetNextFree());
573*795d594fSAndroid Build Coastguard Worker pruned_entry->SetReference(reinterpret_cast32<mirror::Object*>(kDeadLocalValue));
574*795d594fSAndroid Build Coastguard Worker --remaining;
575*795d594fSAndroid Build Coastguard Worker }
576*795d594fSAndroid Build Coastguard Worker DCHECK(free_index == kFreeListEnd || free_index < prune_start)
577*795d594fSAndroid Build Coastguard Worker << "free_index=" << free_index << ", prune_start=" << prune_start;
578*795d594fSAndroid Build Coastguard Worker }
579*795d594fSAndroid Build Coastguard Worker segment_state_.top_index = prune_start;
580*795d594fSAndroid Build Coastguard Worker if (kDebugLRT) {
581*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "+++ removed last entry, pruned " << prune_count
582*795d594fSAndroid Build Coastguard Worker << ", new top= " << segment_state_.top_index;
583*795d594fSAndroid Build Coastguard Worker }
584*795d594fSAndroid Build Coastguard Worker } else {
585*795d594fSAndroid Build Coastguard Worker // Not the top-most entry. This creates a hole.
586*795d594fSAndroid Build Coastguard Worker entry->SetNextFree(GetFirstFreeIndex());
587*795d594fSAndroid Build Coastguard Worker free_entries_list_ = FirstFreeField::Update(entry_index, free_entries_list_);
588*795d594fSAndroid Build Coastguard Worker if (kDebugLRT) {
589*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "+++ removed entry and left hole at " << entry_index;
590*795d594fSAndroid Build Coastguard Worker }
591*795d594fSAndroid Build Coastguard Worker }
592*795d594fSAndroid Build Coastguard Worker
593*795d594fSAndroid Build Coastguard Worker return true;
594*795d594fSAndroid Build Coastguard Worker }
595*795d594fSAndroid Build Coastguard Worker
AssertEmpty()596*795d594fSAndroid Build Coastguard Worker void LocalReferenceTable::AssertEmpty() {
597*795d594fSAndroid Build Coastguard Worker CHECK_EQ(Capacity(), 0u) << "Internal Error: non-empty local reference table.";
598*795d594fSAndroid Build Coastguard Worker }
599*795d594fSAndroid Build Coastguard Worker
Trim()600*795d594fSAndroid Build Coastguard Worker void LocalReferenceTable::Trim() {
601*795d594fSAndroid Build Coastguard Worker ScopedTrace trace(__PRETTY_FUNCTION__);
602*795d594fSAndroid Build Coastguard Worker const size_t num_mem_maps = table_mem_maps_.size();
603*795d594fSAndroid Build Coastguard Worker if (num_mem_maps == 0u) {
604*795d594fSAndroid Build Coastguard Worker // Only small tables; nothing to do here. (Do not unnecessarily prune popped free entries.)
605*795d594fSAndroid Build Coastguard Worker return;
606*795d594fSAndroid Build Coastguard Worker }
607*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(tables_.size(), num_mem_maps + MaxSmallTables());
608*795d594fSAndroid Build Coastguard Worker const size_t top_index = segment_state_.top_index;
609*795d594fSAndroid Build Coastguard Worker // Prune popped free entries before potentially losing their memory.
610*795d594fSAndroid Build Coastguard Worker if (UNLIKELY(GetFirstFreeIndex() != kFreeListEnd) &&
611*795d594fSAndroid Build Coastguard Worker UNLIKELY(GetFirstFreeIndex() >= segment_state_.top_index)) {
612*795d594fSAndroid Build Coastguard Worker PrunePoppedFreeEntries([&](size_t index) { return GetEntry(index); });
613*795d594fSAndroid Build Coastguard Worker }
614*795d594fSAndroid Build Coastguard Worker // Small tables can hold as many entries as the next table.
615*795d594fSAndroid Build Coastguard Worker const size_t small_tables_capacity = GetTableSize(MaxSmallTables());
616*795d594fSAndroid Build Coastguard Worker size_t mem_map_index = 0u;
617*795d594fSAndroid Build Coastguard Worker if (top_index > small_tables_capacity) {
618*795d594fSAndroid Build Coastguard Worker const size_t table_size = TruncToPowerOfTwo(top_index);
619*795d594fSAndroid Build Coastguard Worker const size_t table_index = NumTablesForSize(table_size);
620*795d594fSAndroid Build Coastguard Worker const size_t start_index = top_index - table_size;
621*795d594fSAndroid Build Coastguard Worker mem_map_index = table_index - MaxSmallTables();
622*795d594fSAndroid Build Coastguard Worker if (start_index != 0u) {
623*795d594fSAndroid Build Coastguard Worker ++mem_map_index;
624*795d594fSAndroid Build Coastguard Worker LrtEntry* table = tables_[table_index];
625*795d594fSAndroid Build Coastguard Worker uint8_t* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table[start_index]), gPageSize);
626*795d594fSAndroid Build Coastguard Worker uint8_t* release_end = reinterpret_cast<uint8_t*>(&table[table_size]);
627*795d594fSAndroid Build Coastguard Worker DCHECK_GE(reinterpret_cast<uintptr_t>(release_end),
628*795d594fSAndroid Build Coastguard Worker reinterpret_cast<uintptr_t>(release_start));
629*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED_PARAM(release_end, gPageSize);
630*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED_PARAM(release_end - release_start, gPageSize);
631*795d594fSAndroid Build Coastguard Worker if (release_start != release_end) {
632*795d594fSAndroid Build Coastguard Worker madvise(release_start, release_end - release_start, MADV_DONTNEED);
633*795d594fSAndroid Build Coastguard Worker }
634*795d594fSAndroid Build Coastguard Worker }
635*795d594fSAndroid Build Coastguard Worker }
636*795d594fSAndroid Build Coastguard Worker for (MemMap& mem_map : ArrayRef<MemMap>(table_mem_maps_).SubArray(mem_map_index)) {
637*795d594fSAndroid Build Coastguard Worker madvise(mem_map.Begin(), mem_map.Size(), MADV_DONTNEED);
638*795d594fSAndroid Build Coastguard Worker }
639*795d594fSAndroid Build Coastguard Worker }
640*795d594fSAndroid Build Coastguard Worker
641*795d594fSAndroid Build Coastguard Worker template <typename Visitor>
VisitRootsInternal(Visitor && visitor) const642*795d594fSAndroid Build Coastguard Worker void LocalReferenceTable::VisitRootsInternal(Visitor&& visitor) const {
643*795d594fSAndroid Build Coastguard Worker auto visit_table = [&](LrtEntry* table, size_t count) REQUIRES_SHARED(Locks::mutator_lock_) {
644*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i != count; ) {
645*795d594fSAndroid Build Coastguard Worker LrtEntry* entry;
646*795d594fSAndroid Build Coastguard Worker if (i % kCheckJniEntriesPerReference == 0u && table[i].IsSerialNumber()) {
647*795d594fSAndroid Build Coastguard Worker entry = &table[i + table[i].GetSerialNumber()];
648*795d594fSAndroid Build Coastguard Worker i += kCheckJniEntriesPerReference;
649*795d594fSAndroid Build Coastguard Worker DCHECK_LE(i, count);
650*795d594fSAndroid Build Coastguard Worker } else {
651*795d594fSAndroid Build Coastguard Worker entry = &table[i];
652*795d594fSAndroid Build Coastguard Worker i += 1u;
653*795d594fSAndroid Build Coastguard Worker }
654*795d594fSAndroid Build Coastguard Worker DCHECK(!entry->IsSerialNumber());
655*795d594fSAndroid Build Coastguard Worker if (!entry->IsFree()) {
656*795d594fSAndroid Build Coastguard Worker GcRoot<mirror::Object>* root = entry->GetRootAddress();
657*795d594fSAndroid Build Coastguard Worker DCHECK(!root->IsNull());
658*795d594fSAndroid Build Coastguard Worker visitor(root);
659*795d594fSAndroid Build Coastguard Worker }
660*795d594fSAndroid Build Coastguard Worker }
661*795d594fSAndroid Build Coastguard Worker };
662*795d594fSAndroid Build Coastguard Worker
663*795d594fSAndroid Build Coastguard Worker if (small_table_ != nullptr) {
664*795d594fSAndroid Build Coastguard Worker visit_table(small_table_, segment_state_.top_index);
665*795d594fSAndroid Build Coastguard Worker } else {
666*795d594fSAndroid Build Coastguard Worker uint32_t remaining = segment_state_.top_index;
667*795d594fSAndroid Build Coastguard Worker size_t table_index = 0u;
668*795d594fSAndroid Build Coastguard Worker while (remaining != 0u) {
669*795d594fSAndroid Build Coastguard Worker size_t count = std::min<size_t>(remaining, GetTableSize(table_index));
670*795d594fSAndroid Build Coastguard Worker visit_table(tables_[table_index], count);
671*795d594fSAndroid Build Coastguard Worker ++table_index;
672*795d594fSAndroid Build Coastguard Worker remaining -= count;
673*795d594fSAndroid Build Coastguard Worker }
674*795d594fSAndroid Build Coastguard Worker }
675*795d594fSAndroid Build Coastguard Worker }
676*795d594fSAndroid Build Coastguard Worker
VisitRoots(RootVisitor * visitor,const RootInfo & root_info)677*795d594fSAndroid Build Coastguard Worker void LocalReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
678*795d594fSAndroid Build Coastguard Worker BufferedRootVisitor<kDefaultBufferedRootCount> root_visitor(visitor, root_info);
679*795d594fSAndroid Build Coastguard Worker VisitRootsInternal([&](GcRoot<mirror::Object>* root) REQUIRES_SHARED(Locks::mutator_lock_) {
680*795d594fSAndroid Build Coastguard Worker root_visitor.VisitRoot(*root);
681*795d594fSAndroid Build Coastguard Worker });
682*795d594fSAndroid Build Coastguard Worker }
683*795d594fSAndroid Build Coastguard Worker
Dump(std::ostream & os) const684*795d594fSAndroid Build Coastguard Worker void LocalReferenceTable::Dump(std::ostream& os) const {
685*795d594fSAndroid Build Coastguard Worker os << kLocal << " table dump:\n";
686*795d594fSAndroid Build Coastguard Worker ReferenceTable::Table entries;
687*795d594fSAndroid Build Coastguard Worker VisitRootsInternal([&](GcRoot<mirror::Object>* root) REQUIRES_SHARED(Locks::mutator_lock_) {
688*795d594fSAndroid Build Coastguard Worker entries.push_back(*root);
689*795d594fSAndroid Build Coastguard Worker });
690*795d594fSAndroid Build Coastguard Worker ReferenceTable::Dump(os, entries);
691*795d594fSAndroid Build Coastguard Worker }
692*795d594fSAndroid Build Coastguard Worker
EnsureFreeCapacity(size_t free_capacity,std::string * error_msg)693*795d594fSAndroid Build Coastguard Worker bool LocalReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::string* error_msg) {
694*795d594fSAndroid Build Coastguard Worker // TODO: Pass `previous_state` so that we can check holes.
695*795d594fSAndroid Build Coastguard Worker DCHECK_GE(free_capacity, static_cast<size_t>(1));
696*795d594fSAndroid Build Coastguard Worker size_t top_index = segment_state_.top_index;
697*795d594fSAndroid Build Coastguard Worker DCHECK_LE(top_index, max_entries_);
698*795d594fSAndroid Build Coastguard Worker
699*795d594fSAndroid Build Coastguard Worker if (IsCheckJniEnabled()) {
700*795d594fSAndroid Build Coastguard Worker // High values lead to the maximum size check failing below.
701*795d594fSAndroid Build Coastguard Worker if (free_capacity >= std::numeric_limits<size_t>::max() / kCheckJniEntriesPerReference) {
702*795d594fSAndroid Build Coastguard Worker free_capacity = std::numeric_limits<size_t>::max();
703*795d594fSAndroid Build Coastguard Worker } else {
704*795d594fSAndroid Build Coastguard Worker free_capacity *= kCheckJniEntriesPerReference;
705*795d594fSAndroid Build Coastguard Worker }
706*795d594fSAndroid Build Coastguard Worker }
707*795d594fSAndroid Build Coastguard Worker
708*795d594fSAndroid Build Coastguard Worker // TODO: Include holes from the current segment in the calculation.
709*795d594fSAndroid Build Coastguard Worker if (free_capacity <= max_entries_ - top_index) {
710*795d594fSAndroid Build Coastguard Worker return true;
711*795d594fSAndroid Build Coastguard Worker }
712*795d594fSAndroid Build Coastguard Worker
713*795d594fSAndroid Build Coastguard Worker if (free_capacity > kMaxTableSize - top_index) {
714*795d594fSAndroid Build Coastguard Worker *error_msg = android::base::StringPrintf(
715*795d594fSAndroid Build Coastguard Worker "Requested size exceeds maximum: %zu > %zu (%zu used)",
716*795d594fSAndroid Build Coastguard Worker free_capacity,
717*795d594fSAndroid Build Coastguard Worker kMaxTableSize - top_index,
718*795d594fSAndroid Build Coastguard Worker top_index);
719*795d594fSAndroid Build Coastguard Worker return false;
720*795d594fSAndroid Build Coastguard Worker }
721*795d594fSAndroid Build Coastguard Worker
722*795d594fSAndroid Build Coastguard Worker // Try to increase the table size.
723*795d594fSAndroid Build Coastguard Worker if (!Resize(top_index + free_capacity, error_msg)) {
724*795d594fSAndroid Build Coastguard Worker LOG(WARNING) << "JNI ERROR: Unable to reserve space in EnsureFreeCapacity (" << free_capacity
725*795d594fSAndroid Build Coastguard Worker << "): " << std::endl
726*795d594fSAndroid Build Coastguard Worker << MutatorLockedDumpable<LocalReferenceTable>(*this)
727*795d594fSAndroid Build Coastguard Worker << " Resizing failed: " << *error_msg;
728*795d594fSAndroid Build Coastguard Worker return false;
729*795d594fSAndroid Build Coastguard Worker }
730*795d594fSAndroid Build Coastguard Worker return true;
731*795d594fSAndroid Build Coastguard Worker }
732*795d594fSAndroid Build Coastguard Worker
FreeCapacity() const733*795d594fSAndroid Build Coastguard Worker size_t LocalReferenceTable::FreeCapacity() const {
734*795d594fSAndroid Build Coastguard Worker // TODO: Include holes in current segment.
735*795d594fSAndroid Build Coastguard Worker if (IsCheckJniEnabled()) {
736*795d594fSAndroid Build Coastguard Worker DCHECK_ALIGNED(max_entries_, kCheckJniEntriesPerReference);
737*795d594fSAndroid Build Coastguard Worker // The `segment_state_.top_index` is not necessarily aligned; rounding down.
738*795d594fSAndroid Build Coastguard Worker return (max_entries_ - segment_state_.top_index) / kCheckJniEntriesPerReference;
739*795d594fSAndroid Build Coastguard Worker } else {
740*795d594fSAndroid Build Coastguard Worker return max_entries_ - segment_state_.top_index;
741*795d594fSAndroid Build Coastguard Worker }
742*795d594fSAndroid Build Coastguard Worker }
743*795d594fSAndroid Build Coastguard Worker
744*795d594fSAndroid Build Coastguard Worker } // namespace jni
745*795d594fSAndroid Build Coastguard Worker } // namespace art
746