1/* 2 * Copyright (C) 2009 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.ddmlib; 18 19import com.android.ddmlib.ClientData.IMethodProfilingHandler; 20import com.android.ddmlib.ClientData.MethodProfilingStatus; 21 22import java.io.IOException; 23import java.nio.ByteBuffer; 24 25/** 26 * Handle heap status updates. 27 */ 28final class HandleProfiling extends ChunkHandler { 29 30 public static final int CHUNK_MPRS = type("MPRS"); 31 public static final int CHUNK_MPRE = type("MPRE"); 32 public static final int CHUNK_MPSS = type("MPSS"); 33 public static final int CHUNK_MPSE = type("MPSE"); 34 public static final int CHUNK_MPRQ = type("MPRQ"); 35 public static final int CHUNK_FAIL = type("FAIL"); 36 37 private static final HandleProfiling mInst = new HandleProfiling(); 38 39 private HandleProfiling() {} 40 41 /** 42 * Register for the packets we expect to get from the client. 43 */ 44 public static void register(MonitorThread mt) { 45 mt.registerChunkHandler(CHUNK_MPRE, mInst); 46 mt.registerChunkHandler(CHUNK_MPSE, mInst); 47 mt.registerChunkHandler(CHUNK_MPRQ, mInst); 48 } 49 50 /** 51 * Client is ready. 52 */ 53 @Override 54 public void clientReady(Client client) throws IOException {} 55 56 /** 57 * Client went away. 58 */ 59 @Override 60 public void clientDisconnected(Client client) {} 61 62 /** 63 * Chunk handler entry point. 64 */ 65 @Override 66 public void handleChunk(Client client, int type, ByteBuffer data, 67 boolean isReply, int msgId) { 68 69 Log.d("ddm-prof", "handling " + ChunkHandler.name(type)); 70 71 if (type == CHUNK_MPRE) { 72 handleMPRE(client, data); 73 } else if (type == CHUNK_MPSE) { 74 handleMPSE(client, data); 75 } else if (type == CHUNK_MPRQ) { 76 handleMPRQ(client, data); 77 } else if (type == CHUNK_FAIL) { 78 handleFAIL(client, data); 79 } else { 80 handleUnknownChunk(client, type, data, isReply, msgId); 81 } 82 } 83 84 /** 85 * Send a MPRS (Method PRofiling Start) request to the client. 86 * 87 * The arguments to this method will eventually be passed to 88 * android.os.Debug.startMethodTracing() on the device. 89 * 90 * @param fileName is the name of the file to which profiling data 91 * will be written (on the device); it will have {@link DdmConstants#DOT_TRACE} 92 * appended if necessary 93 * @param bufferSize is the desired buffer size in bytes (8MB is good) 94 * @param flags see startMethodTracing() docs; use 0 for default behavior 95 */ 96 public static void sendMPRS(Client client, String fileName, int bufferSize, 97 int flags) throws IOException { 98 99 ByteBuffer rawBuf = allocBuffer(3*4 + fileName.length() * 2); 100 JdwpPacket packet = new JdwpPacket(rawBuf); 101 ByteBuffer buf = getChunkDataBuf(rawBuf); 102 103 buf.putInt(bufferSize); 104 buf.putInt(flags); 105 buf.putInt(fileName.length()); 106 putString(buf, fileName); 107 108 finishChunkPacket(packet, CHUNK_MPRS, buf.position()); 109 Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName 110 + "', size=" + bufferSize + ", flags=" + flags); 111 client.sendAndConsume(packet, mInst); 112 113 // record the filename we asked for. 114 client.getClientData().setPendingMethodProfiling(fileName); 115 116 // send a status query. this ensure that the status is properly updated if for some 117 // reason starting the tracing failed. 118 sendMPRQ(client); 119 } 120 121 /** 122 * Send a MPRE (Method PRofiling End) request to the client. 123 */ 124 public static void sendMPRE(Client client) throws IOException { 125 ByteBuffer rawBuf = allocBuffer(0); 126 JdwpPacket packet = new JdwpPacket(rawBuf); 127 ByteBuffer buf = getChunkDataBuf(rawBuf); 128 129 // no data 130 131 finishChunkPacket(packet, CHUNK_MPRE, buf.position()); 132 Log.d("ddm-prof", "Sending " + name(CHUNK_MPRE)); 133 client.sendAndConsume(packet, mInst); 134 } 135 136 /** 137 * Handle notification that method profiling has finished writing 138 * data to disk. 139 */ 140 private void handleMPRE(Client client, ByteBuffer data) { 141 byte result; 142 143 // get the filename and make the client not have pending HPROF dump anymore. 144 String filename = client.getClientData().getPendingMethodProfiling(); 145 client.getClientData().setPendingMethodProfiling(null); 146 147 result = data.get(); 148 149 // get the app-level handler for method tracing dump 150 IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); 151 if (handler != null) { 152 if (result == 0) { 153 handler.onSuccess(filename, client); 154 155 Log.d("ddm-prof", "Method profiling has finished"); 156 } else { 157 handler.onEndFailure(client, null /*message*/); 158 159 Log.w("ddm-prof", "Method profiling has failed (check device log)"); 160 } 161 } 162 163 client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); 164 client.update(Client.CHANGE_METHOD_PROFILING_STATUS); 165 } 166 167 /** 168 * Send a MPSS (Method Profiling Streaming Start) request to the client. 169 * 170 * The arguments to this method will eventually be passed to 171 * android.os.Debug.startMethodTracing() on the device. 172 * 173 * @param bufferSize is the desired buffer size in bytes (8MB is good) 174 * @param flags see startMethodTracing() docs; use 0 for default behavior 175 */ 176 public static void sendMPSS(Client client, int bufferSize, 177 int flags) throws IOException { 178 179 ByteBuffer rawBuf = allocBuffer(2*4); 180 JdwpPacket packet = new JdwpPacket(rawBuf); 181 ByteBuffer buf = getChunkDataBuf(rawBuf); 182 183 buf.putInt(bufferSize); 184 buf.putInt(flags); 185 186 finishChunkPacket(packet, CHUNK_MPSS, buf.position()); 187 Log.d("ddm-prof", "Sending " + name(CHUNK_MPSS) 188 + "', size=" + bufferSize + ", flags=" + flags); 189 client.sendAndConsume(packet, mInst); 190 191 // send a status query. this ensure that the status is properly updated if for some 192 // reason starting the tracing failed. 193 sendMPRQ(client); 194 } 195 196 /** 197 * Send a MPSE (Method Profiling Streaming End) request to the client. 198 */ 199 public static void sendMPSE(Client client) throws IOException { 200 ByteBuffer rawBuf = allocBuffer(0); 201 JdwpPacket packet = new JdwpPacket(rawBuf); 202 ByteBuffer buf = getChunkDataBuf(rawBuf); 203 204 // no data 205 206 finishChunkPacket(packet, CHUNK_MPSE, buf.position()); 207 Log.d("ddm-prof", "Sending " + name(CHUNK_MPSE)); 208 client.sendAndConsume(packet, mInst); 209 } 210 211 /** 212 * Handle incoming profiling data. The MPSE packet includes the 213 * complete .trace file. 214 */ 215 private void handleMPSE(Client client, ByteBuffer data) { 216 IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); 217 if (handler != null) { 218 byte[] stuff = new byte[data.capacity()]; 219 data.get(stuff, 0, stuff.length); 220 221 Log.d("ddm-prof", "got trace file, size: " + stuff.length + " bytes"); 222 223 handler.onSuccess(stuff, client); 224 } 225 226 client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); 227 client.update(Client.CHANGE_METHOD_PROFILING_STATUS); 228 } 229 230 /** 231 * Send a MPRQ (Method PRofiling Query) request to the client. 232 */ 233 public static void sendMPRQ(Client client) throws IOException { 234 ByteBuffer rawBuf = allocBuffer(0); 235 JdwpPacket packet = new JdwpPacket(rawBuf); 236 ByteBuffer buf = getChunkDataBuf(rawBuf); 237 238 // no data 239 240 finishChunkPacket(packet, CHUNK_MPRQ, buf.position()); 241 Log.d("ddm-prof", "Sending " + name(CHUNK_MPRQ)); 242 client.sendAndConsume(packet, mInst); 243 } 244 245 /** 246 * Receive response to query. 247 */ 248 private void handleMPRQ(Client client, ByteBuffer data) { 249 byte result; 250 251 result = data.get(); 252 253 if (result == 0) { 254 client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); 255 Log.d("ddm-prof", "Method profiling is not running"); 256 } else { 257 client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.ON); 258 Log.d("ddm-prof", "Method profiling is running"); 259 } 260 client.update(Client.CHANGE_METHOD_PROFILING_STATUS); 261 } 262 263 private void handleFAIL(Client client, ByteBuffer data) { 264 /*int errorCode =*/ data.getInt(); 265 int length = data.getInt() * 2; 266 String message = null; 267 if (length > 0) { 268 byte[] messageBuffer = new byte[length]; 269 data.get(messageBuffer, 0, length); 270 message = new String(messageBuffer); 271 } 272 273 // this can be sent if 274 // - MPRS failed (like wrong permission) 275 // - MPSE failed for whatever reason 276 277 String filename = client.getClientData().getPendingMethodProfiling(); 278 if (filename != null) { 279 // reset the pending file. 280 client.getClientData().setPendingMethodProfiling(null); 281 282 // and notify of failure 283 IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); 284 if (handler != null) { 285 handler.onStartFailure(client, message); 286 } 287 } else { 288 // this is MPRE 289 // notify of failure 290 IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); 291 if (handler != null) { 292 handler.onEndFailure(client, message); 293 } 294 } 295 296 // send a query to know the current status 297 try { 298 sendMPRQ(client); 299 } catch (IOException e) { 300 Log.e("HandleProfiling", e); 301 } 302 } 303} 304 305