Skip to content

Commit

Permalink
[HeapDump] Optimize segmented heap file merging phase
Browse files Browse the repository at this point in the history
Summary: Optimize segmented heap file merging phase by using sendfile
instead of read+write combination.

Test Plan: manual, test/hotspot/jtreg/serviceability

Reviewed-by: denghui.ddh, lei.yul

Issue: dragonwell11#630
  • Loading branch information
y1yang0 committed Aug 21, 2023
1 parent 57ef298 commit ff4792d
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 8 deletions.
8 changes: 8 additions & 0 deletions src/hotspot/os/linux/os_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
# include <sys/mman.h>
# include <sys/stat.h>
# include <sys/select.h>
# include <sys/sendfile.h>
# include <pthread.h>
# include <signal.h>
# include <errno.h>
Expand Down Expand Up @@ -5154,6 +5155,13 @@ jlong os::Linux::fast_thread_cpu_time(clockid_t clockid) {
return (tp.tv_sec * NANOSECS_PER_SEC) + tp.tv_nsec;
}

// copy data between two file descriptor within the kernel
// the number of bytes written to out_fd is returned if transfer was successful
// otherwise, returns -1 that implies an error
jlong os::Linux::sendfile(int out_fd, int in_fd, jlong* offset, jlong count) {
return sendfile64(out_fd, in_fd, (off64_t*)offset, (size_t)count);
}

void os::Linux::initialize_os_info() {
assert(_os_version == 0, "OS info already initialized");

Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/os/linux/os_linux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ class Linux {

static jlong fast_thread_cpu_time(clockid_t clockid);

static jlong sendfile(int out_fd, int in_fd, jlong* offset, jlong count);

static void initialize_os_info();
static bool os_version_is_known();
static uint32_t os_version();
Expand Down
74 changes: 66 additions & 8 deletions src/hotspot/share/services/heapDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
#include "services/threadService.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
#ifdef LINUX
#include "os_linux.hpp"
#endif

/*
* HPROF binary format - description copied from:
Expand Down Expand Up @@ -442,6 +445,7 @@ class DumpWriter : public ResourceObj {
AbstractCompressor* compressor() { return _compressor; }
void set_compressor(AbstractCompressor* p) { _compressor = p; }
bool is_overwrite() const { return _writer->is_overwrite(); }
int get_fd() const { return _writer->get_fd(); }

// writer functions
void write_raw(void* s, size_t len);
Expand Down Expand Up @@ -1609,8 +1613,10 @@ class DumpMerger : public StackObj {
int _dump_seq;

private:
LINUX_ONLY(void merge_file_fast(char* path);)
void merge_file(char* path);
void merge_done();
void set_error(const char* msg);

public:
DumpMerger(const char* path, DumpWriter* writer, int dump_seq) :
Expand All @@ -1622,6 +1628,52 @@ class DumpMerger : public StackObj {
void do_merge();
};

void DumpMerger::set_error(const char* msg) {
assert(msg != NULL, "sanity check");
log_error(heapdump)("%s (file: %s)", msg, _path);
_writer->set_error(msg);
_has_error = true;
}

#ifdef LINUX
void DumpMerger::merge_file_fast(char* path) {
assert(!SafepointSynchronize::is_at_safepoint(), "merging happens outside safepoint");
TraceTime timer("Merge segmented heap file directly", TRACETIME_LOG(Info, heapdump));

int segment_fd = os::open(path, O_RDONLY, 0);
if (segment_fd == -1) {
set_error("Can not open segmented heap file during merging");
return;
}

struct stat st;
if (os::stat(path, &st) != 0) {
::close(segment_fd);
set_error("Can not get segmented heap file size during merging");
return;
}

// A successful call to sendfile may write fewer bytes than requested; the
// caller should be prepared to retry the call if there were unsent bytes.
jlong offset = 0;
while (offset < st.st_size) {
int ret = os::Linux::sendfile(_writer->get_fd(), segment_fd, &offset, st.st_size);
if (ret == -1) {
::close(segment_fd);
set_error("Failed to merge segmented heap file");
return;
}
}

// As sendfile variant does not call the write method of the global writer,
// bytes_written is also incorrect for this variant, we need to explicitly
// accumulate bytes_written for the global writer in this case
julong accum = _writer->bytes_written() + st.st_size;
_writer->set_bytes_written(accum);
::close(segment_fd);
}
#endif

void DumpMerger::merge_done() {
// Writes the HPROF_HEAP_DUMP_END record.
if (!_has_error) {
Expand All @@ -1637,9 +1689,7 @@ void DumpMerger::merge_file(char* path) {

fileStream segment_fs(path, "rb");
if (!segment_fs.is_open()) {
log_error(heapdump)("Can not open segmented heap file %s during merging", path);
_writer->set_error("Can not open segmented heap file during merging");
_has_error = true;
set_error("Can not open segmented heap file during merging");
return;
}

Expand All @@ -1653,10 +1703,7 @@ void DumpMerger::merge_file(char* path) {

_writer->flush();
if (segment_fs.fileSize() != total) {
log_error(heapdump)("Merged heap dump %s is incomplete, expect %ld but read " JLONG_FORMAT " bytes",
path, segment_fs.fileSize(), total);
_writer->set_error("Merged heap dump is incomplete");
_has_error = true;
set_error("Merged heap dump is incomplete");
}
}

Expand All @@ -1669,14 +1716,24 @@ void DumpMerger::do_merge() {
AbstractCompressor* saved_compressor = _writer->compressor();
_writer->set_compressor(NULL);

// merge segmented heap file and remove it anyway
// Merge the content of the remaining files into base file. Regardless of whether
// the merge process is successful or not, these segmented files will be deleted.
char path[JVM_MAXPATHLEN];
for (int i = 0; i < _dump_seq; i++) {
memset(path, 0, JVM_MAXPATHLEN);
os::snprintf(path, JVM_MAXPATHLEN, "%s.p%d", _path, i);
if (!_has_error) {
#ifdef LINUX
// Merge segmented heap files via sendfile, it's more efficient than the
// read+write combination, which would require transferring data to and from
// user space.
merge_file_fast(path);
#else
// Otherwise, fallback to using read+write combination for file merging
merge_file(path);
#endif
}
// Delete selected segmented heap file nevertheless
remove(path);
}

Expand Down Expand Up @@ -2113,6 +2170,7 @@ DumpWriter* VM_HeapDumper::create_local_writer() {

// generate segmented heap file path
const char* base_path = writer()->get_file_path();
// share global compressor, local DumpWriter is not responsible for its life cycle
AbstractCompressor* compressor = writer()->compressor();
int seq = Atomic::add(1, &_dump_seq) - 1;
os::snprintf(path, JVM_MAXPATHLEN, "%s.p%d", base_path, seq);
Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/services/heapDumperCompression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class FileWriter : public AbstractWriter {
const char* get_file_path() { return _path; }

bool is_overwrite() const { return _overwrite; }

int get_fd() const {return _fd; }
};


Expand Down

0 comments on commit ff4792d

Please sign in to comment.