RFR JDK-8170089: nsk/jdi/EventSet/resume/resume008: ERROR: suspendCounts don't match for : Common-Cleaner

Gary Adams gary.adams at oracle.com
Wed Jul 18 11:52:36 UTC 2018


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 resume008 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());
>>
>



More information about the serviceability-dev mailing list