1 /*
   2  * Copyright (c) 2017, 2019, 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 "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp"
  27 #include "oops/instanceKlass.hpp"
  28 #include "oops/oop.inline.hpp"
  29 #include "oops/symbol.hpp"
  30 
  31 static JfrSymbolId::CStringEntry* bootstrap = NULL;
  32 
  33 JfrSymbolId::JfrSymbolId() :
  34   _sym_table(new SymbolTable(this)),
  35   _cstring_table(new CStringTable(this)),
  36   _sym_list(NULL),
  37   _cstring_list(NULL),
  38   _sym_query(NULL),
  39   _cstring_query(NULL),
  40   _symbol_id_counter(1),
  41   _class_unload(false) {
  42   assert(_sym_table != NULL, "invariant");
  43   assert(_cstring_table != NULL, "invariant");
  44   bootstrap = new CStringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME);
  45   assert(bootstrap != NULL, "invariant");
  46   bootstrap->set_id(1);
  47   _cstring_list = bootstrap;
  48 }
  49 
  50 JfrSymbolId::~JfrSymbolId() {
  51   clear();
  52   delete _sym_table;
  53   delete _cstring_table;
  54   delete bootstrap;
  55 }
  56 
  57 void JfrSymbolId::clear() {
  58   assert(_sym_table != NULL, "invariant");
  59   if (_sym_table->has_entries()) {
  60     _sym_table->clear_entries();
  61   }
  62   assert(!_sym_table->has_entries(), "invariant");
  63 
  64   assert(_cstring_table != NULL, "invariant");
  65   if (_cstring_table->has_entries()) {
  66     _cstring_table->clear_entries();
  67   }
  68   assert(!_cstring_table->has_entries(), "invariant");
  69 
  70   _sym_list = NULL;
  71   _symbol_id_counter = 1;
  72 
  73   _sym_query = NULL;
  74   _cstring_query = NULL;
  75 
  76   assert(bootstrap != NULL, "invariant");
  77   bootstrap->reset();
  78   _cstring_list = bootstrap;
  79 }
  80 
  81 void JfrSymbolId::set_class_unload(bool class_unload) {
  82   _class_unload = class_unload;
  83 }
  84 
  85 void JfrSymbolId::on_link(const SymbolEntry* entry) {
  86   assert(entry != NULL, "invariant");
  87   const_cast<Symbol*>(entry->literal())->increment_refcount();
  88   assert(entry->id() == 0, "invariant");
  89   entry->set_id(++_symbol_id_counter);
  90   entry->set_list_next(_sym_list);
  91   _sym_list = entry;
  92 }
  93 
  94 bool JfrSymbolId::on_equals(uintptr_t hash, const SymbolEntry* entry) {
  95   assert(entry != NULL, "invariant");
  96   assert(entry->hash() == hash, "invariant");
  97   assert(_sym_query != NULL, "invariant");
  98   return _sym_query == entry->literal();
  99 }
 100 
 101 void JfrSymbolId::on_unlink(const SymbolEntry* entry) {
 102   assert(entry != NULL, "invariant");
 103   const_cast<Symbol*>(entry->literal())->decrement_refcount();
 104 }
 105 
 106 static const char* resource_to_cstring(const char* resource_str) {
 107   assert(resource_str != NULL, "invariant");
 108   const size_t length = strlen(resource_str);
 109   char* const c_string = JfrCHeapObj::new_array<char>(length + 1);
 110   assert(c_string != NULL, "invariant");
 111   strncpy(c_string, resource_str, length + 1);
 112   return c_string;
 113 }
 114 
 115 void JfrSymbolId::on_link(const CStringEntry* entry) {
 116   assert(entry != NULL, "invariant");
 117   assert(entry->id() == 0, "invariant");
 118   entry->set_id(++_symbol_id_counter);
 119   const_cast<CStringEntry*>(entry)->set_literal(resource_to_cstring(entry->literal()));
 120   entry->set_list_next(_cstring_list);
 121   _cstring_list = entry;
 122 }
 123 
 124 static bool string_compare(const char* query, const char* candidate) {
 125   assert(query != NULL, "invariant");
 126   assert(candidate != NULL, "invariant");
 127   const size_t length = strlen(query);
 128   return strncmp(query, candidate, length) == 0;
 129 }
 130 
 131 bool JfrSymbolId::on_equals(uintptr_t hash, const CStringEntry* entry) {
 132   assert(entry != NULL, "invariant");
 133   assert(entry->hash() == hash, "invariant");
 134   assert(_cstring_query != NULL, "invariant");
 135   return string_compare(_cstring_query, entry->literal());
 136 }
 137 
 138 void JfrSymbolId::on_unlink(const CStringEntry* entry) {
 139   assert(entry != NULL, "invariant");
 140   JfrCHeapObj::free(const_cast<char*>(entry->literal()), strlen(entry->literal() + 1));
 141 }
 142 
 143 traceid JfrSymbolId::bootstrap_name(bool leakp) {
 144   assert(bootstrap != NULL, "invariant");
 145   if (leakp) {
 146     bootstrap->set_leakp();
 147   }
 148   return 1;
 149 }
 150 
 151 traceid JfrSymbolId::mark(const Symbol* symbol, bool leakp) {
 152   assert(symbol != NULL, "invariant");
 153   return mark((uintptr_t)symbol->identity_hash(), symbol, leakp);
 154 }
 155 
 156 traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
 157   assert(data != NULL, "invariant");
 158   assert(_sym_table != NULL, "invariant");
 159   _sym_query = data;
 160   const SymbolEntry& entry = _sym_table->lookup_put(hash, data);
 161   if (_class_unload) {
 162     entry.set_unloading();
 163   }
 164   if (leakp) {
 165     entry.set_leakp();
 166   }
 167   return entry.id();
 168 }
 169 
 170 traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
 171   assert(str != NULL, "invariant");
 172   assert(_cstring_table != NULL, "invariant");
 173   _cstring_query = str;
 174   const CStringEntry& entry = _cstring_table->lookup_put(hash, str);
 175   if (_class_unload) {
 176     entry.set_unloading();
 177   }
 178   if (leakp) {
 179     entry.set_leakp();
 180   }
 181   return entry.id();
 182 }
 183 
 184 /*
 185 * jsr292 anonymous classes symbol is the external name +
 186 * the identity_hashcode slash appended:
 187 *   java.lang.invoke.LambdaForm$BMH/22626602
 188 *
 189 * caller needs ResourceMark
 190 */
 191 
 192 uintptr_t JfrSymbolId::unsafe_anonymous_klass_name_hash(const InstanceKlass* ik) {
 193   assert(ik != NULL, "invariant");
 194   assert(ik->is_unsafe_anonymous(), "invariant");
 195   const oop mirror = ik->java_mirror_no_keepalive();
 196   assert(mirror != NULL, "invariant");
 197   return (uintptr_t)mirror->identity_hash();
 198 }
 199 
 200 static const char* create_unsafe_anonymous_klass_symbol(const InstanceKlass* ik, uintptr_t hash) {
 201   assert(ik != NULL, "invariant");
 202   assert(ik->is_unsafe_anonymous(), "invariant");
 203   assert(hash != 0, "invariant");
 204   char* anonymous_symbol = NULL;
 205   const oop mirror = ik->java_mirror_no_keepalive();
 206   assert(mirror != NULL, "invariant");
 207   char hash_buf[40];
 208   sprintf(hash_buf, "/" UINTX_FORMAT, hash);
 209   const size_t hash_len = strlen(hash_buf);
 210   const size_t result_len = ik->name()->utf8_length();
 211   anonymous_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
 212   ik->name()->as_klass_external_name(anonymous_symbol, (int)result_len + 1);
 213   assert(strlen(anonymous_symbol) == result_len, "invariant");
 214   strcpy(anonymous_symbol + result_len, hash_buf);
 215   assert(strlen(anonymous_symbol) == result_len + hash_len, "invariant");
 216   return anonymous_symbol;
 217 }
 218 
 219 bool JfrSymbolId::is_unsafe_anonymous_klass(const Klass* k) {
 220   assert(k != NULL, "invariant");
 221   return k->is_instance_klass() && ((const InstanceKlass*)k)->is_unsafe_anonymous();
 222 }
 223 
 224 traceid JfrSymbolId::mark_unsafe_anonymous_klass_name(const InstanceKlass* ik, bool leakp) {
 225   assert(ik != NULL, "invariant");
 226   assert(ik->is_unsafe_anonymous(), "invariant");
 227   const uintptr_t hash = unsafe_anonymous_klass_name_hash(ik);
 228   const char* const anonymous_klass_symbol = create_unsafe_anonymous_klass_symbol(ik, hash);
 229   return mark(hash, anonymous_klass_symbol, leakp);
 230 }
 231 
 232 traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
 233   assert(k != NULL, "invariant");
 234   traceid symbol_id = 0;
 235   if (is_unsafe_anonymous_klass(k)) {
 236     assert(k->is_instance_klass(), "invariant");
 237     symbol_id = mark_unsafe_anonymous_klass_name((const InstanceKlass*)k, leakp);
 238   }
 239   if (0 == symbol_id) {
 240     Symbol* const sym = k->name();
 241     if (sym != NULL) {
 242       symbol_id = mark(sym, leakp);
 243     }
 244   }
 245   assert(symbol_id > 0, "a symbol handler must mark the symbol for writing");
 246   return symbol_id;
 247 }
 248 
 249 JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()),
 250                                                     _klass_list(NULL),
 251                                                     _total_count(0) {
 252   initialize(class_unload);
 253   assert(_klass_list != NULL, "invariant");
 254 }
 255 
 256 static const size_t initial_class_list_size = 200;
 257 
 258 void JfrArtifactSet::initialize(bool class_unload, bool clear /* false */) {
 259   assert(_symbol_id != NULL, "invariant");
 260   if (clear) {
 261     _symbol_id->clear();
 262   }
 263   _symbol_id->set_class_unload(class_unload);
 264   _total_count = 0;
 265   // resource allocation
 266   _klass_list = new GrowableArray<const Klass*>(initial_class_list_size, false, mtTracing);
 267 }
 268 
 269 JfrArtifactSet::~JfrArtifactSet() {
 270   _symbol_id->clear();
 271   delete _symbol_id;
 272   // _klass_list will be cleared by a ResourceMark
 273 }
 274 
 275 traceid JfrArtifactSet::bootstrap_name(bool leakp) {
 276   return _symbol_id->bootstrap_name(leakp);
 277 }
 278 
 279 traceid JfrArtifactSet::mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp) {
 280   assert(klass->is_instance_klass(), "invariant");
 281   return _symbol_id->mark_unsafe_anonymous_klass_name((const InstanceKlass*)klass, leakp);
 282 }
 283 
 284 traceid JfrArtifactSet::mark(uintptr_t hash, const Symbol* sym, bool leakp) {
 285   return _symbol_id->mark(hash, sym, leakp);
 286 }
 287 
 288 traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) {
 289   return _symbol_id->mark(klass, leakp);
 290 }
 291 
 292 traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) {
 293   return _symbol_id->mark(symbol, leakp);
 294 }
 295 
 296 traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) {
 297   return _symbol_id->mark(hash, str, leakp);
 298 }
 299 
 300 bool JfrArtifactSet::has_klass_entries() const {
 301   return _klass_list->is_nonempty();
 302 }
 303 
 304 int JfrArtifactSet::entries() const {
 305   return _klass_list->length();
 306 }
 307 
 308 void JfrArtifactSet::register_klass(const Klass* k) {
 309   assert(k != NULL, "invariant");
 310   assert(_klass_list != NULL, "invariant");
 311   assert(_klass_list->find(k) == -1, "invariant");
 312   _klass_list->append(k);
 313 }
 314 
 315 size_t JfrArtifactSet::total_count() const {
 316   return _total_count;
 317 }