OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / sdk / ddms / libs / ddmlib / src / com / android / ddmlib / HandleHeap.java
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 com.android.ddmlib;
18
19 import com.android.ddmlib.ClientData.AllocationTrackingStatus;
20 import com.android.ddmlib.ClientData.IHprofDumpHandler;
21
22 import java.io.IOException;
23 import java.nio.BufferUnderflowException;
24 import java.nio.ByteBuffer;
25 import java.util.ArrayList;
26 import java.util.Collections;
27
28 /**
29  * Handle heap status updates.
30  */
31 final class HandleHeap extends ChunkHandler {
32
33     public static final int CHUNK_HPIF = type("HPIF");
34     public static final int CHUNK_HPST = type("HPST");
35     public static final int CHUNK_HPEN = type("HPEN");
36     public static final int CHUNK_HPSG = type("HPSG");
37     public static final int CHUNK_HPGC = type("HPGC");
38     public static final int CHUNK_HPDU = type("HPDU");
39     public static final int CHUNK_HPDS = type("HPDS");
40     public static final int CHUNK_REAE = type("REAE");
41     public static final int CHUNK_REAQ = type("REAQ");
42     public static final int CHUNK_REAL = type("REAL");
43
44     // args to sendHPSG
45     public static final int WHEN_DISABLE = 0;
46     public static final int WHEN_GC = 1;
47     public static final int WHAT_MERGE = 0; // merge adjacent objects
48     public static final int WHAT_OBJ = 1;   // keep objects distinct
49
50     // args to sendHPIF
51     public static final int HPIF_WHEN_NEVER = 0;
52     public static final int HPIF_WHEN_NOW = 1;
53     public static final int HPIF_WHEN_NEXT_GC = 2;
54     public static final int HPIF_WHEN_EVERY_GC = 3;
55
56     private static final HandleHeap mInst = new HandleHeap();
57
58     private HandleHeap() {}
59
60     /**
61      * Register for the packets we expect to get from the client.
62      */
63     public static void register(MonitorThread mt) {
64         mt.registerChunkHandler(CHUNK_HPIF, mInst);
65         mt.registerChunkHandler(CHUNK_HPST, mInst);
66         mt.registerChunkHandler(CHUNK_HPEN, mInst);
67         mt.registerChunkHandler(CHUNK_HPSG, mInst);
68         mt.registerChunkHandler(CHUNK_HPDS, mInst);
69         mt.registerChunkHandler(CHUNK_REAQ, mInst);
70         mt.registerChunkHandler(CHUNK_REAL, mInst);
71     }
72
73     /**
74      * Client is ready.
75      */
76     @Override
77     public void clientReady(Client client) throws IOException {
78         if (client.isHeapUpdateEnabled()) {
79             //sendHPSG(client, WHEN_GC, WHAT_MERGE);
80             sendHPIF(client, HPIF_WHEN_EVERY_GC);
81         }
82     }
83
84     /**
85      * Client went away.
86      */
87     @Override
88     public void clientDisconnected(Client client) {}
89
90     /**
91      * Chunk handler entry point.
92      */
93     @Override
94     public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) {
95         Log.d("ddm-heap", "handling " + ChunkHandler.name(type));
96
97         if (type == CHUNK_HPIF) {
98             handleHPIF(client, data);
99         } else if (type == CHUNK_HPST) {
100             handleHPST(client, data);
101         } else if (type == CHUNK_HPEN) {
102             handleHPEN(client, data);
103         } else if (type == CHUNK_HPSG) {
104             handleHPSG(client, data);
105         } else if (type == CHUNK_HPDU) {
106             handleHPDU(client, data);
107         } else if (type == CHUNK_HPDS) {
108             handleHPDS(client, data);
109         } else if (type == CHUNK_REAQ) {
110             handleREAQ(client, data);
111         } else if (type == CHUNK_REAL) {
112             handleREAL(client, data);
113         } else {
114             handleUnknownChunk(client, type, data, isReply, msgId);
115         }
116     }
117
118     /*
119      * Handle a heap info message.
120      */
121     private void handleHPIF(Client client, ByteBuffer data) {
122         Log.d("ddm-heap", "HPIF!");
123         try {
124             int numHeaps = data.getInt();
125
126             for (int i = 0; i < numHeaps; i++) {
127                 int heapId = data.getInt();
128                 @SuppressWarnings("unused")
129                 long timeStamp = data.getLong();
130                 @SuppressWarnings("unused")
131                 byte reason = data.get();
132                 long maxHeapSize = (long)data.getInt() & 0x00ffffffff;
133                 long heapSize = (long)data.getInt() & 0x00ffffffff;
134                 long bytesAllocated = (long)data.getInt() & 0x00ffffffff;
135                 long objectsAllocated = (long)data.getInt() & 0x00ffffffff;
136
137                 client.getClientData().setHeapInfo(heapId, maxHeapSize,
138                         heapSize, bytesAllocated, objectsAllocated);
139                 client.update(Client.CHANGE_HEAP_DATA);
140             }
141         } catch (BufferUnderflowException ex) {
142             Log.w("ddm-heap", "malformed HPIF chunk from client");
143         }
144     }
145
146     /**
147      * Send an HPIF (HeaP InFo) request to the client.
148      */
149     public static void sendHPIF(Client client, int when) throws IOException {
150         ByteBuffer rawBuf = allocBuffer(1);
151         JdwpPacket packet = new JdwpPacket(rawBuf);
152         ByteBuffer buf = getChunkDataBuf(rawBuf);
153
154         buf.put((byte)when);
155
156         finishChunkPacket(packet, CHUNK_HPIF, buf.position());
157         Log.d("ddm-heap", "Sending " + name(CHUNK_HPIF) + ": when=" + when);
158         client.sendAndConsume(packet, mInst);
159     }
160
161     /*
162      * Handle a heap segment series start message.
163      */
164     private void handleHPST(Client client, ByteBuffer data) {
165         /* Clear out any data that's sitting around to
166          * get ready for the chunks that are about to come.
167          */
168 //xxx todo: only clear data that belongs to the heap mentioned in <data>.
169         client.getClientData().getVmHeapData().clearHeapData();
170     }
171
172     /*
173      * Handle a heap segment series end message.
174      */
175     private void handleHPEN(Client client, ByteBuffer data) {
176         /* Let the UI know that we've received all of the
177          * data for this heap.
178          */
179 //xxx todo: only seal data that belongs to the heap mentioned in <data>.
180         client.getClientData().getVmHeapData().sealHeapData();
181         client.update(Client.CHANGE_HEAP_DATA);
182     }
183
184     /*
185      * Handle a heap segment message.
186      */
187     private void handleHPSG(Client client, ByteBuffer data) {
188         byte dataCopy[] = new byte[data.limit()];
189         data.rewind();
190         data.get(dataCopy);
191         data = ByteBuffer.wrap(dataCopy);
192         client.getClientData().getVmHeapData().addHeapData(data);
193 //xxx todo: add to the heap mentioned in <data>
194     }
195
196     /**
197      * Sends an HPSG (HeaP SeGment) request to the client.
198      */
199     public static void sendHPSG(Client client, int when, int what)
200         throws IOException {
201
202         ByteBuffer rawBuf = allocBuffer(2);
203         JdwpPacket packet = new JdwpPacket(rawBuf);
204         ByteBuffer buf = getChunkDataBuf(rawBuf);
205
206         buf.put((byte)when);
207         buf.put((byte)what);
208
209         finishChunkPacket(packet, CHUNK_HPSG, buf.position());
210         Log.d("ddm-heap", "Sending " + name(CHUNK_HPSG) + ": when="
211             + when + ", what=" + what);
212         client.sendAndConsume(packet, mInst);
213     }
214
215     /**
216      * Sends an HPGC request to the client.
217      */
218     public static void sendHPGC(Client client)
219         throws IOException {
220         ByteBuffer rawBuf = allocBuffer(0);
221         JdwpPacket packet = new JdwpPacket(rawBuf);
222         ByteBuffer buf = getChunkDataBuf(rawBuf);
223
224         // no data
225
226         finishChunkPacket(packet, CHUNK_HPGC, buf.position());
227         Log.d("ddm-heap", "Sending " + name(CHUNK_HPGC));
228         client.sendAndConsume(packet, mInst);
229     }
230
231     /**
232      * Sends an HPDU request to the client.
233      *
234      * We will get an HPDU response when the heap dump has completed.  On
235      * failure we get a generic failure response.
236      *
237      * @param fileName name of output file (on device)
238      */
239     public static void sendHPDU(Client client, String fileName)
240         throws IOException {
241         ByteBuffer rawBuf = allocBuffer(4 + fileName.length() * 2);
242         JdwpPacket packet = new JdwpPacket(rawBuf);
243         ByteBuffer buf = getChunkDataBuf(rawBuf);
244
245         buf.putInt(fileName.length());
246         putString(buf, fileName);
247
248         finishChunkPacket(packet, CHUNK_HPDU, buf.position());
249         Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName +"'");
250         client.sendAndConsume(packet, mInst);
251         client.getClientData().setPendingHprofDump(fileName);
252     }
253
254     /**
255      * Sends an HPDS request to the client.
256      *
257      * We will get an HPDS response when the heap dump has completed.  On
258      * failure we get a generic failure response.
259      *
260      * This is more expensive for the device than HPDU, because the entire
261      * heap dump is held in RAM instead of spooled out to a temp file.  On
262      * the other hand, permission to write to /sdcard is not required.
263      *
264      * @param fileName name of output file (on device)
265      */
266     public static void sendHPDS(Client client)
267         throws IOException {
268         ByteBuffer rawBuf = allocBuffer(0);
269         JdwpPacket packet = new JdwpPacket(rawBuf);
270         ByteBuffer buf = getChunkDataBuf(rawBuf);
271
272         finishChunkPacket(packet, CHUNK_HPDS, buf.position());
273         Log.d("ddm-heap", "Sending " + name(CHUNK_HPDS));
274         client.sendAndConsume(packet, mInst);
275     }
276
277     /*
278      * Handle notification of completion of a HeaP DUmp.
279      */
280     private void handleHPDU(Client client, ByteBuffer data) {
281         byte result;
282
283         // get the filename and make the client not have pending HPROF dump anymore.
284         String filename = client.getClientData().getPendingHprofDump();
285         client.getClientData().setPendingHprofDump(null);
286
287         // get the dump result
288         result = data.get();
289
290         // get the app-level handler for HPROF dump
291         IHprofDumpHandler handler = ClientData.getHprofDumpHandler();
292         if (handler != null) {
293             if (result == 0) {
294                 handler.onSuccess(filename, client);
295
296                 Log.d("ddm-heap", "Heap dump request has finished");
297             } else {
298                 handler.onEndFailure(client, null);
299                 Log.w("ddm-heap", "Heap dump request failed (check device log)");
300             }
301         }
302     }
303
304     /*
305      * Handle HeaP Dump Streaming response.  "data" contains the full
306      * hprof dump.
307      */
308     private void handleHPDS(Client client, ByteBuffer data) {
309         IHprofDumpHandler handler = ClientData.getHprofDumpHandler();
310         if (handler != null) {
311             byte[] stuff = new byte[data.capacity()];
312             data.get(stuff, 0, stuff.length);
313
314             Log.d("ddm-hprof", "got hprof file, size: " + data.capacity() + " bytes");
315
316             handler.onSuccess(stuff, client);
317         }
318     }
319
320     /**
321      * Sends a REAE (REcent Allocation Enable) request to the client.
322      */
323     public static void sendREAE(Client client, boolean enable)
324         throws IOException {
325         ByteBuffer rawBuf = allocBuffer(1);
326         JdwpPacket packet = new JdwpPacket(rawBuf);
327         ByteBuffer buf = getChunkDataBuf(rawBuf);
328
329         buf.put((byte) (enable ? 1 : 0));
330
331         finishChunkPacket(packet, CHUNK_REAE, buf.position());
332         Log.d("ddm-heap", "Sending " + name(CHUNK_REAE) + ": " + enable);
333         client.sendAndConsume(packet, mInst);
334     }
335
336     /**
337      * Sends a REAQ (REcent Allocation Query) request to the client.
338      */
339     public static void sendREAQ(Client client)
340         throws IOException {
341         ByteBuffer rawBuf = allocBuffer(0);
342         JdwpPacket packet = new JdwpPacket(rawBuf);
343         ByteBuffer buf = getChunkDataBuf(rawBuf);
344
345         // no data
346
347         finishChunkPacket(packet, CHUNK_REAQ, buf.position());
348         Log.d("ddm-heap", "Sending " + name(CHUNK_REAQ));
349         client.sendAndConsume(packet, mInst);
350     }
351
352     /**
353      * Sends a REAL (REcent ALlocation) request to the client.
354      */
355     public static void sendREAL(Client client)
356         throws IOException {
357         ByteBuffer rawBuf = allocBuffer(0);
358         JdwpPacket packet = new JdwpPacket(rawBuf);
359         ByteBuffer buf = getChunkDataBuf(rawBuf);
360
361         // no data
362
363         finishChunkPacket(packet, CHUNK_REAL, buf.position());
364         Log.d("ddm-heap", "Sending " + name(CHUNK_REAL));
365         client.sendAndConsume(packet, mInst);
366     }
367
368     /*
369      * Handle the response from our REcent Allocation Query message.
370      */
371     private void handleREAQ(Client client, ByteBuffer data) {
372         boolean enabled;
373
374         enabled = (data.get() != 0);
375         Log.d("ddm-heap", "REAQ says: enabled=" + enabled);
376
377         client.getClientData().setAllocationStatus(enabled ?
378                 AllocationTrackingStatus.ON : AllocationTrackingStatus.OFF);
379         client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS);
380     }
381
382     /**
383      * Converts a VM class descriptor string ("Landroid/os/Debug;") to
384      * a dot-notation class name ("android.os.Debug").
385      */
386     private String descriptorToDot(String str) {
387         // count the number of arrays.
388         int array = 0;
389         while (str.startsWith("[")) {
390             str = str.substring(1);
391             array++;
392         }
393
394         int len = str.length();
395
396         /* strip off leading 'L' and trailing ';' if appropriate */
397         if (len >= 2 && str.charAt(0) == 'L' && str.charAt(len - 1) == ';') {
398             str = str.substring(1, len-1);
399             str = str.replace('/', '.');
400         } else {
401             // convert the basic types
402             if ("C".equals(str)) {
403                 str = "char";
404             } else if ("B".equals(str)) {
405                 str = "byte";
406             } else if ("Z".equals(str)) {
407                 str = "boolean";
408             } else if ("S".equals(str)) {
409                 str = "short";
410             } else if ("I".equals(str)) {
411                 str = "int";
412             } else if ("J".equals(str)) {
413                 str = "long";
414             } else if ("F".equals(str)) {
415                 str = "float";
416             } else if ("D".equals(str)) {
417                 str = "double";
418             }
419         }
420
421         // now add the array part
422         for (int a = 0 ; a < array; a++) {
423             str = str + "[]";
424         }
425
426         return str;
427     }
428
429     /**
430      * Reads a string table out of "data".
431      *
432      * This is just a serial collection of strings, each of which is a
433      * four-byte length followed by UTF-16 data.
434      */
435     private void readStringTable(ByteBuffer data, String[] strings) {
436         int count = strings.length;
437         int i;
438
439         for (i = 0; i < count; i++) {
440             int nameLen = data.getInt();
441             String descriptor = getString(data, nameLen);
442             strings[i] = descriptorToDot(descriptor);
443         }
444     }
445
446     /*
447      * Handle a REcent ALlocation response.
448      *
449      * Message header (all values big-endian):
450      *   (1b) message header len (to allow future expansion); includes itself
451      *   (1b) entry header len
452      *   (1b) stack frame len
453      *   (2b) number of entries
454      *   (4b) offset to string table from start of message
455      *   (2b) number of class name strings
456      *   (2b) number of method name strings
457      *   (2b) number of source file name strings
458      *   For each entry:
459      *     (4b) total allocation size
460      *     (2b) threadId
461      *     (2b) allocated object's class name index
462      *     (1b) stack depth
463      *     For each stack frame:
464      *       (2b) method's class name
465      *       (2b) method name
466      *       (2b) method source file
467      *       (2b) line number, clipped to 32767; -2 if native; -1 if no source
468      *   (xb) class name strings
469      *   (xb) method name strings
470      *   (xb) source file strings
471      *
472      *   As with other DDM traffic, strings are sent as a 4-byte length
473      *   followed by UTF-16 data.
474      */
475     private void handleREAL(Client client, ByteBuffer data) {
476         Log.e("ddm-heap", "*** Received " + name(CHUNK_REAL));
477         int messageHdrLen, entryHdrLen, stackFrameLen;
478         int numEntries, offsetToStrings;
479         int numClassNames, numMethodNames, numFileNames;
480
481         /*
482          * Read the header.
483          */
484         messageHdrLen = (data.get() & 0xff);
485         entryHdrLen = (data.get() & 0xff);
486         stackFrameLen = (data.get() & 0xff);
487         numEntries = (data.getShort() & 0xffff);
488         offsetToStrings = data.getInt();
489         numClassNames = (data.getShort() & 0xffff);
490         numMethodNames = (data.getShort() & 0xffff);
491         numFileNames = (data.getShort() & 0xffff);
492
493
494         /*
495          * Skip forward to the strings and read them.
496          */
497         data.position(offsetToStrings);
498
499         String[] classNames = new String[numClassNames];
500         String[] methodNames = new String[numMethodNames];
501         String[] fileNames = new String[numFileNames];
502
503         readStringTable(data, classNames);
504         readStringTable(data, methodNames);
505         //System.out.println("METHODS: "
506         //    + java.util.Arrays.deepToString(methodNames));
507         readStringTable(data, fileNames);
508
509         /*
510          * Skip back to a point just past the header and start reading
511          * entries.
512          */
513         data.position(messageHdrLen);
514
515         ArrayList<AllocationInfo> list = new ArrayList<AllocationInfo>(numEntries);
516         for (int i = 0; i < numEntries; i++) {
517             int totalSize;
518             int threadId, classNameIndex, stackDepth;
519
520             totalSize = data.getInt();
521             threadId = (data.getShort() & 0xffff);
522             classNameIndex = (data.getShort() & 0xffff);
523             stackDepth = (data.get() & 0xff);
524             /* we've consumed 9 bytes; gobble up any extra */
525             for (int skip = 9; skip < entryHdrLen; skip++)
526                 data.get();
527
528             StackTraceElement[] steArray = new StackTraceElement[stackDepth];
529
530             /*
531              * Pull out the stack trace.
532              */
533             for (int sti = 0; sti < stackDepth; sti++) {
534                 int methodClassNameIndex, methodNameIndex;
535                 int methodSourceFileIndex;
536                 short lineNumber;
537                 String methodClassName, methodName, methodSourceFile;
538
539                 methodClassNameIndex = (data.getShort() & 0xffff);
540                 methodNameIndex = (data.getShort() & 0xffff);
541                 methodSourceFileIndex = (data.getShort() & 0xffff);
542                 lineNumber = data.getShort();
543
544                 methodClassName = classNames[methodClassNameIndex];
545                 methodName = methodNames[methodNameIndex];
546                 methodSourceFile = fileNames[methodSourceFileIndex];
547
548                 steArray[sti] = new StackTraceElement(methodClassName,
549                     methodName, methodSourceFile, lineNumber);
550
551                 /* we've consumed 8 bytes; gobble up any extra */
552                 for (int skip = 9; skip < stackFrameLen; skip++)
553                     data.get();
554             }
555
556             list.add(new AllocationInfo(classNames[classNameIndex],
557                 totalSize, (short) threadId, steArray));
558         }
559
560         // sort biggest allocations first.
561         Collections.sort(list);
562
563         client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries]));
564         client.update(Client.CHANGE_HEAP_ALLOCATIONS);
565     }
566
567     /*
568      * For debugging: dump the contents of an AllocRecord array.
569      *
570      * The array starts with the oldest known allocation and ends with
571      * the most recent allocation.
572      */
573     @SuppressWarnings("unused")
574     private static void dumpRecords(AllocationInfo[] records) {
575         System.out.println("Found " + records.length + " records:");
576
577         for (AllocationInfo rec: records) {
578             System.out.println("tid=" + rec.getThreadId() + " "
579                 + rec.getAllocatedClass() + " (" + rec.getSize() + " bytes)");
580
581             for (StackTraceElement ste: rec.getStackTrace()) {
582                 if (ste.isNativeMethod()) {
583                     System.out.println("    " + ste.getClassName()
584                         + "." + ste.getMethodName()
585                         + " (Native method)");
586                 } else {
587                     System.out.println("    " + ste.getClassName()
588                         + "." + ste.getMethodName()
589                         + " (" + ste.getFileName()
590                         + ":" + ste.getLineNumber() + ")");
591                 }
592             }
593         }
594     }
595
596 }
597