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
129 bool JvmtiHeapwalkObject::equals(const JvmtiHeapwalkObject& obj1, const JvmtiHeapwalkObject& obj2) {
130 if (obj1 == obj2) { // the same oop/offset/inline_klass
131 return true;
132 }
133
134 if (obj1.is_value() && obj1.inline_klass() == obj2.inline_klass()) {
135 // instances of the same value class
136 return equal_value_objects(obj1.obj(), obj1.offset(), obj2.obj(), obj2.offset(), obj1.inline_klass());
137 }
138 return false;
139 }
140
141
142 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiHeapwalkObject* obj) : _obj(obj) {
143 }
144
145 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src): _h() {
146 if (src._obj != nullptr) {
147 // move object into Handle when copying into the table
148 assert(!src._obj->is_flat(), "cannot put flat object to JvmtiTagMapKey");
149 _is_weak = !src._obj->is_value();
150
151 // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
152 // a heap walk. The object needs to be kept alive when it is published.
153 Universe::heap()->keep_alive(src._obj->obj());
154
155 if (_is_weak) {
156 _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj->obj());
157 } else {
158 _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._obj->obj());
159 }
160 } else {
161 // resizing needs to create a copy.
162 _is_weak = src._is_weak;
163 if (_is_weak) {
164 _wh = src._wh;
165 } else {
166 _h = src._h;
167 }
168 }
169 // obj is always null after a copy.
170 _obj = nullptr;
171 }
172
173 void JvmtiTagMapKey::release_handle() {
174 if (_is_weak) {
175 _wh.release(JvmtiExport::weak_tag_storage());
176 } else {
177 _h.release(JvmtiExport::jvmti_oop_storage());
178 }
179 }
180
181 JvmtiHeapwalkObject JvmtiTagMapKey::heapwalk_object() const {
182 return _obj != nullptr ? JvmtiHeapwalkObject(_obj->obj(), _obj->offset(), _obj->inline_klass(), _obj->layout_kind())
183 : JvmtiHeapwalkObject(object_no_keepalive());
184 }
185
186 oop JvmtiTagMapKey::object() const {
187 assert(_obj == nullptr, "Must have a handle and not object");
188 return _is_weak ? _wh.resolve() : _h.resolve();
189 }
190
191 oop JvmtiTagMapKey::object_no_keepalive() const {
192 assert(_obj == nullptr, "Must have a handle and not object");
193 return _is_weak ? _wh.peek() : _h.peek();
194 }
195
196 unsigned JvmtiTagMapKey::get_hash(const JvmtiTagMapKey& entry) {
197 const JvmtiHeapwalkObject* obj = entry._obj;
198 assert(obj != nullptr, "must lookup obj to hash");
199 if (obj->is_value()) {
200 return get_value_object_hash(*obj);
201 } else {
202 return (unsigned)obj->obj()->identity_hash();
203 }
204 }
205
206 bool JvmtiTagMapKey::equals(const JvmtiTagMapKey& lhs, const JvmtiTagMapKey& rhs) {
207 JvmtiHeapwalkObject lhs_obj = lhs.heapwalk_object();
208 JvmtiHeapwalkObject rhs_obj = rhs.heapwalk_object();
209 return JvmtiHeapwalkObject::equals(lhs_obj, rhs_obj);
210 }
211
212 static const int INITIAL_TABLE_SIZE = 1007;
213 static const int MAX_TABLE_SIZE = 0x3fffffff;
214
215 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
216
217 void JvmtiTagMapTable::clear() {
218 struct RemoveAll {
219 bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
220 entry.release_handle();
221 return true;
222 }
223 } remove_all;
224 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
225 // while iterating over all the elements of the table. If the do_entry() method returns true the element
226 // will be removed.
227 // In this case, we always return true from do_entry to clear all the elements.
228 _table.unlink(&remove_all);
229
230 assert(_table.number_of_entries() == 0, "should have removed all entries");
231 }
232
233 JvmtiTagMapTable::~JvmtiTagMapTable() {
234 clear();
235 }
236
237 jlong* JvmtiTagMapTable::lookup(const JvmtiHeapwalkObject& obj) const {
238 if (is_empty()) {
239 return 0;
240 }
241
242 if (!obj.is_value()) {
243 if (obj.obj()->fast_no_hash_check()) {
244 // Objects in the table all have a hashcode, unless inlined types.
245 return nullptr;
246 }
247 }
248 JvmtiTagMapKey entry(&obj);
249 jlong* found = _table.get(entry);
250 return found;
251 }
252
253
254 jlong JvmtiTagMapTable::find(const JvmtiHeapwalkObject& obj) const {
255 jlong* found = lookup(obj);
256 return found == nullptr ? 0 : *found;
257 }
258
259 void JvmtiTagMapTable::add(const JvmtiHeapwalkObject& obj, jlong tag) {
260 assert(!obj.is_flat(), "Cannot add flat object to JvmtiTagMapTable");
261 JvmtiTagMapKey new_entry(&obj);
262 bool is_added;
263 if (!obj.is_value() && obj.obj()->fast_no_hash_check()) {
264 // Can't be in the table so add it fast.
265 is_added = _table.put_when_absent(new_entry, tag);
266 } else {
267 jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
268 *value = tag; // assign the new tag
269 }
270 if (is_added) {
271 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
272 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
273 log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
274 _table.table_size(), _table.number_of_entries(), max_bucket_size);
275 }
276 }
277 }
278
279 bool JvmtiTagMapTable::update(const JvmtiHeapwalkObject& obj, jlong tag) {
280 jlong* found = lookup(obj);
281 if (found == nullptr) {
282 return false;
283 }
284 *found = tag;
285 return true;
286 }
287
288 bool JvmtiTagMapTable::remove(const JvmtiHeapwalkObject& obj) {
289 JvmtiTagMapKey entry(&obj);
290 auto clean = [](JvmtiTagMapKey & entry, jlong tag) {
291 entry.release_handle();
292 };
293 return _table.remove(entry, clean);
294 }
295
296 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
297 _table.iterate(closure);
298 }
299
300 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
301 struct IsDead {
302 GrowableArray<jlong>* _objects;
303 IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
304 bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
305 if (entry.object_no_keepalive() == nullptr) {
306 if (_objects != nullptr) {
307 _objects->append(tag);
308 }
309 entry.release_handle();
310 return true;
311 }
312 return false;;
313 }
314 } is_dead(objects);
315 _table.unlink(&is_dead);
316 }
317
318
319 JvmtiFlatTagMapKey::JvmtiFlatTagMapKey(const JvmtiHeapwalkObject& obj)
320 : _holder(obj.obj()), _offset(obj.offset()), _inline_klass(obj.inline_klass()), _layout_kind(obj.layout_kind()) {
321 }
322
323 JvmtiFlatTagMapKey::JvmtiFlatTagMapKey(const JvmtiFlatTagMapKey& src) : _h() {
324 // move object into Handle when copying into the table
325 if (src._holder != nullptr) {
326 // Holder object was read with AS_NO_KEEPALIVE. Needs to be kept alive when it is published.
327 Universe::heap()->keep_alive(src._holder);
328 _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._holder);
329 } else {
330 // resizing needs to create a copy.
331 _h = src._h;
332 }
333 // holder object is always null after a copy.
334 _holder = nullptr;
335 _offset = src._offset;
336 _inline_klass = src._inline_klass;
337 _layout_kind = src._layout_kind;
338 }
339
340 JvmtiHeapwalkObject JvmtiFlatTagMapKey::heapwalk_object() const {
341 return JvmtiHeapwalkObject(_holder != nullptr ? _holder : holder_no_keepalive(), _offset, _inline_klass, _layout_kind);
342 }
343
344 oop JvmtiFlatTagMapKey::holder() const {
345 assert(_holder == nullptr, "Must have a handle and not object");
346 return _h.resolve();
347 }
348
349 oop JvmtiFlatTagMapKey::holder_no_keepalive() const {
350 assert(_holder == nullptr, "Must have a handle and not object");
351 return _h.peek();
352
353 }
354
355 void JvmtiFlatTagMapKey::release_handle() {
356 _h.release(JvmtiExport::jvmti_oop_storage());
357 }
358
359 unsigned JvmtiFlatTagMapKey::get_hash(const JvmtiFlatTagMapKey& entry) {
360 return get_value_object_hash(entry._holder, entry._offset, entry._inline_klass);
361 }
362
363 bool JvmtiFlatTagMapKey::equals(const JvmtiFlatTagMapKey& lhs, const JvmtiFlatTagMapKey& rhs) {
364 if (lhs._inline_klass == rhs._inline_klass) {
365 oop lhs_obj = lhs._holder != nullptr ? lhs._holder : lhs._h.peek();
366 oop rhs_obj = rhs._holder != nullptr ? rhs._holder : rhs._h.peek();
367 return equal_value_objects(lhs_obj, lhs._offset, rhs_obj, rhs._offset, lhs._inline_klass);
368 }
369 return false;
370 }
371
372
373 JvmtiFlatTagMapTable::JvmtiFlatTagMapTable(): _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
374
375 JvmtiFlatTagMapTable::~JvmtiFlatTagMapTable() {
376 clear();
377 }
378
379 jlong JvmtiFlatTagMapTable::find(const JvmtiHeapwalkObject& obj) const {
380 if (is_empty()) {
381 return 0;
382 }
383
384 JvmtiFlatTagMapKey entry(obj);
385 jlong* found = _table.get(entry);
386 return found == nullptr ? 0 : *found;
387 }
388
389 void JvmtiFlatTagMapTable::add(const JvmtiHeapwalkObject& obj, jlong tag) {
390 assert(obj.is_value() && obj.is_flat(), "Must be flattened value object");
391 JvmtiFlatTagMapKey entry(obj);
392 bool is_added;
393 jlong* value = _table.put_if_absent(entry, tag, &is_added);
394 *value = tag; // assign the new tag
395 if (is_added) {
396 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
397 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
398 log_info(jvmti, table) ("JvmtiFlatTagMapTable table resized to %d for %d entries max bucket %d",
399 _table.table_size(), _table.number_of_entries(), max_bucket_size);
400 }
401 }
402 }
403
404 jlong JvmtiFlatTagMapTable::remove(const JvmtiHeapwalkObject& obj) {
405 JvmtiFlatTagMapKey entry(obj);
406 jlong ret = 0;
407 auto clean = [&](JvmtiFlatTagMapKey& entry, jlong tag) {
408 ret = tag;
409 entry.release_handle();
410 };
411 _table.remove(entry, clean);
412 return ret;
413 }
414
415 void JvmtiFlatTagMapTable::entry_iterate(JvmtiFlatTagMapKeyClosure* closure) {
416 _table.iterate(closure);
417 }
418
419 void JvmtiFlatTagMapTable::clear() {
420 struct RemoveAll {
421 bool do_entry(JvmtiFlatTagMapKey& entry, const jlong& tag) {
422 entry.release_handle();
423 return true;
424 }
425 } remove_all;
426 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
427 // while iterating over all the elements of the table. If the do_entry() method returns true the element
428 // will be removed.
429 // In this case, we always return true from do_entry to clear all the elements.
430 _table.unlink(&remove_all);
431
432 assert(_table.number_of_entries() == 0, "should have removed all entries");
433 }
|