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 17package com.android.preload.classdataretrieval.jdwp; 18 19import com.android.ddmlib.Client; 20import com.android.preload.classdataretrieval.ClassDataRetriever; 21 22import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; 23import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands; 24import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; 25import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; 26import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase; 27import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper; 28import org.apache.harmony.jpda.tests.share.JPDALogWriter; 29import org.apache.harmony.jpda.tests.share.JPDATestOptions; 30 31import java.util.HashMap; 32import java.util.Map; 33 34public 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