1 /* Copyright (C) 2017 The Android Open Source Project 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This file implements interfaces from the file jvmti.h. This implementation 5 * is licensed under the same terms as the file jvmti.h. The 6 * copyright and license information for the file jvmti.h follows. 7 * 8 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 9 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10 * 11 * This code is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License version 2 only, as 13 * published by the Free Software Foundation. Oracle designates this 14 * particular file as subject to the "Classpath" exception as provided 15 * by Oracle in the LICENSE file that accompanied this code. 16 * 17 * This code is distributed in the hope that it will be useful, but WITHOUT 18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 20 * version 2 for more details (a copy is included in the LICENSE file that 21 * accompanied this code). 22 * 23 * You should have received a copy of the GNU General Public License version 24 * 2 along with this work; if not, write to the Free Software Foundation, 25 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 26 * 27 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 28 * or visit www.oracle.com if you need additional information or have any 29 * questions. 30 */ 31 32 #ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ 33 #define ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ 34 35 #include "jvmti_weak_table.h" 36 37 #include <limits> 38 39 #include "art_jvmti.h" 40 #include "base/logging.h" 41 #include "gc/allocation_listener.h" 42 #include "instrumentation.h" 43 #include "jni_env_ext-inl.h" 44 #include "jvmti_allocator.h" 45 #include "mirror/class.h" 46 #include "mirror/object.h" 47 #include "runtime.h" 48 #include "ScopedLocalRef.h" 49 50 namespace openjdkjvmti { 51 52 template <typename T> 53 void JvmtiWeakTable<T>::Lock() { 54 allow_disallow_lock_.ExclusiveLock(art::Thread::Current()); 55 } 56 template <typename T> 57 void JvmtiWeakTable<T>::Unlock() { 58 allow_disallow_lock_.ExclusiveUnlock(art::Thread::Current()); 59 } 60 template <typename T> 61 void JvmtiWeakTable<T>::AssertLocked() { 62 allow_disallow_lock_.AssertHeld(art::Thread::Current()); 63 } 64 65 template <typename T> 66 void JvmtiWeakTable<T>::UpdateTableWithReadBarrier() { 67 update_since_last_sweep_ = true; 68 69 auto WithReadBarrierUpdater = [&](const art::GcRoot<art::mirror::Object>& original_root, 70 art::mirror::Object* original_obj ATTRIBUTE_UNUSED) 71 REQUIRES_SHARED(art::Locks::mutator_lock_) { 72 return original_root.Read<art::kWithReadBarrier>(); 73 }; 74 75 UpdateTableWith<decltype(WithReadBarrierUpdater), kIgnoreNull>(WithReadBarrierUpdater); 76 } 77 78 template <typename T> 79 bool JvmtiWeakTable<T>::GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, T* result) { 80 // Under concurrent GC, there is a window between moving objects and sweeping of system 81 // weaks in which mutators are active. We may receive a to-space object pointer in obj, 82 // but still have from-space pointers in the table. Explicitly update the table once. 83 // Note: this will keep *all* objects in the table live, but should be a rare occurrence. 84 UpdateTableWithReadBarrier(); 85 return GetTagLocked(self, obj, result); 86 } 87 88 template <typename T> 89 bool JvmtiWeakTable<T>::Remove(art::mirror::Object* obj, /* out */ T* tag) { 90 art::Thread* self = art::Thread::Current(); 91 art::MutexLock mu(self, allow_disallow_lock_); 92 Wait(self); 93 94 return RemoveLocked(self, obj, tag); 95 } 96 template <typename T> 97 bool JvmtiWeakTable<T>::RemoveLocked(art::mirror::Object* obj, T* tag) { 98 art::Thread* self = art::Thread::Current(); 99 allow_disallow_lock_.AssertHeld(self); 100 Wait(self); 101 102 return RemoveLocked(self, obj, tag); 103 } 104 105 template <typename T> 106 bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::mirror::Object* obj, T* tag) { 107 auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); 108 if (it != tagged_objects_.end()) { 109 if (tag != nullptr) { 110 *tag = it->second; 111 } 112 tagged_objects_.erase(it); 113 return true; 114 } 115 116 if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { 117 // Under concurrent GC, there is a window between moving objects and sweeping of system 118 // weaks in which mutators are active. We may receive a to-space object pointer in obj, 119 // but still have from-space pointers in the table. Explicitly update the table once. 120 // Note: this will keep *all* objects in the table live, but should be a rare occurrence. 121 122 // Update the table. 123 UpdateTableWithReadBarrier(); 124 125 // And try again. 126 return RemoveLocked(self, obj, tag); 127 } 128 129 // Not in here. 130 return false; 131 } 132 133 template <typename T> 134 bool JvmtiWeakTable<T>::Set(art::mirror::Object* obj, T new_tag) { 135 art::Thread* self = art::Thread::Current(); 136 art::MutexLock mu(self, allow_disallow_lock_); 137 Wait(self); 138 139 return SetLocked(self, obj, new_tag); 140 } 141 template <typename T> 142 bool JvmtiWeakTable<T>::SetLocked(art::mirror::Object* obj, T new_tag) { 143 art::Thread* self = art::Thread::Current(); 144 allow_disallow_lock_.AssertHeld(self); 145 Wait(self); 146 147 return SetLocked(self, obj, new_tag); 148 } 149 150 template <typename T> 151 bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::mirror::Object* obj, T new_tag) { 152 auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); 153 if (it != tagged_objects_.end()) { 154 it->second = new_tag; 155 return true; 156 } 157 158 if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { 159 // Under concurrent GC, there is a window between moving objects and sweeping of system 160 // weaks in which mutators are active. We may receive a to-space object pointer in obj, 161 // but still have from-space pointers in the table. Explicitly update the table once. 162 // Note: this will keep *all* objects in the table live, but should be a rare occurrence. 163 164 // Update the table. 165 UpdateTableWithReadBarrier(); 166 167 // And try again. 168 return SetLocked(self, obj, new_tag); 169 } 170 171 // New element. 172 auto insert_it = tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(obj), new_tag); 173 DCHECK(insert_it.second); 174 return false; 175 } 176 177 template <typename T> 178 void JvmtiWeakTable<T>::Sweep(art::IsMarkedVisitor* visitor) { 179 if (DoesHandleNullOnSweep()) { 180 SweepImpl<true>(visitor); 181 } else { 182 SweepImpl<false>(visitor); 183 } 184 185 // Under concurrent GC, there is a window between moving objects and sweeping of system 186 // weaks in which mutators are active. We may receive a to-space object pointer in obj, 187 // but still have from-space pointers in the table. We explicitly update the table then 188 // to ensure we compare against to-space pointers. But we want to do this only once. Once 189 // sweeping is done, we know all objects are to-space pointers until the next GC cycle, 190 // so we re-enable the explicit update for the next marking. 191 update_since_last_sweep_ = false; 192 } 193 194 template <typename T> 195 template <bool kHandleNull> 196 void JvmtiWeakTable<T>::SweepImpl(art::IsMarkedVisitor* visitor) { 197 art::Thread* self = art::Thread::Current(); 198 art::MutexLock mu(self, allow_disallow_lock_); 199 200 auto IsMarkedUpdater = [&](const art::GcRoot<art::mirror::Object>& original_root ATTRIBUTE_UNUSED, 201 art::mirror::Object* original_obj) { 202 return visitor->IsMarked(original_obj); 203 }; 204 205 UpdateTableWith<decltype(IsMarkedUpdater), 206 kHandleNull ? kCallHandleNull : kRemoveNull>(IsMarkedUpdater); 207 } 208 209 template <typename T> 210 template <typename Updater, typename JvmtiWeakTable<T>::TableUpdateNullTarget kTargetNull> 211 ALWAYS_INLINE inline void JvmtiWeakTable<T>::UpdateTableWith(Updater& updater) { 212 // We optimistically hope that elements will still be well-distributed when re-inserting them. 213 // So play with the map mechanics, and postpone rehashing. This avoids the need of a side 214 // vector and two passes. 215 float original_max_load_factor = tagged_objects_.max_load_factor(); 216 tagged_objects_.max_load_factor(std::numeric_limits<float>::max()); 217 // For checking that a max load-factor actually does what we expect. 218 size_t original_bucket_count = tagged_objects_.bucket_count(); 219 220 for (auto it = tagged_objects_.begin(); it != tagged_objects_.end();) { 221 DCHECK(!it->first.IsNull()); 222 art::mirror::Object* original_obj = it->first.template Read<art::kWithoutReadBarrier>(); 223 art::mirror::Object* target_obj = updater(it->first, original_obj); 224 if (original_obj != target_obj) { 225 if (kTargetNull == kIgnoreNull && target_obj == nullptr) { 226 // Ignore null target, don't do anything. 227 } else { 228 T tag = it->second; 229 it = tagged_objects_.erase(it); 230 if (target_obj != nullptr) { 231 tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(target_obj), tag); 232 DCHECK_EQ(original_bucket_count, tagged_objects_.bucket_count()); 233 } else if (kTargetNull == kCallHandleNull) { 234 HandleNullSweep(tag); 235 } 236 continue; // Iterator was implicitly updated by erase. 237 } 238 } 239 it++; 240 } 241 242 tagged_objects_.max_load_factor(original_max_load_factor); 243 // TODO: consider rehash here. 244 } 245 246 template <typename T> 247 template <typename Storage, class Allocator> 248 struct JvmtiWeakTable<T>::ReleasableContainer { 249 using allocator_type = Allocator; 250 251 explicit ReleasableContainer(const allocator_type& alloc, size_t reserve = 10) 252 : allocator(alloc), 253 data(reserve > 0 ? allocator.allocate(reserve) : nullptr), 254 size(0), 255 capacity(reserve) { 256 } 257 258 ~ReleasableContainer() { 259 if (data != nullptr) { 260 allocator.deallocate(data, capacity); 261 capacity = 0; 262 size = 0; 263 } 264 } 265 266 Storage* Release() { 267 Storage* tmp = data; 268 269 data = nullptr; 270 size = 0; 271 capacity = 0; 272 273 return tmp; 274 } 275 276 void Resize(size_t new_capacity) { 277 CHECK_GT(new_capacity, capacity); 278 279 Storage* tmp = allocator.allocate(new_capacity); 280 DCHECK(tmp != nullptr); 281 if (data != nullptr) { 282 memcpy(tmp, data, sizeof(Storage) * size); 283 } 284 Storage* old = data; 285 data = tmp; 286 allocator.deallocate(old, capacity); 287 capacity = new_capacity; 288 } 289 290 void Pushback(const Storage& elem) { 291 if (size == capacity) { 292 size_t new_capacity = 2 * capacity + 1; 293 Resize(new_capacity); 294 } 295 data[size++] = elem; 296 } 297 298 Allocator allocator; 299 Storage* data; 300 size_t size; 301 size_t capacity; 302 }; 303 304 template <typename T> 305 jvmtiError JvmtiWeakTable<T>::GetTaggedObjects(jvmtiEnv* jvmti_env, 306 jint tag_count, 307 const T* tags, 308 jint* count_ptr, 309 jobject** object_result_ptr, 310 T** tag_result_ptr) { 311 if (tag_count < 0) { 312 return ERR(ILLEGAL_ARGUMENT); 313 } 314 if (tag_count > 0) { 315 for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { 316 if (tags[i] == 0) { 317 return ERR(ILLEGAL_ARGUMENT); 318 } 319 } 320 } 321 if (tags == nullptr) { 322 return ERR(NULL_POINTER); 323 } 324 if (count_ptr == nullptr) { 325 return ERR(NULL_POINTER); 326 } 327 328 art::Thread* self = art::Thread::Current(); 329 art::MutexLock mu(self, allow_disallow_lock_); 330 Wait(self); 331 332 art::JNIEnvExt* jni_env = self->GetJniEnv(); 333 334 constexpr size_t kDefaultSize = 10; 335 size_t initial_object_size; 336 size_t initial_tag_size; 337 if (tag_count == 0) { 338 initial_object_size = (object_result_ptr != nullptr) ? tagged_objects_.size() : 0; 339 initial_tag_size = (tag_result_ptr != nullptr) ? tagged_objects_.size() : 0; 340 } else { 341 initial_object_size = initial_tag_size = kDefaultSize; 342 } 343 JvmtiAllocator<void> allocator(jvmti_env); 344 ReleasableContainer<jobject, JvmtiAllocator<jobject>> selected_objects(allocator, 345 initial_object_size); 346 ReleasableContainer<T, JvmtiAllocator<T>> selected_tags(allocator, initial_tag_size); 347 348 size_t count = 0; 349 for (auto& pair : tagged_objects_) { 350 bool select; 351 if (tag_count > 0) { 352 select = false; 353 for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { 354 if (tags[i] == pair.second) { 355 select = true; 356 break; 357 } 358 } 359 } else { 360 select = true; 361 } 362 363 if (select) { 364 art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>(); 365 if (obj != nullptr) { 366 count++; 367 if (object_result_ptr != nullptr) { 368 selected_objects.Pushback(jni_env->AddLocalReference<jobject>(obj)); 369 } 370 if (tag_result_ptr != nullptr) { 371 selected_tags.Pushback(pair.second); 372 } 373 } 374 } 375 } 376 377 if (object_result_ptr != nullptr) { 378 *object_result_ptr = selected_objects.Release(); 379 } 380 if (tag_result_ptr != nullptr) { 381 *tag_result_ptr = selected_tags.Release(); 382 } 383 *count_ptr = static_cast<jint>(count); 384 return ERR(NONE); 385 } 386 387 template <typename T> 388 art::mirror::Object* JvmtiWeakTable<T>::Find(T tag) { 389 art::Thread* self = art::Thread::Current(); 390 art::MutexLock mu(self, allow_disallow_lock_); 391 Wait(self); 392 393 for (auto& pair : tagged_objects_) { 394 if (tag == pair.second) { 395 art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>(); 396 if (obj != nullptr) { 397 return obj; 398 } 399 } 400 } 401 return nullptr; 402 } 403 404 } // namespace openjdkjvmti 405 406 #endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ 407