Answers below ...
On 7/18/18, 2:50 PM, Chris Plummer wrote:
Hi Gary,
Who does the resume for the breakpoint event?
eventHandler.addListener(
new EventHandler.EventListener() {
public boolean eventReceived(Event event) {
if (event instanceof BreakpointEvent &&
bpRequest.equals(event.request())) {
synchronized(eventHandler) {
display("Received communication breakpoint
event.");
bpCount++;
eventHandler.notifyAll();
}
return true;
}
return false;
}
}
);
I believe you are looking for this sequence.
At the top of the loop a check is made if
resume() should be called "shouldRunAfterBreakpoint".
lines 96-99 is an early termination. And at the
bottom of the loop, line 240, is the normal
continue the test to the next case.
resume008.java :
...
94 for (int i = 0; ; i++) {
95
96 if (!shouldRunAfterBreakpoint()) {
97 vm.resume();
98 break;
99 }
100
101
102 display(":::::: case: # " + i);
103
104 switch (i) {
105
106 case 0:
107 eventRequest = settingThreadStartRequest (
108 SUSPEND_NONE,
"ThreadStartRequest1");
...
238
239 display("......--> vm.resume()");
240 vm.resume();
241 }
Also:
1. On a thread start event the debugee is suspended, line 141
That's not true for the first ThreadStartEvent since SUSPEND_NONE was
used.
The thread start event is set to SUSPEND_NONE for thread0, but when
the thread start event is observed the resume008 test suspends the vm
immediately after fetching the "number" property.
132 if ( !(newEvent instanceof ThreadStartEvent)) {
133 setFailedStatus("ERROR: new event is not
ThreadStartEvent");
134 } else {
135
136 String property = (String)
newEvent.request().getProperty("number");
137 display(" got new ThreadStartEvent with
propety 'number' == " + property);
138
139 display("......checking up on
EventSet.resume()");
140 display("......--> vm.suspend();");
141 vm.suspend();
Chris
On 7/18/18 4:52 AM, Gary Adams wrote:
There is nothing wrong with the breakpoint in methodForCommunication.
The test uses it to make sure the threads are each tested separately.
The breakpoint eventhandler just displays a message, increments a
counter
and returns.
Let me step through resume008a the debugee to help clarify ...
1. The test thread is created and the synchronized break point is
observed. lines 101-102
2. The thread is started. lines 104,135-137
2a. The main thread blocks on a local object. lines 133, 139
2b. The test thread is started. lines 137,
A run entered message is displayed, line 159
The main thread lock object is notified, line 167
2b1. The main thread continues. line 167, 146
The next test thread is created. line 106
The synchronized breakpoint is observed, line 107
2b2. A run exited message is displayed, line 169
On the resume008 debugger side ...
1. On a thread start event the debugee is suspended, line 141
2. Messages are displayed and a first set of thread suspend counts
is acquired. lines 143-151
3. The threads are resumed, line 152
--->
4. Messages are displayed and a second set of thread suspend
counts is acquired. lines 154-159
The way the test is written the expectation is the debugger steps
2,3,4 will all happen
while the test thread is running.
When the debugger resumes the debuggee threads (debugger step 3)
the debuggee continues from where it left off (debuggee steps
2b,2b1,2b2)
If we complete debuggee step 2b1 (line 107) before the debugger
completes step 4 line 159,
then the synchronized breakpoint will suspend the vm and the counts
will not match
for the SUSPEND_NONE test thread start.
resume008a.java:
100 case 0:
101 thread0 = new
Threadresume008a("thread0");
102 methodForCommunication();
103
104 threadStart(thread0);
105
106 thread1 = new
Threadresume008a("thread1");
107 methodForCommunication();
108 break;
...
135 static int threadStart(Thread t) {
136 synchronized (waitnotifyObj) {
137 t.start();
138 try {
139 waitnotifyObj.wait();
140 } catch ( Exception e) {
141 exitCode = FAILED;
142 logErr(" Exception : " + e );
143 return FAILED;
144 }
145 }
146 return PASSED;
147 }
149 static class Threadresume008a extends Thread {
...
157
158 public void run() {
159 log1(" 'run': enter :: threadName == " + tName);
This is the proposed fix that will let the debugger complete it's second
acquisition of suspend counts while the test thread is still running.
160 // Yield, so the start thread event processing
can be completed.
161 try {
162 Thread.sleep(100);
163 } catch (InterruptedException e) {
164 // ignored
165 }
166 synchronized (waitnotifyObj) {
167 waitnotifyObj.notify();
168 }
169 log1(" 'run': exit :: threadName == " + tName);
170 return;
171 }
172 }
150
151 String tName = null;
152
153 public Threadresume008a(String threadName) {
154 super(threadName);
155 tName = threadName;
156 }
157
158 public void run() {
159 log1(" 'run': enter :: threadName == " + tName);
160 // Yield, so the start thread event processing
can be completed.
161 try {
162 Thread.sleep(100);
163 } catch (InterruptedException e) {
164 // ignored
165 }
166 synchronized (waitnotifyObj) {
167 waitnotifyObj.notify();
168 }
169 log1(" 'run': exit :: threadName == " + tName);
170 return;
171 }
172 }
On 7/18/18, 2:38 AM, Chris Plummer wrote:
Hi Gary,
I've been having trouble following the control flow of this test.
One thing I've stumbled across is the following:
/* A debuggee class must define 'methodForCommunication'
* method and invoke it in points of synchronization
* with a debugger.
*/
setCommunicationBreakpoint(debuggeeClass,"methodForCommunication");
So why isn't this mode of synchronization good enough? Is it because
it was not designed with the understanding that the debugger might
be doing suspended thread counts, and suspending all threads at the
breakpoint messes up the test?
From what I can tell of the test, after the debuggee is started and
hits the default breakpoint at the start of main(), the debugger
then does a vm.resume() at the start of the for loop in the
runTest() method. The debuggee then creates a thread and calls
methodForCommunication(). There is already a breakpoint set there by
the above debuggee code. It's unclear to me what happens as a result
of this breakpoint and how it serves the test. Also unclear to me
who is responsible for the vm.resume() after the breakpoint is hit.
The debugger then requests all ThreadStart events, requesting that
no threads be disabled when it is sent. I think you are saying that
when the ThreadStart event comes in, sometimes we are at the
methodForCommunication breakpoint, with all threads disabled, and
this messes up the thread suspend counts. You want to delay 100ms so
the breakpoint event can be processed and threads resumed again
(although I can't see who actually resumes the thread after hitting
the methodForCommunication breakpoint).
Chris
On 7/17/18 8:33 AM, Gary Adams wrote:
A race condition exists between the debugger and the debuggee.
The first test thread is started with SUSPEND_NONE policy set.
While processing the thread start event the debugger captures
an initial set of thread suspend counts and resumes the
debuggee vm. If the debuggee advances quickly it reaches
the breakpoint set for methodForCommunication. Since the breakpoint
carries with it SUSPEND_ALL policy, when the debugger captures a
second
set of suspend counts, it will not match the expected counts for
a SUSPEND_NONE scenario.
The proposed fix introduces a yield in the debuggee test thread run
method
to allow the debugger to get the expected sampled values.
Issue: https://bugs.openjdk.java.net/browse/JDK-8170089
Webrev: http://cr.openjdk.java.net/~gadams/8170089/webrev.00/
test/hotspot/jtreg/vmTestbase/nsk/share/jdi/TestDebuggerType1.java:
...
186 private void setCommunicationBreakpoint(ReferenceType
refType, String methodName) {
187 Method method = debuggee.methodByName(refType,
methodName);
188 Location location = null;
189 try {
190 location = method.allLineLocations().get(0);
191 } catch (AbsentInformationException e) {
192 throw new Failure(e);
193 }
194 bpRequest = debuggee.makeBreakpoint(location);
195
196 bpRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
197 bpRequest.putProperty("number", "zero");
198 bpRequest.enable();
199
200 eventHandler.addListener(
201 new EventHandler.EventListener() {
202 public boolean eventReceived(Event event) {
203 if (event instanceof BreakpointEvent
&& bpRequest.equals(event.request())) {
204 synchronized(eventHandler) {
205 display("Received
communication breakpoint event.");
206 bpCount++;
207 eventHandler.notifyAll();
208 }
209 return true;
210 }
211 return false;
212 }
213 }
214 );
215 }
test/hotspot/jtreg/vmTestbase/nsk/jdi/EventSet/resume/resume008.java:
...
140 display("......--> vm.suspend();");
141 vm.suspend();
142
143 display(" getting : Map<String,
Integer> suspendsCounts1");
144
145 Map<String, Integer> suspendsCounts1 =
new HashMap<String, Integer>();
146 for (ThreadReference threadReference :
vm.allThreads()) {
147 suspendsCounts1.put(threadReference.name(),
threadReference.suspendCount());
148 }
149 display(suspendsCounts1.toString());
150
151 display(" eventSet.resume;");
152 eventSet.resume();
153
154 display(" getting : Map<String,
Integer> suspendsCounts2");
This is where the breakpoint is encountered before the second set
of suspend counts is acquired.
155 Map<String, Integer> suspendsCounts2 =
new HashMap<String, Integer>();
156 for (ThreadReference threadReference :
vm.allThreads()) {
157 suspendsCounts2.put(threadReference.name(),
threadReference.suspendCount());
158 }
159 display(suspendsCounts2.toString());