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.hprof; 18 19import com.android.ddmlib.Client; 20import com.android.ddmlib.ClientData; 21import com.android.ddmlib.ClientData.IHprofDumpHandler; 22import com.android.preload.classdataretrieval.ClassDataRetriever; 23import com.android.preload.ui.NullProgressMonitor; 24import com.android.tools.perflib.captures.MemoryMappedFileBuffer; 25import com.android.tools.perflib.heap.ClassObj; 26import com.android.tools.perflib.heap.Queries; 27import com.android.tools.perflib.heap.Snapshot; 28 29import java.io.BufferedOutputStream; 30import java.io.File; 31import java.io.FileOutputStream; 32import java.util.HashMap; 33import java.util.Map; 34import java.util.Set; 35 36public class Hprof implements ClassDataRetriever { 37 38 private static GeneralHprofDumpHandler hprofHandler; 39 40 public static void init() { 41 synchronized(Hprof.class) { 42 if (hprofHandler == null) { 43 ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler()); 44 } 45 } 46 } 47 48 public static File doHprof(Client client, int timeout) { 49 GetHprof gh = new GetHprof(client, timeout); 50 return gh.get(); 51 } 52 53 /** 54 * Return a map of class names to class-loader names derived from the hprof dump. 55 * 56 * @param hprofLocalFile 57 */ 58 public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception { 59 Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile)); 60 61 Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null); 62 Map<String, String> retValue = new HashMap<String, String>(); 63 for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) { 64 for (ClassObj c : e.getValue()) { 65 String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString(); 66 String cName = c.getClassName(); 67 int aDepth = 0; 68 while (cName.endsWith("[]")) { 69 cName = cName.substring(0, cName.length()-2); 70 aDepth++; 71 } 72 String newName = transformPrimitiveClass(cName); 73 if (aDepth > 0) { 74 // Need to use kind-a descriptor syntax. If it was transformed, it is primitive. 75 if (newName.equals(cName)) { 76 newName = "L" + newName + ";"; 77 } 78 for (int i = 0; i < aDepth; i++) { 79 newName = "[" + newName; 80 } 81 } 82 retValue.put(newName, cl); 83 } 84 } 85 86 // Free up memory. 87 snapshot.dispose(); 88 89 return retValue; 90 } 91 92 private static Map<String, String> primitiveMapping; 93 94 static { 95 primitiveMapping = new HashMap<>(); 96 primitiveMapping.put("boolean", "Z"); 97 primitiveMapping.put("byte", "B"); 98 primitiveMapping.put("char", "C"); 99 primitiveMapping.put("double", "D"); 100 primitiveMapping.put("float", "F"); 101 primitiveMapping.put("int", "I"); 102 primitiveMapping.put("long", "J"); 103 primitiveMapping.put("short", "S"); 104 primitiveMapping.put("void", "V"); 105 } 106 107 private static String transformPrimitiveClass(String name) { 108 String rep = primitiveMapping.get(name); 109 if (rep != null) { 110 return rep; 111 } 112 return name; 113 } 114 115 private static class GetHprof implements IHprofDumpHandler { 116 117 private File target; 118 private long timeout; 119 private Client client; 120 121 public GetHprof(Client client, long timeout) { 122 this.client = client; 123 this.timeout = timeout; 124 } 125 126 public File get() { 127 synchronized (this) { 128 hprofHandler.addHandler(this); 129 client.dumpHprof(); 130 if (target == null) { 131 try { 132 wait(timeout); 133 } catch (Exception e) { 134 System.out.println(e); 135 } 136 } 137 } 138 139 hprofHandler.removeHandler(this); 140 return target; 141 } 142 143 private void wakeUp() { 144 synchronized (this) { 145 notifyAll(); 146 } 147 } 148 149 @Override 150 public void onEndFailure(Client arg0, String arg1) { 151 System.out.println("GetHprof.onEndFailure"); 152 if (client == arg0) { 153 wakeUp(); 154 } 155 } 156 157 private static File createTargetFile() { 158 try { 159 return File.createTempFile("ddms", ".hprof"); 160 } catch (Exception e) { 161 throw new RuntimeException(e); 162 } 163 } 164 165 @Override 166 public void onSuccess(String arg0, Client arg1) { 167 System.out.println("GetHprof.onSuccess"); 168 if (client == arg1) { 169 try { 170 target = createTargetFile(); 171 arg1.getDevice().getSyncService().pullFile(arg0, 172 target.getAbsoluteFile().toString(), new NullProgressMonitor()); 173 } catch (Exception e) { 174 if (target != null) { 175 target.delete(); 176 } 177 e.printStackTrace(); 178 target = null; 179 } 180 wakeUp(); 181 } 182 } 183 184 @Override 185 public void onSuccess(byte[] arg0, Client arg1) { 186 System.out.println("GetHprof.onSuccess"); 187 if (client == arg1) { 188 try { 189 target = createTargetFile(); 190 BufferedOutputStream out = 191 new BufferedOutputStream(new FileOutputStream(target)); 192 out.write(arg0); 193 out.close(); 194 } catch (Exception e) { 195 if (target != null) { 196 target.delete(); 197 } 198 e.printStackTrace(); 199 target = null; 200 } 201 wakeUp(); 202 } 203 } 204 } 205 206 private int timeout; 207 208 public Hprof(int timeout) { 209 this.timeout = timeout; 210 } 211 212 @Override 213 public Map<String, String> getClassData(Client client) { 214 File hprofLocalFile = Hprof.doHprof(client, timeout); 215 if (hprofLocalFile == null) { 216 throw new RuntimeException("Failed getting dump..."); 217 } 218 System.out.println("Dump file is " + hprofLocalFile); 219 220 try { 221 return analyzeHprof(hprofLocalFile); 222 } catch (Exception e) { 223 throw new RuntimeException(e); 224 } finally { 225 hprofLocalFile.delete(); 226 } 227 } 228} 229