Skip to content

Commit 3760a04

Browse files
committed
8314021: HeapDump: Optimize segmented heap file merging phase
Reviewed-by: amenkov, kevinw
1 parent f226ceb commit 3760a04

File tree

4 files changed

+76
-8
lines changed

4 files changed

+76
-8
lines changed

src/hotspot/os/linux/os_linux.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
# include <sys/mman.h>
9090
# include <sys/stat.h>
9191
# include <sys/select.h>
92+
# include <sys/sendfile.h>
9293
# include <pthread.h>
9394
# include <signal.h>
9495
# include <endian.h>
@@ -4368,6 +4369,13 @@ jlong os::Linux::fast_thread_cpu_time(clockid_t clockid) {
43684369
return (tp.tv_sec * NANOSECS_PER_SEC) + tp.tv_nsec;
43694370
}
43704371

4372+
// copy data between two file descriptor within the kernel
4373+
// the number of bytes written to out_fd is returned if transfer was successful
4374+
// otherwise, returns -1 that implies an error
4375+
jlong os::Linux::sendfile(int out_fd, int in_fd, jlong* offset, jlong count) {
4376+
return sendfile64(out_fd, in_fd, (off64_t*)offset, (size_t)count);
4377+
}
4378+
43714379
// Determine if the vmid is the parent pid for a child in a PID namespace.
43724380
// Return the namespace pid if so, otherwise -1.
43734381
int os::Linux::get_namespace_pid(int vmid) {

src/hotspot/os/linux/os_linux.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class os::Linux {
167167

168168
static jlong fast_thread_cpu_time(clockid_t clockid);
169169

170+
static jlong sendfile(int out_fd, int in_fd, jlong* offset, jlong count);
171+
170172
// Determine if the vmid is the parent pid for a child in a PID namespace.
171173
// Return the namespace pid if so, otherwise -1.
172174
static int get_namespace_pid(int vmid);

src/hotspot/share/services/heapDumper.cpp

+64-8
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
#include "utilities/checkedCast.hpp"
6363
#include "utilities/macros.hpp"
6464
#include "utilities/ostream.hpp"
65+
#ifdef LINUX
66+
#include "os_linux.hpp"
67+
#endif
6568

6669
/*
6770
* HPROF binary format - description copied from:
@@ -630,6 +633,7 @@ class DumpWriter : public AbstractDumpWriter {
630633
AbstractCompressor* compressor() { return _compressor; }
631634
void set_compressor(AbstractCompressor* p) { _compressor = p; }
632635
bool is_overwrite() const { return _writer->is_overwrite(); }
636+
int get_fd() const { return _writer->get_fd(); }
633637

634638
void flush() override;
635639
};
@@ -1533,6 +1537,7 @@ class DumpMerger : public StackObj {
15331537
private:
15341538
void merge_file(char* path);
15351539
void merge_done();
1540+
void set_error(const char* msg);
15361541

15371542
public:
15381543
DumpMerger(const char* path, DumpWriter* writer, int dump_seq) :
@@ -1553,15 +1558,62 @@ void DumpMerger::merge_done() {
15531558
_dump_seq = 0; //reset
15541559
}
15551560

1561+
void DumpMerger::set_error(const char* msg) {
1562+
assert(msg != nullptr, "sanity check");
1563+
log_error(heapdump)("%s (file: %s)", msg, _path);
1564+
_writer->set_error(msg);
1565+
_has_error = true;
1566+
}
1567+
1568+
#ifdef LINUX
1569+
// Merge segmented heap files via sendfile, it's more efficient than the
1570+
// read+write combination, which would require transferring data to and from
1571+
// user space.
1572+
void DumpMerger::merge_file(char* path) {
1573+
assert(!SafepointSynchronize::is_at_safepoint(), "merging happens outside safepoint");
1574+
TraceTime timer("Merge segmented heap file directly", TRACETIME_LOG(Info, heapdump));
1575+
1576+
int segment_fd = os::open(path, O_RDONLY, 0);
1577+
if (segment_fd == -1) {
1578+
set_error("Can not open segmented heap file during merging");
1579+
return;
1580+
}
1581+
1582+
struct stat st;
1583+
if (os::stat(path, &st) != 0) {
1584+
::close(segment_fd);
1585+
set_error("Can not get segmented heap file size during merging");
1586+
return;
1587+
}
1588+
1589+
// A successful call to sendfile may write fewer bytes than requested; the
1590+
// caller should be prepared to retry the call if there were unsent bytes.
1591+
jlong offset = 0;
1592+
while (offset < st.st_size) {
1593+
int ret = os::Linux::sendfile(_writer->get_fd(), segment_fd, &offset, st.st_size);
1594+
if (ret == -1) {
1595+
::close(segment_fd);
1596+
set_error("Failed to merge segmented heap file");
1597+
return;
1598+
}
1599+
}
1600+
1601+
// As sendfile variant does not call the write method of the global writer,
1602+
// bytes_written is also incorrect for this variant, we need to explicitly
1603+
// accumulate bytes_written for the global writer in this case
1604+
julong accum = _writer->bytes_written() + st.st_size;
1605+
_writer->set_bytes_written(accum);
1606+
::close(segment_fd);
1607+
}
1608+
#else
1609+
// Generic implementation using read+write
15561610
void DumpMerger::merge_file(char* path) {
15571611
assert(!SafepointSynchronize::is_at_safepoint(), "merging happens outside safepoint");
15581612
TraceTime timer("Merge segmented heap file", TRACETIME_LOG(Info, heapdump));
15591613

15601614
fileStream segment_fs(path, "rb");
15611615
if (!segment_fs.is_open()) {
1562-
log_error(heapdump)("Can not open segmented heap file %s during merging", path);
1563-
_writer->set_error("Can not open segmented heap file during merging");
1564-
_has_error = true;
1616+
set_error("Can not open segmented heap file during merging");
15651617
return;
15661618
}
15671619

@@ -1575,12 +1627,10 @@ void DumpMerger::merge_file(char* path) {
15751627

15761628
_writer->flush();
15771629
if (segment_fs.fileSize() != total) {
1578-
log_error(heapdump)("Merged heap dump %s is incomplete, expect %ld but read " JLONG_FORMAT " bytes",
1579-
path, segment_fs.fileSize(), total);
1580-
_writer->set_error("Merged heap dump is incomplete");
1581-
_has_error = true;
1630+
set_error("Merged heap dump is incomplete");
15821631
}
15831632
}
1633+
#endif
15841634

15851635
void DumpMerger::do_merge() {
15861636
assert(!SafepointSynchronize::is_at_safepoint(), "merging happens outside safepoint");
@@ -1591,14 +1641,16 @@ void DumpMerger::do_merge() {
15911641
AbstractCompressor* saved_compressor = _writer->compressor();
15921642
_writer->set_compressor(nullptr);
15931643

1594-
// merge segmented heap file and remove it anyway
1644+
// Merge the content of the remaining files into base file. Regardless of whether
1645+
// the merge process is successful or not, these segmented files will be deleted.
15951646
char path[JVM_MAXPATHLEN];
15961647
for (int i = 0; i < _dump_seq; i++) {
15971648
memset(path, 0, JVM_MAXPATHLEN);
15981649
os::snprintf(path, JVM_MAXPATHLEN, "%s.p%d", _path, i);
15991650
if (!_has_error) {
16001651
merge_file(path);
16011652
}
1653+
// Delete selected segmented heap file nevertheless
16021654
remove(path);
16031655
}
16041656

@@ -2012,6 +2064,7 @@ DumpWriter* VM_HeapDumper::create_local_writer() {
20122064

20132065
// generate segmented heap file path
20142066
const char* base_path = writer()->get_file_path();
2067+
// share global compressor, local DumpWriter is not responsible for its life cycle
20152068
AbstractCompressor* compressor = writer()->compressor();
20162069
int seq = Atomic::fetch_then_add(&_dump_seq, 1);
20172070
os::snprintf(path, JVM_MAXPATHLEN, "%s.p%d", base_path, seq);
@@ -2256,6 +2309,9 @@ int HeapDumper::dump(const char* path, outputStream* out, int compression, bool
22562309
}
22572310
}
22582311

2312+
if (compressor != nullptr) {
2313+
delete compressor;
2314+
}
22592315
return (writer.error() == nullptr) ? 0 : -1;
22602316
}
22612317

src/hotspot/share/services/heapDumperCompression.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class FileWriter : public AbstractWriter {
7878
const char* get_file_path() { return _path; }
7979

8080
bool is_overwrite() const { return _overwrite; }
81+
82+
int get_fd() const {return _fd; }
8183
};
8284

8385

0 commit comments

Comments
 (0)