/* * Copyright (c) 2022, 2023, 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 8292217 * @summary Test co-located events (CLE) for MethodEntry, SingleStep, and Breakpoint events. * @run build TestScaffold VMConnection TargetListener TargetAdapter * @run compile -g CLETest.java * @run driver CLETest */ import com.sun.jdi.*; import com.sun.jdi.event.*; import com.sun.jdi.request.*; import java.util.*; class CLEClass1 { public static void foo() { } } class CLEClass2 { public static void foo() { } } /* * The debuggee has a large number of breakpoints pre-setup to help control the test. * They are each hit just once, and in the order of their number. No instructions in the * debuggee are ever executed more than once. * * NOTE: the breakpoints are sensitive to the their line number within the method. * If that changes, then the "breakpoints" table needs to be updated. */ class CLEDebugee { public static void main(String[] args) { runTests(); } public static void runTests() { test1(); test2(); test3(); // BREAKPOINT_3 test4(); // BREAKPOINT_5 test5(); // BREAKPOINT_7 test6(); // BREAKPOINT_9 } // test1 and test2 are testing for the bug described in 8292217. For this test MethodEntry // events are enabled when we hit the breakpoint, and we single step OVER (test1) or // INTO (test2) an instruction with an unresolved contant pool entry. The Debugger will // verify that the generated MethodEntry events during class loading are not improperly // co-located as described the the CR. public static void test1() { CLEClass1.foo(); // BREAKPOINT_1 } public static void test2() { CLEClass2.foo(); // BREAKPOINT_2 } // Tests that MethodEntry, Step, and Breakpoint events that occur at the same // location are properly co-located in the same EventSet. MethodEntry and Step // are enabled when we hit BREAKPOINT_3 above. When the BreakpointEvent for // BREAKPOINT_4 is generated, the EventSet should also include a StepEvent // and a MethodEntryEvent. public static void test3() { int x = 1; // BREAKPOINT_4 } // Same as test3 but only check for co-located MethodEntry and Breakpoint events. // MethodEntry is enabled when we hit BREAKPOINT_5 above. StepEvent is not enabled. // When the BreakpointEvent for BREAKPOINT_6 is generated, the EventSet should also // include a MethodEntryEvent. public static void test4() { int x = 1; // BREAKPOINT_6 } // Same as test3 but only check for co-located Step and Breakpoint events. // StepEvents are enabled when we hit BREAKPOINT_7 above. When the BreakpointEvent // for BREAKPOINT_8 is generated, the EventSet should also include a StepEvent, public static void test5() { int x = 1; // BREAKPOINT_8 } // Same as test3 but only check for co-located MethodEntry and Step events. // MethodEntry and Step events are enabled when we hit BREAKPOINT_9 above. When // the StepEvent is received, the EventSet should also include the MethodEntryEvent. public static void test6() { int x = 1; } } public class CLETest extends TestScaffold { ClassType targetClass; EventRequestManager erm; StepRequest stepRequest; MethodEntryRequest entryRequest; MethodExitRequest exitRequest; int methodEntryCount = 0; int breakpointCount = 0; boolean testcaseFailed = false; int testcase = 0; CLETest(String args[]) { super(args); } public static void main(String[] args) throws Exception { CLETest cle = new CLETest(args); cle.startTests(); } static class MethodBreakpointData { final String method; final String signature; final int lineNumber; public MethodBreakpointData(String method, String signature, int lineNumber) { this.method = method; this.signature = signature; this.lineNumber = lineNumber; } } // Table of all breakpoints based on method name and sig, plus the line number within the method. static MethodBreakpointData[] breakpoints = new MethodBreakpointData[] { new MethodBreakpointData("runTests", "()V", 3), // BREAKPOINT_3 new MethodBreakpointData("runTests", "()V", 4), // BREAKPOINT_5 new MethodBreakpointData("runTests", "()V", 5), // BREAKPOINT_7 new MethodBreakpointData("runTests", "()V", 6), // BREAKPOINT_9 new MethodBreakpointData("test1", "()V", 1), // BREAKPOINT_1 new MethodBreakpointData("test2", "()V", 1), // BREAKPOINT_2 new MethodBreakpointData("test3", "()V", 1), // BREAKPOINT_4 new MethodBreakpointData("test4", "()V", 1), // BREAKPOINT_6 new MethodBreakpointData("test5", "()V", 1) // BREAKPOINT_8 }; public static void printStack(ThreadReference thread) { try { List frames = thread.frames(); Iterator iter = frames.iterator(); while (iter.hasNext()) { StackFrame frame = iter.next(); System.out.println(getLocationString(frame.location())); } } catch (Exception e) { System.out.println("printStack: exception " + e); } } public static String getLocationString(Location loc) { return loc.declaringType().name() + "." + loc.method().name() + ":" + loc.lineNumber(); } /* * Returns true if the specified event types are all co-located in this EventSet, * and no other events are included. Note that the order of the events (when present) * is required to be: MethodEntryEvent, StepEvent, BreakpointEvent. */ public boolean isColocated(EventSet set, boolean needEntry, boolean needStep, boolean needBreakpoint) { int expectedSize = (needEntry ? 1 : 0) + (needStep ? 1 : 0) + (needBreakpoint ? 1 : 0); if (set.size() != expectedSize) { return false; } EventIterator iter = set.eventIterator(); if (needEntry) { Event meEvent = iter.next(); if (!(meEvent instanceof MethodEntryEvent)) { return false; } } if (needStep) { Event ssEvent = iter.next(); if (!(ssEvent instanceof StepEvent)) { return false; } } if (needBreakpoint) { Event bpEvent = iter.next(); if (!(bpEvent instanceof BreakpointEvent)) { return false; } } return true; } public void eventSetReceived(EventSet set) { System.out.println("\nEventSet for test case #" + testcase + ": " + set); switch (testcase) { case 1: case 2: { // During the first two test cases we should never receive an EventSet with // more than one Event in it. if (set.size() != 1) { testcaseFailed = true; // For now, we expect the 2nd test cases to fail due to 8292217, // so don't fail the overall test run as a result of this failure. // There is a workaround in place that allows the 1st test case to pass. if (testcase == 1) { testFailed = true; } System.out.println("TESTCASE #" + testcase + " FAILED" + (testcase == 2 ? "(ignoring)" : "") + ": too many events in EventSet: " + set.size()); } break; } case 3: { // At some point during test3 we should receive co-located MethodEntry, Step, and Breakpoint events. if (isColocated(set, true, true, true)) { testcaseFailed = false; } break; } case 4: { // At some point during test4 we should receive co-located MethodEntry and Breakpoint events. if (isColocated(set, true, false, true)) { testcaseFailed = false; } break; } case 5: { // At some point during test5 we should receive co-located Step and Breakpoint events. if (isColocated(set, false, true, true)) { testcaseFailed = false; } break; } case 6: { // At some point during test6 we should receive co-located MethodEntry and Step events. if (isColocated(set, true, true, false)) { testcaseFailed = false; } break; } } } /* * Most of the control flow of the test is handled via breakpoints. There is one at the start * of each test case that is used to enable other events that we check for during the test case. * In some cases there is an additional Breakpoint enabled for the test cases that is * also used to determine when the test case is complete. Other test cases are completed * when a Step or MethodEntry event arrives. */ public void breakpointReached(BreakpointEvent event) { breakpointCount++; if (breakpointCount != 4 && breakpointCount != 6 && breakpointCount != 8) { testcase++; } System.out.println("Got BreakpointEvent(" + breakpointCount + "): " + getLocationString(event.location())); event.request().disable(); // Setup test1. Completion is checked for in stepCompleted(). if (breakpointCount == 1) { testcaseFailed = false; // assume passing unless error detected entryRequest.enable(); exitRequest.enable(); stepRequest = erm.createStepRequest(mainThread, StepRequest.STEP_LINE, StepRequest.STEP_OVER); stepRequest.addCountFilter(1); stepRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); stepRequest.enable(); } // Setup test2. Completion is checked for in stepCompleted(). if (breakpointCount == 2) { testcaseFailed = false; // assume passing unless error detected entryRequest.enable(); exitRequest.enable(); stepRequest = erm.createStepRequest(mainThread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); stepRequest.addCountFilter(1); stepRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); stepRequest.enable(); } // Setup test3: MethodEntry, Step, and Breakpoint co-located events. // Completion is handled by the next breakpoint being hit. if (breakpointCount == 3) { testcaseFailed = true; // assume failing unless pass detected entryRequest.enable(); stepRequest = erm.createStepRequest(mainThread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); stepRequest.addCountFilter(1); stepRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); stepRequest.enable(); } // Complete test3. We fail if we never saw the expected co-located events. if (breakpointCount == 4) { if (testcaseFailed) { testFailed = true; System.out.println("TESTCASE #3 FAILED: did not get MethodEntry, Step, and Breakpoint co-located events"); } else { System.out.println("TESTCASE #3 PASSED"); } } // Setup test4: MethodEntry and Breakpoint co-located events. // Completion is handled by the next breakpoint being hit. if (breakpointCount == 5) { testcaseFailed = true; // assume failing unless pass detected entryRequest.enable(); } // Complete test4. We fail if we never saw the expected co-located events. if (breakpointCount == 6) { entryRequest.disable(); if (testcaseFailed) { testFailed = true; System.out.println("TESTCASE #4 FAILED: did not get MethodEntry and Breakpoint co-located events"); } else { System.out.println("TESTCASE #4 PASSED"); } } // Setup test5: Step and Breakpoint co-located events. // Completion is handled by the next breakpoint being hit. if (breakpointCount == 7) { testcaseFailed = true; // assume failing unless pass detected stepRequest = erm.createStepRequest(mainThread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); stepRequest.addCountFilter(1); stepRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); stepRequest.enable(); } // Complete test5. We fail if we never saw the expected co-located events. if (breakpointCount == 8) { if (testcaseFailed) { testFailed = true; System.out.println("TESTCASE #5 FAILED: did not get Step and Breakpoint co-located events"); } else { System.out.println("TESTCASE #5 PASSED"); } } // Setup test: MethodEntry and Step co-located events // Completion is handled by the stepCompleted() since there is no additional breakpoint. if (breakpointCount == 9) { testcaseFailed = true; // assume failing unless pass detected entryRequest.enable(); stepRequest = erm.createStepRequest(mainThread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); stepRequest.addCountFilter(1); stepRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); stepRequest.enable(); } } public void stepCompleted(StepEvent event) { System.out.println("Got StepEvent: " + getLocationString(event.location())); event.request().disable(); entryRequest.disable(); if (testcase == 6 && testcaseFailed) { testFailed = true; System.out.println("TESTCASE #6 FAILED: did not get MethodEntry and Step co-located events"); } if (testcase == 1 || testcase == 2 || testcase == 6) { exitRequest.disable(); if (!testcaseFailed) { // We already did a println if the test failed. System.out.println("TESTCASE #" + testcase + " PASSED"); } } } public void methodEntered(MethodEntryEvent event) { System.out.println("Got MethodEntryEvent: " + getLocationString(event.location())); if (methodEntryCount++ == 25) { entryRequest.disable(); // Just in case the test loses control. } } public void methodExited(MethodExitEvent event) { System.out.println("Got MethodExitEvent: " + getLocationString(event.location())); //printStack(event.thread()); exitRequest.disable(); entryRequest.disable(); } protected void runTests() throws Exception { System.out.println("Starting CLETest"); BreakpointEvent bpe = startToMain("CLEDebugee"); targetClass = (ClassType)bpe.location().declaringType(); mainThread = bpe.thread(); System.out.println("Got main thread: " + mainThread); erm = eventRequestManager(); try { // Setup all breakpoints for (MethodBreakpointData bpData : breakpoints) { Location loc = findMethodLocation(targetClass, bpData.method, bpData.signature, bpData.lineNumber); BreakpointRequest req = erm.createBreakpointRequest(loc); req.setSuspendPolicy(EventRequest.SUSPEND_ALL); req.enable(); } // Ask for method entry events entryRequest = erm.createMethodEntryRequest(); entryRequest.addThreadFilter(mainThread); entryRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); // Ask for method exit events exitRequest = erm.createMethodExitRequest(); exitRequest.addThreadFilter(mainThread); exitRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); System.out.println("Waiting for events: "); listenUntilVMDisconnect(); System.out.println("All done..."); } catch (Exception ex){ ex.printStackTrace(); testFailed = true; } if (!testFailed) { println("CLETest: passed"); } else { throw new Exception("CLETest: failed"); } } }