Skip to content

Commit a345df2

Browse files
committed
8280131: jcmd reports "Module jdk.jfr not found." when "jdk.management.jfr" is missing
Reviewed-by: mgronlun, alanb
1 parent ef62b61 commit a345df2

File tree

6 files changed

+233
-74
lines changed

6 files changed

+233
-74
lines changed

src/hotspot/share/jfr/dcmd/jfrDcmds.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,6 @@ bool register_jfr_dcmds() {
5656
return true;
5757
}
5858

59-
static bool is_module_available(outputStream* output, TRAPS) {
60-
return JfrJavaSupport::is_jdk_jfr_module_available(output, THREAD);
61-
}
62-
6359
static bool is_disabled(outputStream* output) {
6460
if (Jfr::is_disabled()) {
6561
if (output != NULL) {
@@ -72,7 +68,28 @@ static bool is_disabled(outputStream* output) {
7268

7369
static bool invalid_state(outputStream* out, TRAPS) {
7470
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
75-
return is_disabled(out) || !is_module_available(out, THREAD);
71+
if (is_disabled(out)) {
72+
return true;
73+
}
74+
if (!JfrJavaSupport::is_jdk_jfr_module_available()) {
75+
JfrJavaSupport::load_jdk_jfr_module(THREAD);
76+
if (HAS_PENDING_EXCEPTION) {
77+
// Log exception here, but let is_jdk_jfr_module_available(out, THREAD)
78+
// handle output to the user.
79+
ResourceMark rm(THREAD);
80+
oop throwable = PENDING_EXCEPTION;
81+
assert(throwable != nullptr, "invariant");
82+
oop msg = java_lang_Throwable::message(throwable);
83+
if (msg != nullptr) {
84+
char* text = java_lang_String::as_utf8_string(msg);
85+
if (text != nullptr) {
86+
log_debug(jfr, startup)("Flight Recorder can not be enabled. %s", text);
87+
}
88+
}
89+
CLEAR_PENDING_EXCEPTION;
90+
}
91+
}
92+
return !JfrJavaSupport::is_jdk_jfr_module_available(out, THREAD);
7693
}
7794

7895
static void handle_pending_exception(outputStream* output, bool startup, oop throwable) {

src/hotspot/share/jfr/jni/jfrJavaSupport.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "runtime/handles.inline.hpp"
4646
#include "runtime/fieldDescriptor.inline.hpp"
4747
#include "runtime/java.hpp"
48+
#include "runtime/javaCalls.hpp"
4849
#include "runtime/javaThread.hpp"
4950
#include "runtime/jniHandles.inline.hpp"
5051
#include "runtime/semaphore.inline.hpp"
@@ -625,6 +626,23 @@ JfrJavaSupport::CAUSE JfrJavaSupport::cause() {
625626
const char* const JDK_JFR_MODULE_NAME = "jdk.jfr";
626627
const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr";
627628

629+
630+
631+
void JfrJavaSupport::load_jdk_jfr_module(TRAPS) {
632+
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
633+
ResourceMark rm(THREAD);
634+
HandleMark hm(THREAD);
635+
Handle h_module_name = java_lang_String::create_from_str(JDK_JFR_MODULE_NAME, CHECK);
636+
JavaValue result(T_OBJECT);
637+
JavaCalls::call_static(&result,
638+
vmClasses::module_Modules_klass(),
639+
vmSymbols::loadModule_name(),
640+
vmSymbols::loadModule_signature(),
641+
h_module_name,
642+
CHECK
643+
);
644+
}
645+
628646
static bool is_jdk_jfr_module_in_readability_graph() {
629647
// take one of the packages in the module to be located and query for its definition.
630648
TempNewSymbol pkg_sym = SymbolTable::new_symbol(JDK_JFR_PACKAGE_NAME);

src/hotspot/share/jfr/jni/jfrJavaSupport.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class JfrJavaSupport : public AllStatic {
9696
static void throw_class_format_error(const char* message, TRAPS);
9797
static void throw_runtime_exception(const char* message, TRAPS);
9898

99+
static void load_jdk_jfr_module(TRAPS);
99100
static bool is_jdk_jfr_module_available();
100101
static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
101102

src/hotspot/share/runtime/arguments.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,14 @@ bool Arguments::check_vm_args_consistency() {
19571957
}
19581958
#endif
19591959

1960+
#if INCLUDE_JFR
1961+
if (status && (FlightRecorderOptions || StartFlightRecording)) {
1962+
if (!create_numbered_module_property("jdk.module.addmods", "jdk.jfr", addmods_count++)) {
1963+
return false;
1964+
}
1965+
}
1966+
#endif
1967+
19601968
#ifndef SUPPORT_RESERVED_STACK_AREA
19611969
if (StackReservedPages != 0) {
19621970
FLAG_SET_CMDLINE(StackReservedPages, 0);

test/jdk/jdk/jfr/jvm/TestJfrJavaBase.java

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* Copyright (c) 2022, 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+
package jdk.jfr.jvm;
24+
25+
import java.io.File;
26+
import java.io.IOException;
27+
import java.nio.file.Files;
28+
import java.nio.file.Path;
29+
import java.util.ArrayList;
30+
import java.util.Arrays;
31+
import java.util.List;
32+
import java.util.spi.ToolProvider;
33+
34+
import jdk.test.lib.JDKToolFinder;
35+
import jdk.test.lib.Platform;
36+
import jdk.test.lib.process.OutputAnalyzer;
37+
import jdk.test.lib.process.ProcessTools;
38+
39+
/**
40+
* @test
41+
* @key jfr
42+
* @summary Checks that a JDK image with and without the jdk.jfr module behaves
43+
* as expected
44+
* @requires vm.hasJFR
45+
* @library /test/lib
46+
* @run driver jdk.jfr.jvm.TestModularImage
47+
*/
48+
public class TestModularImage {
49+
private static final String STARTED_RECORDING = "Started recording";
50+
private static final String HELLO_WORLD = "hello, world";
51+
private static final String ERROR_LINE1 = "Error occurred during initialization of boot layer";
52+
private static final String ERROR_LINE2 = "java.lang.module.FindException: Module jdk.jfr not found";
53+
54+
private static final ToolProvider javac = find("javac");
55+
private static final ToolProvider jlink = find("jlink");
56+
57+
private static final Path out = Path.of("out");
58+
private static final Path src = out.resolve("src");
59+
private static final Path classes = out.resolve("classes");
60+
61+
private static final Path testJDK = Path.of(System.getProperty("test.jdk"));
62+
private static final Path jmods = testJDK.resolve("jmods");
63+
64+
private static final String modulePath = jmods.toString() + File.pathSeparator + classes.toString();
65+
66+
public static void main(String[] args) throws Exception {
67+
preparseSourceTree();
68+
compileSourceCode();
69+
70+
// Jcmd for the current JVM where jdk.attach module is available
71+
String currentJcmd = JDKToolFinder.getJDKTool("jcmd");
72+
currentJcmd = Path.of(currentJcmd).normalize().toAbsolutePath().toString();
73+
74+
// Image 1: Should be able to start JFR if jdk.jfr module is present
75+
Path javaBin1 = jlink("hello.world,jdk.jfr", "with-jfr");
76+
testCommandLineWithJFR(javaBin1);
77+
testJcmdWithJFR(javaBin1, currentJcmd);
78+
79+
// Image 2: Should fail if jdk.jfr module is not present
80+
Path javaBin2 = jlink("hello.world", "without-jfr");
81+
testCommandLineWithoutJFR(javaBin2);
82+
testJcmdWithoutJFR(javaBin2, currentJcmd);
83+
}
84+
85+
private static void testCommandLineWithJFR(Path binPath) throws Exception {
86+
var result = java(binPath, "-XX:StartFlightRecording", "--module", "hello.world/hello.Main");
87+
result.shouldNotContain(ERROR_LINE1);
88+
result.shouldNotContain(ERROR_LINE2);
89+
result.shouldContain(HELLO_WORLD);
90+
result.shouldContain(STARTED_RECORDING);
91+
result.shouldHaveExitValue(0);
92+
}
93+
94+
private static void testJcmdWithJFR(Path binPath, String jcmd) throws Exception {
95+
var result = java(binPath, "--module", "hello.world/hello.Main", jcmd);
96+
result.shouldContain(HELLO_WORLD);
97+
result.shouldNotContain(ERROR_LINE1);
98+
result.shouldNotContain(ERROR_LINE2);
99+
result.shouldContain(STARTED_RECORDING);
100+
result.shouldHaveExitValue(0);
101+
}
102+
103+
private static void testCommandLineWithoutJFR(Path binPath) throws Exception {
104+
var result = java(binPath, "-XX:StartFlightRecording", "--module", "hello.world/hello.Main");
105+
result.shouldContain(ERROR_LINE1);
106+
result.shouldContain(ERROR_LINE2);
107+
result.shouldNotContain(HELLO_WORLD);
108+
result.shouldNotContain(STARTED_RECORDING);
109+
result.shouldHaveExitValue(1);
110+
}
111+
112+
private static void testJcmdWithoutJFR(Path binPath, String jcmd) throws Exception {
113+
OutputAnalyzer result = java(binPath, "--module", "hello.world/hello.Main", jcmd);
114+
result.shouldContain(HELLO_WORLD);
115+
result.shouldContain("Module jdk.jfr not found.");
116+
result.shouldContain("Flight Recorder can not be enabled.");
117+
result.shouldNotContain(STARTED_RECORDING);
118+
result.shouldHaveExitValue(0);
119+
}
120+
121+
private static ToolProvider find(String tool) {
122+
return ToolProvider.findFirst(tool).orElseThrow(() -> new RuntimeException("No " + tool));
123+
}
124+
125+
private static void preparseSourceTree() throws IOException {
126+
String main =
127+
"""
128+
package hello;
129+
import java.io.ByteArrayOutputStream;
130+
public class Main {
131+
public static void main(String... args) throws Exception {
132+
System.out.println("hello, world!");
133+
if (args.length > 0) {
134+
long pid = ProcessHandle.current().pid();
135+
String jcmd = args[0];
136+
String[] cmds = { jcmd, Long.toString(pid), "JFR.start" };
137+
Process process = new ProcessBuilder(cmds).redirectErrorStream(true).start();
138+
process.waitFor();
139+
var baos = new ByteArrayOutputStream();
140+
process.getInputStream().transferTo(baos);
141+
System.out.println(baos.toString());
142+
System.exit(process.exitValue());
143+
}
144+
}
145+
}
146+
""";
147+
String moduleInfo = "module hello.world {}";
148+
Path helloWorld = src.resolve("hello.world");
149+
Files.createDirectories(helloWorld.resolve("hello"));
150+
Files.write(helloWorld.resolve("module-info.java"), moduleInfo.getBytes());
151+
Files.write(helloWorld.resolve("hello").resolve("Main.java"), main.getBytes());
152+
}
153+
154+
private static void compileSourceCode() {
155+
javac.run(System.out, System.err,
156+
"--module-source-path", src.toString(),
157+
"--module", "hello.world",
158+
"-d", classes.toString());
159+
}
160+
161+
private static Path jlink(String modules, String output) {
162+
jlink.run(System.out, System.err,
163+
"--add-modules", modules,
164+
"--module-path", modulePath,
165+
"--output", output);
166+
return Path.of(output).resolve("bin").toAbsolutePath();
167+
}
168+
169+
private static OutputAnalyzer java(Path jvm, String... args) throws Exception {
170+
ProcessBuilder pb = new ProcessBuilder();
171+
String java = Platform.isWindows() ? "java.exe" : "java";
172+
List<String> arguments = new ArrayList<>();
173+
arguments.add(jvm.resolve(java).toString());
174+
arguments.addAll(Arrays.asList(args));
175+
pb.command(arguments);
176+
pb.directory(jvm.toFile());
177+
System.out.println("Executing: java " + String.join(" ", args));
178+
OutputAnalyzer result = ProcessTools.executeProcess(pb);
179+
System.out.println("--- Output ----" + "-".repeat(65));
180+
System.out.println(result.getOutput());
181+
System.out.println("-".repeat(80));
182+
return result;
183+
}
184+
}

0 commit comments

Comments
 (0)