• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.HeapSegment.HeapSegmentElement;
20 
21 import java.nio.BufferUnderflowException;
22 import java.nio.ByteBuffer;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.TreeMap;
32 import java.util.TreeSet;
33 
34 
35 /**
36  * Contains the data of a {@link Client}.
37  */
38 public class ClientData {
39     /* This is a place to stash data associated with a Client, such as thread
40     * states or heap data.  ClientData maps 1:1 to Client, but it's a little
41     * cleaner if we separate the data out.
42     *
43     * Message handlers are welcome to stash arbitrary data here.
44     *
45     * IMPORTANT: The data here is written by HandleFoo methods and read by
46     * FooPanel methods, which run in different threads.  All non-trivial
47     * access should be synchronized against the ClientData object.
48     */
49 
50 
51     /** Temporary name of VM to be ignored. */
52     private final static String PRE_INITIALIZED = "<pre-initialized>"; //$NON-NLS-1$
53 
54     public static enum DebuggerStatus {
55         /** Debugger connection status: not waiting on one, not connected to one, but accepting
56          * new connections. This is the default value. */
57         DEFAULT,
58         /**
59          * Debugger connection status: the application's VM is paused, waiting for a debugger to
60          * connect to it before resuming. */
61         WAITING,
62         /** Debugger connection status : Debugger is connected */
63         ATTACHED,
64         /** Debugger connection status: The listening port for debugger connection failed to listen.
65          * No debugger will be able to connect. */
66         ERROR;
67     }
68 
69     public static enum AllocationTrackingStatus {
70         /**
71          * Allocation tracking status: unknown.
72          * <p/>This happens right after a {@link Client} is discovered
73          * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query
74          * regarding its allocation tracking status.
75          * @see Client#requestAllocationStatus()
76          */
77         UNKNOWN,
78         /** Allocation tracking status: the {@link Client} is not tracking allocations. */
79         OFF,
80         /** Allocation tracking status: the {@link Client} is tracking allocations. */
81         ON;
82     }
83 
84     public static enum MethodProfilingStatus {
85         /**
86          * Method profiling status: unknown.
87          * <p/>This happens right after a {@link Client} is discovered
88          * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query
89          * regarding its method profiling status.
90          * @see Client#requestMethodProfilingStatus()
91          */
92         UNKNOWN,
93         /** Method profiling status: the {@link Client} is not profiling method calls. */
94         OFF,
95         /** Method profiling status: the {@link Client} is profiling method calls. */
96         ON;
97     }
98 
99     /**
100      * Name of the value representing the max size of the heap, in the {@link Map} returned by
101      * {@link #getVmHeapInfo(int)}
102      */
103     public final static String HEAP_MAX_SIZE_BYTES = "maxSizeInBytes"; //$NON-NLS-1$
104     /**
105      * Name of the value representing the size of the heap, in the {@link Map} returned by
106      * {@link #getVmHeapInfo(int)}
107      */
108     public final static String HEAP_SIZE_BYTES = "sizeInBytes"; //$NON-NLS-1$
109     /**
110      * Name of the value representing the number of allocated bytes of the heap, in the
111      * {@link Map} returned by {@link #getVmHeapInfo(int)}
112      */
113     public final static String HEAP_BYTES_ALLOCATED = "bytesAllocated"; //$NON-NLS-1$
114     /**
115      * Name of the value representing the number of objects in the heap, in the {@link Map}
116      * returned by {@link #getVmHeapInfo(int)}
117      */
118     public final static String HEAP_OBJECTS_ALLOCATED = "objectsAllocated"; //$NON-NLS-1$
119 
120     /**
121      * String for feature enabling starting/stopping method profiling
122      * @see #hasFeature(String)
123      */
124     public final static String FEATURE_PROFILING = "method-trace-profiling"; //$NON-NLS-1$
125 
126     /**
127      * String for feature enabling direct streaming of method profiling data
128      * @see #hasFeature(String)
129      */
130     public final static String FEATURE_PROFILING_STREAMING = "method-trace-profiling-streaming"; //$NON-NLS-1$
131 
132     /**
133      * String for feature allowing to dump hprof files
134      * @see #hasFeature(String)
135      */
136     public final static String FEATURE_HPROF = "hprof-heap-dump"; //$NON-NLS-1$
137 
138     /**
139      * String for feature allowing direct streaming of hprof dumps
140      * @see #hasFeature(String)
141      */
142     public final static String FEATURE_HPROF_STREAMING = "hprof-heap-dump-streaming"; //$NON-NLS-1$
143 
144     private static IHprofDumpHandler sHprofDumpHandler;
145     private static IMethodProfilingHandler sMethodProfilingHandler;
146 
147     // is this a DDM-aware client?
148     private boolean mIsDdmAware;
149 
150     // the client's process ID
151     private final int mPid;
152 
153     // Java VM identification string
154     private String mVmIdentifier;
155 
156     // client's self-description
157     private String mClientDescription;
158 
159     // how interested are we in a debugger?
160     private DebuggerStatus mDebuggerInterest;
161 
162     // List of supported features by the client.
163     private final HashSet<String> mFeatures = new HashSet<String>();
164 
165     // Thread tracking (THCR, THDE).
166     private TreeMap<Integer,ThreadInfo> mThreadMap;
167 
168     /** VM Heap data */
169     private final HeapData mHeapData = new HeapData();
170     /** Native Heap data */
171     private final HeapData mNativeHeapData = new HeapData();
172 
173     private HashMap<Integer, HashMap<String, Long>> mHeapInfoMap =
174             new HashMap<Integer, HashMap<String, Long>>();
175 
176 
177     /** library map info. Stored here since the backtrace data
178      * is computed on a need to display basis.
179      */
180     private ArrayList<NativeLibraryMapInfo> mNativeLibMapInfo =
181         new ArrayList<NativeLibraryMapInfo>();
182 
183     /** Native Alloc info list */
184     private ArrayList<NativeAllocationInfo> mNativeAllocationList =
185         new ArrayList<NativeAllocationInfo>();
186     private int mNativeTotalMemory;
187 
188     private AllocationInfo[] mAllocations;
189     private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN;
190 
191     private String mPendingHprofDump;
192 
193     private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN;
194     private String mPendingMethodProfiling;
195 
196     /**
197      * Heap Information.
198      * <p/>The heap is composed of several {@link HeapSegment} objects.
199      * <p/>A call to {@link #isHeapDataComplete()} will indicate if the segments (available through
200      * {@link #getHeapSegments()}) represent the full heap.
201      */
202     public static class HeapData {
203         private TreeSet<HeapSegment> mHeapSegments = new TreeSet<HeapSegment>();
204         private boolean mHeapDataComplete = false;
205         private byte[] mProcessedHeapData;
206         private Map<Integer, ArrayList<HeapSegmentElement>> mProcessedHeapMap;
207 
208         /**
209          * Abandon the current list of heap segments.
210          */
clearHeapData()211         public synchronized void clearHeapData() {
212             /* Abandon the old segments instead of just calling .clear().
213              * This lets the user hold onto the old set if it wants to.
214              */
215             mHeapSegments = new TreeSet<HeapSegment>();
216             mHeapDataComplete = false;
217         }
218 
219         /**
220          * Add raw HPSG chunk data to the list of heap segments.
221          *
222          * @param data The raw data from an HPSG chunk.
223          */
addHeapData(ByteBuffer data)224         synchronized void addHeapData(ByteBuffer data) {
225             HeapSegment hs;
226 
227             if (mHeapDataComplete) {
228                 clearHeapData();
229             }
230 
231             try {
232                 hs = new HeapSegment(data);
233             } catch (BufferUnderflowException e) {
234                 System.err.println("Discarding short HPSG data (length " + data.limit() + ")");
235                 return;
236             }
237 
238             mHeapSegments.add(hs);
239         }
240 
241         /**
242          * Called when all heap data has arrived.
243          */
sealHeapData()244         synchronized void sealHeapData() {
245             mHeapDataComplete = true;
246         }
247 
248         /**
249          * Returns whether the heap data has been sealed.
250          */
isHeapDataComplete()251         public boolean isHeapDataComplete() {
252             return mHeapDataComplete;
253         }
254 
255         /**
256          * Get the collected heap data, if sealed.
257          *
258          * @return The list of heap segments if the heap data has been sealed, or null if it hasn't.
259          */
getHeapSegments()260         public Collection<HeapSegment> getHeapSegments() {
261             if (isHeapDataComplete()) {
262                 return mHeapSegments;
263             }
264             return null;
265         }
266 
267         /**
268          * Sets the processed heap data.
269          *
270          * @param heapData The new heap data (can be null)
271          */
setProcessedHeapData(byte[] heapData)272         public void setProcessedHeapData(byte[] heapData) {
273             mProcessedHeapData = heapData;
274         }
275 
276         /**
277          * Get the processed heap data, if present.
278          *
279          * @return the processed heap data, or null.
280          */
getProcessedHeapData()281         public byte[] getProcessedHeapData() {
282             return mProcessedHeapData;
283         }
284 
setProcessedHeapMap(Map<Integer, ArrayList<HeapSegmentElement>> heapMap)285         public void setProcessedHeapMap(Map<Integer, ArrayList<HeapSegmentElement>> heapMap) {
286             mProcessedHeapMap = heapMap;
287         }
288 
getProcessedHeapMap()289         public Map<Integer, ArrayList<HeapSegmentElement>> getProcessedHeapMap() {
290             return mProcessedHeapMap;
291         }
292     }
293 
294     /**
295      * Handlers able to act on HPROF dumps.
296      */
297     public interface IHprofDumpHandler {
298         /**
299          * Called when a HPROF dump succeeded.
300          * @param remoteFilePath the device-side path of the HPROF file.
301          * @param client the client for which the HPROF file was.
302          */
onSuccess(String remoteFilePath, Client client)303         void onSuccess(String remoteFilePath, Client client);
304 
305         /**
306          * Called when a HPROF dump was successful.
307          * @param data the data containing the HPROF file, streamed from the VM
308          * @param client the client that was profiled.
309          */
onSuccess(byte[] data, Client client)310         void onSuccess(byte[] data, Client client);
311 
312         /**
313          * Called when a hprof dump failed to end on the VM side
314          * @param client the client that was profiled.
315          * @param message an optional (<code>null<code> ok) error message to be displayed.
316          */
onEndFailure(Client client, String message)317         void onEndFailure(Client client, String message);
318     }
319 
320     /**
321      * Handlers able to act on Method profiling info
322      */
323     public interface IMethodProfilingHandler {
324         /**
325          * Called when a method tracing was successful.
326          * @param remoteFilePath the device-side path of the trace file.
327          * @param client the client that was profiled.
328          */
onSuccess(String remoteFilePath, Client client)329         void onSuccess(String remoteFilePath, Client client);
330 
331         /**
332          * Called when a method tracing was successful.
333          * @param data the data containing the trace file, streamed from the VM
334          * @param client the client that was profiled.
335          */
onSuccess(byte[] data, Client client)336         void onSuccess(byte[] data, Client client);
337 
338         /**
339          * Called when method tracing failed to start
340          * @param client the client that was profiled.
341          * @param message an optional (<code>null<code> ok) error message to be displayed.
342          */
onStartFailure(Client client, String message)343         void onStartFailure(Client client, String message);
344 
345         /**
346          * Called when method tracing failed to end on the VM side
347          * @param client the client that was profiled.
348          * @param message an optional (<code>null<code> ok) error message to be displayed.
349          */
onEndFailure(Client client, String message)350         void onEndFailure(Client client, String message);
351     }
352 
353     /**
354      * Sets the handler to receive notifications when an HPROF dump succeeded or failed.
355      */
setHprofDumpHandler(IHprofDumpHandler handler)356     public static void setHprofDumpHandler(IHprofDumpHandler handler) {
357         sHprofDumpHandler = handler;
358     }
359 
getHprofDumpHandler()360     static IHprofDumpHandler getHprofDumpHandler() {
361         return sHprofDumpHandler;
362     }
363 
364     /**
365      * Sets the handler to receive notifications when an HPROF dump succeeded or failed.
366      */
setMethodProfilingHandler(IMethodProfilingHandler handler)367     public static void setMethodProfilingHandler(IMethodProfilingHandler handler) {
368         sMethodProfilingHandler = handler;
369     }
370 
getMethodProfilingHandler()371     static IMethodProfilingHandler getMethodProfilingHandler() {
372         return sMethodProfilingHandler;
373     }
374 
375     /**
376      * Generic constructor.
377      */
ClientData(int pid)378     ClientData(int pid) {
379         mPid = pid;
380 
381         mDebuggerInterest = DebuggerStatus.DEFAULT;
382         mThreadMap = new TreeMap<Integer,ThreadInfo>();
383     }
384 
385     /**
386      * Returns whether the process is DDM-aware.
387      */
isDdmAware()388     public boolean isDdmAware() {
389         return mIsDdmAware;
390     }
391 
392     /**
393      * Sets DDM-aware status.
394      */
isDdmAware(boolean aware)395     void isDdmAware(boolean aware) {
396         mIsDdmAware = aware;
397     }
398 
399     /**
400      * Returns the process ID.
401      */
getPid()402     public int getPid() {
403         return mPid;
404     }
405 
406     /**
407      * Returns the Client's VM identifier.
408      */
getVmIdentifier()409     public String getVmIdentifier() {
410         return mVmIdentifier;
411     }
412 
413     /**
414      * Sets VM identifier.
415      */
setVmIdentifier(String ident)416     void setVmIdentifier(String ident) {
417         mVmIdentifier = ident;
418     }
419 
420     /**
421      * Returns the client description.
422      * <p/>This is generally the name of the package defined in the
423      * <code>AndroidManifest.xml</code>.
424      *
425      * @return the client description or <code>null</code> if not the description was not yet
426      * sent by the client.
427      */
getClientDescription()428     public String getClientDescription() {
429         return mClientDescription;
430     }
431 
432     /**
433      * Sets client description.
434      *
435      * There may be a race between HELO and APNM.  Rather than try
436      * to enforce ordering on the device, we just don't allow an empty
437      * name to replace a specified one.
438      */
setClientDescription(String description)439     void setClientDescription(String description) {
440         if (mClientDescription == null && description.length() > 0) {
441             /*
442              * The application VM is first named <pre-initialized> before being assigned
443              * its real name.
444              * Depending on the timing, we can get an APNM chunk setting this name before
445              * another one setting the final actual name. So if we get a SetClientDescription
446              * with this value we ignore it.
447              */
448             if (PRE_INITIALIZED.equals(description) == false) {
449                 mClientDescription = description;
450             }
451         }
452     }
453 
454     /**
455      * Returns the debugger connection status.
456      */
getDebuggerConnectionStatus()457     public DebuggerStatus getDebuggerConnectionStatus() {
458         return mDebuggerInterest;
459     }
460 
461     /**
462      * Sets debugger connection status.
463      */
setDebuggerConnectionStatus(DebuggerStatus status)464     void setDebuggerConnectionStatus(DebuggerStatus status) {
465         mDebuggerInterest = status;
466     }
467 
468     /**
469      * Sets the current heap info values for the specified heap.
470      *
471      * @param heapId The heap whose info to update
472      * @param sizeInBytes The size of the heap, in bytes
473      * @param bytesAllocated The number of bytes currently allocated in the heap
474      * @param objectsAllocated The number of objects currently allocated in
475      *                         the heap
476      */
477     // TODO: keep track of timestamp, reason
setHeapInfo(int heapId, long maxSizeInBytes, long sizeInBytes, long bytesAllocated, long objectsAllocated)478     synchronized void setHeapInfo(int heapId, long maxSizeInBytes,
479             long sizeInBytes, long bytesAllocated, long objectsAllocated) {
480         HashMap<String, Long> heapInfo = new HashMap<String, Long>();
481         heapInfo.put(HEAP_MAX_SIZE_BYTES, maxSizeInBytes);
482         heapInfo.put(HEAP_SIZE_BYTES, sizeInBytes);
483         heapInfo.put(HEAP_BYTES_ALLOCATED, bytesAllocated);
484         heapInfo.put(HEAP_OBJECTS_ALLOCATED, objectsAllocated);
485         mHeapInfoMap.put(heapId, heapInfo);
486     }
487 
488     /**
489      * Returns the {@link HeapData} object for the VM.
490      */
getVmHeapData()491     public HeapData getVmHeapData() {
492         return mHeapData;
493     }
494 
495     /**
496      * Returns the {@link HeapData} object for the native code.
497      */
getNativeHeapData()498     HeapData getNativeHeapData() {
499         return mNativeHeapData;
500     }
501 
502     /**
503      * Returns an iterator over the list of known VM heap ids.
504      * <p/>
505      * The caller must synchronize on the {@link ClientData} object while iterating.
506      *
507      * @return an iterator over the list of heap ids
508      */
getVmHeapIds()509     public synchronized Iterator<Integer> getVmHeapIds() {
510         return mHeapInfoMap.keySet().iterator();
511     }
512 
513     /**
514      * Returns the most-recent info values for the specified VM heap.
515      *
516      * @param heapId The heap whose info should be returned
517      * @return a map containing the info values for the specified heap.
518      *         Returns <code>null</code> if the heap ID is unknown.
519      */
getVmHeapInfo(int heapId)520     public synchronized Map<String, Long> getVmHeapInfo(int heapId) {
521         return mHeapInfoMap.get(heapId);
522     }
523 
524     /**
525      * Adds a new thread to the list.
526      */
addThread(int threadId, String threadName)527     synchronized void addThread(int threadId, String threadName) {
528         ThreadInfo attr = new ThreadInfo(threadId, threadName);
529         mThreadMap.put(threadId, attr);
530     }
531 
532     /**
533      * Removes a thread from the list.
534      */
removeThread(int threadId)535     synchronized void removeThread(int threadId) {
536         mThreadMap.remove(threadId);
537     }
538 
539     /**
540      * Returns the list of threads as {@link ThreadInfo} objects.
541      * <p/>The list is empty until a thread update was requested with
542      * {@link Client#requestThreadUpdate()}.
543      */
getThreads()544     public synchronized ThreadInfo[] getThreads() {
545         Collection<ThreadInfo> threads = mThreadMap.values();
546         return threads.toArray(new ThreadInfo[threads.size()]);
547     }
548 
549     /**
550      * Returns the {@link ThreadInfo} by thread id.
551      */
getThread(int threadId)552     synchronized ThreadInfo getThread(int threadId) {
553         return mThreadMap.get(threadId);
554     }
555 
clearThreads()556     synchronized void clearThreads() {
557         mThreadMap.clear();
558     }
559 
560     /**
561      * Returns the list of {@link NativeAllocationInfo}.
562      * @see Client#requestNativeHeapInformation()
563      */
getNativeAllocationList()564     public synchronized List<NativeAllocationInfo> getNativeAllocationList() {
565         return Collections.unmodifiableList(mNativeAllocationList);
566     }
567 
568     /**
569      * adds a new {@link NativeAllocationInfo} to the {@link Client}
570      * @param allocInfo The {@link NativeAllocationInfo} to add.
571      */
addNativeAllocation(NativeAllocationInfo allocInfo)572     synchronized void addNativeAllocation(NativeAllocationInfo allocInfo) {
573         mNativeAllocationList.add(allocInfo);
574     }
575 
576     /**
577      * Clear the current malloc info.
578      */
clearNativeAllocationInfo()579     synchronized void clearNativeAllocationInfo() {
580         mNativeAllocationList.clear();
581     }
582 
583     /**
584      * Returns the total native memory.
585      * @see Client#requestNativeHeapInformation()
586      */
getTotalNativeMemory()587     public synchronized int getTotalNativeMemory() {
588         return mNativeTotalMemory;
589     }
590 
setTotalNativeMemory(int totalMemory)591     synchronized void setTotalNativeMemory(int totalMemory) {
592         mNativeTotalMemory = totalMemory;
593     }
594 
addNativeLibraryMapInfo(long startAddr, long endAddr, String library)595     synchronized void addNativeLibraryMapInfo(long startAddr, long endAddr, String library) {
596         mNativeLibMapInfo.add(new NativeLibraryMapInfo(startAddr, endAddr, library));
597     }
598 
599     /**
600      * Returns the list of native libraries mapped in memory for this client.
601      */
getMappedNativeLibraries()602     public synchronized List<NativeLibraryMapInfo> getMappedNativeLibraries() {
603         return Collections.unmodifiableList(mNativeLibMapInfo);
604     }
605 
setAllocationStatus(AllocationTrackingStatus status)606     synchronized void setAllocationStatus(AllocationTrackingStatus status) {
607         mAllocationStatus = status;
608     }
609 
610     /**
611      * Returns the allocation tracking status.
612      * @see Client#requestAllocationStatus()
613      */
getAllocationStatus()614     public synchronized AllocationTrackingStatus getAllocationStatus() {
615         return mAllocationStatus;
616     }
617 
setAllocations(AllocationInfo[] allocs)618     synchronized void setAllocations(AllocationInfo[] allocs) {
619         mAllocations = allocs;
620     }
621 
622     /**
623      * Returns the list of tracked allocations.
624      * @see Client#requestAllocationDetails()
625      */
getAllocations()626     public synchronized AllocationInfo[] getAllocations() {
627         return mAllocations;
628     }
629 
addFeature(String feature)630     void addFeature(String feature) {
631         mFeatures.add(feature);
632     }
633 
634     /**
635      * Returns true if the {@link Client} supports the given <var>feature</var>
636      * @param feature The feature to test.
637      * @return true if the feature is supported
638      *
639      * @see ClientData#FEATURE_PROFILING
640      * @see ClientData#FEATURE_HPROF
641      */
hasFeature(String feature)642     public boolean hasFeature(String feature) {
643         return mFeatures.contains(feature);
644     }
645 
646     /**
647      * Sets the device-side path to the hprof file being written
648      * @param pendingHprofDump the file to the hprof file
649      */
setPendingHprofDump(String pendingHprofDump)650     void setPendingHprofDump(String pendingHprofDump) {
651         mPendingHprofDump = pendingHprofDump;
652     }
653 
654     /**
655      * Returns the path to the device-side hprof file being written.
656      */
getPendingHprofDump()657     String getPendingHprofDump() {
658         return mPendingHprofDump;
659     }
660 
hasPendingHprofDump()661     public boolean hasPendingHprofDump() {
662         return mPendingHprofDump != null;
663     }
664 
setMethodProfilingStatus(MethodProfilingStatus status)665     synchronized void setMethodProfilingStatus(MethodProfilingStatus status) {
666         mProfilingStatus = status;
667     }
668 
669     /**
670      * Returns the method profiling status.
671      * @see Client#requestMethodProfilingStatus()
672      */
getMethodProfilingStatus()673     public synchronized MethodProfilingStatus getMethodProfilingStatus() {
674         return mProfilingStatus;
675     }
676 
677     /**
678      * Sets the device-side path to the method profile file being written
679      * @param pendingMethodProfiling the file being written
680      */
setPendingMethodProfiling(String pendingMethodProfiling)681     void setPendingMethodProfiling(String pendingMethodProfiling) {
682         mPendingMethodProfiling = pendingMethodProfiling;
683     }
684 
685     /**
686      * Returns the path to the device-side method profiling file being written.
687      */
getPendingMethodProfiling()688     String getPendingMethodProfiling() {
689         return mPendingMethodProfiling;
690     }
691 }
692 
693