blob: dbd4c89b27cf249548e01f04b7ffe1d6f9ab1051 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.preload.classdataretrieval.jdwp;
import com.android.ddmlib.Client;
import com.android.preload.classdataretrieval.ClassDataRetriever;
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.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.JPDALogWriter;
import org.apache.harmony.jpda.tests.share.JPDATestOptions;
import java.util.HashMap;
import java.util.Map;
public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
private final Client client;
public JDWPClassDataRetriever() {
this(null);
}
public JDWPClassDataRetriever(Client client) {
this.client = client;
}
@Override
protected String getDebuggeeClassName() {
return "<unset>";
}
@Override
public Map<String, String> getClassData(Client client) {
return new JDWPClassDataRetriever(client).retrieve();
}
private Map<String, String> retrieve() {
if (client == null) {
throw new IllegalStateException();
}
settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
settings.setDebuggeeSuspend("n");
logWriter = new JPDALogWriter(System.out, "", false);
try {
internalSetUp();
return retrieveImpl();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
internalTearDown();
}
}
private Map<String, String> retrieveImpl() {
try {
// Suspend the app.
{
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return null;
}
}
// List all classes.
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return null;
}
int classCount = reply.getNextValueAsInt();
System.out.println("Runtime reported " + classCount + " classes.");
Map<Long, String> classes = new HashMap<Long, String>();
Map<Long, String> arrayClasses = new HashMap<Long, String>();
for (int i = 0; i < classCount; i++) {
byte refTypeTag = reply.getNextValueAsByte();
long typeID = reply.getNextValueAsReferenceTypeID();
String signature = reply.getNextValueAsString();
/* int status = */ reply.getNextValueAsInt();
switch (refTypeTag) {
case JDWPConstants.TypeTag.CLASS:
case JDWPConstants.TypeTag.INTERFACE:
classes.put(typeID, signature);
break;
case JDWPConstants.TypeTag.ARRAY:
arrayClasses.put(typeID, signature);
break;
}
}
Map<String, String> result = new HashMap<String, String>();
// Parse all classes.
for (Map.Entry<Long, String> entry : classes.entrySet()) {
long typeID = entry.getKey();
String signature = entry.getValue();
if (!checkClass(typeID, signature, result)) {
System.err.println("Issue investigating " + signature);
}
}
// For arrays, look at the leaf component type.
for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
long typeID = entry.getKey();
String signature = entry.getValue();
if (!checkArrayClass(typeID, signature, result)) {
System.err.println("Issue investigating " + signature);
}
}
return result;
} finally {
// Resume the app.
{
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
/* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
}
}
}
private boolean checkClass(long typeID, String signature, Map<String, String> result) {
CommandPacket packet = new CommandPacket(
JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
packet.setNextValueAsReferenceTypeID(typeID);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return false;
}
long classLoaderID = reply.getNextValueAsObjectID();
// TODO: Investigate the classloader to have a better string?
String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
result.put(getClassName(signature), classLoaderString);
return true;
}
private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
// Classloaders of array classes are the same as the component class'.
CommandPacket packet = new CommandPacket(
JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
packet.setNextValueAsReferenceTypeID(typeID);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return false;
}
long classLoaderID = reply.getNextValueAsObjectID();
// TODO: Investigate the classloader to have a better string?
String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
// For array classes, we *need* the signature directly.
result.put(signature, classLoaderString);
return true;
}
private static String getClassName(String signature) {
String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
return withoutLAndSemicolon.replace('/', '.');
}
private static JPDATestOptions createTestOptions(String address) {
JPDATestOptions options = new JPDATestOptions();
options.setAttachConnectorKind();
options.setTimeout(1000);
options.setWaitingTime(1000);
options.setTransportAddress(address);
return options;
}
@Override
protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
return new PreloadDebugeeWrapper(settings, logWriter);
}
}