8219143: jdb should support breakpoint thread filters
Summary: add thread filter to stop command.
Reviewed-by: sspitsyn, amenkov
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java Mon Feb 25 16:05:06 2019 -0800
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java Mon Feb 25 18:54:40 2019 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -44,20 +44,24 @@
String methodId;
List<String> methodArgs;
int lineNumber;
+ ThreadReference threadFilter; /* Thread to break in. null if global breakpoint. */
+ public static final String locationTokenDelimiter = ":( \t\n\r";
- BreakpointSpec(ReferenceTypeSpec refSpec, int lineNumber) {
+ BreakpointSpec(ReferenceTypeSpec refSpec, int lineNumber, ThreadReference threadFilter) {
super(refSpec);
this.methodId = null;
this.methodArgs = null;
this.lineNumber = lineNumber;
+ this.threadFilter = threadFilter;
}
- BreakpointSpec(ReferenceTypeSpec refSpec, String methodId,
+ BreakpointSpec(ReferenceTypeSpec refSpec, String methodId, ThreadReference threadFilter,
List<String> methodArgs) throws MalformedMemberNameException {
super(refSpec);
this.methodId = methodId;
this.methodArgs = methodArgs;
this.lineNumber = 0;
+ this.threadFilter = threadFilter;
if (!isValidMethodName(methodId)) {
throw new MalformedMemberNameException(methodId);
}
@@ -78,8 +82,11 @@
throw new InvalidTypeException();
}
EventRequestManager em = refType.virtualMachine().eventRequestManager();
- EventRequest bp = em.createBreakpointRequest(location);
+ BreakpointRequest bp = em.createBreakpointRequest(location);
bp.setSuspendPolicy(suspendPolicy);
+ if (threadFilter != null) {
+ bp.addThreadFilter(threadFilter);
+ }
bp.enable();
return bp;
}
@@ -104,7 +111,8 @@
public int hashCode() {
return refSpec.hashCode() + lineNumber +
((methodId != null) ? methodId.hashCode() : 0) +
- ((methodArgs != null) ? methodArgs.hashCode() : 0);
+ ((methodArgs != null) ? methodArgs.hashCode() : 0) +
+ ((threadFilter != null) ? threadFilter.hashCode() : 0);
}
@Override
@@ -118,6 +126,9 @@
((methodArgs != null) ?
methodArgs.equals(breakpoint.methodArgs)
: methodArgs == breakpoint.methodArgs) &&
+ ((threadFilter != null) ?
+ threadFilter.equals(breakpoint.threadFilter)
+ : threadFilter == breakpoint.threadFilter) &&
refSpec.equals(breakpoint.refSpec) &&
(lineNumber == breakpoint.lineNumber);
} else {
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/Commands.java Mon Feb 25 16:05:06 2019 -0800
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/Commands.java Mon Feb 25 18:54:40 2019 -0800
@@ -1037,16 +1037,16 @@
}
- private void printBreakpointCommandUsage(String atForm, String inForm) {
- MessageOutput.println("printbreakpointcommandusage",
- new Object [] {atForm, inForm});
+ private void printBreakpointCommandUsage(String usageMessage) {
+ MessageOutput.println(usageMessage);
}
- protected BreakpointSpec parseBreakpointSpec(StringTokenizer t,
- String atForm, String inForm) {
+ protected BreakpointSpec parseBreakpointSpec(StringTokenizer t, String next_token,
+ ThreadReference threadFilter,
+ String usageMessage) {
BreakpointSpec breakpoint = null;
try {
- String token = t.nextToken(":( \t\n\r");
+ String token = next_token;
// We can't use hasMoreTokens here because it will cause any leading
// paren to be lost.
@@ -1064,16 +1064,24 @@
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setParseIntegerOnly(true);
- Number n = nf.parse(lineToken);
+ Number n;
+ try {
+ n = nf.parse(lineToken);
+ } catch (java.text.ParseException pe) {
+ MessageOutput.println("Invalid line number specified");
+ printBreakpointCommandUsage(usageMessage);
+ return null;
+ }
int lineNumber = n.intValue();
if (t.hasMoreTokens()) {
- printBreakpointCommandUsage(atForm, inForm);
+ MessageOutput.println("Extra tokens after breakpoint location");
+ printBreakpointCommandUsage(usageMessage);
return null;
}
try {
breakpoint = Env.specList.createBreakpoint(classId,
- lineNumber);
+ lineNumber, threadFilter);
} catch (ClassNotFoundException exc) {
MessageOutput.println("is not a valid class name", classId);
}
@@ -1082,7 +1090,8 @@
int idot = token.lastIndexOf('.');
if ( (idot <= 0) || /* No dot or dot in first char */
(idot >= token.length() - 1) ) { /* dot in last char */
- printBreakpointCommandUsage(atForm, inForm);
+ MessageOutput.println("Invalid <class>.<method_name> specification");
+ printBreakpointCommandUsage(usageMessage);
return null;
}
String methodName = token.substring(idot + 1);
@@ -1090,9 +1099,9 @@
List<String> argumentList = null;
if (rest != null) {
if (!rest.startsWith("(") || !rest.endsWith(")")) {
- MessageOutput.println("Invalid method specification:",
+ MessageOutput.println("Invalid <method_name> specification:",
methodName + rest);
- printBreakpointCommandUsage(atForm, inForm);
+ printBreakpointCommandUsage(usageMessage);
return null;
}
// Trim the parens
@@ -1107,6 +1116,7 @@
try {
breakpoint = Env.specList.createBreakpoint(classId,
methodName,
+ threadFilter,
argumentList);
} catch (MalformedMemberNameException exc) {
MessageOutput.println("is not a valid method name", methodName);
@@ -1115,7 +1125,7 @@
}
}
} catch (Exception e) {
- printBreakpointCommandUsage(atForm, inForm);
+ printBreakpointCommandUsage(usageMessage);
return null;
}
return breakpoint;
@@ -1145,33 +1155,74 @@
}
void commandStop(StringTokenizer t) {
- String atIn;
byte suspendPolicy = EventRequest.SUSPEND_ALL;
+ ThreadReference threadFilter = null;
- if (t.hasMoreTokens()) {
- atIn = t.nextToken();
- if (atIn.equals("go") && t.hasMoreTokens()) {
- suspendPolicy = EventRequest.SUSPEND_NONE;
- atIn = t.nextToken();
- } else if (atIn.equals("thread") && t.hasMoreTokens()) {
- suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
- atIn = t.nextToken();
- }
- } else {
+ /*
+ * Allowed syntax:
+ * stop [go|thread] [<thread_id>] <at|in> <location>
+ * If no options are given, the current list of breakpoints is printed.
+ * If "go" is specified, then immediately resume after stopping. No threads are suspended.
+ * If "thread" is specified, then only suspend the thread we stop in.
+ * If neither "go" nor "thread" are specified, then suspend all threads.
+ * If an integer <thread_id> is specified, then only stop in the specified thread.
+ * <location> can either be a line number or a method:
+ * - <class id>:<line>
+ * - <class id>.<method>[(argument_type,...)]
+ */
+
+ if (!t.hasMoreTokens()) {
listBreakpoints();
return;
}
- BreakpointSpec spec = parseBreakpointSpec(t, "stop at", "stop in");
- if (spec != null) {
- // Enforcement of "at" vs. "in". The distinction is really
- // unnecessary and we should consider not checking for this
- // (and making "at" and "in" optional).
- if (atIn.equals("at") && spec.isMethodBreakpoint()) {
- MessageOutput.println("Use stop at to set a breakpoint at a line number");
- printBreakpointCommandUsage("stop at", "stop in");
+ String token = t.nextToken();
+
+ /* Check for "go" or "thread" modifiers. */
+ if (token.equals("go") && t.hasMoreTokens()) {
+ suspendPolicy = EventRequest.SUSPEND_NONE;
+ token = t.nextToken();
+ } else if (token.equals("thread") && t.hasMoreTokens()) {
+ suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
+ token = t.nextToken();
+ }
+
+ /* Handle <thread_id> modifier. */
+ if (!token.equals("at") && !token.equals("in")) {
+ Long threadid;
+ try {
+ threadid = Long.decode(token);
+ } catch (NumberFormatException nfe) {
+ MessageOutput.println("Expected at, in, or an integer <thread_id>:", token);
+ printBreakpointCommandUsage("printstopcommandusage");
return;
}
+ try {
+ ThreadInfo threadInfo = ThreadInfo.getThreadInfo(token);
+ if (threadInfo == null) {
+ MessageOutput.println("Invalid <thread_id>:", token);
+ return;
+ }
+ threadFilter = threadInfo.getThread();
+ token = t.nextToken(BreakpointSpec.locationTokenDelimiter);
+ } catch (VMNotConnectedException vmnce) {
+ MessageOutput.println("<thread_id> option not valid until the VM is started with the run command");
+ return;
+ }
+
+ }
+
+ /* Make sure "at" or "in" comes next. */
+ if (!token.equals("at") && !token.equals("in")) {
+ MessageOutput.println("Missing at or in");
+ printBreakpointCommandUsage("printstopcommandusage");
+ return;
+ }
+
+ token = t.nextToken(BreakpointSpec.locationTokenDelimiter);
+
+ BreakpointSpec spec = parseBreakpointSpec(t, token, threadFilter, "printstopcommandusage");
+ if (spec != null) {
spec.suspendPolicy = suspendPolicy;
resolveNow(spec);
}
@@ -1183,7 +1234,8 @@
return;
}
- BreakpointSpec spec = parseBreakpointSpec(t, "clear", "clear");
+ String token = t.nextToken(BreakpointSpec.locationTokenDelimiter);
+ BreakpointSpec spec = parseBreakpointSpec(t, token, null, "printclearcommandusage");
if (spec != null) {
if (Env.specList.delete(spec)) {
MessageOutput.println("Removed:", spec.toString());
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventRequestSpecList.java Mon Feb 25 16:05:06 2019 -0800
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventRequestSpecList.java Mon Feb 25 18:54:40 2019 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -36,6 +36,7 @@
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.event.ClassPrepareEvent;
+import com.sun.jdi.ThreadReference;
import java.util.ArrayList;
import java.util.Collections;
@@ -108,21 +109,21 @@
}
}
- BreakpointSpec createBreakpoint(String classPattern, int line)
+ BreakpointSpec createBreakpoint(String classPattern, int line, ThreadReference threadFilter)
throws ClassNotFoundException {
ReferenceTypeSpec refSpec =
new PatternReferenceTypeSpec(classPattern);
- return new BreakpointSpec(refSpec, line);
+ return new BreakpointSpec(refSpec, line, threadFilter);
}
BreakpointSpec createBreakpoint(String classPattern,
- String methodId,
+ String methodId, ThreadReference threadFilter,
List<String> methodArgs)
throws MalformedMemberNameException,
ClassNotFoundException {
ReferenceTypeSpec refSpec =
new PatternReferenceTypeSpec(classPattern);
- return new BreakpointSpec(refSpec, methodId, methodArgs);
+ return new BreakpointSpec(refSpec, methodId, threadFilter, methodArgs);
}
EventRequestSpec createExceptionCatch(String classPattern,
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTYResources.java Mon Feb 25 16:05:06 2019 -0800
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTYResources.java Mon Feb 25 18:54:40 2019 -0800
@@ -120,12 +120,14 @@
{"Exception occurred caught", "Exception occurred: {0} (to be caught at: {1})"},
{"Exception occurred uncaught", "Exception occurred: {0} (uncaught)"},
{"Exceptions caught:", "Break when these exceptions occur:"},
+ {"Expected at, in, or an integer <thread_id>:", "Expected \"at\", \"in\", or an integer <thread_id>: {0}"},
{"expr is null", "{0} = null"},
{"expr is value", "{0} = {1}"},
{"expr is value <collected>", " {0} = {1} <collected>"},
{"Expression cannot be void", "Expression cannot be void"},
{"Expression must evaluate to an object", "Expression must evaluate to an object"},
{"extends:", "extends: {0}"},
+ {"Extra tokens after breakpoint location", "Extra tokens after breakpoint location"},
{"Failed reading output", "Failed reading output of child java interpreter."},
{"Fatal error", "Fatal error:"},
{"Field access encountered before after", "Field ({0}) is {1}, will be {2}: "},
@@ -154,11 +156,14 @@
{"Invalid connect type", "Invalid connect type"},
{"Invalid consecutive invocations", "Invalid consecutive invocations"},
{"Invalid exception object", "Invalid exception object"},
- {"Invalid method specification:", "Invalid method specification: {0}"},
+ {"Invalid line number specified", "Invalid line number specified"},
+ {"Invalid <method_name> specification:", "Invalid <method_name> specification: {0}"},
{"Invalid option on class command", "Invalid option on class command"},
{"invalid option", "invalid option: {0}"},
{"Invalid thread status.", "Invalid thread status."},
+ {"Invalid <thread_id>:", "Invalid <thread_id>: {0}"},
{"Invalid transport name:", "Invalid transport name: {0}"},
+ {"Invalid <class>.<method_name> specification", "Invalid <class>.<method_name> specification"},
{"I/O exception occurred:", "I/O Exception occurred: {0}"},
{"is an ambiguous method name in", "\"{0}\" is an ambiguous method name in \"{1}\""},
{"is an invalid line number for", "{0,number,integer} is an invalid line number for {1}"},
@@ -191,6 +196,7 @@
{"Method exitedValue:", "Method exited: return value = {0}, "},
{"Method is overloaded; specify arguments", "Method {0} is overloaded; specify arguments"},
{"minus version", "This is {0} version {1,number,integer}.{2,number,integer} (Java SE version {3})"},
+ {"Missing at or in", "Missing \"at\" or \"in\""},
{"Monitor information for thread", "Monitor information for thread {0}:"},
{"Monitor information for expr", "Monitor information for {0} ({1}):"},
{"More than one class named", "More than one class named: ''{0}''"},
@@ -241,7 +247,18 @@
{"Owned by:", " Owned by: {0}, entry count: {1,number,integer}"},
{"Owned monitor:", " Owned monitor: {0}"},
{"Parse exception:", "Parse Exception: {0}"},
- {"printbreakpointcommandusage", "Usage: {0} <class>:<line_number> or\n {1} <class>.<method_name>[(argument_type,...)]"},
+ {"printclearcommandusage", "Usage clear <class>:<line_number> or\n clear <class>.<method_name>[(argument_type,...)]"},
+ {"printstopcommandusage",
+ "Usage: stop [go|thread] [<thread_id>] <at|in> <location>\n" +
+ " If \"go\" is specified, immediately resume after stopping\n" +
+ " If \"thread\" is specified, only suspend the thread we stop in\n" +
+ " If neither \"go\" nor \"thread\" are specified, suspend all threads\n" +
+ " If an integer <thread_id> is specified, only stop in the specified thread\n" +
+ " \"at\" and \"in\" have the same meaning\n" +
+ " <location> can either be a line number or a method:\n" +
+ " <class_id>:<line_number>\n" +
+ " <class_id>.<method>[(argument_type,...)]"
+ },
{"Removed:", "Removed: {0}"},
{"Requested stack frame is no longer active:", "Requested stack frame is no longer active: {0,number,integer}"},
{"run <args> command is valid only with launched VMs", "'run <args>' command is valid only with launched VMs"},
@@ -292,6 +309,8 @@
{"Thread not suspended", "Thread not suspended"},
{"thread group number description name", "{0,number,integer}. {1} {2}"},
{"Threadgroup name not specified.", "Threadgroup name not specified."},
+ {"<thread_id> option not valid until the VM is started with the run command",
+ "<thread_id> option not valid until the VM is started with the run command"},
{"Threads must be suspended", "Threads must be suspended"},
{"trace method exit in effect for", "trace method exit in effect for {0}"},
{"trace method exits in effect", "trace method exits in effect"},
@@ -318,7 +337,6 @@
{"Usage: unmonitor <monitor#>", "Usage: unmonitor <monitor#>"},
{"Usage: up [n frames]", "Usage: up [n frames]"},
{"Use java minus X to see", "Use 'java -X' to see the available non-standard options"},
- {"Use stop at to set a breakpoint at a line number", "Use 'stop at' to set a breakpoint at a line number"},
{"VM already running. use cont to continue after events.", "VM already running. Use 'cont' to continue after events."},
{"VM Started:", "VM Started: "},
{"vmstartexception", "VM start exception: {0}"},
@@ -357,9 +375,17 @@
"threadgroups -- list threadgroups\n" +
"threadgroup <name> -- set current threadgroup\n" +
"\n" +
- "stop in <class id>.<method>[(argument_type,...)]\n" +
- " -- set a breakpoint in a method\n" +
- "stop at <class id>:<line> -- set a breakpoint at a line\n" +
+ "stop [go|thread] [<thread_id>] <at|in> <location>\n" +
+ " -- set a breakpoint\n" +
+ " -- if no options are given, the current list of breakpoints is printed\n" +
+ " -- if \"go\" is specified, immediately resume after stopping\n" +
+ " -- if \"thread\" is specified, only suspend the thread we stop in\n" +
+ " -- if neither \"go\" nor \"thread\" are specified, suspend all threads\n" +
+ " -- if an integer <thread_id> is specified, only stop in the specified thread\n" +
+ " -- \"at\" and \"in\" have the same meaning\n" +
+ " -- <location> can either be a line number or a method:\n" +
+ " -- <class_id>:<line_number>\n" +
+ " -- <class_id>.<method>[(argument_type,...)]\n" +
"clear <class id>.<method>[(argument_type,...)]\n" +
" -- clear a breakpoint in a method\n" +
"clear <class id>:<line> -- clear a breakpoint at a line\n" +
@@ -412,7 +438,7 @@
"<n> <command> -- repeat command n times\n" +
"# <command> -- discard (no-op)\n" +
"help (or ?) -- list commands\n" +
- "dbgtrace [flag] -- same as dbgtrace command line option" +
+ "dbgtrace [flag] -- same as dbgtrace command line option\n" +
"version -- print version information\n" +
"exit (or quit) -- exit debugger\n" +
"\n" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdbStopThreadidTest.java Mon Feb 25 18:54:40 2019 -0800
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2019, 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
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8219143
+ * @summary Tests that using the "stop in" threadid option will properly cause the
+ * breakpoint to only be triggered when hit in the specified thread.
+ *
+ * @library /test/lib
+ * @run compile -g JdbStopThreadidTest.java
+ * @run main/othervm JdbStopThreadidTest
+ */
+
+import lib.jdb.Jdb;
+import lib.jdb.JdbCommand;
+import lib.jdb.JdbTest;
+
+import java.util.regex.*;
+
+class JdbStopThreadidTestTarg {
+ static Object lockObj = new Object();
+
+ public static void main(String[] args) {
+ test();
+ }
+
+ private static void test() {
+ JdbStopThreadidTestTarg test = new JdbStopThreadidTestTarg();
+ MyThread myThread1 = test.new MyThread("MYTHREAD-1");
+ MyThread myThread2 = test.new MyThread("MYTHREAD-2");
+ MyThread myThread3 = test.new MyThread("MYTHREAD-3");
+
+ synchronized (lockObj) {
+ myThread1.start();
+ myThread2.start();
+ myThread3.start();
+ // Wait for all threads to have started. Note they all block on lockObj after starting.
+ while (!myThread1.started || !myThread2.started || !myThread3.started) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ }
+ }
+ // Stop here so the test can setup the breakpoint in MYTHREAD-2
+ brkMethod();
+ }
+
+ // Wait for all threads to finish before exiting
+ try {
+ myThread1.join();
+ myThread2.join();
+ myThread3.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ static void brkMethod() {
+ }
+
+ public static void print(Object obj) {
+ System.out.println(obj);
+ }
+
+ class MyThread extends Thread {
+ volatile boolean started = false;
+
+ public MyThread(String name) {
+ super(name);
+ }
+
+ public void run() {
+ started = true;
+ synchronized (JdbStopThreadidTestTarg.lockObj) {
+ }
+ brkMethod();
+ }
+
+ void brkMethod() {
+ }
+ }
+}
+
+public class JdbStopThreadidTest extends JdbTest {
+ public static void main(String argv[]) {
+ new JdbStopThreadidTest().run();
+ }
+
+ private JdbStopThreadidTest() {
+ super(DEBUGGEE_CLASS);
+ }
+
+ private static final String DEBUGGEE_CLASS = JdbStopThreadidTestTarg.class.getName();
+ private static final String DEBUGGEE_THREAD_CLASS = JdbStopThreadidTestTarg.class.getName() + "$MyThread";
+ private static Pattern threadidPattern = Pattern.compile("MyThread\\)(\\S+)\\s+MYTHREAD-2");
+
+ @Override
+ protected void runCases() {
+ jdb.command(JdbCommand.stopIn(DEBUGGEE_CLASS, "brkMethod"));
+ jdb.command(JdbCommand.run().waitForPrompt("Breakpoint hit: \"thread=main\"", true));
+ jdb.command(JdbCommand.threads());
+
+ // Find the threadid for MYTHREAD-2 in the "threads" command output
+ String output = jdb.getJdbOutput();
+ Matcher m = threadidPattern.matcher(output);
+ String threadid = null;
+ if (m.find()) {
+ threadid = m.group(1);
+ } else {
+ throw new RuntimeException("FAILED: Did not match threadid pattern.");
+ }
+
+ // Setup a breakpoint in MYTHREAD-2.
+ jdb.command(JdbCommand.stopInThreadid(DEBUGGEE_THREAD_CLASS, "brkMethod", threadid));
+
+ // Continue until MYTHREAD-2 breakpoint is hit. If we hit any other breakpoint before
+ // then (we aren't suppose to), then this test will fail.
+ jdb.command(JdbCommand.cont().waitForPrompt("Breakpoint hit: \"thread=MYTHREAD-2\", \\S+MyThread.brkMethod", true));
+ // Continue until the application exits. Once again, hitting a breakpoint will cause
+ // a failure because we are not suppose to hit one.
+ jdb.command(JdbCommand.cont().waitForPrompt(Jdb.APPLICATION_EXIT, true));
+ }
+}
--- a/test/jdk/com/sun/jdi/lib/jdb/JdbCommand.java Mon Feb 25 16:05:06 2019 -0800
+++ b/test/jdk/com/sun/jdi/lib/jdb/JdbCommand.java Mon Feb 25 18:54:40 2019 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -162,6 +162,9 @@
public static JdbCommand stopIn(String targetClass, String methodName) {
return new JdbCommand("stop in " + targetClass + "." + methodName);
}
+ public static JdbCommand stopInThreadid(String targetClass, String methodName, String threadid) {
+ return new JdbCommand("stop " + threadid + " in " + targetClass + "." + methodName);
+ }
public static JdbCommand thread(int threadNumber) {
return new JdbCommand("thread " + threadNumber);
}
@@ -226,6 +229,10 @@
return new JdbCommand("methods " + classId);
}
+ public static JdbCommand threads() {
+ return new JdbCommand("threads");
+ }
+
// trace [go] methods [thread]
// -- trace method entries and exits.
// -- All threads are suspended unless 'go' is specified