Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/Debugee.java
41161 views
/*1* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share.jdi;2425import nsk.share.*;26import nsk.share.jpda.*;2728import com.sun.jdi.*;29import com.sun.jdi.request.*;30import com.sun.jdi.event.*;3132import java.util.*;3334/**35* This class is used to interact with debugee VM using JDI features.36* <p>37* This class is wrapper for debugee VM constructed by <code>Binder</code>38* and it uses <code>com.sun.jdi.VirtualMachine</code> to interact with debugee VM.39* <p>40* In addition to the general abities to control of debugee VM process,41* provided by the base class <code>DebugeeProcess</code>, this class42* adds also several service methods over the JDI features to simplify interaction43* with debugee VM (such as finding classes, setting breakpoints,44* handling events, and so on.).45*46* @see Binder47* @see DebugeeProcess48*/49abstract public class Debugee extends DebugeeProcess {5051/**52* Mirror of the debugee VM. This must be initialized by every53* particular non-abstract class extending Debugee class.54*/55protected VirtualMachine vm = null;5657/** Binder that created this debugee. */58protected Binder binder = null;5960/** Argument handler. */61protected ArgumentHandler argumentHandler = null;6263/** Create new <code>Debugee</code> object for a given binder. */64protected Debugee (Binder binder) {65super(binder);66this.binder = binder;67this.argumentHandler = (ArgumentHandler)binder.getArgumentHandler();68}6970/** Setup <code>Debugee</code> object with given VM mirror. */71public void setupVM(VirtualMachine vm) {72if (this.vm != null) {73throw new TestBug("Setting duplicated VM mirror for Debugee object");74}75this.vm = vm;76int traceMode = argumentHandler.getTraceMode();77if (traceMode != VirtualMachine.TRACE_NONE) {78display("Setting JDI trace mode to: " + argumentHandler.getTraceModeString());79setDebugTraceMode(traceMode);80}81}8283/** Return <code>Binder</code> of the debugee object. */84public Binder getBinder() {85return binder;86}8788/** Return JDI mirror of the debugee VM. */89public VirtualMachine VM() {90return vm;91}9293/** Return <code>EventRequestManager</code> of the debugee object. */94public EventRequestManager getEventRequestManager() {95return vm.eventRequestManager();96}9798// --------------------------------------------------- //99100/** List of the currently running threads. */101public ThreadReference[] threads () {102List list = vm.allThreads();103int size = list.size();104ThreadReference array[] = new ThreadReference[size];105Iterator iterator = list.iterator();106for (int i = 0; i < size; i++)107array[i] = (ThreadReference) iterator.next();108if (iterator.hasNext())109throw new Oddity("extra element in a list?");110return array;111}112113/** List of all types loaded by the debugee VM. */114public ReferenceType[] classes() {115return classes(null);116}117118/**119* List of all classes of the given <code>name</code> loaded by120* the debugee VM; or list of all classes, if <code>name</code>121* is <code>null</code>.122*/123private ReferenceType[] classes (String name) {124List list = (name==null)? vm.allClasses(): vm.classesByName(name);125int size = list.size();126ReferenceType array[] = new ReferenceType [ size ];127Iterator iterator = list.iterator();128for (int i=0; i<size; i++)129array[i] = (ReferenceType) iterator.next();130if (iterator.hasNext())131throw new Oddity("extra element in a list?");132return array;133}134135// --------------------------------------------------- //136137/**138* Return mirror for the only class of the given <code>name</code>139* loaded by the debugee VM; or throw TestBug exception if there140* are more than one such class found. TestFailure exception141* will be thrown in case when mirrors for classes with different142* names or duplicated mirrors were returned.143* Return <code>null</code> if there is no such class loaded.144*/145public ReferenceType classByName (String name) {146ReferenceType classes[] = this.classes(name);147148// if on first call debuggee doesn't return needed class try get this class one more time after delay to avoid 6446633149if (classes == null || classes.length == 0) {150try {151Thread.sleep(1000);152}153catch(InterruptedException e) {154throw new TestBug("Unexpected InterruptedException");155}156157classes = this.classes(name);158}159160if (classes == null || classes.length == 0)161return null;162163// analyze returned mirrors and throw appropriate exception164if (classes.length > 1) {165boolean duplicatesFound = false;166boolean differentNamesFound = false;167boolean visited[] = new boolean[classes.length];168complain("Classes that were found for name \"" + name + "\":");169for(ReferenceType klass : classes) {170complain("\t" + klass);171}172for(int c = 0; c < classes.length; c++) {173if(visited[c]) {174continue;175}176if(!classes[c].name().equals(name)) {177differentNamesFound = true;178continue;179}180for(int i = c + 1; i < classes.length; i++) {181if(visited[i]) {182continue;183} else {184visited[i] = true;185}186if(classes[c].classLoader() == classes[i].classLoader()) {187duplicatesFound = true;188}189}190}191if(duplicatesFound) {192throw new TestFailure("classes with the same name and " +193"loaded with the same class loader " +194"were found.");195} else if(differentNamesFound) {196throw new TestFailure("class with name different from '" + name +197"' was returned by VirutualMachine.classesByName.");198} else {199throw new TestBug("found " + classes.length + " such classes: " + name);200}201}202return classes[0];203}204205/**206* Return mirror for the only method of the given <code>refType</code>207* class in the debugee VM; or throw TestBug exception if there208* are more than one such method found. Return <code>null</code> if209* there is no such method found.210*/211public Method methodByName(ReferenceType refType, String name) {212List methods = refType.methodsByName(name);213if (methods == null || methods.isEmpty()) return null;214if (methods.size() > 1)215throw new TestBug(216"found " + methods.size() + " such methods: " + name);217Method method = (Method)methods.get(0);218return method;219}220221/**222* Return a currently running thread of the given <code>name</code>; or223* throw TestBug exception if there are more than one such thread found.224* Return <code>null</code> if there is no such thread.225*/226public ThreadReference threadByName (String name) {227ThreadReference threads[] = this.threads();228int count = 0, index = -1;229for (int i = 0; i < threads.length; i++) {230if (threads[i].name().compareTo(name)==0) {231count++;232index = i;233}234}235if (count == 0)236return null;237if (count > 1)238throw new TestBug(239"found " + count + " such threads: " + name);240return threads[index];241}242243244public ThreadReference threadByNameOrThrow(String name) throws JDITestRuntimeException {245246List all = vm.allThreads();247ListIterator li = all.listIterator();248for (; li.hasNext(); ) {249ThreadReference thread = (ThreadReference) li.next();250if (thread.name().equals(name))251return thread;252}253throw new JDITestRuntimeException("** Thread IS NOT found ** : " + name);254}255256// --------------------------------------------------- //257258/**259* Returns Location object for given line number in specified method or null260* if no location for this line is found.261*262* @param method method mirror containing given line number263* @param line line number to find location264*/265public Location getLineLocation(Method method, int line) {266List locs = null;267try {268locs = method.allLineLocations();269} catch(AbsentInformationException e) {270throw new TestBug("Unable to find location for line " + line + ": " + e);271}272Iterator iter = locs.iterator();273while (iter.hasNext()) {274Location location = (Location)iter.next();275if (location.lineNumber() == line) {276return location;277}278}279return null;280}281282/**283* Returns Location object for given line number in specified reference type or null284* if no location for this line is found.285*286* @param refType reference type mirror containing given line number287* @param line line number to find location288*/289public Location getLineLocation(ReferenceType refType, int line) {290List locs = null;291try {292locs = refType.allLineLocations();293} catch(AbsentInformationException e) {294throw new TestBug("Unable to find location for line " + line + ": " + e);295}296Iterator iter = locs.iterator();297while (iter.hasNext()) {298Location location = (Location)iter.next();299if (location.lineNumber() == line) {300return location;301}302}303return null;304}305306// --------------------------------------------------- //307308/**309* Make disabled breakpoint to given location and return BreakpointRequest.310*311* @param location location to set breakpoint312*313* @see #setBreakpoint(Method, int)314* @see #setBreakpoint(ReferenceType, String, int)315*/316public BreakpointRequest makeBreakpoint(Location location) {317EventRequestManager evm = getEventRequestManager();318BreakpointRequest request = evm.createBreakpointRequest(location);319display("Breakpoint set:\n\t" + request);320return request;321}322323/**324* Make disabled breakpoint to given line number in specified method325* and return BreakpointRequest.326*327* @param method method mirror to set breakpoint328* @param lineNumber line number inside the method329*330* @throws Failure if no location found for specified line number331*332* @see #makeBreakpoint(Location)333* @see #makeBreakpoint(ReferenceType, String, int)334*/335public BreakpointRequest makeBreakpoint(Method method, int lineNumber) {336Location location = getLineLocation(method, lineNumber);337if (location == null) {338throw new Failure("No location found for setting breakpoint to line " + lineNumber);339}340return makeBreakpoint(location);341}342343/**344* Make disabled breakpoint to given line number for specified method name345* of the given reference type and return BreakpointRequest.346*347* @param refType reference type for specified method348* @param methodName method name to set breakpoint349* @param lineNumber line number inside the method350*351* @throws Failure if no location found for specified line number352*353* @see #makeBreakpoint(Method, int)354*/355public BreakpointRequest makeBreakpoint(ReferenceType refType,356String methodName, int lineNumber) {357Method method = methodByName(refType, methodName);358if (method == null) {359throw new Failure("No method found for setting breakpoint: " + methodName);360}361return makeBreakpoint(method, lineNumber);362}363364/**365* Set and enable breakpoint to given line number for specified method366* and return BreakpointRequest.367*368* @param method method mirror to set breakpoint369* @param lineNumber line number inside the method370*371* @throws Failure if no location found for specified line number372*373* @see #setBreakpoint(ReferenceType, String, int)374*/375public BreakpointRequest setBreakpoint(Method method, int lineNumber) {376BreakpointRequest request = makeBreakpoint(method, lineNumber);377request.enable();378return request;379}380381/**382* Set and enable breakpoint to given line number for specified method name383* of the given reference type and return BreakpointRequest.384*385* @param refType reference type for specified method386* @param methodName method name to set breakpoint387* @param lineNumber line number inside the method388*389* @throws Failure if no location found for specified line number390*391* @see #setBreakpoint(Method, int)392*/393public BreakpointRequest setBreakpoint(ReferenceType refType,394String methodName, int lineNumber) {395BreakpointRequest request = makeBreakpoint(refType, methodName, lineNumber);396request.enable();397return request;398}399400// --------------------------------------------------- //401402/** Suspend the debugee VM. */403public void suspend() {404vm.suspend();405}406407/** Resume the debugee VM. */408public void resume() {409vm.resume();410}411412/** Dispose the debugee VM. */413public void dispose() {414vm.dispose();415}416417/*418* Set internal JDI tracing mode.419*/420public void setDebugTraceMode(int traceMode) {421vm.setDebugTraceMode(traceMode);422}423424// --------------------------------------------------- //425426/**427* Wait for the requested event and skip other events.428*429* @param request non-null value for events generated by this430* event request; null value for <code>VMStartEvent</code>.431* @param timeout timeout in milliseconds to wait for the requested event.432*433* @throws InterruptedException if another thread has interrupted this thread434*/435public Event waitingEvent(EventRequest request, long timeout)436throws InterruptedException {437438if (request == null) {439throw new Failure("Null request specified for waiting events: " + request);440}441442long timeToFinish = System.currentTimeMillis() + timeout;443long timeLeft = timeout;444boolean exit = false;445446display("Waiting for event by request:\n\t" + request);447448EventQueue eventQueue = vm.eventQueue();449while (timeLeft > 0 && !exit) {450451EventSet eventSet = eventQueue.remove(timeLeft);452if (eventSet == null) {453continue;454}455456EventIterator eventIterator = eventSet.eventIterator();457while (eventIterator.hasNext()) {458459Event event = eventIterator.nextEvent();460EventRequest eventRequest = event.request();461462if (request == eventRequest || request.equals(eventRequest)) {463display("Got requested event:\n\t" + event);464return event;465} else if (event instanceof VMDeathEvent) {466display("Ignore unexpected VMDeathEvent");467} else if (event instanceof VMDisconnectEvent) {468display("Got unexpected VMDisconnectEvent");469exit = true;470break;471} else {472display("Ignore unexpected event:\n\t" + event);473} // if474475} // while476477timeLeft = timeToFinish - System.currentTimeMillis();478479} // while480481return null;482}483484/*485* Wait for VM to initialize by receiving initial VM_START event for specified timeout.486*/487public void waitForVMInit(long timeout) {488waitForVMInit(vm ,log, timeout);489}490491/*492* This static method is also used by nsk.share.jdi.ConnectorTest493*/494static public void waitForVMInit(VirtualMachine vm, Log log, long timeout) {495try {496EventSet eventSet = vm.eventQueue().remove(timeout);497if (eventSet == null) {498throw new Failure("No VMStartEvent received for timeout: " + timeout + " ms");499}500EventIterator iterator = eventSet.eventIterator();501while (iterator.hasNext()) {502Event event = iterator.nextEvent();503if (event == null) {504throw new Failure("Null event received instead of VMStartEvent");505}506if (event instanceof VMStartEvent) {507log.display("Initial VMStartEvent received: " + event);508} else {509throw new Failure("Unexpected event received instead of VMStartEvent: " + event);510}511}512int suspendPolicy = eventSet.suspendPolicy();513if (suspendPolicy != EventRequest.SUSPEND_ALL) {514throw new Failure("Suspend policy of VMStartEvent is not SUSPEND_ALL: " + suspendPolicy);515}516} catch (InterruptedException e) {517e.printStackTrace(log.getOutStream());518throw new Failure("Thread interrupted while waiting for VMStartEvent:\n\t" + e);519}520}521522// --------------------------------------------------- //523524/**525* Bind to debuggee VM using <code>Binder</code> and make initial526* synchronization via IOPipe.527*528* @param argHandler command line arguments handler to make <code>Binder</code> object529* @param log <code>Log</code> object to log messages530* @param mainClassName main class of debugee531*532* @throws Failure if there were problems with binding to debuggee VM533*534* @see Binder#bindToDebugee(String)535*/536public static Debugee prepareDebugee(ArgumentHandler argHandler, Log log,537String mainClassName) {538Binder binder = new Binder(argHandler, log);539Debugee debugee = binder.bindToDebugee(mainClassName);540541debugee.createIOPipe();542543debugee.redirectStderr(log, DEBUGEE_STDERR_LOG_PREFIX);544debugee.resume();545546debugee.receiveExpectedSignal("ready");547548return debugee;549}550551/**552* Send <code>"quit"</code> signal, wait for debugee VM exit and check exit.553*554* @throws Failure if exit status is not <code>Consts.JCK_STATUS_BASE</code>555*556* @see #endDebugee()557*/558public void quit() {559sendSignal("quit");560int status = endDebugee();561if ( status != Consts.JCK_STATUS_BASE ) {562throw new Failure("Got unexpected debugee VM exit status: " + status563+ " (not " + Consts.JCK_STATUS_BASE + ")");564}565display("Got expected debugee VM exit status: " + status);566}567568/*569* Dispose debuggee VM, wait for it to exit, close all resources and return570* exit status code.571*/572public int endDebugee() {573int status = waitFor();574if (vm != null) {575try {576vm.dispose();577} catch (VMDisconnectedException ignore) {578}579vm = null;580}581return status;582}583584/*585* Print information about all threads in debuggee VM586*/587protected void printThreadsInfo(VirtualMachine vm) {588try {589log.display("------------ Try to print debuggee threads before killing process ------------");590if (vm == null) {591log.display("Can't print threads info because 'vm' is null");592return;593}594List<ThreadReference> threads = vm.allThreads();595log.display("Threads: " + threads);596log.display("Total threads: " + threads.size());597for (ThreadReference thread : threads) {598log.display("\nThread: " + thread.name());599log.display("Is suspended: " + thread.isSuspended());600log.display("Is at breakpoint: " + thread.isAtBreakpoint());601boolean wasSuspended = false;602try {603if (!thread.isSuspended()) {604log.display("\n suspend thread to get its stack \n");605thread.suspend();606wasSuspended = true;607}608log.display("Stack frame count: " + thread.frameCount());609if (thread.frameCount() > 0) {610log.display("Frames:");611for (StackFrame frame : thread.frames()) {612Location location = frame.location();613log.display(location.declaringType().name() + "." + location.method().name() + ", line: " + location.lineNumber());614}615}616} finally {617if (wasSuspended) {618log.display("\n resume thread \n");619thread.resume();620}621}622}623log.display("----------------------------------------------------------------------");624} catch (Throwable t) {625log.complain("");626t.printStackTrace(log.getOutStream());627}628}629630/**631* Force debugge VM to exit using JDI interface if possible.632*/633protected void killDebugee() {634try {635// print information about debuggee threads to simplify failure analysis636printThreadsInfo(vm);637} finally {638if (vm != null) {639try {640display("Killing debuggee by forcing target VM to exit");641vm.exit(97);642display("Debugee VM successfully forced to exit");643vm = null;644} catch (VMDisconnectedException e) {645display("Ignore VMDisconnectedException while forcing debuggee VM to exit:\n\t"646+ e);647}648}649}650}651652}653654655