mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-22 05:50:03 +00:00
8262520: Add SA Command Line Debugger support to connect to debug server
Reviewed-by: cjplummer, kevinw
This commit is contained in:
parent
e5ce97b12d
commit
70342e8513
@ -36,7 +36,7 @@ Each CLHSDB command can have zero or more arguments and optionally end with outp
|
||||
<code>
|
||||
Available commands:
|
||||
assert true | false <font color="red">turn on/off asserts in SA code</font>
|
||||
attach pid | exec core <font color="red">attach SA to a process or core</font>
|
||||
attach pid | exec core | debugserver <font color="red">attach SA to a process, core, or remote debug server</font>
|
||||
buildreplayjars [all | boot | app] <font color="red">build jars for replay, boot.jar for bootclasses, app.jar for application classes</font>
|
||||
class name <font color="red">find a Java class from debuggee and print oop</font>
|
||||
classes <font color="red">print all loaded Java classes with Klass*</font>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,6 +33,10 @@ import java.util.*;
|
||||
public class CLHSDB {
|
||||
|
||||
public CLHSDB(JVMDebugger d) {
|
||||
pid = -1;
|
||||
execPath = null;
|
||||
coreFilename = null;
|
||||
debugServerName = null;
|
||||
jvmDebugger = d;
|
||||
}
|
||||
|
||||
@ -42,10 +46,12 @@ public class CLHSDB {
|
||||
|
||||
public void run() {
|
||||
// If jvmDebugger is already set, we have been given a JVMDebugger.
|
||||
// Otherwise, if pidText != null we are supposed to attach to it.
|
||||
// Finally, if execPath != null, it is the path of a jdk/bin/java
|
||||
// Otherwise, if pid != -1 we are supposed to attach to it.
|
||||
// If execPath != null, it is the path of a jdk/bin/java
|
||||
// and coreFilename is the pathname of a core file we are
|
||||
// supposed to attach to.
|
||||
// Finally, if debugServerName != null, we are supposed to
|
||||
// connect to remote debug server.
|
||||
|
||||
agent = new HotSpotAgent();
|
||||
|
||||
@ -57,10 +63,12 @@ public class CLHSDB {
|
||||
|
||||
if (jvmDebugger != null) {
|
||||
attachDebugger(jvmDebugger);
|
||||
} else if (pidText != null) {
|
||||
attachDebugger(pidText);
|
||||
} else if (pid != -1) {
|
||||
attachDebugger(pid);
|
||||
} else if (execPath != null) {
|
||||
attachDebugger(execPath, coreFilename);
|
||||
} else if (debugServerName != null) {
|
||||
connect(debugServerName);
|
||||
}
|
||||
|
||||
|
||||
@ -71,12 +79,15 @@ public class CLHSDB {
|
||||
public boolean isAttached() {
|
||||
return attached;
|
||||
}
|
||||
public void attach(String pid) {
|
||||
public void attach(int pid) {
|
||||
attachDebugger(pid);
|
||||
}
|
||||
public void attach(String java, String core) {
|
||||
attachDebugger(java, core);
|
||||
}
|
||||
public void attach(String debugServerName) {
|
||||
connect(debugServerName);
|
||||
}
|
||||
public void detach() {
|
||||
detachDebugger();
|
||||
}
|
||||
@ -84,8 +95,10 @@ public class CLHSDB {
|
||||
if (attached) {
|
||||
detachDebugger();
|
||||
}
|
||||
if (pidText != null) {
|
||||
attach(pidText);
|
||||
if (pid != -1) {
|
||||
attach(pid);
|
||||
} else if (debugServerName != null) {
|
||||
connect(debugServerName);
|
||||
} else {
|
||||
attach(execPath, coreFilename);
|
||||
}
|
||||
@ -107,10 +120,10 @@ public class CLHSDB {
|
||||
private JVMDebugger jvmDebugger;
|
||||
private boolean attached;
|
||||
// These had to be made data members because they are referenced in inner classes.
|
||||
private String pidText;
|
||||
private int pid;
|
||||
private String execPath;
|
||||
private String coreFilename;
|
||||
private String debugServerName;
|
||||
|
||||
private void doUsage() {
|
||||
System.out.println("Usage: java CLHSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
|
||||
@ -122,6 +135,11 @@ public class CLHSDB {
|
||||
}
|
||||
|
||||
private CLHSDB(String[] args) {
|
||||
pid = -1;
|
||||
execPath = null;
|
||||
coreFilename = null;
|
||||
debugServerName = null;
|
||||
|
||||
switch (args.length) {
|
||||
case (0):
|
||||
break;
|
||||
@ -134,9 +152,7 @@ public class CLHSDB {
|
||||
// If all numbers, it is a PID to attach to
|
||||
// Else, it is a pathname to a .../bin/java for a core file.
|
||||
try {
|
||||
int unused = Integer.parseInt(args[0]);
|
||||
// If we get here, we have a PID and not a core file name
|
||||
pidText = args[0];
|
||||
pid = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException e) {
|
||||
execPath = args[0];
|
||||
coreFilename = "core";
|
||||
@ -163,17 +179,10 @@ public class CLHSDB {
|
||||
/** NOTE we are in a different thread here than either the main
|
||||
thread or the Swing/AWT event handler thread, so we must be very
|
||||
careful when creating or removing widgets */
|
||||
private void attachDebugger(String pidText) {
|
||||
private void attachDebugger(int pid) {
|
||||
this.pid = pid;
|
||||
try {
|
||||
this.pidText = pidText;
|
||||
pid = Integer.parseInt(pidText);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
System.err.print("Unable to parse process ID \"" + pidText + "\".\nPlease enter a number.");
|
||||
}
|
||||
|
||||
try {
|
||||
System.err.println("Attaching to process " + pid + ", please wait...");
|
||||
System.out.println("Attaching to process " + pid + ", please wait...");
|
||||
|
||||
// FIXME: display exec'd debugger's output messages during this
|
||||
// lengthy call
|
||||
@ -214,16 +223,17 @@ public class CLHSDB {
|
||||
/** NOTE we are in a different thread here than either the main
|
||||
thread or the Swing/AWT event handler thread, so we must be very
|
||||
careful when creating or removing widgets */
|
||||
private void connect(final String remoteMachineName) {
|
||||
private void connect(final String debugServerName) {
|
||||
// Try to open this core file
|
||||
try {
|
||||
System.err.println("Connecting to debug server, please wait...");
|
||||
agent.attach(remoteMachineName);
|
||||
System.out.println("Connecting to debug server, please wait...");
|
||||
agent.attach(debugServerName);
|
||||
this.debugServerName = debugServerName;
|
||||
attached = true;
|
||||
}
|
||||
catch (DebuggerException e) {
|
||||
final String errMsg = formatMessage(e.getMessage(), 80);
|
||||
System.err.println("Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg);
|
||||
System.err.println("Unable to connect to debug server \"" + debugServerName + "\":\n\n" + errMsg);
|
||||
agent.detach();
|
||||
e.printStackTrace();
|
||||
return;
|
||||
|
||||
@ -114,8 +114,9 @@ public class CommandProcessor {
|
||||
public abstract static class DebuggerInterface {
|
||||
public abstract HotSpotAgent getAgent();
|
||||
public abstract boolean isAttached();
|
||||
public abstract void attach(String pid);
|
||||
public abstract void attach(int pid);
|
||||
public abstract void attach(String java, String core);
|
||||
public abstract void attach(String debugServerName);
|
||||
public abstract void detach();
|
||||
public abstract void reattach();
|
||||
}
|
||||
@ -382,12 +383,19 @@ public class CommandProcessor {
|
||||
postAttach();
|
||||
}
|
||||
},
|
||||
new Command("attach", "attach pid | exec core", true) {
|
||||
new Command("attach", "attach pid | exec core | remote_server", true) {
|
||||
public void doit(Tokens t) {
|
||||
int tokens = t.countTokens();
|
||||
if (tokens == 1) {
|
||||
preAttach();
|
||||
debugger.attach(t.nextToken());
|
||||
String arg = t.nextToken();
|
||||
try {
|
||||
// Attempt to attach as a PID
|
||||
debugger.attach(Integer.parseInt(arg));
|
||||
} catch (NumberFormatException e) {
|
||||
// Attempt to connect to remote debug server
|
||||
debugger.attach(arg);
|
||||
}
|
||||
postAttach();
|
||||
} else if (tokens == 2) {
|
||||
preAttach();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -78,10 +78,10 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
private JInternalFrame consoleFrame;
|
||||
private WorkerThread workerThread;
|
||||
// These had to be made data members because they are referenced in inner classes.
|
||||
private String pidText;
|
||||
private int pid;
|
||||
private String execPath;
|
||||
private String coreFilename;
|
||||
private String debugServerName;
|
||||
|
||||
private void doUsage() {
|
||||
System.out.println("Usage: java HSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
|
||||
@ -94,10 +94,19 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
}
|
||||
|
||||
public HSDB(JVMDebugger d) {
|
||||
pid = -1;
|
||||
execPath = null;
|
||||
coreFilename = null;
|
||||
debugServerName = null;
|
||||
jvmDebugger = d;
|
||||
}
|
||||
|
||||
private HSDB(String[] args) {
|
||||
pid = -1;
|
||||
execPath = null;
|
||||
coreFilename = null;
|
||||
debugServerName = null;
|
||||
|
||||
switch (args.length) {
|
||||
case (0):
|
||||
break;
|
||||
@ -109,9 +118,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
// If all numbers, it is a PID to attach to
|
||||
// Else, it is a pathname to a .../bin/java for a core file.
|
||||
try {
|
||||
int unused = Integer.parseInt(args[0]);
|
||||
// If we get here, we have a PID and not a core file name
|
||||
pidText = args[0];
|
||||
pid = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException e) {
|
||||
execPath = args[0];
|
||||
coreFilename = "core";
|
||||
@ -422,17 +429,21 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
});
|
||||
|
||||
// If jvmDebugger is already set, we have been given a JVMDebugger.
|
||||
// Otherwise, if pidText != null we are supposed to attach to it.
|
||||
// Finally, if execPath != null, it is the path of a jdk/bin/java
|
||||
// Otherwise, if pid != -1 we are supposed to attach to it.
|
||||
// If execPath != null, it is the path of a jdk/bin/java
|
||||
// and coreFilename is the pathname of a core file we are
|
||||
// supposed to attach to.
|
||||
// Finally, if debugServerName != null, we are supposed to
|
||||
// connect to remote debug server.
|
||||
|
||||
if (jvmDebugger != null) {
|
||||
attach(jvmDebugger);
|
||||
} else if (pidText != null) {
|
||||
attach(pidText);
|
||||
} else if (pid != -1) {
|
||||
attach(pid);
|
||||
} else if (execPath != null) {
|
||||
attach(execPath, coreFilename);
|
||||
} else if (debugServerName != null) {
|
||||
connect(debugServerName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,7 +467,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
desktop.remove(attachDialog);
|
||||
workerThread.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
attach(pidTextField.getText());
|
||||
attach(Integer.parseInt(pidTextField.getText()));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1172,24 +1183,8 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
/** NOTE we are in a different thread here than either the main
|
||||
thread or the Swing/AWT event handler thread, so we must be very
|
||||
careful when creating or removing widgets */
|
||||
private void attach(String pidText) {
|
||||
try {
|
||||
this.pidText = pidText;
|
||||
pid = Integer.parseInt(pidText);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
setMenuItemsEnabled(attachMenuItems, true);
|
||||
JOptionPane.showInternalMessageDialog(desktop,
|
||||
"Unable to parse process ID \"" + HSDB.this.pidText + "\".\nPlease enter a number.",
|
||||
"Parse error",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
private void attach(int pid) {
|
||||
this.pid = pid;
|
||||
// Try to attach to this process
|
||||
Runnable remover = new Runnable() {
|
||||
public void run() {
|
||||
@ -1293,7 +1288,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
/** NOTE we are in a different thread here than either the main
|
||||
thread or the Swing/AWT event handler thread, so we must be very
|
||||
careful when creating or removing widgets */
|
||||
private void connect(final String remoteMachineName) {
|
||||
private void connect(final String debugServerName) {
|
||||
// Try to open this core file
|
||||
Runnable remover = new Runnable() {
|
||||
public void run() {
|
||||
@ -1313,7 +1308,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
}
|
||||
});
|
||||
|
||||
agent.attach(remoteMachineName);
|
||||
agent.attach(debugServerName);
|
||||
if (agent.getDebugger().hasConsole()) {
|
||||
showDbgConsoleMenuItem.setEnabled(true);
|
||||
}
|
||||
@ -1327,7 +1322,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
public void run() {
|
||||
setMenuItemsEnabled(attachMenuItems, true);
|
||||
JOptionPane.showInternalMessageDialog(desktop,
|
||||
"Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg,
|
||||
"Unable to connect to machine \"" + debugServerName + "\":\n\n" + errMsg,
|
||||
"Unable to Connect",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
@ -1499,11 +1494,14 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
public boolean isAttached() {
|
||||
return attached;
|
||||
}
|
||||
public void attach(String pid) {
|
||||
public void attach(int pid) {
|
||||
HSDB.this.attach(pid);
|
||||
}
|
||||
public void attach(String java, String core) {
|
||||
}
|
||||
public void attach(String debugServerName) {
|
||||
HSDB.this.connect(debugServerName);
|
||||
}
|
||||
public void detach() {
|
||||
detachDebugger();
|
||||
}
|
||||
@ -1511,8 +1509,10 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
|
||||
if (attached) {
|
||||
detachDebugger();
|
||||
}
|
||||
if (pidText != null) {
|
||||
attach(pidText);
|
||||
if (pid != -1) {
|
||||
attach(pid);
|
||||
} else if (debugServerName != null) {
|
||||
connect(debugServerName);
|
||||
} else {
|
||||
attach(execPath, coreFilename);
|
||||
}
|
||||
|
||||
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021 NTT DATA.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
import jdk.test.lib.JDKToolLauncher;
|
||||
import jdk.test.lib.apps.LingeredApp;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.SA.SATestUtils;
|
||||
|
||||
import jtreg.SkippedException;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8262520
|
||||
* @summary Test clhsdb connect, detach, reattach commands
|
||||
* @requires vm.hasSA
|
||||
* @requires os.family != "windows"
|
||||
* @library /test/lib
|
||||
* @run main/othervm ClhsdbAttachToDebugServer
|
||||
*/
|
||||
|
||||
public class ClhsdbAttachToDebugServer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work.
|
||||
|
||||
if (SATestUtils.needsPrivileges()) {
|
||||
// This tests has issues if you try adding privileges on OSX. The debugd process cannot
|
||||
// be killed if you do this (because it is a root process and the test is not), so the destroy()
|
||||
// call fails to do anything, and then waitFor() will time out. If you try to manually kill it with
|
||||
// a "sudo kill" command, that seems to work, but then leaves the LingeredApp it was
|
||||
// attached to in a stuck state for some unknown reason, causing the stopApp() call
|
||||
// to timeout. For that reason we don't run this test when privileges are needed. Note
|
||||
// it does appear to run fine as root, so we still allow it to run on OSX when privileges
|
||||
// are not required.
|
||||
throw new SkippedException("Cannot run this test on OSX if adding privileges is required.");
|
||||
}
|
||||
|
||||
System.out.println("Starting ClhsdbAttachToDebugServer test");
|
||||
|
||||
LingeredApp theApp = null;
|
||||
DebugdUtils debugd = null;
|
||||
try {
|
||||
theApp = LingeredApp.startApp();
|
||||
System.out.println("Started LingeredApp with pid " + theApp.getPid());
|
||||
debugd = new DebugdUtils(null);
|
||||
debugd.attach(theApp.getPid());
|
||||
|
||||
JDKToolLauncher jhsdbLauncher = JDKToolLauncher.createUsingTestJDK("jhsdb");
|
||||
jhsdbLauncher.addToolArg("clhsdb");
|
||||
|
||||
Process jhsdb = (SATestUtils.createProcessBuilder(jhsdbLauncher)).start();
|
||||
OutputAnalyzer out = new OutputAnalyzer(jhsdb);
|
||||
|
||||
try (PrintStream console = new PrintStream(jhsdb.getOutputStream(), true)) {
|
||||
console.println("echo true");
|
||||
console.println("verbose true");
|
||||
console.println("attach localhost");
|
||||
console.println("class java.lang.Object");
|
||||
console.println("detach");
|
||||
console.println("reattach");
|
||||
console.println("class java.lang.String");
|
||||
console.println("quit");
|
||||
}
|
||||
|
||||
jhsdb.waitFor();
|
||||
System.out.println(out.getStdout());
|
||||
System.err.println(out.getStderr());
|
||||
|
||||
out.stderrShouldBeEmptyIgnoreDeprecatedWarnings();
|
||||
out.shouldMatch("^java/lang/Object @0x[0-9a-f]+$"); // for "class java.lang.Object"
|
||||
out.shouldMatch("^java/lang/String @0x[0-9a-f]+$"); // for "class java.lang.String"
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// This will detect most SA failures, including during the attach.
|
||||
out.shouldNotMatch("^sun.jvm.hotspot.debugger.DebuggerException:.*$");
|
||||
// This will detect unexpected exceptions, like NPEs and asserts, that are caught
|
||||
// by sun.jvm.hotspot.CommandProcessor.
|
||||
out.shouldNotMatch("^Error: .*$");
|
||||
} catch (SkippedException se) {
|
||||
throw se;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Test ERROR " + ex, ex);
|
||||
} finally {
|
||||
if (debugd != null) {
|
||||
debugd.detach();
|
||||
}
|
||||
LingeredApp.stopApp(theApp);
|
||||
}
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user