Skip to content

Commit 1535d08

Browse files
committed
8371944: AOT configuration is corrupted when app closes System.out
Reviewed-by: kvn, iveresov
1 parent f5bc6ee commit 1535d08

File tree

6 files changed

+121
-13
lines changed

6 files changed

+121
-13
lines changed

src/hotspot/share/cds/aotMetaspace.cpp

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ intx AOTMetaspace::_relocation_delta;
114114
char* AOTMetaspace::_requested_base_address;
115115
Array<Method*>* AOTMetaspace::_archived_method_handle_intrinsics = nullptr;
116116
bool AOTMetaspace::_use_optimized_module_handling = true;
117+
FileMapInfo* AOTMetaspace::_output_mapinfo = nullptr;
117118

118119
// The CDS archive is divided into the following regions:
119120
// rw - read-write metadata
@@ -322,6 +323,24 @@ void AOTMetaspace::initialize_for_static_dump() {
322323
AOTMetaspace::unrecoverable_writing_error();
323324
}
324325
_symbol_region.init(&_symbol_rs, &_symbol_vs);
326+
if (CDSConfig::is_dumping_preimage_static_archive()) {
327+
// We are in the AOT training run. User code is executed.
328+
//
329+
// On Windows, if the user code closes System.out and we open the AOT config file for output
330+
// only at VM exit, we might get back the same file HANDLE as stdout, and the AOT config
331+
// file may get corrupted by UL logs. By opening early, we ensure that the output
332+
// HANDLE is different than stdout so we can avoid such corruption.
333+
open_output_mapinfo();
334+
} else {
335+
// No need for the above as we won't execute any user code.
336+
}
337+
}
338+
339+
void AOTMetaspace::open_output_mapinfo() {
340+
const char* static_archive = CDSConfig::output_archive_path();
341+
assert(static_archive != nullptr, "sanity");
342+
_output_mapinfo = new FileMapInfo(static_archive, true);
343+
_output_mapinfo->open_as_output();
325344
}
326345

327346
// Called by universe_post_init()
@@ -655,15 +674,14 @@ class VM_PopulateDumpSharedSpace : public VM_Operation {
655674

656675
public:
657676

658-
VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b) :
659-
VM_Operation(), _mapped_heap_info(), _streamed_heap_info(), _map_info(nullptr), _builder(b) {}
677+
VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b, FileMapInfo* map_info) :
678+
VM_Operation(), _mapped_heap_info(), _streamed_heap_info(), _map_info(map_info), _builder(b) {}
660679

661680
bool skip_operation() const { return false; }
662681

663682
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
664683
ArchiveMappedHeapInfo* mapped_heap_info() { return &_mapped_heap_info; }
665684
ArchiveStreamedHeapInfo* streamed_heap_info() { return &_streamed_heap_info; }
666-
FileMapInfo* map_info() const { return _map_info; }
667685
void doit(); // outline because gdb sucks
668686
bool allow_nested_vm_operations() const { return true; }
669687
}; // class VM_PopulateDumpSharedSpace
@@ -795,12 +813,6 @@ void VM_PopulateDumpSharedSpace::doit() {
795813
CppVtables::zero_archived_vtables();
796814

797815
// Write the archive file
798-
if (CDSConfig::is_dumping_final_static_archive()) {
799-
FileMapInfo::free_current_info(); // FIXME: should not free current info
800-
}
801-
const char* static_archive = CDSConfig::output_archive_path();
802-
assert(static_archive != nullptr, "sanity");
803-
_map_info = new FileMapInfo(static_archive, true);
804816
_map_info->populate_header(AOTMetaspace::core_region_alignment());
805817
_map_info->set_early_serialized_data(early_serialized_data);
806818
_map_info->set_serialized_data(serialized_data);
@@ -1138,7 +1150,14 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
11381150
}
11391151
#endif
11401152

1141-
VM_PopulateDumpSharedSpace op(builder);
1153+
if (!CDSConfig::is_dumping_preimage_static_archive()) {
1154+
if (CDSConfig::is_dumping_final_static_archive()) {
1155+
FileMapInfo::free_current_info(); // FIXME: should not free current info
1156+
}
1157+
open_output_mapinfo();
1158+
}
1159+
1160+
VM_PopulateDumpSharedSpace op(builder, _output_mapinfo);
11421161
VMThread::execute(&op);
11431162

11441163
if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) {
@@ -1152,7 +1171,9 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
11521171
CDSConfig::disable_dumping_aot_code();
11531172
}
11541173

