| /* |
| * Copyright (C) 2009 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.ddmlib; |
| |
| import com.android.ddmlib.ClientData.IMethodProfilingHandler; |
| import com.android.ddmlib.ClientData.MethodProfilingStatus; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Handle heap status updates. |
| */ |
| final class HandleProfiling extends ChunkHandler { |
| |
| public static final int CHUNK_MPRS = type("MPRS"); |
| public static final int CHUNK_MPRE = type("MPRE"); |
| public static final int CHUNK_MPSS = type("MPSS"); |
| public static final int CHUNK_MPSE = type("MPSE"); |
| public static final int CHUNK_SPSS = type("SPSS"); |
| public static final int CHUNK_SPSE = type("SPSE"); |
| public static final int CHUNK_MPRQ = type("MPRQ"); |
| public static final int CHUNK_FAIL = type("FAIL"); |
| |
| private static final HandleProfiling mInst = new HandleProfiling(); |
| |
| private HandleProfiling() {} |
| |
| /** |
| * Register for the packets we expect to get from the client. |
| */ |
| public static void register(MonitorThread mt) { |
| mt.registerChunkHandler(CHUNK_MPRE, mInst); |
| mt.registerChunkHandler(CHUNK_MPSE, mInst); |
| mt.registerChunkHandler(CHUNK_MPRQ, mInst); |
| } |
| |
| /** |
| * Client is ready. |
| */ |
| @Override |
| public void clientReady(Client client) throws IOException {} |
| |
| /** |
| * Client went away. |
| */ |
| @Override |
| public void clientDisconnected(Client client) {} |
| |
| /** |
| * Chunk handler entry point. |
| */ |
| @Override |
| public void handleChunk(Client client, int type, ByteBuffer data, |
| boolean isReply, int msgId) { |
| |
| Log.d("ddm-prof", "handling " + ChunkHandler.name(type)); |
| |
| if (type == CHUNK_MPRE) { |
| handleMPRE(client, data); |
| } else if (type == CHUNK_MPSE) { |
| handleMPSE(client, data); |
| } else if (type == CHUNK_MPRQ) { |
| handleMPRQ(client, data); |
| } else if (type == CHUNK_FAIL) { |
| handleFAIL(client, data); |
| } else { |
| handleUnknownChunk(client, type, data, isReply, msgId); |
| } |
| } |
| |
| /** |
| * Send a MPRS (Method PRofiling Start) request to the client. |
| * |
| * The arguments to this method will eventually be passed to |
| * android.os.Debug.startMethodTracing() on the device. |
| * |
| * @param fileName is the name of the file to which profiling data |
| * will be written (on the device); it will have {@link DdmConstants#DOT_TRACE} |
| * appended if necessary |
| * @param bufferSize is the desired buffer size in bytes (8MB is good) |
| * @param flags see startMethodTracing() docs; use 0 for default behavior |
| */ |
| public static void sendMPRS(Client client, String fileName, int bufferSize, |
| int flags) throws IOException { |
| |
| ByteBuffer rawBuf = allocBuffer(3*4 + fileName.length() * 2); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| buf.putInt(bufferSize); |
| buf.putInt(flags); |
| buf.putInt(fileName.length()); |
| ByteBufferUtil.putString(buf, fileName); |
| |
| finishChunkPacket(packet, CHUNK_MPRS, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName |
| + "', size=" + bufferSize + ", flags=" + flags); |
| client.sendAndConsume(packet, mInst); |
| |
| // record the filename we asked for. |
| client.getClientData().setPendingMethodProfiling(fileName); |
| |
| // send a status query. this ensure that the status is properly updated if for some |
| // reason starting the tracing failed. |
| sendMPRQ(client); |
| } |
| |
| /** |
| * Send a MPRE (Method PRofiling End) request to the client. |
| */ |
| public static void sendMPRE(Client client) throws IOException { |
| ByteBuffer rawBuf = allocBuffer(0); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| // no data |
| |
| finishChunkPacket(packet, CHUNK_MPRE, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_MPRE)); |
| client.sendAndConsume(packet, mInst); |
| } |
| |
| /** |
| * Handle notification that method profiling has finished writing |
| * data to disk. |
| */ |
| private void handleMPRE(Client client, ByteBuffer data) { |
| byte result; |
| |
| // get the filename and make the client not have pending HPROF dump anymore. |
| String filename = client.getClientData().getPendingMethodProfiling(); |
| client.getClientData().setPendingMethodProfiling(null); |
| |
| result = data.get(); |
| |
| // get the app-level handler for method tracing dump |
| IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); |
| if (handler != null) { |
| if (result == 0) { |
| handler.onSuccess(filename, client); |
| |
| Log.d("ddm-prof", "Method profiling has finished"); |
| } else { |
| handler.onEndFailure(client, null /*message*/); |
| |
| Log.w("ddm-prof", "Method profiling has failed (check device log)"); |
| } |
| } |
| |
| client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); |
| client.update(Client.CHANGE_METHOD_PROFILING_STATUS); |
| } |
| |
| /** |
| * Send a MPSS (Method Profiling Streaming Start) request to the client. |
| * |
| * The arguments to this method will eventually be passed to |
| * android.os.Debug.startMethodTracing() on the device. |
| * |
| * @param bufferSize is the desired buffer size in bytes (8MB is good) |
| * @param flags see startMethodTracing() docs; use 0 for default behavior |
| */ |
| public static void sendMPSS(Client client, int bufferSize, |
| int flags) throws IOException { |
| |
| ByteBuffer rawBuf = allocBuffer(2*4); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| buf.putInt(bufferSize); |
| buf.putInt(flags); |
| |
| finishChunkPacket(packet, CHUNK_MPSS, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_MPSS) |
| + "', size=" + bufferSize + ", flags=" + flags); |
| client.sendAndConsume(packet, mInst); |
| |
| // send a status query. this ensure that the status is properly updated if for some |
| // reason starting the tracing failed. |
| sendMPRQ(client); |
| } |
| |
| /** |
| * Send a SPSS (Sampling Profiling Streaming Start) request to the client. |
| * |
| * @param bufferSize is the desired buffer size in bytes (8MB is good) |
| * @param samplingInterval sampling interval |
| * @param samplingIntervalTimeUnits units for sampling interval |
| */ |
| public static void sendSPSS(Client client, int bufferSize, int samplingInterval, |
| TimeUnit samplingIntervalTimeUnits) throws IOException { |
| int interval = (int) samplingIntervalTimeUnits.toMicros(samplingInterval); |
| |
| ByteBuffer rawBuf = allocBuffer(3*4); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| buf.putInt(bufferSize); |
| buf.putInt(0); // flags |
| buf.putInt(interval); |
| |
| finishChunkPacket(packet, CHUNK_SPSS, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_SPSS) |
| + "', size=" + bufferSize + ", flags=0, samplingInterval=" + interval); |
| client.sendAndConsume(packet, mInst); |
| |
| // send a status query. this ensure that the status is properly updated if for some |
| // reason starting the tracing failed. |
| sendMPRQ(client); |
| } |
| |
| /** |
| * Send a MPSE (Method Profiling Streaming End) request to the client. |
| */ |
| public static void sendMPSE(Client client) throws IOException { |
| ByteBuffer rawBuf = allocBuffer(0); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| // no data |
| |
| finishChunkPacket(packet, CHUNK_MPSE, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_MPSE)); |
| client.sendAndConsume(packet, mInst); |
| } |
| |
| /** |
| * Send a SPSE (Sampling Profiling Streaming End) request to the client. |
| */ |
| public static void sendSPSE(Client client) throws IOException { |
| ByteBuffer rawBuf = allocBuffer(0); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| // no data |
| |
| finishChunkPacket(packet, CHUNK_SPSE, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_SPSE)); |
| client.sendAndConsume(packet, mInst); |
| } |
| |
| /** |
| * Handle incoming profiling data. The MPSE packet includes the |
| * complete .trace file. |
| */ |
| private void handleMPSE(Client client, ByteBuffer data) { |
| IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); |
| if (handler != null) { |
| byte[] stuff = new byte[data.capacity()]; |
| data.get(stuff, 0, stuff.length); |
| |
| Log.d("ddm-prof", "got trace file, size: " + stuff.length + " bytes"); |
| |
| handler.onSuccess(stuff, client); |
| } |
| |
| client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); |
| client.update(Client.CHANGE_METHOD_PROFILING_STATUS); |
| } |
| |
| /** |
| * Send a MPRQ (Method PRofiling Query) request to the client. |
| */ |
| public static void sendMPRQ(Client client) throws IOException { |
| ByteBuffer rawBuf = allocBuffer(0); |
| JdwpPacket packet = new JdwpPacket(rawBuf); |
| ByteBuffer buf = getChunkDataBuf(rawBuf); |
| |
| // no data |
| |
| finishChunkPacket(packet, CHUNK_MPRQ, buf.position()); |
| Log.d("ddm-prof", "Sending " + name(CHUNK_MPRQ)); |
| client.sendAndConsume(packet, mInst); |
| } |
| |
| /** |
| * Receive response to query. |
| */ |
| private void handleMPRQ(Client client, ByteBuffer data) { |
| byte result; |
| |
| result = data.get(); |
| |
| if (result == 0) { |
| client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); |
| Log.d("ddm-prof", "Method profiling is not running"); |
| } else if (result == 1) { |
| client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.TRACER_ON); |
| Log.d("ddm-prof", "Method tracing is active"); |
| } else if (result == 2) { |
| client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.SAMPLER_ON); |
| Log.d("ddm-prof", "Sampler based profiling is active"); |
| } |
| client.update(Client.CHANGE_METHOD_PROFILING_STATUS); |
| } |
| |
| private void handleFAIL(Client client, ByteBuffer data) { |
| /*int errorCode =*/ data.getInt(); |
| int length = data.getInt() * 2; |
| String message = null; |
| if (length > 0) { |
| byte[] messageBuffer = new byte[length]; |
| data.get(messageBuffer, 0, length); |
| message = new String(messageBuffer); |
| } |
| |
| // this can be sent if |
| // - MPRS failed (like wrong permission) |
| // - MPSE failed for whatever reason |
| |
| String filename = client.getClientData().getPendingMethodProfiling(); |
| if (filename != null) { |
| // reset the pending file. |
| client.getClientData().setPendingMethodProfiling(null); |
| |
| // and notify of failure |
| IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); |
| if (handler != null) { |
| handler.onStartFailure(client, message); |
| } |
| } else { |
| // this is MPRE |
| // notify of failure |
| IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); |
| if (handler != null) { |
| handler.onEndFailure(client, message); |
| } |
| } |
| |
| // send a query to know the current status |
| try { |
| sendMPRQ(client); |
| } catch (IOException e) { |
| Log.e("HandleProfiling", e); |
| } |
| } |
| } |
| |