Skip to content

Commit aa9c49f

Browse files
committed
8280131: jcmd reports "Module jdk.jfr not found." when "jdk.management.jfr" is missing
Backport-of: a345df20d0a85b90e6703fba5582cacc5ba38a6d
1 parent fa003ff commit aa9c49f

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
@@ -103,10 +103,6 @@ class JNIHandleBlockManager : public StackObj {
103103
}
104104
};
105105

106-
static bool is_module_available(outputStream* output, TRAPS) {
107-
return JfrJavaSupport::is_jdk_jfr_module_available(output, THREAD);
108-
}
109-
110106
static bool is_disabled(outputStream* output) {
111107
if (Jfr::is_disabled()) {
112108
if (output != NULL) {
@@ -119,7 +115,28 @@ static bool is_disabled(outputStream* output) {
119115

120116
static bool invalid_state(outputStream* out, TRAPS) {
121117
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
122-
return is_disabled(out) || !is_module_available(out, THREAD);
118+
if (is_disabled(out)) {
119+
return true;
120+
}
121+
if (!JfrJavaSupport::is_jdk_jfr_module_available()) {
122+
JfrJavaSupport::load_jdk_jfr_module(THREAD);
123+
if (HAS_PENDING_EXCEPTION) {
124+
// Log exception here, but let is_jdk_jfr_module_available(out, THREAD)
125+
// handle output to the user.
126+
ResourceMark rm(THREAD);
127+
oop throwable = PENDING_EXCEPTION;
128+
assert(throwable != nullptr, "invariant");
129+
oop msg = java_lang_Throwable::message(throwable);
130+
if (msg != nullptr) {
131+
char* text = java_lang_String::as_utf8_string(msg);
132+
if (text != nullptr) {
133+
log_debug(jfr, startup)("Flight Recorder can not be enabled. %s", text);
134+
}
135+
}
136+
CLEAR_PENDING_EXCEPTION;
137+
}
138+
}
139+
return !JfrJavaSupport::is_jdk_jfr_module_available(out, THREAD);
123140
}
124141

125142
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
@@ -41,6 +41,7 @@
4141
#include "runtime/handles.inline.hpp"
4242
#include "runtime/fieldDescriptor.inline.hpp"
4343
#include "runtime/java.hpp"
44+
#include "runtime/javaCalls.hpp"
4445
#include "runtime/jniHandles.inline.hpp"
4546
#include "runtime/semaphore.inline.hpp"
4647
#include "runtime/synchronizer.hpp"
@@ -607,6 +608,23 @@ JfrJavaSupport::CAUSE JfrJavaSupport::cause() {
607608
const char* const JDK_JFR_MODULE_NAME = "jdk.jfr";
608609
const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr";
609610

611+
612+
613+
void JfrJavaSupport::load_jdk_jfr_module(TRAPS) {
614+
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
615+
ResourceMark rm(THREAD);
616+
HandleMark hm(THREAD);
617+
Handle h_module_name = java_lang_String::create_from_str(JDK_JFR_MODULE_NAME, CHECK);
618+
JavaValue result(T_OBJECT);
619+
JavaCalls::call_static(&result,
620+
vmClasses::module_Modules_klass(),
621+
vmSymbols::loadModule_name(),
622+
vmSymbols::loadModule_signature(),
623+
h_module_name,
624+
CHECK
625+
);
626+
}
627+
610628
static bool is_jdk_jfr_module_in_readability_graph() {
611629
// take one of the packages in the module to be located and query for its definition.
612630
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
@@ -86,6 +86,7 @@ class JfrJavaSupport : public AllStatic {
8686
static void throw_class_format_error(const char* message, TRAPS);
8787
static void throw_runtime_exception(const char* message, TRAPS);
8888

89+
static void load_jdk_jfr_module(TRAPS);
8990
static bool is_jdk_jfr_module_available();
9091
static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
9192

src/hotspot/share/runtime/arguments.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,6 +1996,14 @@ bool Arguments::check_vm_args_consistency() {
19961996
}
19971997
#endif
19981998

1999+
#if INCLUDE_JFR
2000+
if (status && (FlightRecorderOptions || StartFlightRecording)) {
2001+
if (!create_numbered_module_property("jdk.module.addmods", "jdk.jfr", addmods_count++)) {
2002+
return false;
2003+
}
2004+
}
2005+
#endif
2006+
19992007
#ifndef SUPPORT_RESERVED_STACK_AREA
20002008
if (StackReservedPages != 0) {
20012009
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)