Path: blob/master/src/hotspot/share/classfile/compactHashtable.cpp
40949 views
/*1* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "jvm.h"26#include "cds/archiveBuilder.hpp"27#include "cds/heapShared.inline.hpp"28#include "classfile/compactHashtable.hpp"29#include "classfile/javaClasses.hpp"30#include "logging/logMessage.hpp"31#include "memory/metadataFactory.hpp"32#include "runtime/arguments.hpp"33#include "runtime/globals.hpp"34#include "runtime/vmThread.hpp"35#include "utilities/numberSeq.hpp"36#include <sys/stat.h>3738#if INCLUDE_CDS39/////////////////////////////////////////////////////40//41// The compact hash table writer implementations42//43CompactHashtableWriter::CompactHashtableWriter(int num_entries,44CompactHashtableStats* stats) {45Arguments::assert_is_dumping_archive();46assert(num_entries >= 0, "sanity");47_num_buckets = calculate_num_buckets(num_entries);48assert(_num_buckets > 0, "no buckets");4950_num_entries_written = 0;51_buckets = NEW_C_HEAP_ARRAY(GrowableArray<Entry>*, _num_buckets, mtSymbol);52for (int i=0; i<_num_buckets; i++) {53_buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray<Entry>(0, mtSymbol);54}5556_stats = stats;57_compact_buckets = NULL;58_compact_entries = NULL;59_num_empty_buckets = 0;60_num_value_only_buckets = 0;61_num_other_buckets = 0;62}6364CompactHashtableWriter::~CompactHashtableWriter() {65for (int index = 0; index < _num_buckets; index++) {66GrowableArray<Entry>* bucket = _buckets[index];67delete bucket;68}6970FREE_C_HEAP_ARRAY(GrowableArray<Entry>*, _buckets);71}7273size_t CompactHashtableWriter::estimate_size(int num_entries) {74int num_buckets = calculate_num_buckets(num_entries);75size_t bucket_bytes = ArchiveBuilder::ro_array_bytesize<u4>(num_buckets + 1);7677// In worst case, we have no VALUE_ONLY_BUCKET_TYPE, so each entry takes 2 slots78int entries_space = 2 * num_entries;79size_t entry_bytes = ArchiveBuilder::ro_array_bytesize<u4>(entries_space);8081return bucket_bytes82+ entry_bytes83+ SimpleCompactHashtable::calculate_header_size();84}8586// Add a symbol entry to the temporary hash table87void CompactHashtableWriter::add(unsigned int hash, u4 value) {88int index = hash % _num_buckets;89_buckets[index]->append_if_missing(Entry(hash, value));90_num_entries_written++;91}9293void CompactHashtableWriter::allocate_table() {94int entries_space = 0;95for (int index = 0; index < _num_buckets; index++) {96GrowableArray<Entry>* bucket = _buckets[index];97int bucket_size = bucket->length();98if (bucket_size == 1) {99entries_space++;100} else if (bucket_size > 1) {101entries_space += 2 * bucket_size;102}103}104105if (entries_space & ~BUCKET_OFFSET_MASK) {106vm_exit_during_initialization("CompactHashtableWriter::allocate_table: Overflow! "107"Too many entries.");108}109110_compact_buckets = ArchiveBuilder::new_ro_array<u4>(_num_buckets + 1);111_compact_entries = ArchiveBuilder::new_ro_array<u4>(entries_space);112113_stats->bucket_count = _num_buckets;114_stats->bucket_bytes = align_up(_compact_buckets->size() * BytesPerWord,115SharedSpaceObjectAlignment);116_stats->hashentry_count = _num_entries_written;117_stats->hashentry_bytes = align_up(_compact_entries->size() * BytesPerWord,118SharedSpaceObjectAlignment);119}120121// Write the compact table's buckets122void CompactHashtableWriter::dump_table(NumberSeq* summary) {123u4 offset = 0;124for (int index = 0; index < _num_buckets; index++) {125GrowableArray<Entry>* bucket = _buckets[index];126int bucket_size = bucket->length();127if (bucket_size == 1) {128// bucket with one entry is compacted and only has the symbol offset129_compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE));130131Entry ent = bucket->at(0);132_compact_entries->at_put(offset++, ent.value());133_num_value_only_buckets++;134} else {135// regular bucket, each entry is a symbol (hash, offset) pair136_compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE));137138for (int i=0; i<bucket_size; i++) {139Entry ent = bucket->at(i);140_compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash141_compact_entries->at_put(offset++, ent.value());142}143if (bucket_size == 0) {144_num_empty_buckets++;145} else {146_num_other_buckets++;147}148}149summary->add(bucket_size);150}151152// Mark the end of the buckets153_compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE));154assert(offset == (u4)_compact_entries->length(), "sanity");155}156157158// Write the compact table159void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) {160NumberSeq summary;161allocate_table();162dump_table(&summary);163164int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes;165address base_address = address(SharedBaseAddress);166cht->init(base_address, _num_entries_written, _num_buckets,167_compact_buckets->data(), _compact_entries->data());168169LogMessage(cds, hashtables) msg;170if (msg.is_info()) {171double avg_cost = 0.0;172if (_num_entries_written > 0) {173avg_cost = double(table_bytes)/double(_num_entries_written);174}175msg.info("Shared %s table stats -------- base: " PTR_FORMAT,176table_name, (intptr_t)base_address);177msg.info("Number of entries : %9d", _num_entries_written);178msg.info("Total bytes used : %9d", table_bytes);179msg.info("Average bytes per entry : %9.3f", avg_cost);180msg.info("Average bucket size : %9.3f", summary.avg());181msg.info("Variance of bucket size : %9.3f", summary.variance());182msg.info("Std. dev. of bucket size: %9.3f", summary.sd());183msg.info("Maximum bucket size : %9d", (int)summary.maximum());184msg.info("Empty buckets : %9d", _num_empty_buckets);185msg.info("Value_Only buckets : %9d", _num_value_only_buckets);186msg.info("Other buckets : %9d", _num_other_buckets);187}188}189190/////////////////////////////////////////////////////////////191//192// The CompactHashtable implementation193//194195void SimpleCompactHashtable::init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) {196_bucket_count = bucket_count;197_entry_count = entry_count;198_base_address = base_address;199_buckets = buckets;200_entries = entries;201}202203size_t SimpleCompactHashtable::calculate_header_size() {204// We have 5 fields. Each takes up sizeof(intptr_t). See WriteClosure::do_u4205size_t bytes = sizeof(intptr_t) * 5;206return bytes;207}208209void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) {210// NOTE: if you change this function, you MUST change the number 5 in211// calculate_header_size() accordingly.212soc->do_u4(&_entry_count);213soc->do_u4(&_bucket_count);214soc->do_ptr((void**)&_buckets);215soc->do_ptr((void**)&_entries);216if (soc->reading()) {217_base_address = (address)SharedBaseAddress;218}219}220#endif // INCLUDE_CDS221222#ifndef O_BINARY // if defined (Win32) use binary files.223#define O_BINARY 0 // otherwise do nothing.224#endif225226////////////////////////////////////////////////////////227//228// HashtableTextDump229//230HashtableTextDump::HashtableTextDump(const char* filename) : _fd(-1) {231struct stat st;232if (os::stat(filename, &st) != 0) {233quit("Unable to get hashtable dump file size", filename);234}235_size = st.st_size;236_fd = os::open(filename, O_RDONLY | O_BINARY, 0);237if (_fd < 0) {238quit("Unable to open hashtable dump file", filename);239}240_base = os::map_memory(_fd, filename, 0, NULL, _size, true, false);241if (_base == NULL) {242quit("Unable to map hashtable dump file", filename);243}244_p = _base;245_end = _base + st.st_size;246_filename = filename;247_prefix_type = Unknown;248_line_no = 1;249}250251HashtableTextDump::~HashtableTextDump() {252os::unmap_memory((char*)_base, _size);253if (_fd >= 0) {254close(_fd);255}256}257258void HashtableTextDump::quit(const char* err, const char* msg) {259vm_exit_during_initialization(err, msg);260}261262void HashtableTextDump::corrupted(const char *p, const char* msg) {263char info[100];264jio_snprintf(info, sizeof(info),265"%s. Corrupted at line %d (file pos %d)",266msg, _line_no, (int)(p - _base));267quit(info, _filename);268}269270bool HashtableTextDump::skip_newline() {271if (_p[0] == '\r' && _p[1] == '\n') {272_p += 2;273} else if (_p[0] == '\n') {274_p += 1;275} else {276corrupted(_p, "Unexpected character");277}278_line_no++;279return true;280}281282int HashtableTextDump::skip(char must_be_char) {283corrupted_if(remain() < 1, "Truncated");284corrupted_if(*_p++ != must_be_char, "Unexpected character");285return 0;286}287288void HashtableTextDump::skip_past(char c) {289for (;;) {290corrupted_if(remain() < 1, "Truncated");291if (*_p++ == c) {292return;293}294}295}296297void HashtableTextDump::check_version(const char* ver) {298int len = (int)strlen(ver);299corrupted_if(remain() < len, "Truncated");300if (strncmp(_p, ver, len) != 0) {301quit("wrong version of hashtable dump file", _filename);302}303_p += len;304skip_newline();305}306307void HashtableTextDump::scan_prefix_type() {308_p++;309if (strncmp(_p, "SECTION: String", 15) == 0) {310_p += 15;311_prefix_type = StringPrefix;312} else if (strncmp(_p, "SECTION: Symbol", 15) == 0) {313_p += 15;314_prefix_type = SymbolPrefix;315} else {316_prefix_type = Unknown;317}318skip_newline();319}320321int HashtableTextDump::scan_prefix(int* utf8_length) {322if (*_p == '@') {323scan_prefix_type();324}325326switch (_prefix_type) {327case SymbolPrefix:328*utf8_length = scan_symbol_prefix(); break;329case StringPrefix:330*utf8_length = scan_string_prefix(); break;331default:332tty->print_cr("Shared input data type: Unknown.");333corrupted(_p, "Unknown data type");334}335336return _prefix_type;337}338339int HashtableTextDump::scan_string_prefix() {340// Expect /[0-9]+: /341int utf8_length = 0;342get_num(':', &utf8_length);343if (*_p != ' ') {344corrupted(_p, "Wrong prefix format for string");345}346_p++;347return utf8_length;348}349350int HashtableTextDump::scan_symbol_prefix() {351// Expect /[0-9]+ (-|)[0-9]+: /352int utf8_length = 0;353get_num(' ', &utf8_length);354if (*_p == '-') {355_p++;356}357int ref_num;358get_num(':', &ref_num);359if (*_p != ' ') {360corrupted(_p, "Wrong prefix format for symbol");361}362_p++;363return utf8_length;364}365366jchar HashtableTextDump::unescape(const char* from, const char* end, int count) {367jchar value = 0;368369corrupted_if(from + count > end, "Truncated");370371for (int i=0; i<count; i++) {372char c = *from++;373switch (c) {374case '0': case '1': case '2': case '3': case '4':375case '5': case '6': case '7': case '8': case '9':376value = (value << 4) + c - '0';377break;378case 'a': case 'b': case 'c':379case 'd': case 'e': case 'f':380value = (value << 4) + 10 + c - 'a';381break;382case 'A': case 'B': case 'C':383case 'D': case 'E': case 'F':384value = (value << 4) + 10 + c - 'A';385break;386default:387ShouldNotReachHere();388}389}390return value;391}392393void HashtableTextDump::get_utf8(char* utf8_buffer, int utf8_length) {394// cache in local vars395const char* from = _p;396const char* end = _end;397char* to = utf8_buffer;398int n = utf8_length;399400for (; n > 0 && from < end; n--) {401if (*from != '\\') {402*to++ = *from++;403} else {404corrupted_if(from + 2 > end, "Truncated");405char c = from[1];406from += 2;407switch (c) {408case 'x':409{410jchar value = unescape(from, end, 2);411from += 2;412assert(value <= 0xff, "sanity");413*to++ = (char)(value & 0xff);414}415break;416case 't': *to++ = '\t'; break;417case 'n': *to++ = '\n'; break;418case 'r': *to++ = '\r'; break;419case '\\': *to++ = '\\'; break;420default:421corrupted(_p, "Unsupported character");422}423}424}425corrupted_if(n > 0, "Truncated"); // expected more chars but file has ended426_p = from;427skip_newline();428}429430// NOTE: the content is NOT the same as431// UTF8::as_quoted_ascii(const char* utf8_str, int utf8_length, char* buf, int buflen).432// We want to escape \r\n\t so that output [1] is more readable; [2] can be more easily433// parsed by scripts; [3] quickly processed by HashtableTextDump::get_utf8()434void HashtableTextDump::put_utf8(outputStream* st, const char* utf8_string, int utf8_length) {435const char *c = utf8_string;436const char *end = c + utf8_length;437for (; c < end; c++) {438switch (*c) {439case '\t': st->print("\\t"); break;440case '\r': st->print("\\r"); break;441case '\n': st->print("\\n"); break;442case '\\': st->print("\\\\"); break;443default:444if (isprint(*c)) {445st->print("%c", *c);446} else {447st->print("\\x%02x", ((unsigned int)*c) & 0xff);448}449}450}451}452453454