• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "nativehelper/ScopedLocalRef.h"
48 #include "runtime.h"
49 
50 namespace openjdkjvmti {
51 
52 template <typename T>
Lock()53 void JvmtiWeakTable<T>::Lock() {
54   allow_disallow_lock_.ExclusiveLock(art::Thread::Current());
55 }
56 template <typename T>
Unlock()57 void JvmtiWeakTable<T>::Unlock() {
58   allow_disallow_lock_.ExclusiveUnlock(art::Thread::Current());
59 }
60 template <typename T>
AssertLocked()61 void JvmtiWeakTable<T>::AssertLocked() {
62   allow_disallow_lock_.AssertHeld(art::Thread::Current());
63 }
64 
65 template <typename T>
UpdateTableWithReadBarrier()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>
GetTagSlowPath(art::Thread * self,art::mirror::Object * obj,T * result)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>
Remove(art::mirror::Object * obj,T * tag)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>
RemoveLocked(art::mirror::Object * obj,T * tag)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>
RemoveLocked(art::Thread * self,art::mirror::Object * obj,T * tag)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>
Set(art::mirror::Object * obj,T new_tag)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>
SetLocked(art::mirror::Object * obj,T new_tag)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>
SetLocked(art::Thread * self,art::mirror::Object * obj,T new_tag)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>
Sweep(art::IsMarkedVisitor * visitor)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>
SweepImpl(art::IsMarkedVisitor * visitor)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>
UpdateTableWith(Updater & updater)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