Skip to content

Commit 63ce88b

Browse files
author
David Holmes
committed
8304147: JVM crash during shutdown when dumping dynamic archive
Reviewed-by: ccheung, matsaave, coleenp
1 parent 554bccf commit 63ce88b

File tree

7 files changed

+168
-55
lines changed

7 files changed

+168
-55
lines changed

src/hotspot/share/cds/dynamicArchive.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -377,18 +377,36 @@ void DynamicArchive::check_for_dynamic_dump() {
377377
}
378378
}
379379

380-
void DynamicArchive::prepare_for_dump_at_exit() {
381-
EXCEPTION_MARK;
382-
ResourceMark rm(THREAD);
380+
void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name) {
381+
ExceptionMark em(current);
382+
ResourceMark rm(current);
383+
HandleMark hm(current);
384+
385+
if (!DynamicDumpSharedSpaces || archive_name == nullptr) {
386+
return;
387+
}
388+
389+
log_info(cds, dynamic)("Preparing for dynamic dump at exit in thread %s", current->name());
390+
391+
JavaThread* THREAD = current; // For TRAPS processing related to link_shared_classes
383392
MetaspaceShared::link_shared_classes(false/*not from jcmd*/, THREAD);
384-
if (HAS_PENDING_EXCEPTION) {
385-
log_error(cds)("Dynamic dump has failed");
386-
log_error(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(),
387-
java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
388-
// We cannot continue to dump the archive anymore.
389-
DynamicDumpSharedSpaces = false;
390-
CLEAR_PENDING_EXCEPTION;
393+
if (!HAS_PENDING_EXCEPTION) {
394+
// copy shared path table to saved.
395+
FileMapInfo::clone_shared_path_table(current);
396+
if (!HAS_PENDING_EXCEPTION) {
397+
VM_PopulateDynamicDumpSharedSpace op(archive_name);
398+
VMThread::execute(&op);
399+
return;
400+
}
391401
}
402+
403+
// One of the prepatory steps failed
404+
oop ex = current->pending_exception();
405+
log_error(cds)("Dynamic dump has failed");
406+
log_error(cds)("%s: %s", ex->klass()->external_name(),
407+
java_lang_String::as_utf8_string(java_lang_Throwable::message(ex)));
408+
CLEAR_PENDING_EXCEPTION;
409+
DynamicDumpSharedSpaces = false; // Just for good measure
392410
}
393411

394412
// This is called by "jcmd VM.cds dynamic_dump"
@@ -397,21 +415,12 @@ void DynamicArchive::dump_for_jcmd(const char* archive_name, TRAPS) {
397415
assert(ArchiveClassesAtExit == nullptr, "already checked in arguments.cpp");
398416
assert(DynamicDumpSharedSpaces, "already checked by check_for_dynamic_dump() during VM startup");
399417
MetaspaceShared::link_shared_classes(true/*from jcmd*/, CHECK);
400-
dump(archive_name, THREAD);
401-
}
402-
403-
void DynamicArchive::dump(const char* archive_name, TRAPS) {
404418
// copy shared path table to saved.
405419
FileMapInfo::clone_shared_path_table(CHECK);
406-
407420
VM_PopulateDynamicDumpSharedSpace op(archive_name);
408421
VMThread::execute(&op);
409422
}
410423

411-
bool DynamicArchive::should_dump_at_vm_exit() {
412-
return DynamicDumpSharedSpaces && (ArchiveClassesAtExit != nullptr);
413-
}
414-
415424
bool DynamicArchive::validate(FileMapInfo* dynamic_info) {
416425
assert(!dynamic_info->is_static(), "must be");
417426
// Check if the recorded base archive matches with the current one

src/hotspot/share/cds/dynamicArchive.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,8 @@ class DynamicArchiveHeader : public FileMapHeader {
5959
class DynamicArchive : AllStatic {
6060
public:
6161
static void check_for_dynamic_dump();
62-
static bool should_dump_at_vm_exit();
63-
static void prepare_for_dump_at_exit();
6462
static void dump_for_jcmd(const char* archive_name, TRAPS);
65-
static void dump(const char* archive_name, TRAPS);
63+
static void dump_at_exit(JavaThread* current, const char* archive_name);
6664
static bool is_mapped() { return FileMapInfo::dynamic_info() != nullptr; }
6765
static bool validate(FileMapInfo* dynamic_info);
6866
};

src/hotspot/share/prims/jvm.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,6 @@ JVM_END
426426
extern volatile jint vm_created;
427427

428428
JVM_ENTRY_NO_ENV(void, JVM_BeforeHalt())
429-
#if INCLUDE_CDS
430-
// Link all classes for dynamic CDS dumping before vm exit.
431-
if (DynamicArchive::should_dump_at_vm_exit()) {
432-
DynamicArchive::prepare_for_dump_at_exit();
433-
}
434-
#endif
435429
EventShutdown event;
436430
if (event.should_commit()) {
437431
event.set_reason("Shutdown requested from Java");

src/hotspot/share/runtime/java.cpp

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,14 @@ void before_exit(JavaThread* thread, bool halt) {
439439
}
440440
#endif
441441

442+
#if INCLUDE_CDS
443+
// Dynamic CDS dumping must happen whilst we can still reliably
444+
// run Java code.
445+
DynamicArchive::dump_at_exit(thread, ArchiveClassesAtExit);
446+
assert(!thread->has_pending_exception(), "must be");
447+
#endif
448+
449+
442450
// Actual shutdown logic begins here.
443451

444452
#if INCLUDE_JVMCI
@@ -514,21 +522,6 @@ void before_exit(JavaThread* thread, bool halt) {
514522
// Note: we don't wait until it actually dies.
515523
os::terminate_signal_thread();
516524

517-
#if INCLUDE_CDS
518-
if (DynamicArchive::should_dump_at_vm_exit()) {
519-
assert(ArchiveClassesAtExit != nullptr, "Must be already set");
520-
ExceptionMark em(thread);
521-
DynamicArchive::dump(ArchiveClassesAtExit, thread);
522-
if (thread->has_pending_exception()) {
523-
ResourceMark rm(thread);
524-
oop pending_exception = thread->pending_exception();
525-
log_error(cds)("ArchiveClassesAtExit has failed %s: %s", pending_exception->klass()->external_name(),
526-
java_lang_String::as_utf8_string(java_lang_Throwable::message(pending_exception)));
527-
thread->clear_pending_exception();
528-
}
529-
}
530-
#endif
531-
532525
print_statistics();
533526
Universe::heap()->print_tracing_info();
534527

src/hotspot/share/runtime/javaThread.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,22 +2023,11 @@ bool JavaThread::sleep(jlong millis) {
20232023
void JavaThread::invoke_shutdown_hooks() {
20242024
HandleMark hm(this);
20252025

2026-
// We could get here with a pending exception, if so clear it now or
2027-
// it will cause MetaspaceShared::link_shared_classes to
2028-
// fail for dynamic dump.
2026+
// We could get here with a pending exception, if so clear it now.
20292027
if (this->has_pending_exception()) {
20302028
this->clear_pending_exception();
20312029
}
20322030

2033-
#if INCLUDE_CDS
2034-
// Link all classes for dynamic CDS dumping before vm exit.
2035-
// Same operation is being done in JVM_BeforeHalt for handling the
2036-
// case where the application calls System.exit().
2037-
if (DynamicArchive::should_dump_at_vm_exit()) {
2038-
DynamicArchive::prepare_for_dump_at_exit();
2039-
}
2040-
#endif
2041-
20422031
EXCEPTION_MARK;
20432032
Klass* shutdown_klass =
20442033
SystemDictionary::resolve_or_null(vmSymbols::java_lang_Shutdown(),
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* @test
27+
* @bug 8304147
28+
* @summary Test the exit race for dynamic dumping at exit
29+
* @requires vm.cds
30+
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes
31+
* @build ExitRace jdk.test.whitebox.WhiteBox
32+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar exitRace.jar ExitRace ExitRace$1 ExitRace$1$1
33+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
34+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ExitRaceTest
35+
*/
36+
37+
import jdk.test.lib.helpers.ClassFileInstaller;
38+
39+
public class ExitRaceTest extends DynamicArchiveTestBase {
40+
41+
public static void main(String[] args) throws Exception {
42+
runTest(ExitRaceTest::test);
43+
}
44+
45+
static void test() throws Exception {
46+
String topArchiveName = getNewArchiveName();
47+
String appJar = ClassFileInstaller.getJarPath("exitRace.jar");
48+
String mainClass = "ExitRace";
49+
50+
dump(topArchiveName,
51+
"-Xlog:cds+dynamic=debug",
52+
"-cp", appJar, mainClass)
53+
.assertNormalExit(output -> {
54+
output.shouldHaveExitValue(0)
55+
.shouldContain("Preparing for dynamic dump")
56+
.reportDiagnosticSummary();
57+
});
58+
}
59+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
// Try to trigger concurrent dynamic-dump-at-exit processing by creating a race
26+
// between normal VM termination by the last non-daemon thread exiting, and a
27+
// call to Runtime.halt().
28+
29+
public class ExitRace {
30+
31+
static volatile int terminationPhase = 0;
32+
33+
public static void main(String [] args) {
34+
35+
// Need to spawn a new non-daemon thread so the main thread will
36+
// have time to become the DestroyJavaVM thread.
37+
Thread runner = new Thread("Runner") {
38+
public void run() {
39+
// This thread will be the one to trigger normal VM termination
40+
// when it exits. We first create a daemon thread to call
41+
// Runtime.halt() and then wait for it to tell us to exit.
42+
43+
Thread daemon = new Thread("Daemon") {
44+
public void run() {
45+
// Let main thread go
46+
terminationPhase = 1;
47+
// Spin until main thread is active again
48+
while (terminationPhase == 1)
49+
;
50+
Runtime.getRuntime().halt(0); // Normal exit code
51+
}
52+
};
53+
daemon.setDaemon(true);
54+
daemon.start();
55+
56+
// Wait until daemon is ready
57+
while (terminationPhase == 0) {
58+
try {
59+
Thread.sleep(10);
60+
} catch (InterruptedException cantHappen) {
61+
}
62+
}
63+
64+
// Release daemon thread
65+
terminationPhase++;
66+
// Normal exit
67+
}
68+
};
69+
runner.start();
70+
}
71+
}

0 commit comments

Comments
 (0)