Path: blob/master/src/hotspot/share/services/heapDumperCompression.cpp
41144 views
/*1* Copyright (c) 2020 SAP SE. 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 "runtime/arguments.hpp"27#include "runtime/mutexLocker.hpp"28#include "runtime/os.hpp"29#include "runtime/thread.inline.hpp"30#include "services/heapDumperCompression.hpp"313233char const* FileWriter::open_writer() {34assert(_fd < 0, "Must not already be open");3536_fd = os::create_binary_file(_path, false); // don't replace existing file3738if (_fd < 0) {39return os::strerror(errno);40}4142return NULL;43}4445FileWriter::~FileWriter() {46if (_fd >= 0) {47os::close(_fd);48_fd = -1;49}50}5152char const* FileWriter::write_buf(char* buf, ssize_t size) {53assert(_fd >= 0, "Must be open");54assert(size > 0, "Must write at least one byte");5556ssize_t n = (ssize_t) os::write(_fd, buf, (uint) size);5758if (n <= 0) {59return os::strerror(errno);60}6162return NULL;63}646566typedef char const* (*GzipInitFunc)(size_t, size_t*, size_t*, int);67typedef size_t(*GzipCompressFunc)(char*, size_t, char*, size_t, char*, size_t,68int, char*, char const**);6970static GzipInitFunc gzip_init_func;71static GzipCompressFunc gzip_compress_func;7273void* GZipCompressor::load_gzip_func(char const* name) {74char path[JVM_MAXPATHLEN];75char ebuf[1024];76void* handle;77MutexLocker locker(Zip_lock, Monitor::_no_safepoint_check_flag);7879if (os::dll_locate_lib(path, sizeof(path), Arguments::get_dll_dir(), "zip")) {80handle = os::dll_load(path, ebuf, sizeof ebuf);8182if (handle != NULL) {83return os::dll_lookup(handle, name);84}85}8687return NULL;88}8990char const* GZipCompressor::init(size_t block_size, size_t* needed_out_size,91size_t* needed_tmp_size) {92_block_size = block_size;93_is_first = true;9495if (gzip_compress_func == NULL) {96gzip_compress_func = (GzipCompressFunc) load_gzip_func("ZIP_GZip_Fully");9798if (gzip_compress_func == NULL) {99return "Cannot get ZIP_GZip_Fully function";100}101}102103if (gzip_init_func == NULL) {104gzip_init_func = (GzipInitFunc) load_gzip_func("ZIP_GZip_InitParams");105106if (gzip_init_func == NULL) {107return "Cannot get ZIP_GZip_InitParams function";108}109}110111char const* result = gzip_init_func(block_size, needed_out_size,112needed_tmp_size, _level);113*needed_out_size += 1024; // Add extra space for the comment in the first chunk.114115return result;116}117118char const* GZipCompressor::compress(char* in, size_t in_size, char* out, size_t out_size,119char* tmp, size_t tmp_size, size_t* compressed_size) {120char const* msg = NULL;121122if (_is_first) {123char buf[128];124// Write the block size used as a comment in the first gzip chunk, so the125// code used to read it later can make a good choice of the buffer sizes it uses.126jio_snprintf(buf, sizeof(buf), "HPROF BLOCKSIZE=" SIZE_FORMAT, _block_size);127*compressed_size = gzip_compress_func(in, in_size, out, out_size, tmp, tmp_size, _level,128buf, &msg);129_is_first = false;130} else {131*compressed_size = gzip_compress_func(in, in_size, out, out_size, tmp, tmp_size, _level,132NULL, &msg);133}134135return msg;136}137138WorkList::WorkList() {139_head._next = &_head;140_head._prev = &_head;141}142143void WorkList::insert(WriteWork* before, WriteWork* work) {144work->_prev = before;145work->_next = before->_next;146before->_next = work;147work->_next->_prev = work;148}149150WriteWork* WorkList::remove(WriteWork* work) {151if (work != NULL) {152assert(work->_next != work, "Invalid next");153assert(work->_prev != work, "Invalid prev");154work->_prev->_next = work->_next;;155work->_next->_prev = work->_prev;156work->_next = NULL;157work->_prev = NULL;158}159160return work;161}162163void WorkList::add_by_id(WriteWork* work) {164if (is_empty()) {165add_first(work);166} else {167WriteWork* last_curr = &_head;168WriteWork* curr = _head._next;169170while (curr->_id < work->_id) {171last_curr = curr;172curr = curr->_next;173174if (curr == &_head) {175add_last(work);176return;177}178}179180insert(last_curr, work);181}182}183184185186CompressionBackend::CompressionBackend(AbstractWriter* writer,187AbstractCompressor* compressor, size_t block_size, size_t max_waste) :188_active(false),189_err(NULL),190_nr_of_threads(0),191_works_created(0),192_work_creation_failed(false),193_id_to_write(0),194_next_id(0),195_in_size(block_size),196_max_waste(max_waste),197_out_size(0),198_tmp_size(0),199_written(0),200_writer(writer),201_compressor(compressor),202_lock(new (std::nothrow) PaddedMonitor(Mutex::leaf, "HProf Compression Backend",203true, Mutex::_safepoint_check_never)) {204if (_writer == NULL) {205set_error("Could not allocate writer");206} else if (_lock == NULL) {207set_error("Could not allocate lock");208} else {209set_error(_writer->open_writer());210}211212if (_compressor != NULL) {213set_error(_compressor->init(_in_size, &_out_size, &_tmp_size));214}215216_current = allocate_work(_in_size, _out_size, _tmp_size);217218if (_current == NULL) {219set_error("Could not allocate memory for buffer");220}221222_active = (_err == NULL);223}224225CompressionBackend::~CompressionBackend() {226assert(!_active, "Must not be active by now");227assert(_nr_of_threads == 0, "Must have no active threads");228assert(_to_compress.is_empty() && _finished.is_empty(), "Still work to do");229230free_work_list(&_unused);231free_work(_current);232assert(_works_created == 0, "All work must have been freed");233234delete _compressor;235delete _writer;236delete _lock;237}238239void CompressionBackend::deactivate() {240assert(_active, "Must be active");241242MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);243244// Make sure we write the last partially filled buffer.245if ((_current != NULL) && (_current->_in_used > 0)) {246_current->_id = _next_id++;247_to_compress.add_last(_current);248_current = NULL;249ml.notify_all();250}251252// Wait for the threads to drain the compression work list and do some work yourself.253while (!_to_compress.is_empty()) {254do_foreground_work();255}256257_active = false;258ml.notify_all();259}260261void CompressionBackend::thread_loop() {262{263MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);264_nr_of_threads++;265}266267WriteWork* work;268while ((work = get_work()) != NULL) {269do_compress(work);270finish_work(work);271}272273MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);274_nr_of_threads--;275assert(_nr_of_threads >= 0, "Too many threads finished");276}277278void CompressionBackend::set_error(char const* new_error) {279if ((new_error != NULL) && (_err == NULL)) {280_err = new_error;281}282}283284WriteWork* CompressionBackend::allocate_work(size_t in_size, size_t out_size,285size_t tmp_size) {286WriteWork* result = (WriteWork*) os::malloc(sizeof(WriteWork), mtInternal);287288if (result == NULL) {289_work_creation_failed = true;290return NULL;291}292293_works_created++;294result->_in = (char*) os::malloc(in_size, mtInternal);295result->_in_max = in_size;296result->_in_used = 0;297result->_out = NULL;298result->_tmp = NULL;299300if (result->_in == NULL) {301goto fail;302}303304if (out_size > 0) {305result->_out = (char*) os::malloc(out_size, mtInternal);306result->_out_used = 0;307result->_out_max = out_size;308309if (result->_out == NULL) {310goto fail;311}312}313314if (tmp_size > 0) {315result->_tmp = (char*) os::malloc(tmp_size, mtInternal);316result->_tmp_max = tmp_size;317318if (result->_tmp == NULL) {319goto fail;320}321}322323return result;324325fail:326free_work(result);327_work_creation_failed = true;328return NULL;329}330331void CompressionBackend::free_work(WriteWork* work) {332if (work != NULL) {333os::free(work->_in);334os::free(work->_out);335os::free(work->_tmp);336os::free(work);337--_works_created;338}339}340341void CompressionBackend::free_work_list(WorkList* list) {342while (!list->is_empty()) {343free_work(list->remove_first());344}345}346347void CompressionBackend::do_foreground_work() {348assert(!_to_compress.is_empty(), "Must have work to do");349assert(_lock->owned_by_self(), "Must have the lock");350351WriteWork* work = _to_compress.remove_first();352MutexUnlocker mu(_lock, Mutex::_no_safepoint_check_flag);353do_compress(work);354finish_work(work);355}356357WriteWork* CompressionBackend::get_work() {358MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);359360while (_active && _to_compress.is_empty()) {361ml.wait();362}363364return _to_compress.remove_first();365}366367void CompressionBackend::get_new_buffer(char** buffer, size_t* used, size_t* max) {368if (_active) {369MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);370371if (*used > 0) {372_current->_in_used += *used;373374// Check if we do not waste more than _max_waste. If yes, write the buffer.375// Otherwise return the rest of the buffer as the new buffer.376if (_current->_in_max - _current->_in_used <= _max_waste) {377_current->_id = _next_id++;378_to_compress.add_last(_current);379_current = NULL;380ml.notify_all();381} else {382*buffer = _current->_in + _current->_in_used;383*used = 0;384*max = _current->_in_max - _current->_in_used;385386return;387}388}389390while ((_current == NULL) && _unused.is_empty() && _active) {391// Add more work objects if needed.392if (!_work_creation_failed && (_works_created <= _nr_of_threads)) {393WriteWork* work = allocate_work(_in_size, _out_size, _tmp_size);394395if (work != NULL) {396_unused.add_first(work);397}398} else if (!_to_compress.is_empty() && (_nr_of_threads == 0)) {399do_foreground_work();400} else {401ml.wait();402}403}404405if (_current == NULL) {406_current = _unused.remove_first();407}408409if (_current != NULL) {410_current->_in_used = 0;411_current->_out_used = 0;412*buffer = _current->_in;413*used = 0;414*max = _current->_in_max;415416return;417}418}419420*buffer = NULL;421*used = 0;422*max = 0;423424return;425}426427void CompressionBackend::do_compress(WriteWork* work) {428if (_compressor != NULL) {429char const* msg = _compressor->compress(work->_in, work->_in_used, work->_out,430work->_out_max,431work->_tmp, _tmp_size, &work->_out_used);432433if (msg != NULL) {434MutexLocker ml(_lock, Mutex::_no_safepoint_check_flag);435set_error(msg);436}437}438}439440void CompressionBackend::finish_work(WriteWork* work) {441MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);442443_finished.add_by_id(work);444445// Write all finished works as far as we can.446while (!_finished.is_empty() && (_finished.first()->_id == _id_to_write)) {447WriteWork* to_write = _finished.remove_first();448size_t size = _compressor == NULL ? to_write->_in_used : to_write->_out_used;449char* p = _compressor == NULL ? to_write->_in : to_write->_out;450char const* msg = NULL;451452if (_err == NULL) {453_written += size;454MutexUnlocker mu(_lock, Mutex::_no_safepoint_check_flag);455msg = _writer->write_buf(p, (ssize_t) size);456}457458set_error(msg);459_unused.add_first(to_write);460_id_to_write++;461}462463ml.notify_all();464}465466467