1155-
bool status = write_static_archive(&builder, op.map_info(), op.mapped_heap_info(), op.streamed_heap_info());
1174+
bool status = write_static_archive(&builder, _output_mapinfo, op.mapped_heap_info(), op.streamed_heap_info());
1175+
assert(!_output_mapinfo->is_open(), "Must be closed already");
1176+
_output_mapinfo = nullptr;
11561177
if (status && CDSConfig::is_dumping_preimage_static_archive()) {
11571178
tty->print_cr("%s AOTConfiguration recorded: %s",
11581179
CDSConfig::has_temp_aot_config_file() ? "Temporary" : "", AOTConfiguration);
@@ -1173,11 +1194,10 @@ bool AOTMetaspace::write_static_archive(ArchiveBuilder* builder,
11731194
// relocate the data so that it can be mapped to AOTMetaspace::requested_base_address()
11741195
// without runtime relocation.
11751196
builder->relocate_to_requested();
1176-
1177-
map_info->open_as_output();
11781197
if (!map_info->is_open()) {
11791198
return false;
11801199
}
1200+
map_info->prepare_for_writing();
11811201
builder->write_archive(map_info, mapped_heap_info, streamed_heap_info);
11821202
return true;
11831203
}

src/hotspot/share/cds/aotMetaspace.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class AOTMetaspace : AllStatic {
6060
static char* _requested_base_address;
6161
static bool _use_optimized_module_handling;
6262
static Array<Method*>* _archived_method_handle_intrinsics;
63+
static FileMapInfo* _output_mapinfo;
6364

6465
public:
6566
enum {
@@ -185,6 +186,7 @@ class AOTMetaspace : AllStatic {
185186
private:
186187
static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN;
187188
static void fork_and_dump_final_static_archive(TRAPS);
189+
static void open_output_mapinfo();
188190
static bool write_static_archive(ArchiveBuilder* builder,
189191
FileMapInfo* map_info,
190192
ArchiveMappedHeapInfo* mapped_heap_info,

src/hotspot/share/cds/dynamicArchive.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio
353353
assert(dynamic_info != nullptr, "Sanity");
354354

355355
dynamic_info->open_as_output();
356+
dynamic_info->prepare_for_writing();
356357
ArchiveBuilder::write_archive(dynamic_info, nullptr, nullptr);
357358

358359
address base = _requested_dynamic_archive_bottom;

src/hotspot/share/cds/filemap.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,9 @@ void FileMapInfo::open_as_output() {
779779
}
780780
_fd = fd;
781781
_file_open = true;
782+
}
782783

784+
void FileMapInfo::prepare_for_writing() {
783785
// Seek past the header. We will write the header after all regions are written
784786
// and their CRCs computed.
785787
size_t header_bytes = header()->header_size();

src/hotspot/share/cds/filemap.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ class FileMapInfo : public CHeapObj<mtInternal> {
365365
// File manipulation.
366366
bool open_as_input() NOT_CDS_RETURN_(false);
367367
void open_as_output();
368+
void prepare_for_writing();
368369
void write_header();
369370
void write_region(int region, char* base, size_t size,
370371
bool read_only, bool allow_exec);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2025, 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+
* @summary AOT configuration should not be corrupted even if the app closes System.out in the training run
28+
* @bug 8371944
29+
* @library /test/jdk/lib/testlibrary /test/lib
30+
* @build CloseSystemOut
31+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar CloseSystemOutApp
32+
* @run driver CloseSystemOut
33+
*/
34+
35+
import java.io.PrintWriter;
36+
import jdk.test.lib.cds.CDSAppTester;
37+
import jdk.test.lib.helpers.ClassFileInstaller;
38+
import jdk.test.lib.process.OutputAnalyzer;
39+
40+
public class CloseSystemOut {
41+
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
42+
static final String mainClass = "CloseSystemOutApp";
43+
44+
public static void main(String[] args) throws Exception {
45+
Tester tester = new Tester();
46+
tester.run(new String[] {"AOT", "--two-step-training"} );
47+
}
48+
49+
static class Tester extends CDSAppTester {
50+
public Tester() {
51+
super(mainClass);
52+
}
53+
54+
@Override
55+
public String classpath(RunMode runMode) {
56+
return appJar;
57+
}
58+
59+
@Override
60+
public String[] appCommandLine(RunMode runMode) {
61+
return new String[] {mainClass};
62+
}
63+
64+
@Override
65+
public void checkExecution(OutputAnalyzer out, RunMode runMode) {
66+
if (runMode != RunMode.ASSEMBLY) {
67+
out.shouldContain("Hello Confused World");
68+
}
69+
}
70+
}
71+
}
72+
73+
class CloseSystemOutApp {
74+
public static void main(String args[]) {
75+
// Naive code that ends up closing System.out/err when we
76+
// leave the "try" block
77+
try (var err = new PrintWriter(System.err);
78+
var out = new PrintWriter(System.out)) {
79+
out.println("Hello Confused World");
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)