1/* 2 * Copyright (c) 1998, 2012, 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#ifndef SHARE_VM_CODE_OOPRECORDER_HPP 26#define SHARE_VM_CODE_OOPRECORDER_HPP 27 28#include "memory/universe.hpp" 29#include "runtime/handles.hpp" 30#include "utilities/growableArray.hpp" 31 32// Recording and retrieval of either oop relocations or metadata in compiled code. 33 34class CodeBlob; 35 36template <class T> class ValueRecorder : public StackObj { 37 public: 38 // A two-way mapping from positive indexes to oop handles. 39 // The zero index is reserved for a constant (sharable) null. 40 // Indexes may not be negative. 41 42 // Use the given arena to manage storage, if not NULL. 43 // By default, uses the current ResourceArea. 44 ValueRecorder(Arena* arena = NULL); 45 46 // Generate a new index on which nmethod::oop_addr_at will work. 47 // allocate_index and find_index never return the same index, 48 // and allocate_index never returns the same index twice. 49 // In fact, two successive calls to allocate_index return successive ints. 50 int allocate_index(T h) { 51 return add_handle(h, false); 52 } 53 54 // For a given jobject or Metadata*, this will return the same index 55 // repeatedly. The index can later be given to nmethod::oop_at or 56 // metadata_at to retrieve the oop. 57 // However, the oop must not be changed via nmethod::oop_addr_at. 58 int find_index(T h) { 59 int index = maybe_find_index(h); 60 if (index < 0) { // previously unallocated 61 index = add_handle(h, true); 62 } 63 return index; 64 } 65 66 // returns the size of the generated oop/metadata table, for sizing the 67 // CodeBlob. Must be called after all oops are allocated! 68 int size(); 69 70 // Retrieve the value at a given index. 71 T at(int index); 72 73 int count() { 74 if (_handles == NULL) return 0; 75 // there is always a NULL virtually present as first object 76 return _handles->length() + first_index; 77 } 78 79 // Helper function; returns false for NULL or Universe::non_oop_word(). 80 bool is_real(T h) { 81 return h != NULL && h != (T)Universe::non_oop_word(); 82 } 83 84 // copy the generated table to nmethod 85 void copy_values_to(nmethod* nm); 86 87 bool is_unused() { return _handles == NULL && !_complete; } 88#ifdef ASSERT 89 bool is_complete() { return _complete; } 90#endif 91 92 private: 93 // variant of find_index which does not allocate if not found (yields -1) 94 int maybe_find_index(T h); 95 96 // leaky hash table of handle => index, to help detect duplicate insertion 97 template <class X> class IndexCache : public ResourceObj { 98 // This class is only used by the ValueRecorder class. 99 friend class ValueRecorder; 100 enum { 101 _log_cache_size = 9, 102 _cache_size = (1<<_log_cache_size), 103 // Index entries are ints. The LSBit is a collision indicator. 104 _collision_bit_shift = 0, 105 _collision_bit = 1, 106 _index_shift = _collision_bit_shift+1 107 }; 108 int _cache[_cache_size]; 109 static juint cache_index(X handle) { 110 juint ci = (int) (intptr_t) handle; 111 ci ^= ci >> (BitsPerByte*2); 112 ci += ci >> (BitsPerByte*1); 113 return ci & (_cache_size-1); 114 } 115 int* cache_location(X handle) { 116 return &_cache[ cache_index(handle) ]; 117 } 118 static bool cache_location_collision(int* cloc) { 119 return ((*cloc) & _collision_bit) != 0; 120 } 121 static int cache_location_index(int* cloc) { 122 return (*cloc) >> _index_shift; 123 } 124 static void set_cache_location_index(int* cloc, int index) { 125 int cval0 = (*cloc); 126 int cval1 = (index << _index_shift); 127 if (cval0 != 0 && cval1 != cval0) cval1 += _collision_bit; 128 (*cloc) = cval1; 129 } 130 IndexCache(); 131 }; 132 133 void maybe_initialize(); 134 int add_handle(T h, bool make_findable); 135 136 enum { null_index = 0, first_index = 1, index_cache_threshold = 20 }; 137 138 GrowableArray<T>* _handles; // ordered list (first is always NULL) 139 GrowableArray<int>* _no_finds; // all unfindable indexes; usually empty 140 IndexCache<T>* _indexes; // map: handle -> its probable index 141 Arena* _arena; 142 bool _complete; 143 144#ifdef ASSERT 145 static int _find_index_calls, _hit_indexes, _missed_indexes; 146#endif 147}; 148 149class OopRecorder; 150 151class ObjectLookup : public ResourceObj { 152 private: 153 class ObjectEntry { 154 private: 155 jobject _value; 156 int _index; 157 158 public: 159 ObjectEntry(jobject value, int index) : _value(value), _index(index) {} 160 ObjectEntry() : _value(NULL), _index(0) {} 161 oop oop_value() const; 162 int index() { return _index; } 163 }; 164 165 GrowableArray<ObjectEntry> _values; 166 unsigned int _gc_count; 167 168 // Utility sort functions 169 static int sort_by_address(oop a, oop b); 170 static int sort_by_address(ObjectEntry* a, ObjectEntry* b); 171 static int sort_oop_by_address(oop const& a, ObjectEntry const& b); 172 173 public: 174 ObjectLookup(); 175 176 // Resort list if a GC has occurred since the last sort 177 void maybe_resort(); 178 int find_index(jobject object, OopRecorder* oop_recorder); 179}; 180 181class OopRecorder : public ResourceObj { 182 private: 183 ValueRecorder<jobject> _oops; 184 ValueRecorder<Metadata*> _metadata; 185 ObjectLookup* _object_lookup; 186 public: 187 OopRecorder(Arena* arena = NULL, bool deduplicate = false): _oops(arena), _metadata(arena) { 188 if (deduplicate) { 189 _object_lookup = new ObjectLookup(); 190 } else { 191 _object_lookup = NULL; 192 } 193 } 194 195 int allocate_oop_index(jobject h) { 196 return _oops.allocate_index(h); 197 } 198 virtual int find_index(jobject h) { 199 return _object_lookup != NULL ? _object_lookup->find_index(h, this) : _oops.find_index(h); 200 } 201 jobject oop_at(int index) { 202 return _oops.at(index); 203 } 204 int oop_size() { 205 return _oops.size(); 206 } 207 int oop_count() { 208 return _oops.count(); 209 } 210 bool is_real(jobject h) { 211 return _oops.is_real(h); 212 } 213 214 int allocate_metadata_index(Metadata* oop) { 215 return _metadata.allocate_index(oop); 216 } 217 virtual int find_index(Metadata* h) { 218 return _metadata.find_index(h); 219 } 220 Metadata* metadata_at(int index) { 221 return _metadata.at(index); 222 } 223 int metadata_size() { 224 return _metadata.size(); 225 } 226 int metadata_count() { 227 return _metadata.count(); 228 } 229 bool is_real(Metadata* h) { 230 return _metadata.is_real(h); 231 } 232 233 bool is_unused() { 234 return _oops.is_unused() && _metadata.is_unused(); 235 } 236 237 void freeze() { 238 _oops.size(); 239 _metadata.size(); 240 } 241 242 void copy_values_to(nmethod* nm) { 243 if (!_oops.is_unused()) { 244 _oops.copy_values_to(nm); 245 } 246 if (!_metadata.is_unused()) { 247 _metadata.copy_values_to(nm); 248 } 249 } 250 251#ifdef ASSERT 252 bool is_complete() { 253 assert(_oops.is_complete() == _metadata.is_complete(), "must agree"); 254 return _oops.is_complete(); 255 } 256#endif 257}; 258 259 260#endif // SHARE_VM_CODE_OOPRECORDER_HPP 261