| /* |
| * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "classfile/classLoaderData.hpp" |
| #include "classfile/javaClasses.hpp" |
| #include "jfr/support/jfrSymbolTable.hpp" |
| #include "oops/klass.hpp" |
| #include "oops/symbol.hpp" |
| #include "runtime/mutexLocker.hpp" |
| |
| // incremented on each rotation |
| static u8 checkpoint_id = 1; |
| |
| // creates a unique id by combining a checkpoint relative symbol id (2^24) |
| // with the current checkpoint id (2^40) |
| #define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id))) |
| |
| static traceid create_symbol_id(traceid artifact_id) { |
| return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0; |
| } |
| |
| static uintptr_t string_hash(const char* str) { |
| return java_lang_String::hash_code(reinterpret_cast<const jbyte*>(str), static_cast<int>(strlen(str))); |
| } |
| |
| static JfrSymbolTable::StringEntry* bootstrap = nullptr; |
| |
| static JfrSymbolTable* _instance = nullptr; |
| |
| static JfrSymbolTable& instance() { |
| assert(_instance != nullptr, "invariant"); |
| return *_instance; |
| } |
| |
| JfrSymbolTable* JfrSymbolTable::create() { |
| assert(_instance == nullptr, "invariant"); |
| assert_locked_or_safepoint(ClassLoaderDataGraph_lock); |
| _instance = new JfrSymbolTable(); |
| return _instance; |
| } |
| |
| void JfrSymbolTable::destroy() { |
| assert_locked_or_safepoint(ClassLoaderDataGraph_lock); |
| if (_instance != nullptr) { |
| delete _instance; |
| _instance = nullptr; |
| } |
| assert(_instance == nullptr, "invariant"); |
| } |
| |
| JfrSymbolTable::JfrSymbolTable() : |
| _symbols(new Symbols(this)), |
| _strings(new Strings(this)), |
| _symbol_list(nullptr), |
| _string_list(nullptr), |
| _symbol_query(nullptr), |
| _string_query(nullptr), |
| _id_counter(1), |
| _class_unload(false) { |
| assert(_symbols != nullptr, "invariant"); |
| assert(_strings != nullptr, "invariant"); |
| bootstrap = new StringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME); |
| assert(bootstrap != nullptr, "invariant"); |
| bootstrap->set_id(create_symbol_id(1)); |
| _string_list = bootstrap; |
| } |
| |
| JfrSymbolTable::~JfrSymbolTable() { |
| clear(); |
| delete _symbols; |
| delete _strings; |
| delete bootstrap; |
| } |
| |
| void JfrSymbolTable::clear() { |
| assert(_symbols != nullptr, "invariant"); |
| if (_symbols->has_entries()) { |
| _symbols->clear_entries(); |
| } |
| assert(!_symbols->has_entries(), "invariant"); |
| |
| assert(_strings != nullptr, "invariant"); |
| if (_strings->has_entries()) { |
| _strings->clear_entries(); |
| } |
| assert(!_strings->has_entries(), "invariant"); |
| |
| _symbol_list = nullptr; |
| _id_counter = 1; |
| |
| _symbol_query = nullptr; |
| _string_query = nullptr; |
| |
| assert(bootstrap != nullptr, "invariant"); |
| bootstrap->reset(); |
| _string_list = bootstrap; |
| } |
| |
| void JfrSymbolTable::set_class_unload(bool class_unload) { |
| _class_unload = class_unload; |
| } |
| |
| void JfrSymbolTable::increment_checkpoint_id() { |
| assert_lock_strong(ClassLoaderDataGraph_lock); |
| clear(); |
| ++checkpoint_id; |
| } |
| |
| template <typename T> |
| inline void JfrSymbolTable::assign_id(T* entry) { |
| assert(entry != nullptr, "invariant"); |
| assert(entry->id() == 0, "invariant"); |
| entry->set_id(create_symbol_id(++_id_counter)); |
| } |
| |
| void JfrSymbolTable::on_link(const SymbolEntry* entry) { |
| assign_id(entry); |
| const_cast<Symbol*>(entry->literal())->increment_refcount(); |
| entry->set_list_next(_symbol_list); |
| _symbol_list = entry; |
| } |
| |
| bool JfrSymbolTable::on_equals(uintptr_t hash, const SymbolEntry* entry) { |
| assert(entry != nullptr, "invariant"); |
| assert(entry->hash() == hash, "invariant"); |
| assert(_symbol_query != nullptr, "invariant"); |
| return _symbol_query == entry->literal(); |
| } |
| |
| void JfrSymbolTable::on_unlink(const SymbolEntry* entry) { |
| assert(entry != nullptr, "invariant"); |
| const_cast<Symbol*>(entry->literal())->decrement_refcount(); |
| } |
| |
| static const char* resource_to_c_heap_string(const char* resource_str) { |
| assert(resource_str != nullptr, "invariant"); |
| const size_t length = strlen(resource_str); |
| char* const c_string = JfrCHeapObj::new_array<char>(length + 1); |
| assert(c_string != nullptr, "invariant"); |
| strncpy(c_string, resource_str, length + 1); |
| return c_string; |
| } |
| |
| void JfrSymbolTable::on_link(const StringEntry* entry) { |
| assign_id(entry); |
| const_cast<StringEntry*>(entry)->set_literal(resource_to_c_heap_string(entry->literal())); |
| entry->set_list_next(_string_list); |
| _string_list = entry; |
| } |
| |
| static bool string_compare(const char* query, const char* candidate) { |
| assert(query != nullptr, "invariant"); |
| assert(candidate != nullptr, "invariant"); |
| const size_t length = strlen(query); |
| return strncmp(query, candidate, length) == 0; |
| } |
| |
| bool JfrSymbolTable::on_equals(uintptr_t hash, const StringEntry* entry) { |
| assert(entry != nullptr, "invariant"); |
| assert(entry->hash() == hash, "invariant"); |
| assert(_string_query != nullptr, "invariant"); |
| return string_compare(_string_query, entry->literal()); |
| } |
| |
| void JfrSymbolTable::on_unlink(const StringEntry* entry) { |
| assert(entry != nullptr, "invariant"); |
| JfrCHeapObj::free(const_cast<char*>(entry->literal()), strlen(entry->literal() + 1)); |
| } |
| |
| traceid JfrSymbolTable::bootstrap_name(bool leakp) { |
| assert(bootstrap != nullptr, "invariant"); |
| if (leakp) { |
| bootstrap->set_leakp(); |
| } |
| return bootstrap->id(); |
| } |
| |
| traceid JfrSymbolTable::mark(const Symbol* sym, bool leakp /* false */) { |
| assert(sym != nullptr, "invariant"); |
| return mark(sym->identity_hash(), sym, leakp); |
| } |
| |
| traceid JfrSymbolTable::mark(uintptr_t hash, const Symbol* sym, bool leakp) { |
| assert(sym != nullptr, "invariant"); |
| assert(_symbols != nullptr, "invariant"); |
| _symbol_query = sym; |
| const SymbolEntry& entry = _symbols->lookup_put(hash, sym); |
| if (_class_unload) { |
| entry.set_unloading(); |
| } |
| if (leakp) { |
| entry.set_leakp(); |
| } |
| return entry.id(); |
| } |
| |
| traceid JfrSymbolTable::mark(const char* str, bool leakp /* false*/) { |
| return mark(string_hash(str), str, leakp); |
| } |
| |
| traceid JfrSymbolTable::mark(uintptr_t hash, const char* str, bool leakp) { |
| assert(str != nullptr, "invariant"); |
| assert(_strings != nullptr, "invariant"); |
| _string_query = str; |
| const StringEntry& entry = _strings->lookup_put(hash, str); |
| if (_class_unload) { |
| entry.set_unloading(); |
| } |
| if (leakp) { |
| entry.set_leakp(); |
| } |
| return entry.id(); |
| } |
| |
| /* |
| * The hidden class symbol is the external name with the |
| * address of its Klass slash appended. |
| * |
| * "java.lang.invoke.LambdaForm$DMH/0x0000000037144c00" |
| * |
| * Caller needs ResourceMark. |
| */ |
| traceid JfrSymbolTable::mark_hidden_klass_name(const Klass* k, bool leakp) { |
| assert(k != nullptr, "invariant"); |
| assert(k->is_hidden(), "invariant"); |
| const uintptr_t hash = k->name()->identity_hash(); |
| return mark(hash, k->external_name(), leakp); |
| } |
| |
| traceid JfrSymbolTable::mark(const Klass* k, bool leakp) { |
| assert(k != nullptr, "invariant"); |
| traceid symbol_id = 0; |
| if (k->is_hidden()) { |
| symbol_id = mark_hidden_klass_name(k, leakp); |
| } else { |
| Symbol* const sym = k->name(); |
| if (sym != nullptr) { |
| symbol_id = mark(sym, leakp); |
| } |
| } |
| assert(symbol_id > 0, "a symbol handler must mark the symbol for writing"); |
| return symbol_id; |
| } |
| |
| template <typename T> |
| traceid JfrSymbolTable::add_impl(const T* sym) { |
| assert(sym != nullptr, "invariant"); |
| assert(_instance != nullptr, "invariant"); |
| assert_locked_or_safepoint(ClassLoaderDataGraph_lock); |
| return instance().mark(sym); |
| } |
| |
| traceid JfrSymbolTable::add(const Symbol* sym) { |
| return add_impl(sym); |
| } |
| |
| traceid JfrSymbolTable::add(const char* str) { |
| return add_impl(str); |
| } |