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 "memory/allocation.hpp"
26 #include "memory/universe.hpp"
27 #include "oops/oop.inline.hpp"
28 #include "oops/weakHandle.inline.hpp"
29 #include "prims/jvmtiExport.hpp"
30 #include "prims/jvmtiTagMapTable.hpp"
31
32
33 JvmtiTagMapKey::JvmtiTagMapKey(oop obj) : _obj(obj) {}
34
35 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src) {
36 // move object into WeakHandle when copying into the table
37 if (src._obj != nullptr) {
38
39 // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
40 // a heap walk. The object needs to be kept alive when it is published.
41 Universe::heap()->keep_alive(src._obj);
42
43 _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj);
44 } else {
45 // resizing needs to create a copy.
46 _wh = src._wh;
47 }
48 // obj is always null after a copy.
49 _obj = nullptr;
50 }
51
52 void JvmtiTagMapKey::release_weak_handle() {
53 _wh.release(JvmtiExport::weak_tag_storage());
54 }
55
56 oop JvmtiTagMapKey::object() const {
57 assert(_obj == nullptr, "Must have a handle and not object");
58 return _wh.resolve();
59 }
60
61 oop JvmtiTagMapKey::object_no_keepalive() const {
62 assert(_obj == nullptr, "Must have a handle and not object");
63 return _wh.peek();
64 }
65
66 static const int INITIAL_TABLE_SIZE = 1007;
67 static const int MAX_TABLE_SIZE = 0x3fffffff;
68
69 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
70
71 void JvmtiTagMapTable::clear() {
72 struct RemoveAll {
73 bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
74 entry.release_weak_handle();
75 return true;
76 }
77 } remove_all;
78 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
79 // while iterating over all the elements of the table. If the do_entry() method returns true the element
80 // will be removed.
81 // In this case, we always return true from do_entry to clear all the elements.
82 _table.unlink(&remove_all);
83
84 assert(_table.number_of_entries() == 0, "should have removed all entries");
85 }
86
87 JvmtiTagMapTable::~JvmtiTagMapTable() {
88 clear();
89 }
90
91 jlong JvmtiTagMapTable::find(oop obj) {
92 if (is_empty()) {
93 return 0;
94 }
95
96 if (obj->fast_no_hash_check()) {
97 // Objects in the table all have a hashcode.
98 return 0;
99 }
100
101 JvmtiTagMapKey jtme(obj);
102 jlong* found = _table.get(jtme);
103 return found == nullptr ? 0 : *found;
104 }
105
106 void JvmtiTagMapTable::add(oop obj, jlong tag) {
107 JvmtiTagMapKey new_entry(obj);
108 bool is_added;
109 if (obj->fast_no_hash_check()) {
110 // Can't be in the table so add it fast.
111 is_added = _table.put_when_absent(new_entry, tag);
112 } else {
113 jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
114 *value = tag; // assign the new tag
115 }
116 if (is_added) {
117 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
118 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
119 log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
120 _table.table_size(), _table.number_of_entries(), max_bucket_size);
121 }
122 }
123 }
124
125 void JvmtiTagMapTable::remove(oop obj) {
126 JvmtiTagMapKey jtme(obj);
127 auto clean = [] (JvmtiTagMapKey& entry, jlong tag) {
128 entry.release_weak_handle();
129 };
130 _table.remove(jtme, clean);
131 }
132
133 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
134 _table.iterate(closure);
135 }
136
137 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
138 struct IsDead {
139 GrowableArray<jlong>* _objects;
140 IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
141 bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
142 if (entry.object_no_keepalive() == nullptr) {
143 if (_objects != nullptr) {
144 _objects->append(tag);
145 }
146 entry.release_weak_handle();
147 return true;
148 }
149 return false;;
150 }
151 } is_dead(objects);
152 _table.unlink(&is_dead);
153 }
|
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 "memory/allocation.hpp"
26 #include "memory/universe.hpp"
27 #include "oops/fieldStreams.inline.hpp"
28 #include "oops/inlineKlass.hpp"
29 #include "oops/oop.inline.hpp"
30 #include "oops/weakHandle.inline.hpp"
31 #include "prims/jvmtiExport.hpp"
32 #include "prims/jvmtiTagMapTable.hpp"
33
34
35 static unsigned get_value_object_hash(oop holder, int offset, Klass* klass) {
36 assert(klass->is_inline_klass(), "Must be InlineKlass");
37 // For inline types, use the klass as a hash code and let the equals match the obj.
38 // It might have a long bucket but TBD to improve this if a customer situation arises.
39 return (unsigned)((int64_t)klass >> 3);
40 }
41
42 static unsigned get_value_object_hash(const JvmtiHeapwalkObject & obj) {
43 assert(obj.is_value(), "Must be value class");
44 return get_value_object_hash(obj.obj(), obj.offset(), obj.inline_klass());
45 }
46
47 static bool equal_oops(oop obj1, oop obj2); // forward declaration
48
49 static bool equal_fields(char type, oop obj1, int offset1, oop obj2, int offset2) {
50 switch (type) {
51 case JVM_SIGNATURE_BOOLEAN:
52 return obj1->bool_field(offset1) == obj2->bool_field(offset2);
53 case JVM_SIGNATURE_CHAR:
54 return obj1->char_field(offset1) == obj2->char_field(offset2);
55 case JVM_SIGNATURE_FLOAT:
56 return obj1->float_field(offset1) == obj2->float_field(offset2);
57 case JVM_SIGNATURE_DOUBLE:
58 return obj1->double_field(offset1) == obj2->double_field(offset2);
59 case JVM_SIGNATURE_BYTE:
60 return obj1->byte_field(offset1) == obj2->byte_field(offset2);
61 case JVM_SIGNATURE_SHORT:
62 return obj1->short_field(offset1) == obj2->short_field(offset2);
63 case JVM_SIGNATURE_INT:
64 return obj1->int_field(offset1) == obj2->int_field(offset2);
65 case JVM_SIGNATURE_LONG:
66 return obj1->long_field(offset1) == obj2->long_field(offset2);
67 case JVM_SIGNATURE_CLASS:
68 case JVM_SIGNATURE_ARRAY:
69 return equal_oops(obj1->obj_field(offset1), obj2->obj_field(offset2));
70 }
71 ShouldNotReachHere();
72 }
73
74 static bool is_null_flat_field(oop obj, int offset, InlineKlass* klass) {
75 return klass->is_payload_marked_as_null(cast_from_oop<address>(obj) + offset);
76 }
77
78 // For heap-allocated objects offset is 0 and 'klass' is obj1->klass() (== obj2->klass()).
79 // For flattened objects offset is the offset in the holder object, 'klass' is inlined object class.
80 // The object must be prechecked for non-null values.
81 static bool equal_value_objects(oop obj1, int offset1, oop obj2, int offset2, InlineKlass* klass) {
82 for (JavaFieldStream fld(klass); !fld.done(); fld.next()) {
83 // ignore static fields
84 if (fld.access_flags().is_static()) {
85 continue;
86 }
87 int field_offset1 = offset1 + fld.offset() - (offset1 > 0 ? klass->payload_offset() : 0);
88 int field_offset2 = offset2 + fld.offset() - (offset2 > 0 ? klass->payload_offset() : 0);
89 if (fld.is_flat()) { // flat value field
90 InstanceKlass* holder_klass = fld.field_holder();
91 InlineKlass* field_klass = holder_klass->get_inline_type_field_klass(fld.index());
92 if (!fld.is_null_free_inline_type()) {
93 bool field1_is_null = is_null_flat_field(obj1, field_offset1, field_klass);
94 bool field2_is_null = is_null_flat_field(obj2, field_offset2, field_klass);
95 if (field1_is_null != field2_is_null) {
96 return false;
97 }
98 if (field1_is_null) { // if both fields are null, go to next field
99 continue;
100 }
101 }
102
103 if (!equal_value_objects(obj1, field_offset1, obj2, field_offset2, field_klass)) {
104 return false;
105 }
106 } else {
107 if (!equal_fields(fld.signature()->char_at(0), obj1, field_offset1, obj2, field_offset2)) {
108 return false;
109 }
110 }
111 }
112 return true;
113 }
114
115 // handles null oops
116 static bool equal_oops(oop obj1, oop obj2) {
117 if (obj1 == obj2) {
118 return true;
119 }
120
121 if (obj1 != nullptr && obj2 != nullptr && obj1->klass() == obj2->klass() && obj1->is_inline_type()) {
122 InlineKlass* vk = InlineKlass::cast(obj1->klass());
123 return equal_value_objects(obj1, 0, obj2, 0, vk);
124 }
125 return false;
126 }
127
128 bool JvmtiHeapwalkObject::equals(const JvmtiHeapwalkObject& obj1, const JvmtiHeapwalkObject& obj2) {
129 if (obj1 == obj2) { // the same oop/offset/inline_klass
130 return true;
131 }
132
133 if (obj1.is_value() && obj1.inline_klass() == obj2.inline_klass()) {
134 // instances of the same value class
135 return equal_value_objects(obj1.obj(), obj1.offset(), obj2.obj(), obj2.offset(), obj1.inline_klass());
136 }
137 return false;
138 }
139
140 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiHeapwalkObject* obj) : _obj(obj) {
141 }
142
143 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src): _h() {
144 if (src._obj != nullptr) {
145 // move object into Handle when copying into the table
146 assert(!src._obj->is_flat(), "cannot put flat object to JvmtiTagMapKey");
147 _is_weak = !src._obj->is_value();
148
149 // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
150 // a heap walk. The object needs to be kept alive when it is published.
151 Universe::heap()->keep_alive(src._obj->obj());
152
153 if (_is_weak) {
154 _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj->obj());
155 } else {
156 _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._obj->obj());
157 }
158 } else {
159 // resizing needs to create a copy.
160 _is_weak = src._is_weak;
161 if (_is_weak) {
162 _wh = src._wh;
163 } else {
164 _h = src._h;
165 }
166 }
167 // obj is always null after a copy.
168 _obj = nullptr;
169 }
170
171 void JvmtiTagMapKey::release_handle() {
172 if (_is_weak) {
173 _wh.release(JvmtiExport::weak_tag_storage());
174 } else {
175 _h.release(JvmtiExport::jvmti_oop_storage());
176 }
177 }
178
179 JvmtiHeapwalkObject JvmtiTagMapKey::heapwalk_object() const {
180 if (_obj != nullptr) {
181 return JvmtiHeapwalkObject(_obj->obj(), _obj->offset(), _obj->inline_klass(), _obj->layout_kind());
182 }
183 oop obj = object_no_keepalive();
184 if (obj == nullptr) {
185 // The object was deleted by CG while tag still exists
186 return JvmtiHeapwalkObject();
187 }
188 return JvmtiHeapwalkObject(obj);
189 }
190
191 oop JvmtiTagMapKey::object() const {
192 assert(_obj == nullptr, "Must have a handle and not object");
193 return _is_weak ? _wh.resolve() : _h.resolve();
194 }
195
196 oop JvmtiTagMapKey::object_no_keepalive() const {
197 assert(_obj == nullptr, "Must have a handle and not object");
198 return _is_weak ? _wh.peek() : _h.peek();
199 }
200
201 unsigned JvmtiTagMapKey::get_hash(const JvmtiTagMapKey& entry) {
202 const JvmtiHeapwalkObject* obj = entry._obj;
203 assert(obj != nullptr, "must lookup obj to hash");
204 if (obj->is_value()) {
205 return get_value_object_hash(*obj);
206 } else {
207 return (unsigned)obj->obj()->identity_hash();
208 }
209 }
210
211 bool JvmtiTagMapKey::equals(const JvmtiTagMapKey& lhs, const JvmtiTagMapKey& rhs) {
212 JvmtiHeapwalkObject lhs_obj = lhs.heapwalk_object();
213 JvmtiHeapwalkObject rhs_obj = rhs.heapwalk_object();
214 return JvmtiHeapwalkObject::equals(lhs_obj, rhs_obj);
215 }
216
217 static const int INITIAL_TABLE_SIZE = 1007;
218 static const int MAX_TABLE_SIZE = 0x3fffffff;
219
220 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
221
222 void JvmtiTagMapTable::clear() {
223 struct RemoveAll {
224 bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
225 entry.release_handle();
226 return true;
227 }
228 } remove_all;
229 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
230 // while iterating over all the elements of the table. If the do_entry() method returns true the element
231 // will be removed.
232 // In this case, we always return true from do_entry to clear all the elements.
233 _table.unlink(&remove_all);
234
235 assert(_table.number_of_entries() == 0, "should have removed all entries");
236 }
237
238 JvmtiTagMapTable::~JvmtiTagMapTable() {
239 clear();
240 }
241
242 jlong* JvmtiTagMapTable::lookup(const JvmtiHeapwalkObject& obj) const {
243 if (is_empty()) {
244 return nullptr;
245 }
246
247 if (!obj.is_value()) {
248 if (obj.obj()->fast_no_hash_check()) {
249 // Objects in the table all have a hashcode, unless inlined types.
250 return nullptr;
251 }
252 }
253 JvmtiTagMapKey entry(&obj);
254 jlong* found = _table.get(entry);
255 return found;
256 }
257
258
259 jlong JvmtiTagMapTable::find(const JvmtiHeapwalkObject& obj) const {
260 jlong* found = lookup(obj);
261 return found == nullptr ? 0 : *found;
262 }
263
264 void JvmtiTagMapTable::add(const JvmtiHeapwalkObject& obj, jlong tag) {
265 assert(!obj.is_flat(), "Cannot add flat object to JvmtiTagMapTable");
266 JvmtiTagMapKey new_entry(&obj);
267 bool is_added;
268 if (!obj.is_value() && obj.obj()->fast_no_hash_check()) {
269 // Can't be in the table so add it fast.
270 is_added = _table.put_when_absent(new_entry, tag);
271 } else {
272 jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
273 *value = tag; // assign the new tag
274 }
275 if (is_added) {
276 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
277 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
278 log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
279 _table.table_size(), _table.number_of_entries(), max_bucket_size);
280 }
281 }
282 }
283
284 bool JvmtiTagMapTable::update(const JvmtiHeapwalkObject& obj, jlong tag) {
285 jlong* found = lookup(obj);
286 if (found == nullptr) {
287 return false;
288 }
289 *found = tag;
290 return true;
291 }
292
293 bool JvmtiTagMapTable::remove(const JvmtiHeapwalkObject& obj) {
294 JvmtiTagMapKey entry(&obj);
295 auto clean = [](JvmtiTagMapKey & entry, jlong tag) {
296 entry.release_handle();
297 };
298 return _table.remove(entry, clean);
299 }
300
301 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
302 _table.iterate(closure);
303 }
304
305 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
306 struct IsDead {
307 GrowableArray<jlong>* _objects;
308 IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
309 bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
310 if (entry.object_no_keepalive() == nullptr) {
311 if (_objects != nullptr) {
312 _objects->append(tag);
313 }
314 entry.release_handle();
315 return true;
316 }
317 return false;;
318 }
319 } is_dead(objects);
320 _table.unlink(&is_dead);
321 }
322
323 JvmtiFlatTagMapKey::JvmtiFlatTagMapKey(const JvmtiHeapwalkObject& obj)
324 : _holder(obj.obj()), _offset(obj.offset()), _inline_klass(obj.inline_klass()), _layout_kind(obj.layout_kind()) {
325 }
326
327 JvmtiFlatTagMapKey::JvmtiFlatTagMapKey(const JvmtiFlatTagMapKey& src) : _h() {
328 // move object into Handle when copying into the table
329 if (src._holder != nullptr) {
330 // Holder object was read with AS_NO_KEEPALIVE. Needs to be kept alive when it is published.
331 Universe::heap()->keep_alive(src._holder);
332 _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._holder);
333 } else {
334 // resizing needs to create a copy.
335 _h = src._h;
336 }
337 // holder object is always null after a copy.
338 _holder = nullptr;
339 _offset = src._offset;
340 _inline_klass = src._inline_klass;
341 _layout_kind = src._layout_kind;
342 }
343
344 JvmtiHeapwalkObject JvmtiFlatTagMapKey::heapwalk_object() const {
345 return JvmtiHeapwalkObject(_holder != nullptr ? _holder : holder_no_keepalive(), _offset, _inline_klass, _layout_kind);
346 }
347
348 oop JvmtiFlatTagMapKey::holder() const {
349 assert(_holder == nullptr, "Must have a handle and not object");
350 return _h.resolve();
351 }
352
353 oop JvmtiFlatTagMapKey::holder_no_keepalive() const {
354 assert(_holder == nullptr, "Must have a handle and not object");
355 return _h.peek();
356
357 }
358
359 void JvmtiFlatTagMapKey::release_handle() {
360 _h.release(JvmtiExport::jvmti_oop_storage());
361 }
362
363 unsigned JvmtiFlatTagMapKey::get_hash(const JvmtiFlatTagMapKey& entry) {
364 return get_value_object_hash(entry._holder, entry._offset, entry._inline_klass);
365 }
366
367 bool JvmtiFlatTagMapKey::equals(const JvmtiFlatTagMapKey& lhs, const JvmtiFlatTagMapKey& rhs) {
368 if (lhs._inline_klass == rhs._inline_klass) {
369 oop lhs_obj = lhs._holder != nullptr ? lhs._holder : lhs._h.peek();
370 oop rhs_obj = rhs._holder != nullptr ? rhs._holder : rhs._h.peek();
371 return equal_value_objects(lhs_obj, lhs._offset, rhs_obj, rhs._offset, lhs._inline_klass);
372 }
373 return false;
374 }
375
376 JvmtiFlatTagMapTable::JvmtiFlatTagMapTable(): _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
377
378 JvmtiFlatTagMapTable::~JvmtiFlatTagMapTable() {
379 clear();
380 }
381
382 jlong JvmtiFlatTagMapTable::find(const JvmtiHeapwalkObject& obj) const {
383 if (is_empty()) {
384 return 0;
385 }
386
387 JvmtiFlatTagMapKey entry(obj);
388 jlong* found = _table.get(entry);
389 return found == nullptr ? 0 : *found;
390 }
391
392 void JvmtiFlatTagMapTable::add(const JvmtiHeapwalkObject& obj, jlong tag) {
393 assert(obj.is_value() && obj.is_flat(), "Must be flattened value object");
394 JvmtiFlatTagMapKey entry(obj);
395 bool is_added;
396 jlong* value = _table.put_if_absent(entry, tag, &is_added);
397 *value = tag; // assign the new tag
398 if (is_added) {
399 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
400 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
401 log_info(jvmti, table) ("JvmtiFlatTagMapTable table resized to %d for %d entries max bucket %d",
402 _table.table_size(), _table.number_of_entries(), max_bucket_size);
403 }
404 }
405 }
406
407 jlong JvmtiFlatTagMapTable::remove(const JvmtiHeapwalkObject& obj) {
408 JvmtiFlatTagMapKey entry(obj);
409 jlong ret = 0;
410 auto clean = [&](JvmtiFlatTagMapKey& entry, jlong tag) {
411 ret = tag;
412 entry.release_handle();
413 };
414 _table.remove(entry, clean);
415 return ret;
416 }
417
418 void JvmtiFlatTagMapTable::entry_iterate(JvmtiFlatTagMapKeyClosure* closure) {
419 _table.iterate(closure);
420 }
421
422 void JvmtiFlatTagMapTable::clear() {
423 struct RemoveAll {
424 bool do_entry(JvmtiFlatTagMapKey& entry, const jlong& tag) {
425 entry.release_handle();
426 return true;
427 }
428 } remove_all;
429 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
430 // while iterating over all the elements of the table. If the do_entry() method returns true the element
431 // will be removed.
432 // In this case, we always return true from do_entry to clear all the elements.
433 _table.unlink(&remove_all);
434
435 assert(_table.number_of_entries() == 0, "should have removed all entries");
436 }
|