/* * 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. */ /** * @author Vitaly A. Provodin */ /** * Created on 29.01.2005 */ package org.apache.harmony.jpda.tests.jdwp.share; 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.EventPacket; 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.Packet; import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; /** * Basic class for unit tests which use only one debuggee VM. */ public abstract class JDWPTestCase extends JDWPRawTestCase { /** * DebuggeeWrapper instance for launched debuggee VM. */ protected JDWPUnitDebuggeeWrapper debuggeeWrapper; /** * EventPacket instance with received VM_START event. */ protected EventPacket initialEvent = null; /** * Overrides inherited method to launch one debuggee VM, establish JDWP * connection, and wait for VM_START event. */ protected void internalSetUp() throws Exception { super.internalSetUp(); // launch debuggee process debuggeeWrapper = createDebuggeeWrapper(); beforeDebuggeeStart(debuggeeWrapper); startDebuggeeWrapper(); // receive and handle initial event receiveInitialEvent(); // adjust JDWP types length debuggeeWrapper.vmMirror.adjustTypeLength(); logWriter.println("Adjusted VM-dependent type lengths"); } /** * Creates wrapper for debuggee process. */ protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() { if (settings.getDebuggeeLaunchKind().equals("manual")) { return new JDWPManualDebuggeeWrapper(settings, logWriter); } else { return new JDWPUnitDebuggeeWrapper(settings, logWriter); } } /** * Starts wrapper for debuggee process. */ protected void startDebuggeeWrapper() { debuggeeWrapper.start(); logWriter.println("Established JDWP connection with debuggee VM"); } /** * Receives initial VM_INIT event if debuggee is suspended on event. */ protected void receiveInitialEvent() { if (settings.isDebuggeeSuspend()) { initialEvent = debuggeeWrapper.vmMirror.receiveCertainEvent(JDWPConstants.EventKind.VM_INIT); logWriter.println("Received inital VM_INIT event"); } } /** * Overrides inherited method to stop started debuggee VM and close all * connections. */ protected void internalTearDown() { if (debuggeeWrapper != null) { debuggeeWrapper.stop(); logWriter.println("Closed JDWP connection with debuggee VM"); } super.internalTearDown(); } /** * This method is invoked right before starting debuggee VM. */ protected void beforeDebuggeeStart(JDWPUnitDebuggeeWrapper debuggeeWrapper) { } /** * Opens JDWP connection with debuggee (doesn't run debuggee and doesn't * establish synchronize connection). */ public void openConnection() { debuggeeWrapper.openConnection(); logWriter.println("Opened transport connection"); debuggeeWrapper.vmMirror.adjustTypeLength(); logWriter.println("Adjusted VM-dependent type lengths"); } /** * Closes JDWP connection with debuggee (doesn't terminate debuggee and * doesn't stop synchronize connection). */ public void closeConnection() { if (debuggeeWrapper != null) { debuggeeWrapper.disposeConnection(); try { debuggeeWrapper.vmMirror.closeConnection(); } catch (Exception e) { throw new TestErrorException(e); } logWriter.println("Closed transport connection"); } } /** * Helper that returns reference type signature of input object ID. * * @param objectID - * debuggee object ID * @return object signature of reference type */ protected String getObjectSignature(long objectID) { long classID = getObjectReferenceType(objectID); return getClassSignature(classID); } /** * Helper that returns reference type ID for input object ID. * * @param objectID - * debuggee object ID * @return reference type ID */ protected long getObjectReferenceType(long objectID) { CommandPacket command = new CommandPacket( JDWPCommands.ObjectReferenceCommandSet.CommandSetID, JDWPCommands.ObjectReferenceCommandSet.ReferenceTypeCommand); command.setNextValueAsReferenceTypeID(objectID); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command); checkReplyPacket(reply, "ObjectReference::ReferenceType command"); // byte refTypeTag = reply.getNextValueAsByte(); long objectRefTypeID = reply.getNextValueAsReferenceTypeID(); return objectRefTypeID; } /** * Helper for getting method ID of corresponding class and method name. * * @param classID - * class ID * @param methodName - * method name * @return method ID */ protected long getMethodID(long classID, String methodName) { CommandPacket command = new CommandPacket( JDWPCommands.ReferenceTypeCommandSet.CommandSetID, JDWPCommands.ReferenceTypeCommandSet.MethodsCommand); command.setNextValueAsClassID(classID); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command); checkReplyPacket(reply, "ReferenceType::Methods command"); int methods = reply.getNextValueAsInt(); for (int i = 0; i < methods; i++) { long methodID = reply.getNextValueAsMethodID(); String name = reply.getNextValueAsString(); // method name reply.getNextValueAsString(); // method signature reply.getNextValueAsInt(); // method modifiers if (name.equals(methodName)) { return methodID; } } return -1; } /** * Issues LineTable command. * * @param classID - * class ID * @param methodID - * method ID * @return reply packet */ protected ReplyPacket getLineTable(long classID, long methodID) { CommandPacket lineTableCommand = new CommandPacket( JDWPCommands.MethodCommandSet.CommandSetID, JDWPCommands.MethodCommandSet.LineTableCommand); lineTableCommand.setNextValueAsReferenceTypeID(classID); lineTableCommand.setNextValueAsMethodID(methodID); ReplyPacket lineTableReply = debuggeeWrapper.vmMirror .performCommand(lineTableCommand); checkReplyPacket(lineTableReply, "Method::LineTable command"); return lineTableReply; } /** * Helper for getting method name of corresponding class and method ID. * * @param classID class id * @param methodID method id * @return String */ 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); checkReplyPacket(reply, "ReferenceType::Methods 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"; } /** * Returns jni signature for selected classID * * @param classID * @return jni signature for selected classID */ protected String getClassSignature(long classID) { CommandPacket command = new CommandPacket( JDWPCommands.ReferenceTypeCommandSet.CommandSetID, JDWPCommands.ReferenceTypeCommandSet.SignatureCommand); command.setNextValueAsReferenceTypeID(classID); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command); checkReplyPacket(reply, "ReferenceType::Signature command"); String signature = reply.getNextValueAsString(); return signature; } /** * Returns classID for the selected jni signature * * @param signature * @return classID for the selected jni signature */ 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); checkReplyPacket(reply, "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; } /** * Returns reference type ID. * * @param signature * @return type ID for the selected jni signature */ protected long getReferenceTypeID(String signature) { CommandPacket packet = new CommandPacket( JDWPCommands.VirtualMachineCommandSet.CommandSetID, JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand); packet.setNextValueAsString(signature); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); checkReplyPacket(reply, "VirtualMachine::ClassesBySignature command"); int classes = reply.getNextValueAsInt(); // this class may be loaded only once assertEquals("Invalid number of classes for reference type: " + signature + ",", 1, classes); byte refTypeTag = reply.getNextValueAsByte(); long classID = reply.getNextValueAsReferenceTypeID(); int status = reply.getNextValueAsInt(); logWriter.println("VirtualMachine.ClassesBySignature: classes=" + classes + " refTypeTag=" + refTypeTag + " typeID= " + classID + " status=" + status); assertAllDataRead(reply); assertEquals("", JDWPConstants.TypeTag.CLASS, refTypeTag); return classID; } /** * Helper function for resuming debuggee. */ protected void resumeDebuggee() { logWriter.println("=> Resume debuggee"); CommandPacket packet = new CommandPacket( JDWPCommands.VirtualMachineCommandSet.CommandSetID, JDWPCommands.VirtualMachineCommandSet.ResumeCommand); logWriter.println("Sending VirtualMachine::Resume command..."); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); checkReplyPacket(reply, "VirtualMachine::Resume command"); assertAllDataRead(reply); } /** * Performs string creation in debuggee. * * @param value - * content for new string * @return StringID of new created string */ protected long createString(String value) { CommandPacket packet = new CommandPacket( JDWPCommands.VirtualMachineCommandSet.CommandSetID, JDWPCommands.VirtualMachineCommandSet.CreateStringCommand); packet.setNextValueAsString(value); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); checkReplyPacket(reply, "VirtualMachine::CreateString command"); long stringID = reply.getNextValueAsStringID(); return stringID; } /** * Returns corresponding string from string ID. * * @param stringID - * string ID * @return string value */ protected String getStringValue(long stringID) { CommandPacket packet = new CommandPacket( JDWPCommands.StringReferenceCommandSet.CommandSetID, JDWPCommands.StringReferenceCommandSet.ValueCommand); packet.setNextValueAsObjectID(stringID); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); checkReplyPacket(reply, "StringReference::Value command"); String returnedTestString = reply.getNextValueAsString(); return returnedTestString; } /** * Multiple field verification routine. * * @param refTypeID - * reference type ID * @param checkedFieldNames - * list of field names to be checked * @return list of field IDs */ protected long[] checkFields(long refTypeID, String checkedFieldNames[]) { return checkFields(refTypeID, checkedFieldNames, null, null); } /** * Single field verification routine. * * @param refTypeID - * reference type ID * @param fieldName - * name of single field * @return filed ID */ protected long checkField(long refTypeID, String fieldName) { return checkFields(refTypeID, new String[] { fieldName }, null, null)[0]; } /** * Multiple field verification routine. * * @param refTypeID - * reference type ID * @param checkedFieldNames - * list of field names to be checked * @param expectedSignatures - * list of expected field signatures * @param expectedModifiers - * list of expected field modifiers * @return list of field IDs */ protected long[] checkFields(long refTypeID, String checkedFieldNames[], String expectedSignatures[], int expectedModifiers[]) { boolean checkedFieldFound[] = new boolean[checkedFieldNames.length]; long checkedFieldIDs[] = new long[checkedFieldNames.length]; logWriter .println("=> Send ReferenceType::Fields command and get field ID(s)"); CommandPacket fieldsCommand = new CommandPacket( JDWPCommands.ReferenceTypeCommandSet.CommandSetID, JDWPCommands.ReferenceTypeCommandSet.FieldsCommand); fieldsCommand.setNextValueAsReferenceTypeID(refTypeID); ReplyPacket fieldsReply = debuggeeWrapper.vmMirror .performCommand(fieldsCommand); fieldsCommand = null; checkReplyPacket(fieldsReply, "ReferenceType::Fields command"); int returnedFieldsNumber = fieldsReply.getNextValueAsInt(); logWriter .println("=> Returned fields number = " + returnedFieldsNumber); int checkedFieldsNumber = checkedFieldNames.length; final int fieldSyntheticFlag = 0xf0000000; int nameDuplicated = 0; String fieldNameDuplicated = null; // <= collects all duplicated fields int nameMissing = 0; String fieldNameMissing = null; // <= collects all missed fields for (int i = 0; i < returnedFieldsNumber; i++) { long returnedFieldID = fieldsReply.getNextValueAsFieldID(); String returnedFieldName = fieldsReply.getNextValueAsString(); String returnedFieldSignature = fieldsReply.getNextValueAsString(); int returnedFieldModifiers = fieldsReply.getNextValueAsInt(); logWriter.println(""); logWriter.println("=> Field ID: " + returnedFieldID); logWriter.println("=> Field name: " + returnedFieldName); logWriter.println("=> Field signature: " + returnedFieldSignature); logWriter.println("=> Field modifiers: 0x" + Integer.toHexString(returnedFieldModifiers)); if ((returnedFieldModifiers & fieldSyntheticFlag) == fieldSyntheticFlag) { continue; // do not check synthetic fields } for (int k = 0; k < checkedFieldsNumber; k++) { if (!checkedFieldNames[k].equals(returnedFieldName)) { continue; } if (checkedFieldFound[k]) { logWriter.println(""); logWriter .println("## FAILURE: The field is found repeatedly in the list"); logWriter.println("## Field Name: " + returnedFieldName); logWriter.println("## Field ID: " + returnedFieldID); logWriter.println("## Field Signature: " + returnedFieldSignature); logWriter.println("## Field Modifiers: 0x" + Integer.toHexString(returnedFieldModifiers)); fieldNameDuplicated = (0 == nameDuplicated ? returnedFieldName : fieldNameDuplicated + "," + returnedFieldName); nameDuplicated++; break; } checkedFieldFound[k] = true; checkedFieldIDs[k] = returnedFieldID; if (null != expectedSignatures) { assertString( "Invalid field signature is returned for field:" + returnedFieldName + ",", expectedSignatures[k], returnedFieldSignature); } if (null != expectedModifiers) { assertEquals( "Invalid field modifiers are returned for field:" + returnedFieldName + ",", expectedModifiers[k], returnedFieldModifiers); } break; } } for (int k = 0; k < checkedFieldsNumber; k++) { if (!checkedFieldFound[k]) { logWriter.println(""); logWriter .println("\n## FAILURE: Expected field is NOT found in the list of retuned fields:"); logWriter.println("## Field name = " + checkedFieldNames[k]); fieldNameMissing = 0 == nameMissing ? checkedFieldNames[k] : fieldNameMissing + "," + checkedFieldNames[k]; nameMissing++; // break; } } // String thisTestName = this.getClass().getName(); // logWriter.println("==> " + thisTestName + " for " + thisCommandName + // ": FAILED"); if (nameDuplicated > 1) { fail("Duplicated fields are found in the retuned by FieldsCommand list: " + fieldNameDuplicated); } if (nameDuplicated > 0) { fail("Duplicated field is found in the retuned by FieldsCommand list: " + fieldNameDuplicated); } if (nameMissing > 1) { fail("Expected fields are NOT found in the retuned by FieldsCommand list: " + fieldNameMissing); } if (nameMissing > 0) { fail("Expected field is NOT found in the retuned by FieldsCommand list: " + fieldNameMissing); } logWriter.println(""); if (1 == checkedFieldsNumber) { logWriter .println("=> Expected field was found and field ID was got"); } else { logWriter .println("=> Expected fields were found and field IDs were got"); } assertAllDataRead(fieldsReply); return checkedFieldIDs; } /** * Helper for checking reply packet error code. Calls junit fail if packet * error code does not equal to expected error code. * * @param reply - * returned from debuggee packet * @param message - * additional message * @param errorCodeExpected - * array of expected error codes */ protected void checkReplyPacket(ReplyPacket reply, String message, int errorCodeExpected) { checkReplyPacket(reply, message, new int[] { errorCodeExpected }); } /** * Helper for checking reply packet error code. Calls junit fail if packet * error code does not equal NONE. * * @param reply - * returned from debuggee packet * @param message - * additional message */ protected void checkReplyPacket(ReplyPacket reply, String message) { checkReplyPacket(reply, message, JDWPConstants.Error.NONE); } /** * Helper for checking reply packet error code. Calls junit fail if packet * error code does not equal to expected error code. * * @param reply - * returned from debuggee packet * @param message - * additional message * @param expected - * array of expected error codes */ protected void checkReplyPacket(ReplyPacket reply, String message, int[] expected) { checkReplyPacket(reply, message, expected, true /* failSign */); } /** * Helper for checking reply packet error code. If reply packet does not * have error - returns true. Otherwise does not call junit fail - simply * prints error message and returns false. if packet error code does not * equal NONE. * * @param reply - * returned from debuggee packet * @param message - * additional message * @return true if error is not found, or false otherwise */ protected boolean checkReplyPacketWithoutFail(ReplyPacket reply, String message) { return checkReplyPacket(reply, message, new int[] { JDWPConstants.Error.NONE }, false /* failSign */); } /** * Helper for checking reply packet error code. If reply packet does not * have unexpected error - returns true. If reply packet has got unexpected * error: If failSign param = true - calls junit fail. Otherwise prints * message about error and returns false. * * @param reply - * returned from debuggee packet * @param message - * additional message * @param expected - * array of expected error codes * @param failSign - * defines to call junit fail or not * @return true if unexpected errors are not found, or false otherwise */ protected boolean checkReplyPacket(ReplyPacket reply, String message, int[] expected, boolean failSign) { // check reply code against expected int errorCode = reply.getErrorCode(); for (int i = 0; i < expected.length; i++) { if (reply.getErrorCode() == expected[i]) { return true; // OK } } // replay code validation failed // start error message composition if (null == message) { message = ""; } else { message = message + ", "; } // format error message if (expected.length == 1 && JDWPConstants.Error.NONE == expected[0]) { message = message + "Error Code:<" + errorCode + "(" + JDWPConstants.Error.getName(errorCode) + ")>"; } else { message = message + "Unexpected error code:<" + errorCode + "(" + JDWPConstants.Error.getName(errorCode) + ")>" + ", Expected error code" + (expected.length == 1 ? ":" : "s:"); for (int i = 0; i < expected.length; i++) { message = message + (i > 0 ? ",<" : "<") + expected[i] + "(" + JDWPConstants.Error.getName(expected[i]) + ")>"; } } if (failSign) { printErrorAndFail(message); } logWriter.printError(message); return false; } /** * Helper for comparison numbers and printing string equivalents. * * @param message - * user message * @param expected - * expected value * @param actual - * actual value * @param strExpected - * string equivalent of expected value * @param strActual - * string equivalent of actual value */ protected void assertEquals(String message, long expected, long actual, String strExpected, String strActual) { if (expected == actual) { return; // OK } if (null == message) { message = ""; } if (null == strExpected) { strExpected = expected + ""; } else { strExpected = expected + "(" + strExpected + ")"; } if (null == strActual) { strActual = actual + ""; } else { strActual = actual + "(" + strActual + ")"; } printErrorAndFail(message + " expected:<" + strExpected + "> but was:<" + strActual + ">"); } /** * Asserts that two strings are equal. * * @param message - * user message * @param expected - * expected string * @param actual - * actual string */ protected void assertString(String message, String expected, String actual) { if (null == expected) { expected = ""; } if (null == actual) { actual = ""; } if (expected.equals(actual)) { return; // OK } printErrorAndFail(message + " expected:<" + expected + "> but was:<" + actual + ">"); } /** * Helper for checking reply packet data has been read. * * @param reply - * reply packet from debuggee */ protected void assertAllDataRead(Packet reply) { if (reply.isAllDataRead()) { return; // OK } printErrorAndFail("Not all data has been read"); } /** * Prints error message in log writer and in junit fail. * * @param message - * error message */ protected void printErrorAndFail(String message) { logWriter.printError(message); fail(message); } /** * Helper for setting static int field in class with new value. * * @param classSignature - * String defining signature of class * @param fieldName - * String defining field name in specified class * @param newValue - * int value to set for specified field * @return true, if setting is successfully, or false otherwise */ protected boolean setStaticIntField(String classSignature, String fieldName, int newValue) { long classID = debuggeeWrapper.vmMirror.getClassID(classSignature); if (classID == -1) { logWriter .println("## setStaticIntField(): Can NOT get classID for class signature = '" + classSignature + "'"); return false; } long fieldID = debuggeeWrapper.vmMirror.getFieldID(classID, fieldName); if (fieldID == -1) { logWriter .println("## setStaticIntField(): Can NOT get fieldID for field = '" + fieldName + "'"); return false; } CommandPacket packet = new CommandPacket( JDWPCommands.ClassTypeCommandSet.CommandSetID, JDWPCommands.ClassTypeCommandSet.SetValuesCommand); packet.setNextValueAsReferenceTypeID(classID); packet.setNextValueAsInt(1); packet.setNextValueAsFieldID(fieldID); packet.setNextValueAsInt(newValue); ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); int errorCode = reply.getErrorCode(); if (errorCode != JDWPConstants.Error.NONE) { logWriter .println("## setStaticIntField(): Can NOT set value for field = '" + fieldName + "' in class = '" + classSignature + "'; ClassType.SetValues command reurns error = " + errorCode); return false; } return true; } /** * Removes breakpoint of the given event kind corresponding to the given * request id. * * @param eventKind * request event kind * @param requestID * request id * @param verbose * print or don't extra log info */ protected void clearEvent(byte eventKind, int requestID, boolean verbose) { CommandPacket packet = new CommandPacket( JDWPCommands.EventRequestCommandSet.CommandSetID, JDWPCommands.EventRequestCommandSet.ClearCommand); packet.setNextValueAsByte(eventKind); packet.setNextValueAsInt(requestID); if (verbose) { logWriter.println("Clearing event: " + JDWPConstants.EventKind.getName(eventKind) + ", id: " + requestID); } ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); checkReplyPacket(reply, "EventRequest::Clear command"); assertAllDataRead(reply); } }