Skip to content

Commit f18b1ac

Browse files
committed
8371944: AOT configuration is corrupted when app closes System.out
Reviewed-by: phh, iklam Backport-of: 1535d08
1 parent c3eecc0 commit f18b1ac

File tree

6 files changed

+121
-13
lines changed

6 files changed

+121
-13
lines changed

src/hotspot/share/cds/dynamicArchive.cpp

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

350350
dynamic_info->open_as_output();
351+
dynamic_info->prepare_for_writing();
351352
ArchiveHeapInfo no_heap_for_dynamic_dump;
352353
ArchiveBuilder::write_archive(dynamic_info, &no_heap_for_dynamic_dump);
353354

src/hotspot/share/cds/filemap.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,9 @@ void FileMapInfo::open_as_output() {
766766
}
767767
_fd = fd;
768768
_file_open = true;
769+
}
769770

771+
void FileMapInfo::prepare_for_writing() {
770772
// Seek past the header. We will write the header after all regions are written
771773
// and their CRCs computed.
772774
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
@@ -359,6 +359,7 @@ class FileMapInfo : public CHeapObj<mtInternal> {
359359
// File manipulation.
360360
bool open_as_input() NOT_CDS_RETURN_(false);
361361
void open_as_output();
362+
void prepare_for_writing();
362363
void write_header();
363364
void write_region(int region, char* base, size_t size,
364365
bool read_only, bool allow_exec);

src/hotspot/share/cds/metaspaceShared.cpp

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ intx MetaspaceShared::_relocation_delta;
113113
char* MetaspaceShared::_requested_base_address;
114114
Array<Method*>* MetaspaceShared::_archived_method_handle_intrinsics = nullptr;
115115
bool MetaspaceShared::_use_optimized_module_handling = true;
116+
FileMapInfo* MetaspaceShared::_output_mapinfo = nullptr;
116117

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

326345
// Called by universe_post_init()
@@ -551,14 +570,13 @@ class VM_PopulateDumpSharedSpace : public VM_Operation {
551570

552571
public:
553572

554-
VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b) :
555-
VM_Operation(), _heap_info(), _map_info(nullptr), _builder(b) {}
573+
VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b, FileMapInfo* map_info) :
574+
VM_Operation(), _heap_info(), _map_info(map_info), _builder(b) {}
556575

557576
bool skip_operation() const { return false; }
558577

559578
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
560579
ArchiveHeapInfo* heap_info() { return &_heap_info; }
561-
FileMapInfo* map_info() const { return _map_info; }
562580
void doit(); // outline because gdb sucks
563581
bool allow_nested_vm_operations() const { return true; }
564582
}; // class VM_PopulateDumpSharedSpace
@@ -688,12 +706,6 @@ void VM_PopulateDumpSharedSpace::doit() {
688706
CppVtables::zero_archived_vtables();
689707

690708
// Write the archive file
691-
if (CDSConfig::is_dumping_final_static_archive()) {
692-
FileMapInfo::free_current_info(); // FIXME: should not free current info
693-
}
694-
const char* static_archive = CDSConfig::output_archive_path();
695-
assert(static_archive != nullptr, "sanity");
696-
_map_info = new FileMapInfo(static_archive, true);
697709
_map_info->populate_header(MetaspaceShared::core_region_alignment());
698710
_map_info->set_early_serialized_data(early_serialized_data);
699711
_map_info->set_serialized_data(serialized_data);
@@ -1012,7 +1024,14 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
10121024
}
10131025
#endif
10141026

1015-
VM_PopulateDumpSharedSpace op(builder);
1027+
if (!CDSConfig::is_dumping_preimage_static_archive()) {
1028+
if (CDSConfig::is_dumping_final_static_archive()) {
1029+
FileMapInfo::free_current_info(); // FIXME: should not free current info
1030+
}
1031+
open_output_mapinfo();
1032+
}
1033+
1034+
VM_PopulateDumpSharedSpace op(builder, _output_mapinfo);
10161035
VMThread::execute(&op);
10171036

10181037
if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) {
@@ -1026,7 +1045,9 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
10261045
CDSConfig::disable_dumping_aot_code();
10271046
}
10281047

1029-
bool status = write_static_archive(&builder, op.map_info(), op.heap_info());
1048+
bool status = write_static_archive(&builder, _output_mapinfo, op.heap_info());
1049+
assert(!_output_mapinfo->is_open(), "Must be closed already");
1050+
_output_mapinfo = nullptr;
10301051
if (status && CDSConfig::is_dumping_preimage_static_archive()) {
10311052
tty->print_cr("%s AOTConfiguration recorded: %s",
10321053
CDSConfig::has_temp_aot_config_file() ? "Temporary" : "", AOTConfiguration);
@@ -1044,11 +1065,10 @@ bool MetaspaceShared::write_static_archive(ArchiveBuilder* builder, FileMapInfo*
10441065
// relocate the data so that it can be mapped to MetaspaceShared::requested_base_address()
10451066
// without runtime relocation.
10461067
builder->relocate_to_requested();
1047-
1048-
map_info->open_as_output();
10491068
if (!map_info->is_open()) {
10501069
return false;
10511070
}
1071+
map_info->prepare_for_writing();
10521072
builder->write_archive(map_info, heap_info);
10531073

10541074
if (AllowArchivingWithJavaAgent) {

src/hotspot/share/cds/metaspaceShared.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class MetaspaceShared : AllStatic {
5959
static char* _requested_base_address;
6060
static bool _use_optimized_module_handling;
6161
static Array<Method*>* _archived_method_handle_intrinsics;
62+
static FileMapInfo* _output_mapinfo;
6263

6364
public:
6465
enum {
@@ -180,6 +181,7 @@ class MetaspaceShared : AllStatic {
180181
private:
181182
static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN;
182183
static void fork_and_dump_final_static_archive(TRAPS);
184+
static void open_output_mapinfo();
183185
static bool write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, ArchiveHeapInfo* heap_info);
184186
static FileMapInfo* open_static_archive();
185187
static FileMapInfo* open_dynamic_archive();
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)