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 
17 package android.ddm;
18 
19 import org.apache.harmony.dalvik.ddmc.Chunk;
20 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
21 import org.apache.harmony.dalvik.ddmc.DdmServer;
22 import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
23 import android.os.Debug;
24 import android.util.Log;
25 import java.io.IOException;
26 import java.nio.ByteBuffer;
27 
28 /**
29  * Handle native and virtual heap requests.
30  */
31 public 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 */
DdmHandleHeap()47     private DdmHandleHeap() {}
48 
49     /**
50      * Register for the messages we're interested in.
51      */
register()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      */
connected()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      */
disconnected()74     public void disconnected() {}
75 
76     /**
77      * Handle a chunk of data.
78      */
handleChunk(Chunk request)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      */
handleHPIF(Chunk request)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      */
handleHPSGNHSG(Chunk request, boolean isNative)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      */
handleHPDU(Chunk request)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      */
handleHPDS(Chunk request)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      */
handleHPGC(Chunk request)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      */
handleREAE(Chunk request)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      */
handleREAQ(Chunk request)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      */
handleREAL(Chunk request)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