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