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