blob: 96fa1c646ef7a1bc0dbd0d831f0f41eadf10032c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand;
import org.apache.harmony.jpda.tests.framework.LogWriter;
import org.apache.harmony.jpda.tests.framework.TestErrorException;
import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
import org.apache.harmony.jpda.tests.framework.jdwp.Location;
import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
import org.apache.harmony.jpda.tests.share.JPDATestOptions;
/**
* Base class for debuggers that are used in tests for DebuggerOnDemand functionality.
*/
public abstract class LaunchedDebugger extends JDWPTestCase {
// synchronization channel between debugger and debuggee
protected JPDADebuggeeSynchronizer synchronizer;
// synchronization channel between debugger and test
protected JPDADebuggeeSynchronizer testSynchronizer;
// name of tested debuggee class
public static final String DEBUGGEE_CLASS_NAME =
"org/apache/harmony/jpda/tests/jdwp/DebuggerOnDemand/OnthrowDebuggerLaunchDebuggee";
// signature of the tested debuggee class
public static final String DEBUGGEE_CLASS_SIGNATURE = "L" + DEBUGGEE_CLASS_NAME + ";";
/**
* This method is invoked right before attaching to debuggee VM.
* It forces to use attaching connector and fixed transport address.
*/
/*
protected void beforeConnectionSetUp() {
settings.setAttachConnectorKind();
if (settings.getTransportAddress() == null) {
settings.setTransportAddress(JPDADebuggerOnDemandOptions.DEFAULT_ATTACHING_ADDRESS);
}
logWriter.println("DEBUGGER: Use ATTACH connector kind");
super.beforeConnectionSetUp();
}
*/
/**
* Overrides inherited method to resume debuggee VM and then to establish
* sync connection with debuggee and server.
*/
protected void internalSetUp() throws Exception {
// estabslish synch connection with test
logWriter.println("Establish synch connection between debugger and test");
JPDADebuggerOnDemandOptions debuggerSettings = new JPDADebuggerOnDemandOptions();
testSynchronizer = new JPDADebuggerOnDemandSynchronizer(logWriter, debuggerSettings);
testSynchronizer.startClient();
logWriter.println("Established synch connection between debugger and test");
// handle JDWP connection with debuggee
super.internalSetUp();
// establish synch connection with debuggee
logWriter.println("Establish synch connection between debugger and debuggee");
synchronizer = new JPDADebuggeeSynchronizer(logWriter, settings);;
synchronizer.startClient();
logWriter.println("Established synch connection between debugger and debuggee");
}
/**
* Creates wrapper for debuggee process.
*/
protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
return new JPDADebuggerOnDemandDebuggeeWrapper(settings, logWriter);
}
/**
* Receives initial EXCEPTION event if debuggee is suspended on event.
*/
protected void receiveInitialEvent() {
if (settings.isDebuggeeSuspend()) {
initialEvent =
debuggeeWrapper.vmMirror.receiveCertainEvent(JDWPConstants.EventKind.EXCEPTION);
logWriter.println("Received inital EXCEPTION event");
debuggeeWrapper.resume();
logWriter.println("Resumed debuggee VM");
}
}
/**
* Overrides inherited method to close sync connection upon exit.
*/
protected void internalTearDown() {
// close synch connection with debuggee
if (synchronizer != null) {
synchronizer.stop();
logWriter.println("Closed synch connection between debugger and debuggee");
}
// close synch connection with test
if (testSynchronizer != null) {
testSynchronizer.stop();
logWriter.println("Closed synch connection between debugger and test");
}
// close connections with debuggee
super.internalTearDown();
}
protected String getDebuggeeClassName() {
return null;
}
///////////////////////////////////////////////////////////////////
/**
* This class contains information about frame of a java thread.
*/
public class FrameInfo {
long frameID;
Location location;
public FrameInfo(long frameID, Location location) {
super();
this.frameID = frameID;
this.location = location;
}
/**
* @return Returns the frameID.
*/
public long getFrameID() {
return frameID;
}
/**
* @return Returns the location.
*/
public Location getLocation() {
return location;
}
}
protected FrameInfo[] jdwpGetFrames(long threadID, int startFrame, int length) {
CommandPacket packet = new CommandPacket(
JDWPCommands.ThreadReferenceCommandSet.CommandSetID,
JDWPCommands.ThreadReferenceCommandSet.FramesCommand);
packet.setNextValueAsThreadID(threadID);
packet.setNextValueAsInt(startFrame);
packet.setNextValueAsInt(length);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (!checkReplyPacketWithoutFail(reply, "ThreadReference::FramesCommand command")) {
throw new TestErrorException("Error during performing ThreadReference::Frames command");
}
int frames = reply.getNextValueAsInt();
FrameInfo[] frameInfos = new FrameInfo[frames];
for (int i = 0; i < frames; i++) {
long frameID = reply.getNextValueAsLong();
Location location = reply.getNextValueAsLocation();
frameInfos[i] = new FrameInfo(frameID, location);
}
return frameInfos;
}
protected long getClassIDBySignature(String signature) {
logWriter.println("=> Getting reference type ID for class: " + signature);
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand);
packet.setNextValueAsString(signature);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (!checkReplyPacketWithoutFail(reply, "VirtualMachine::ClassesBySignature command")) {
throw new TestErrorException("Error during performing VirtualMachine::ClassesBySignature command");
}
int classes = reply.getNextValueAsInt();
logWriter.println("=> Returned number of classes: " + classes);
long classID = 0;
for (int i = 0; i < classes; i++) {
reply.getNextValueAsByte();
classID = reply.getNextValueAsReferenceTypeID();
reply.getNextValueAsInt();
// we need the only class, even if there were multiply ones
break;
}
assertTrue("VirtualMachine::ClassesBySignature command returned invalid classID:<" +
classID + "> for signature " + signature, classID > 0);
return classID;
}
protected void printStackFrame(int NumberOfFrames, FrameInfo[] frameInfos) {
for (int i = 0; i < NumberOfFrames; i++) {
logWriter.println(" ");
logWriter
.println("=> #" + i + " frameID=" + frameInfos[i].frameID);
String methodName = "";
try {
methodName = getMethodName(frameInfos[i].location.classID,
frameInfos[i].location.methodID);
} catch (TestErrorException e) {
throw new TestErrorException(e);
}
logWriter.println("=> method name=" + methodName);
}
logWriter.println(" ");
}
protected String getMethodName(long classID, long methodID) {
CommandPacket packet = new CommandPacket(
JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
JDWPCommands.ReferenceTypeCommandSet.MethodsCommand);
packet.setNextValueAsClassID(classID);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (!checkReplyPacketWithoutFail(reply, "ReferenceType::Methods command")) {
throw new TestErrorException("Error during performing ReferenceType::Method command");
}
int methods = reply.getNextValueAsInt();
for (int i = 0; i < methods; i++) {
long mid = reply.getNextValueAsMethodID();
String name = reply.getNextValueAsString();
reply.getNextValueAsString();
reply.getNextValueAsInt();
if (mid == methodID) {
return name;
}
}
return "unknown";
}
//////////////////////////////////////////////////////////////////////////////////////
/**
* This class provides functionality to establish synch connection between debugger and test.
*/
protected static class JPDADebuggerOnDemandSynchronizer extends JPDADebuggeeSynchronizer {
public JPDADebuggerOnDemandSynchronizer(LogWriter logWriter, JPDADebuggerOnDemandOptions settings) {
super(logWriter, settings);
this.settings = settings;
}
public int getSyncPortNumber() {
return ((JPDADebuggerOnDemandOptions)settings).getSyncDebuggerPortNumber();
}
}
/**
* This class provides customization of DebuggeeWrapper that attaches to already launched debuggee.
*/
protected static class JPDADebuggerOnDemandDebuggeeWrapper extends JDWPUnitDebuggeeWrapper {
public JPDADebuggerOnDemandDebuggeeWrapper(JPDATestOptions settings, LogWriter logWriter) {
super(settings, logWriter);
}
/**
* Attaches to already launched debuggee process and
* establishes JDWP connection.
*/
public void start() {
try {
transport = createTransportWrapper();
openConnection();
logWriter.println("Established connection");
} catch (Exception e) {
throw new TestErrorException(e);
}
}
/**
* Closes all connections but does not wait for debuggee process to exit.
*/
public void stop() {
disposeConnection();
closeConnection();
}
}
/**
* This class provides additional options to debuggers, that are used in DebuggerOnDemand tests.
* Currently the following additional options are supported:
* <ul>
* <li><i>jpda.settings.syncDebuggerPort</i>
* - port number for sync connection between debugger and test
* </ul>
*
*/
protected static class JPDADebuggerOnDemandOptions extends JPDATestOptions {
public static final String DEFAULT_SYNC_DEBUGGER_PORT = "9899";
/**
* Returns port number string for sync connection between debugger and test.
*/
public String getSyncDebuggerPortString() {
return getProperty("jpda.settings.syncDebuggerPort", null);
}
/**
* Returns port number for sync connection between debugger and test.
*/
public int getSyncDebuggerPortNumber() {
String buf = getSyncDebuggerPortString();
if (buf == null) {
buf = DEFAULT_SYNC_DEBUGGER_PORT;
}
try {
return Integer.parseInt(buf);
} catch (NumberFormatException e) {
throw new TestErrorException(e);
}
}
}
}