Skip to content

Commit a831599

Browse files
Do markChunkFinal and patch header like regular rotatios. Add tests.
1 parent 30b036a commit a831599

File tree

5 files changed

+182
-155
lines changed

5 files changed

+182
-155
lines changed

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/jfr/PosixJfrEmergencyDumpSupport.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,19 @@ public RawFileDescriptor chunkPath() {
117117
if (!openEmergencyDumpFile()) {
118118
return WordFactory.nullPointer();
119119
}
120-
// We can directly use the emergency dump file name as the new chunk since there are no other chunk files.
120+
// We can directly use the emergency dump file name as the new chunk since there are no
121+
// other chunk files.
121122
return emergencyFd;
122123
}
123124
Log.log().string("Creating a new emergency chunk file in the JFR disk repository").newline();
124125
return createEmergencyChunkPath();
125126
}
126127

127-
/** The normal chunkfile name format is: repository path + file separator + date time + extension.
128-
* In this case we just use a hardcoded string instead of date time, which will successfully rank last in lexographic order among other chunkfile names.*/
128+
/**
129+
* The normal chunkfile name format is: repository path + file separator + date time +
130+
* extension. In this case we just use a hardcoded string instead of date time, which will
131+
* successfully rank last in lexographic order among other chunkfile names.
132+
*/
129133
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+3/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp#L418-L431")
130134
private RawFileDescriptor createEmergencyChunkPath() {
131135
clearPathBuffer();
@@ -297,7 +301,7 @@ private boolean openDirectory() {
297301

298302
private CCharPointer getRepositoryLocation() {
299303
clearPathBuffer();
300-
writeToPathBuffer(repositoryLocationBytes,0);
304+
writeToPathBuffer(repositoryLocationBytes, 0);
301305
return getPathBuffer();
302306
}
303307

@@ -382,7 +386,7 @@ private void clearPathBuffer() {
382386
LibC.memset(getPathBuffer(), Word.signed(0), Word.unsigned(JVM_MAXPATHLEN));
383387
}
384388

385-
private int writeToPathBuffer(byte[] bytes, int idx){
389+
private int writeToPathBuffer(byte[] bytes, int idx) {
386390
for (int i = 0; i < bytes.length; i++) {
387391
getPathBuffer().write(idx++, bytes[i]);
388392
}
@@ -420,8 +424,6 @@ class PosixJfrEmergencyDumpFeature extends JfrEmergencyDumpFeature {
420424

421425
@Override
422426
public void afterRegistration(AfterRegistrationAccess access) {
423-
PosixJfrEmergencyDumpSupport support = new PosixJfrEmergencyDumpSupport();
424-
ImageSingletons.add(JfrEmergencyDumpSupport.class, support);
425-
ImageSingletons.add(PosixJfrEmergencyDumpSupport.class, support);
427+
ImageSingletons.add(JfrEmergencyDumpSupport.class, new PosixJfrEmergencyDumpSupport());
426428
}
427429
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrChunkFileWriter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ public void closeFileForEmergencyDump() {
275275
writeThreadCheckpoint(true);
276276
writeFlushCheckpoint(true);
277277
writeMetadataEvent();
278-
patchFileHeader(true);
278+
// Header must be marked COMPLETE, unlike at flushpoints.
279+
patchFileHeader(false);
279280

280281
getFileSupport().close(fd);
281282
filename = null;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ public void setDumpPath(String dumpPathText) {
601601
*/
602602
public String getDumpPath() {
603603
if (JfrEmergencyDumpSupport.singleton().getDumpPath() == null) {
604-
JfrEmergencyDumpSupport.singleton().setDumpPath(Target_jdk_jfr_internal_util_Utils.getPathInProperty("user.home", null).toString());
604+
JfrEmergencyDumpSupport.singleton().setDumpPath(Target_jdk_jfr_internal_util_Utils.getPathInProperty("user.home", null).toString());
605605
}
606606
return JfrEmergencyDumpSupport.singleton().getDumpPath();
607607
}
@@ -758,6 +758,7 @@ public void vmErrorRotation() {
758758
chunkWriter.openFile(fd);
759759
}
760760
assert chunkWriter.hasOpenFile();
761+
chunkWriter.markChunkFinal();
761762
chunkWriter.closeFileForEmergencyDump();
762763
JfrEmergencyDumpSupport.singleton().onVmError();
763764
} finally {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2021, 2022, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
27+
package com.oracle.svm.test.jfr;
28+
29+
import com.oracle.svm.test.jfr.events.StringEvent;
30+
import jdk.jfr.Recording;
31+
import jdk.jfr.consumer.RecordedEvent;
32+
import org.junit.Test;
33+
34+
import java.nio.file.Files;
35+
import java.nio.file.Path;
36+
import java.util.ArrayList;
37+
import java.util.List;
38+
39+
import static com.oracle.svm.test.jfr.AbstractJfrTest.getEvents;
40+
import static org.junit.Assert.assertEquals;
41+
import static org.junit.Assert.assertTrue;
42+
43+
import com.oracle.svm.core.jfr.SubstrateJVM;
44+
45+
/**
46+
* This test commits events across multiple chunk files and ensure that the events all appear in the
47+
* emergency dump. This would indicate that the chunk files from the disk repository we merged
48+
* correctly along with in-flight data.
49+
*/
50+
public class TestEmergencyDump extends JfrRecordingTest {
51+
@Test
52+
public void test() throws Throwable {
53+
List<String> expectedStrings = new ArrayList();
54+
expectedStrings.add("first");
55+
expectedStrings.add("second");
56+
expectedStrings.add("third");
57+
58+
String[] testedEvents = new String[]{"com.jfr.String"};
59+
Recording recording = startRecording(testedEvents);
60+
61+
// This event will be in chunk #1 in disk repository.
62+
StringEvent e1 = new StringEvent();
63+
e1.message = expectedStrings.get(0);
64+
e1.commit();
65+
66+
// Invoke chunk rotation.
67+
recording.dump(createTempJfrFile());
68+
69+
// This event will be in chunk #2 in disk repository.
70+
StringEvent e2 = new StringEvent();
71+
e2.message = expectedStrings.get(1);
72+
e2.commit();
73+
74+
// Invoke chunk rotation.
75+
recording.dump(createTempJfrFile());
76+
77+
// This event will be in in-flight and should be flushed upon emergency dump.
78+
StringEvent e3 = new StringEvent();
79+
e3.message = expectedStrings.get(2);
80+
e3.commit();
81+
82+
SubstrateJVM.get().vmErrorRotation();
83+
recording.stop();
84+
recording.close();
85+
86+
String dumpFile = "hs_oom_pid_" + ProcessHandle.current().pid() + ".jfr";
87+
Path p = Path.of(dumpFile);
88+
assertTrue("emergency dump file does not exist.", Files.exists(p));
89+
List<RecordedEvent> events = getEvents(Path.of(dumpFile), testedEvents, true);
90+
for (RecordedEvent event : events) {
91+
assertTrue(expectedStrings.remove(event.getString("message")));
92+
}
93+
assertEquals(0, expectedStrings.size());
94+
95+
Files.deleteIfExists(p);
96+
}
97+
}
Lines changed: 71 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,71 @@
1-
///*
2-
// * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
3-
// * Copyright (c) 2021, 2022, Red Hat Inc. All rights reserved.
4-
// * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5-
// *
6-
// * This code is free software; you can redistribute it and/or modify it
7-
// * under the terms of the GNU General Public License version 2 only, as
8-
// * published by the Free Software Foundation. Oracle designates this
9-
// * particular file as subject to the "Classpath" exception as provided
10-
// * by Oracle in the LICENSE file that accompanied this code.
11-
// *
12-
// * This code is distributed in the hope that it will be useful, but WITHOUT
13-
// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14-
// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15-
// * version 2 for more details (a copy is included in the LICENSE file that
16-
// * accompanied this code).
17-
// *
18-
// * You should have received a copy of the GNU General Public License version
19-
// * 2 along with this work; if not, write to the Free Software Foundation,
20-
// * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21-
// *
22-
// * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23-
// * or visit www.oracle.com if you need additional information or have any
24-
// * questions.
25-
// */
26-
//
27-
//package com.oracle.svm.test.jfr;
28-
//
29-
//import com.oracle.svm.test.jfr.events.ClassEvent;
30-
//import jdk.jfr.Recording;
31-
//import jdk.jfr.consumer.RecordedEvent;
32-
//import org.junit.Test;
33-
//
34-
//import java.util.List;
35-
//
36-
//import com.oracle.svm.core.SubstrateUtil;
37-
//import com.oracle.svm.core.headers.LibC;
38-
//import com.oracle.svm.core.nmt.NmtCategory;
39-
//import com.oracle.svm.core.posix.headers.Dirent;
40-
//import com.oracle.svm.core.posix.headers.Errno;
41-
//import com.oracle.svm.core.posix.headers.Fcntl;
42-
//import com.oracle.svm.core.posix.headers.Unistd;
43-
//import org.graalvm.word.Pointer;
44-
//import jdk.graal.compiler.word.Word;
45-
//import org.graalvm.word.WordFactory;
46-
////import jdk.jfr.internal.LogLevel;
47-
////import jdk.jfr.internal.LogTag;
48-
////import jdk.jfr.internal.Logger;
49-
//import org.graalvm.nativeimage.c.type.CCharPointer;
50-
//import org.graalvm.nativeimage.ImageSingletons;
51-
//import org.graalvm.nativeimage.Platform;
52-
//import org.graalvm.nativeimage.Platforms;
53-
//import org.graalvm.nativeimage.StackValue;
54-
//
55-
//import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
56-
//import com.oracle.svm.core.Uninterruptible;
57-
//import com.oracle.svm.core.jfr.JfrEmergencyDumpFeature;
58-
//import com.oracle.svm.core.jfr.JfrEmergencyDumpSupport;
59-
//import com.oracle.svm.core.memory.NativeMemory;
60-
//import com.oracle.svm.core.memory.NullableNativeMemory;
61-
//import com.oracle.svm.core.os.RawFileOperationSupport;
62-
//import com.oracle.svm.core.os.RawFileOperationSupport.FileAccessMode;
63-
//import com.oracle.svm.core.os.RawFileOperationSupport.FileCreationMode;
64-
//import com.oracle.svm.core.os.RawFileOperationSupport.RawFileDescriptor;
65-
//import com.oracle.svm.core.util.BasedOnJDKFile;
66-
//import com.oracle.svm.core.collections.GrowableWordArray;
67-
//import com.oracle.svm.core.collections.GrowableWordArrayAccess;
68-
//
69-
//import jdk.graal.compiler.api.replacements.Fold;
70-
//
71-
//import java.nio.charset.StandardCharsets;
72-
//
73-
//import static com.oracle.svm.core.posix.headers.Fcntl.O_NOFOLLOW;
74-
//import static com.oracle.svm.core.posix.headers.Fcntl.O_RDONLY;
75-
//
76-
//import static org.junit.Assert.assertEquals;
77-
//
78-
//public class TestGrowableWordArrayQuickSort {
79-
// @Test
80-
// public void test() throws Throwable {
81-
// GrowableWordArray gwa = StackValue.get(GrowableWordArray.class);
82-
// GrowableWordArrayAccess.initialize(gwa);
83-
// GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(3), NmtCategory.JFR);
84-
// GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(1), NmtCategory.JFR);
85-
// GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(5), NmtCategory.JFR);
86-
// GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(4), NmtCategory.JFR);
87-
// GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(2), NmtCategory.JFR);
88-
// GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(4), NmtCategory.JFR);
89-
// qsort(gwa, 0,5);
90-
// for (int i=0; i < 6; i ++){
91-
// System.out.println(GrowableWordArrayAccess.read(gwa, i).rawValue());
92-
// }
93-
// }
94-
//
95-
// private static void qsort(GrowableWordArray gwa, int low, int high) {
96-
// if (low < high) {
97-
// int pivotIndex = partition(gwa, low, high);
98-
//
99-
// qsort(gwa, low, pivotIndex - 1);
100-
// qsort(gwa, pivotIndex + 1, high);
101-
// }
102-
// }
103-
//
104-
// private static int partition(GrowableWordArray gwa, int low, int high) {
105-
// // Choose the last element as pivot
106-
// CCharPointer pivot = GrowableWordArrayAccess.read(gwa, high);
107-
//
108-
// // Pointer for the greater element
109-
// int i = low - 1;
110-
//
111-
// // Traverse through all elements
112-
// // If element is smaller than or equal to pivot, swap it
113-
// for (int j = low; j < high; j++) {
114-
// if (compare(GrowableWordArrayAccess.read(gwa, j), pivot)) {
115-
// i++;
116-
//
117-
// // Swap elements at i and j
118-
// CCharPointer temp = GrowableWordArrayAccess.read(gwa, i);
119-
// GrowableWordArrayAccess.write(gwa,i, GrowableWordArrayAccess.read(gwa, j));
120-
// GrowableWordArrayAccess.write(gwa, j, temp);
121-
// }
122-
// }
123-
//
124-
// // Swap the pivot element with the element at i+1
125-
// CCharPointer temp = GrowableWordArrayAccess.read(gwa, i + 1);
126-
// GrowableWordArrayAccess.write(gwa, i + 1, GrowableWordArrayAccess.read(gwa, high));
127-
// GrowableWordArrayAccess.write(gwa, high, temp);
128-
//
129-
// // Return the partition index
130-
// return i + 1;
131-
// }
132-
//
133-
//// static boolean compare(CCharPointer a, CCharPointer b) {
134-
//// // Use strcmp(CCharPointer s1, CCharPointer s2) and strchr(CCharPointer str, int c)
135-
////
136-
//// return a.read() <= b.read();
137-
//// }
138-
//
139-
// static boolean compare(CCharPointer a, CCharPointer b) {
140-
// // Use strcmp(CCharPointer s1, CCharPointer s2) and strchr(CCharPointer str, int c)
141-
//
142-
// return a.rawValue() <= b.rawValue();
143-
// }
144-
//
145-
//}
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2025, 2025, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
27+
package com.oracle.svm.test.jfr;
28+
29+
import org.junit.Test;
30+
31+
import com.oracle.svm.core.nmt.NmtCategory;
32+
import jdk.graal.compiler.word.Word;
33+
import org.graalvm.word.WordFactory;
34+
import org.graalvm.nativeimage.StackValue;
35+
36+
import com.oracle.svm.core.collections.GrowableWordArray;
37+
import com.oracle.svm.core.collections.GrowableWordArrayAccess;
38+
39+
import static org.junit.Assert.assertTrue;
40+
41+
public class TestGrowableWordArrayQuickSort {
42+
@Test
43+
public void test() throws Throwable {
44+
GrowableWordArray gwa = StackValue.get(GrowableWordArray.class);
45+
GrowableWordArrayAccess.initialize(gwa);
46+
GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(3), NmtCategory.JFR);
47+
GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(1), NmtCategory.JFR);
48+
GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(5), NmtCategory.JFR);
49+
GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(4), NmtCategory.JFR);
50+
GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(2), NmtCategory.JFR);
51+
GrowableWordArrayAccess.add(gwa, WordFactory.unsigned(4), NmtCategory.JFR);
52+
GrowableWordArrayAccess.qsort(gwa, 0, gwa.getSize() - 1, TestGrowableWordArrayQuickSort::compare);
53+
long last = 0;
54+
for (int i = 0; i < gwa.getSize(); i++) {
55+
long current = GrowableWordArrayAccess.read(gwa, i).rawValue();
56+
assertTrue(last <= current);
57+
last = current;
58+
}
59+
}
60+
61+
static int compare(Word a, Word b) {
62+
if (a.rawValue() < b.rawValue()) {
63+
return -1;
64+
} else if (a.rawValue() == b.rawValue()) {
65+
return 0;
66+
} else {
67+
return 1;
68+
}
69+
}
70+
71+
}

0 commit comments

Comments
 (0)