1 /*
  2  * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 package nsk.share.jdi;
 25 
 26 import com.sun.jdi.IntegerValue;
 27 import com.sun.jdi.Location;
 28 import com.sun.jdi.Method;
 29 import com.sun.jdi.ReferenceType;
 30 import com.sun.jdi.ThreadReference;
 31 import com.sun.jdi.VirtualMachine;
 32 import com.sun.jdi.event.BreakpointEvent;
 33 import com.sun.jdi.event.Event;
 34 import com.sun.jdi.event.EventIterator;
 35 import com.sun.jdi.event.EventQueue;
 36 import com.sun.jdi.event.EventSet;
 37 import com.sun.jdi.event.ThreadDeathEvent;
 38 import com.sun.jdi.event.ThreadStartEvent;
 39 import com.sun.jdi.request.BreakpointRequest;
 40 import com.sun.jdi.request.EventRequest;
 41 import com.sun.jdi.request.EventRequestManager;
 42 import java.util.List;
 43 import nsk.share.Log;
 44 import nsk.share.MainWrapper;
 45 
 46 public class JDIBase {
 47 
 48     // Exit code constants
 49     public static final int PASSED = 0;
 50     public static final int FAILED = 2;
 51     public static final int PASS_BASE = 95;
 52 
 53 
 54     // Log helpers
 55     private final String sHeader1 = "\n=> " + this.getClass().getName().replace(".", "/") + " ";
 56 
 57     private static final String
 58             sHeader2 = "--> debugger: ",
 59             sHeader3 = "##> debugger: ";
 60 
 61     public final void log1(String message) {
 62         logHandler.display(sHeader1 + message);
 63     }
 64 
 65     public final void log2(String message) {
 66         logHandler.display(sHeader2 + message);
 67     }
 68 
 69     public final void log3(String message) {
 70         logHandler.complain(sHeader3 + message);
 71     }
 72 
 73     protected Log logHandler;
 74 
 75     // common variables used by tests
 76     protected Debugee debuggee;
 77     protected ArgumentHandler argsHandler;
 78     protected VirtualMachine vm;
 79     protected ReferenceType debuggeeClass;
 80     protected static int testExitCode = PASSED;
 81     protected long waitTime;
 82 
 83     // used by tests with breakpoint communication
 84     protected EventRequestManager eventRManager;
 85     protected EventQueue eventQueue;
 86     protected EventSet eventSet;
 87     protected EventIterator eventIterator;
 88 
 89     // additional fields initialized during breakpoint communication
 90     protected Location breakpLocation = null;
 91     protected BreakpointEvent bpEvent;
 92 
 93     protected final BreakpointRequest settingBreakpoint(ThreadReference thread,
 94                                                      ReferenceType testedClass,
 95                                                      String methodName,
 96                                                      String bpLine,
 97                                                      String property)
 98             throws JDITestRuntimeException {
 99 
100         log2("......setting up a breakpoint:");
101         log2("       thread: " + thread + "; class: " + testedClass +
102                 "; method: " + methodName + "; line: " + bpLine);
103 
104         List alllineLocations = null;
105         Location lineLocation = null;
106         BreakpointRequest breakpRequest = null;
107 
108         try {
109             Method method = (Method) testedClass.methodsByName(methodName).get(0);
110 
111             alllineLocations = method.allLineLocations();
112 
113             int n =
114                     ((IntegerValue) testedClass.getValue(testedClass.fieldByName(bpLine))).value();
115             if (n > alllineLocations.size()) {
116                 log3("ERROR:  TEST_ERROR_IN_settingBreakpoint(): number is out of bound of method's lines");
117             } else {
118                 lineLocation = (Location) alllineLocations.get(n);
119                 breakpLocation = lineLocation;
120                 try {
121                     breakpRequest = eventRManager.createBreakpointRequest(lineLocation);
122                     breakpRequest.putProperty("number", property);
123                     breakpRequest.addThreadFilter(thread);
124                     breakpRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
125                 } catch (Exception e1) {
126                     log3("ERROR: inner Exception within settingBreakpoint() : " + e1);
127                     breakpRequest = null;
128                 }
129             }
130         } catch (Exception e2) {
131             log3("ERROR: ATTENTION:  outer Exception within settingBreakpoint() : " + e2);
132             breakpRequest = null;
133         }
134 
135         if (breakpRequest == null) {
136             log2("      A BREAKPOINT HAS NOT BEEN SET UP");
137             throw new JDITestRuntimeException("**FAILURE to set up a breakpoint**");
138         }
139 
140         log2("      a breakpoint has been set up");
141         return breakpRequest;
142     }
143 
144     protected final void getEventSet() throws JDITestRuntimeException {
145         try {
146             eventSet = eventQueue.remove(waitTime);
147             if (eventSet == null) {
148                 throw new JDITestRuntimeException("** TIMEOUT while waiting for event **");
149             }
150             eventIterator = eventSet.eventIterator();
151         } catch (Exception e) {
152             throw new JDITestRuntimeException("** EXCEPTION while waiting for event ** : " + e);
153         }
154     }
155 
156     // Special version of getEventSet for ThreadStartEvent/ThreadDeathEvent.
157     // When ThreadStartRequest and/or ThreadDeathRequest are enabled,
158     // we can get the events from system threads unexpected for tests.
159     // The method skips ThreadStartEvent/ThreadDeathEvent events
160     // for all threads except the expected one.
161     // Note: don't limit ThreadStartRequest/ThreadDeathRequest request by addCountFilter(),
162     // as it limits the requested event to be reported at most once.
163     protected void getEventSetForThreadStartDeath(String threadName) throws JDITestRuntimeException {
164         while (true) {
165             getEventSet();
166             Event event = eventIterator.nextEvent();
167             if (event instanceof ThreadStartEvent evt) {
168                 if (evt.thread().name().equals(threadName)) {
169                     break;
170                 }
171                 log2("Got ThreadStartEvent for '" + evt.thread().name()
172                         + "' instead of '" + threadName + "', skipping");
173             } else if (event instanceof ThreadDeathEvent evt) {
174                 if (evt.thread().name().equals(threadName)) {
175                     break;
176                 }
177                 log2("Got ThreadDeathEvent for '" + evt.thread().name()
178                         + "' instead of '" + threadName + "', skipping");
179             } else {
180                 // not ThreadStartEvent nor ThreadDeathEvent
181                 break;
182             }
183             eventSet.resume();
184         }
185         // reset the iterator before return
186         eventIterator = eventSet.eventIterator();
187     }
188 
189     protected void breakpointForCommunication() throws JDITestRuntimeException {
190 
191         log2("breakpointForCommunication");
192         getEventSet();
193 
194         Event event = eventIterator.nextEvent();
195         if (event instanceof BreakpointEvent) {
196             bpEvent = (BreakpointEvent) event;
197             return;
198         }
199 
200         throw new JDITestRuntimeException("** event '" + event + "' IS NOT a breakpoint **");
201     }
202 
203     // Similar to breakpointForCommunication, but skips Locatable events from unexpected locations.
204     // It's useful for cases when enabled event requests can cause notifications from system threads
205     // (like MethodEntryRequest, MethodExitRequest).
206     protected void breakpointForCommunication(String debuggeeName) throws JDITestRuntimeException {
207         log2("breakpointForCommunication");
208         while (true) {
209             getEventSet();
210 
211             Event event = eventIterator.nextEvent();
212             if (event instanceof BreakpointEvent) {
213                 return;
214             }
215             if (EventFilters.filtered(event, debuggeeName)) {
216                 log2("  got unexpected event: " + event + ", skipping");
217                 eventSet.resume();
218             } else {
219                 throw new JDITestRuntimeException("** event '" + event + "' IS NOT a breakpoint **");
220             }
221         }
222     }
223 
224 }