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