Home | History | Annotate | Download | only in jdwp
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.preload.classdataretrieval.jdwp;
     18 
     19 import com.android.ddmlib.Client;
     20 import com.android.preload.classdataretrieval.ClassDataRetriever;
     21 
     22 import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
     23 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
     24 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
     25 import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
     26 import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
     27 import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
     28 import org.apache.harmony.jpda.tests.share.JPDALogWriter;
     29 import org.apache.harmony.jpda.tests.share.JPDATestOptions;
     30 
     31 import java.util.HashMap;
     32 import java.util.Map;
     33 
     34 public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
     35 
     36     private final Client client;
     37 
     38     public JDWPClassDataRetriever() {
     39         this(null);
     40     }
     41 
     42     public JDWPClassDataRetriever(Client client) {
     43         this.client = client;
     44     }
     45 
     46 
     47     @Override
     48     protected String getDebuggeeClassName() {
     49         return "<unset>";
     50     }
     51 
     52     @Override
     53     public Map<String, String> getClassData(Client client) {
     54         return new JDWPClassDataRetriever(client).retrieve();
     55     }
     56 
     57     private Map<String, String> retrieve() {
     58         if (client == null) {
     59             throw new IllegalStateException();
     60         }
     61 
     62         settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
     63         settings.setDebuggeeSuspend("n");
     64 
     65         logWriter = new JPDALogWriter(System.out, "", false);
     66 
     67         try {
     68             internalSetUp();
     69 
     70             return retrieveImpl();
     71         } catch (Exception e) {
     72             e.printStackTrace();
     73             return null;
     74         } finally {
     75             internalTearDown();
     76         }
     77     }
     78 
     79     private Map<String, String> retrieveImpl() {
     80         try {
     81             // Suspend the app.
     82             {
     83                 CommandPacket packet = new CommandPacket(
     84                         JDWPCommands.VirtualMachineCommandSet.CommandSetID,
     85                         JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
     86                 ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
     87                 if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
     88                     return null;
     89                 }
     90             }
     91 
     92             // List all classes.
     93             CommandPacket packet = new CommandPacket(
     94                     JDWPCommands.VirtualMachineCommandSet.CommandSetID,
     95                     JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
     96             ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
     97 
     98             if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
     99                 return null;
    100             }
    101 
    102             int classCount = reply.getNextValueAsInt();
    103             System.out.println("Runtime reported " + classCount + " classes.");
    104 
    105             Map<Long, String> classes = new HashMap<Long, String>();
    106             Map<Long, String> arrayClasses = new HashMap<Long, String>();
    107 
    108             for (int i = 0; i < classCount; i++) {
    109                 byte refTypeTag = reply.getNextValueAsByte();
    110                 long typeID = reply.getNextValueAsReferenceTypeID();
    111                 String signature = reply.getNextValueAsString();
    112                 /* int status = */ reply.getNextValueAsInt();
    113 
    114                 switch (refTypeTag) {
    115                     case JDWPConstants.TypeTag.CLASS:
    116                     case JDWPConstants.TypeTag.INTERFACE:
    117                         classes.put(typeID, signature);
    118                         break;
    119 
    120                     case JDWPConstants.TypeTag.ARRAY:
    121                         arrayClasses.put(typeID, signature);
    122                         break;
    123                 }
    124             }
    125 
    126             Map<String, String> result = new HashMap<String, String>();
    127 
    128             // Parse all classes.
    129             for (Map.Entry<Long, String> entry : classes.entrySet()) {
    130                 long typeID = entry.getKey();
    131                 String signature = entry.getValue();
    132 
    133                 if (!checkClass(typeID, signature, result)) {
    134                     System.err.println("Issue investigating " + signature);
    135                 }
    136             }
    137 
    138             // For arrays, look at the leaf component type.
    139             for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
    140                 long typeID = entry.getKey();
    141                 String signature = entry.getValue();
    142 
    143                 if (!checkArrayClass(typeID, signature, result)) {
    144                     System.err.println("Issue investigating " + signature);
    145                 }
    146             }
    147 
    148             return result;
    149         } finally {
    150             // Resume the app.
    151             {
    152                 CommandPacket packet = new CommandPacket(
    153                         JDWPCommands.VirtualMachineCommandSet.CommandSetID,
    154                         JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
    155                 /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
    156             }
    157         }
    158     }
    159 
    160     private boolean checkClass(long typeID, String signature, Map<String, String> result) {
    161         CommandPacket packet = new CommandPacket(
    162                 JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
    163                 JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
    164         packet.setNextValueAsReferenceTypeID(typeID);
    165         ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
    166         if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
    167             return false;
    168         }
    169 
    170         long classLoaderID = reply.getNextValueAsObjectID();
    171 
    172         // TODO: Investigate the classloader to have a better string?
    173         String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
    174 
    175         result.put(getClassName(signature), classLoaderString);
    176 
    177         return true;
    178     }
    179 
    180     private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
    181         // Classloaders of array classes are the same as the component class'.
    182         CommandPacket packet = new CommandPacket(
    183                 JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
    184                 JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
    185         packet.setNextValueAsReferenceTypeID(typeID);
    186         ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
    187         if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
    188             return false;
    189         }
    190 
    191         long classLoaderID = reply.getNextValueAsObjectID();
    192 
    193         // TODO: Investigate the classloader to have a better string?
    194         String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
    195 
    196         // For array classes, we *need* the signature directly.
    197         result.put(signature, classLoaderString);
    198 
    199         return true;
    200     }
    201 
    202     private static String getClassName(String signature) {
    203         String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
    204         return withoutLAndSemicolon.replace('/', '.');
    205     }
    206 
    207 
    208     private static JPDATestOptions createTestOptions(String address) {
    209         JPDATestOptions options = new JPDATestOptions();
    210         options.setAttachConnectorKind();
    211         options.setTimeout(1000);
    212         options.setWaitingTime(1000);
    213         options.setTransportAddress(address);
    214         return options;
    215     }
    216 
    217     @Override
    218     protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
    219         return new PreloadDebugeeWrapper(settings, logWriter);
    220     }
    221 }
    222