/* * 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 ""; } @Override public Map getClassData(Client client) { return new JDWPClassDataRetriever(client).retrieve(); } private Map 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 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 classes = new HashMap(); Map arrayClasses = new HashMap(); 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 result = new HashMap(); // Parse all classes. for (Map.Entry 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 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 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 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); } }