1/*
2 * Copyright (C) 2007 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 android.ddm;
18
19import org.apache.harmony.dalvik.ddmc.Chunk;
20import org.apache.harmony.dalvik.ddmc.ChunkHandler;
21import org.apache.harmony.dalvik.ddmc.DdmServer;
22import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
23import android.os.Debug;
24import android.util.Log;
25import java.io.IOException;
26import java.nio.ByteBuffer;
27
28/**
29 * Handle native and virtual heap requests.
30 */
31public class DdmHandleHeap extends ChunkHandler {
32
33    public static final int CHUNK_HPIF = type("HPIF");
34    public static final int CHUNK_HPSG = type("HPSG");
35    public static final int CHUNK_HPDU = type("HPDU");
36    public static final int CHUNK_HPDS = type("HPDS");
37    public static final int CHUNK_NHSG = type("NHSG");
38    public static final int CHUNK_HPGC = type("HPGC");
39    public static final int CHUNK_REAE = type("REAE");
40    public static final int CHUNK_REAQ = type("REAQ");
41    public static final int CHUNK_REAL = type("REAL");
42
43    private static DdmHandleHeap mInstance = new DdmHandleHeap();
44
45
46    /* singleton, do not instantiate */
47    private DdmHandleHeap() {}
48
49    /**
50     * Register for the messages we're interested in.
51     */
52    public static void register() {
53        DdmServer.registerHandler(CHUNK_HPIF, mInstance);
54        DdmServer.registerHandler(CHUNK_HPSG, mInstance);
55        DdmServer.registerHandler(CHUNK_HPDU, mInstance);
56        DdmServer.registerHandler(CHUNK_HPDS, mInstance);
57        DdmServer.registerHandler(CHUNK_NHSG, mInstance);
58        DdmServer.registerHandler(CHUNK_HPGC, mInstance);
59        DdmServer.registerHandler(CHUNK_REAE, mInstance);
60        DdmServer.registerHandler(CHUNK_REAQ, mInstance);
61        DdmServer.registerHandler(CHUNK_REAL, mInstance);
62    }
63
64    /**
65     * Called when the DDM server connects.  The handler is allowed to
66     * send messages to the server.
67     */
68    public void connected() {}
69
70    /**
71     * Called when the DDM server disconnects.  Can be used to disable
72     * periodic transmissions or clean up saved state.
73     */
74    public void disconnected() {}
75
76    /**
77     * Handle a chunk of data.
78     */
79    public Chunk handleChunk(Chunk request) {
80        if (false)
81            Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
82        int type = request.type;
83
84        if (type == CHUNK_HPIF) {
85            return handleHPIF(request);
86        } else if (type == CHUNK_HPSG) {
87            return handleHPSGNHSG(request, false);
88        } else if (type == CHUNK_HPDU) {
89            return handleHPDU(request);
90        } else if (type == CHUNK_HPDS) {
91            return handleHPDS(request);
92        } else if (type == CHUNK_NHSG) {
93            return handleHPSGNHSG(request, true);
94        } else if (type == CHUNK_HPGC) {
95            return handleHPGC(request);
96        } else if (type == CHUNK_REAE) {
97            return handleREAE(request);
98        } else if (type == CHUNK_REAQ) {
99            return handleREAQ(request);
100        } else if (type == CHUNK_REAL) {
101            return handleREAL(request);
102        } else {
103            throw new RuntimeException("Unknown packet "
104                + ChunkHandler.name(type));
105        }
106    }
107
108    /*
109     * Handle a "HeaP InFo" request.
110     */
111    private Chunk handleHPIF(Chunk request) {
112        ByteBuffer in = wrapChunk(request);
113
114        int when = in.get();
115        if (false)
116            Log.v("ddm-heap", "Heap segment enable: when=" + when);
117
118        boolean ok = DdmVmInternal.heapInfoNotify(when);
119        if (!ok) {
120            return createFailChunk(1, "Unsupported HPIF what");
121        } else {
122            return null;        // empty response
123        }
124    }
125
126    /*
127     * Handle a "HeaP SeGment" or "Native Heap SeGment" request.
128     */
129    private Chunk handleHPSGNHSG(Chunk request, boolean isNative) {
130        ByteBuffer in = wrapChunk(request);
131
132        int when = in.get();
133        int what = in.get();
134        if (false)
135            Log.v("ddm-heap", "Heap segment enable: when=" + when
136                + ", what=" + what + ", isNative=" + isNative);
137
138        boolean ok = DdmVmInternal.heapSegmentNotify(when, what, isNative);
139        if (!ok) {
140            return createFailChunk(1, "Unsupported HPSG what/when");
141        } else {
142            // TODO: if "when" is non-zero and we want to see a dump
143            //       right away, initiate a GC.
144            return null;        // empty response
145        }
146    }
147
148    /*
149     * Handle a "HeaP DUmp" request.
150     *
151     * This currently just returns a result code.  We could pull up
152     * the entire contents of the file and return them, but hprof dump
153     * files can be a few megabytes.
154     */
155    private Chunk handleHPDU(Chunk request) {
156        ByteBuffer in = wrapChunk(request);
157        byte result;
158
159        /* get the filename for the output file */
160        int len = in.getInt();
161        String fileName = getString(in, len);
162        if (false)
163            Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
164
165        try {
166            Debug.dumpHprofData(fileName);
167            result = 0;
168        } catch (UnsupportedOperationException uoe) {
169            Log.w("ddm-heap", "hprof dumps not supported in this VM");
170            result = -1;
171        } catch (IOException ioe) {
172            result = -1;
173        } catch (RuntimeException re) {
174            result = -1;
175        }
176
177        /* create a non-empty reply so the handler fires on completion */
178        byte[] reply = { result };
179        return new Chunk(CHUNK_HPDU, reply, 0, reply.length);
180    }
181
182    /*
183     * Handle a "HeaP Dump Streaming" request.
184     *
185     * This tells the VM to create a heap dump and send it directly to
186     * DDMS.  The dumps are large enough that we don't want to copy the
187     * data into a byte[] and send it from here.
188     */
189    private Chunk handleHPDS(Chunk request) {
190        ByteBuffer in = wrapChunk(request);
191        byte result;
192
193        /* get the filename for the output file */
194        if (false)
195            Log.d("ddm-heap", "Heap dump: [DDMS]");
196
197        String failMsg = null;
198        try {
199            Debug.dumpHprofDataDdms();
200        } catch (UnsupportedOperationException uoe) {
201            failMsg = "hprof dumps not supported in this VM";
202        } catch (RuntimeException re) {
203            failMsg = "Exception: " + re.getMessage();
204        }
205
206        if (failMsg != null) {
207            Log.w("ddm-heap", failMsg);
208            return createFailChunk(1, failMsg);
209        } else {
210            return null;
211        }
212    }
213
214    /*
215     * Handle a "HeaP Garbage Collection" request.
216     */
217    private Chunk handleHPGC(Chunk request) {
218        //ByteBuffer in = wrapChunk(request);
219
220        if (false)
221            Log.d("ddm-heap", "Heap GC request");
222        Runtime.getRuntime().gc();
223
224        return null;        // empty response
225    }
226
227    /*
228     * Handle a "REcent Allocation Enable" request.
229     */
230    private Chunk handleREAE(Chunk request) {
231        ByteBuffer in = wrapChunk(request);
232        boolean enable;
233
234        enable = (in.get() != 0);
235
236        if (false)
237            Log.d("ddm-heap", "Recent allocation enable request: " + enable);
238
239        DdmVmInternal.enableRecentAllocations(enable);
240
241        return null;        // empty response
242    }
243
244    /*
245     * Handle a "REcent Allocation Query" request.
246     */
247    private Chunk handleREAQ(Chunk request) {
248        //ByteBuffer in = wrapChunk(request);
249
250        byte[] reply = new byte[1];
251        reply[0] = DdmVmInternal.getRecentAllocationStatus() ? (byte)1 :(byte)0;
252        return new Chunk(CHUNK_REAQ, reply, 0, reply.length);
253    }
254
255    /*
256     * Handle a "REcent ALlocations" request.
257     */
258    private Chunk handleREAL(Chunk request) {
259        //ByteBuffer in = wrapChunk(request);
260
261        if (false)
262            Log.d("ddm-heap", "Recent allocations request");
263
264        /* generate the reply in a ready-to-go format */
265        byte[] reply = DdmVmInternal.getRecentAllocations();
266        return new Chunk(CHUNK_REAL, reply, 0, reply.length);
267    }
268}
269
270