1 /*
2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 #include "precompiled.hpp"
26 #include "gc/shared/oopStorage.hpp"
27 #include "jvmtifiles/jvmtiEnv.hpp"
28 #include "logging/log.hpp"
29 #include "memory/allocation.hpp"
30 #include "memory/resourceArea.hpp"
31 #include "memory/universe.hpp"
32 #include "oops/oop.inline.hpp"
33 #include "oops/weakHandle.inline.hpp"
34 #include "prims/jvmtiEventController.inline.hpp"
35 #include "prims/jvmtiExport.hpp"
36 #include "prims/jvmtiTagMapTable.hpp"
37 #include "utilities/hashtable.inline.hpp"
38 #include "utilities/macros.hpp"
39 
object()40 oop JvmtiTagMapEntry::object() {
41   return literal().resolve();
42 }
43 
object_no_keepalive()44 oop JvmtiTagMapEntry::object_no_keepalive() {
45   // Just peek at the object without keeping it alive.
46   return literal().peek();
47 }
48 
JvmtiTagMapTable()49 JvmtiTagMapTable::JvmtiTagMapTable()
50   : Hashtable<WeakHandle, mtServiceability>(_table_size, sizeof(JvmtiTagMapEntry)) {}
51 
clear()52 void JvmtiTagMapTable::clear() {
53   // Clear this table
54   log_debug(jvmti, table)("JvmtiTagMapTable cleared");
55   for (int i = 0; i < table_size(); ++i) {
56     for (JvmtiTagMapEntry* m = bucket(i); m != NULL;) {
57       JvmtiTagMapEntry* entry = m;
58       // read next before freeing.
59       m = m->next();
60       free_entry(entry);
61     }
62     JvmtiTagMapEntry** p = bucket_addr(i);
63     *p = NULL; // clear out buckets.
64   }
65   assert(number_of_entries() == 0, "should have removed all entries");
66   assert(new_entry_free_list() == NULL, "entry present on JvmtiTagMapTable's free list");
67 }
68 
~JvmtiTagMapTable()69 JvmtiTagMapTable::~JvmtiTagMapTable() {
70   clear();
71   // base class ~BasicHashtable deallocates the buckets.
72 }
73 
74 // Entries are C_Heap allocated
new_entry(unsigned int hash,WeakHandle w,jlong tag)75 JvmtiTagMapEntry* JvmtiTagMapTable::new_entry(unsigned int hash, WeakHandle w, jlong tag) {
76   JvmtiTagMapEntry* entry = (JvmtiTagMapEntry*)Hashtable<WeakHandle, mtServiceability>::allocate_new_entry(hash, w);
77   entry->set_tag(tag);
78   return entry;
79 }
80 
free_entry(JvmtiTagMapEntry * entry)81 void JvmtiTagMapTable::free_entry(JvmtiTagMapEntry* entry) {
82   unlink_entry(entry);
83   entry->literal().release(JvmtiExport::weak_tag_storage()); // release to OopStorage
84   FREE_C_HEAP_ARRAY(char, entry);
85 }
86 
compute_hash(oop obj)87 unsigned int JvmtiTagMapTable::compute_hash(oop obj) {
88   assert(obj != NULL, "obj is null");
89   return Universe::heap()->hash_oop(obj);
90 }
91 
find(int index,unsigned int hash,oop obj)92 JvmtiTagMapEntry* JvmtiTagMapTable::find(int index, unsigned int hash, oop obj) {
93   assert(obj != NULL, "Cannot search for a NULL object");
94 
95   for (JvmtiTagMapEntry* p = bucket(index); p != NULL; p = p->next()) {
96     if (p->hash() == hash) {
97 
98       // Peek the object to check if it is the right target.
99       oop target = p->object_no_keepalive();
100 
101       // The obj is in the table as a target already
102       if (target == obj) {
103         ResourceMark rm;
104         log_trace(jvmti, table)("JvmtiTagMap entry found for %s index %d",
105                                 obj->print_value_string(), index);
106         // The object() accessor makes sure the target object is kept alive before
107         // leaking out.
108         (void)p->object();
109         return p;
110       }
111     }
112   }
113   return NULL;
114 }
115 
find(oop obj)116 JvmtiTagMapEntry* JvmtiTagMapTable::find(oop obj) {
117   unsigned int hash = compute_hash(obj);
118   int index = hash_to_index(hash);
119   return find(index, hash, obj);
120 }
121 
add(oop obj,jlong tag)122 JvmtiTagMapEntry* JvmtiTagMapTable::add(oop obj, jlong tag) {
123   unsigned int hash = compute_hash(obj);
124   int index = hash_to_index(hash);
125   // One was added while acquiring the lock
126   assert(find(index, hash, obj) == NULL, "shouldn't already be present");
127 
128   // obj was read with AS_NO_KEEPALIVE, or equivalent.
129   // The object needs to be kept alive when it is published.
130   Universe::heap()->keep_alive(obj);
131 
132   WeakHandle w(JvmtiExport::weak_tag_storage(), obj);
133   JvmtiTagMapEntry* p = new_entry(hash, w, tag);
134   Hashtable<WeakHandle, mtServiceability>::add_entry(index, p);
135   ResourceMark rm;
136   log_trace(jvmti, table)("JvmtiTagMap entry added for %s index %d",
137                           obj->print_value_string(), index);
138 
139   // Resize if the table is getting too big.
140   resize_if_needed();
141 
142   return p;
143 }
144 
remove(oop obj)145 void JvmtiTagMapTable::remove(oop obj) {
146   unsigned int hash = compute_hash(obj);
147   int index = hash_to_index(hash);
148   JvmtiTagMapEntry** p = bucket_addr(index);
149   JvmtiTagMapEntry* entry = bucket(index);
150   while (entry != NULL) {
151     oop target = entry->object_no_keepalive();
152     if (target != NULL && target == obj) {
153       log_trace(jvmti, table)("JvmtiTagMap entry removed for index %d", index);
154       *p = entry->next();
155       free_entry(entry);
156       return; // done
157     }
158     // get next entry and address
159     p = entry->next_addr();
160     entry = entry->next();
161   }
162 }
163 
entry_iterate(JvmtiTagMapEntryClosure * closure)164 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapEntryClosure* closure) {
165   for (int i = 0; i < table_size(); ++i) {
166     for (JvmtiTagMapEntry* p = bucket(i); p != NULL; p = p->next()) {
167       closure->do_entry(p);
168     }
169   }
170 }
171 
172 const int _resize_load_trigger = 5;       // load factor that will trigger the resize
173 static bool _resizable = true;
174 
resize_if_needed()175 void JvmtiTagMapTable::resize_if_needed() {
176   if (_resizable && number_of_entries() > (_resize_load_trigger*table_size())) {
177     int desired_size = calculate_resize(true);
178     if (desired_size == table_size()) {
179       _resizable = false; // hit max
180     } else {
181       if (!resize(desired_size)) {
182         // Something went wrong, turn resizing off
183         _resizable = false;
184       }
185       log_info(jvmti, table) ("JvmtiTagMap table resized to %d", table_size());
186     }
187   }
188 }
189 
190 // Serially remove entries for dead oops from the table, and notify jvmti.
remove_dead_entries(JvmtiEnv * env,bool post_object_free)191 void JvmtiTagMapTable::remove_dead_entries(JvmtiEnv* env, bool post_object_free) {
192   int oops_removed = 0;
193   int oops_counted = 0;
194   for (int i = 0; i < table_size(); ++i) {
195     JvmtiTagMapEntry** p = bucket_addr(i);
196     JvmtiTagMapEntry* entry = bucket(i);
197     while (entry != NULL) {
198       oops_counted++;
199       oop l = entry->object_no_keepalive();
200       if (l != NULL) {
201         p = entry->next_addr();
202       } else {
203         // Entry has been removed.
204         oops_removed++;
205         log_trace(jvmti, table)("JvmtiTagMap entry removed for index %d", i);
206         jlong tag = entry->tag();
207         *p = entry->next();
208         free_entry(entry);
209 
210         // post the event to the profiler
211         if (post_object_free) {
212           JvmtiExport::post_object_free(env, tag);
213         }
214 
215       }
216       // get next entry
217       entry = (JvmtiTagMapEntry*)HashtableEntry<WeakHandle, mtServiceability>::make_ptr(*p);
218     }
219   }
220 
221   log_info(jvmti, table) ("JvmtiTagMap entries counted %d removed %d; %s",
222                           oops_counted, oops_removed, post_object_free ? "free object posted" : "no posting");
223 }
224 
225 // Rehash oops in the table
rehash()226 void JvmtiTagMapTable::rehash() {
227   ResourceMark rm;
228   GrowableArray<JvmtiTagMapEntry*> moved_entries;
229 
230   int oops_counted = 0;
231   for (int i = 0; i < table_size(); ++i) {
232     JvmtiTagMapEntry** p = bucket_addr(i);
233     JvmtiTagMapEntry* entry = bucket(i);
234     while (entry != NULL) {
235       oops_counted++;
236       oop l = entry->object_no_keepalive();
237       if (l != NULL) {
238         // Check if oop has moved, ie its hashcode is different
239         // than the one entered in the table.
240         unsigned int new_hash = compute_hash(l);
241         if (entry->hash() != new_hash) {
242           *p = entry->next();
243           entry->set_hash(new_hash);
244           unlink_entry(entry);
245           moved_entries.push(entry);
246         } else {
247           p = entry->next_addr();
248         }
249       } else {
250         // Skip removed oops. They may still have to be posted.
251         p = entry->next_addr();
252       }
253       // get next entry
254       entry = (JvmtiTagMapEntry*)HashtableEntry<WeakHandle, mtServiceability>::make_ptr(*p);
255     }
256   }
257 
258   int rehash_len = moved_entries.length();
259   // Now add back in the entries that were removed.
260   for (int i = 0; i < rehash_len; i++) {
261     JvmtiTagMapEntry* moved_entry = moved_entries.at(i);
262     int index = hash_to_index(moved_entry->hash());
263     Hashtable<WeakHandle, mtServiceability>::add_entry(index, moved_entry);
264   }
265 
266   log_info(jvmti, table) ("JvmtiTagMap entries counted %d rehashed %d",
267                           oops_counted, rehash_len);
268 }
269