Home | History | Annotate | Download | only in ddmlib
      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          */
    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          */
    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          */
    244         synchronized void sealHeapData() {
    245             mHeapDataComplete = true;
    246         }
    247 
    248         /**
    249          * Returns whether the heap data has been sealed.
    250          */
    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          */
    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          */
    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          */
    281         public byte[] getProcessedHeapData() {
    282             return mProcessedHeapData;
    283         }
    284 
    285         public void setProcessedHeapMap(Map<Integer, ArrayList<HeapSegmentElement>> heapMap) {
    286             mProcessedHeapMap = heapMap;
    287         }
    288 
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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      */
    356     public static void setHprofDumpHandler(IHprofDumpHandler handler) {
    357         sHprofDumpHandler = handler;
    358     }
    359 
    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      */
    367     public static void setMethodProfilingHandler(IMethodProfilingHandler handler) {
    368         sMethodProfilingHandler = handler;
    369     }
    370 
    371     static IMethodProfilingHandler getMethodProfilingHandler() {
    372         return sMethodProfilingHandler;
    373     }
    374 
    375     /**
    376      * Generic constructor.
    377      */
    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      */
    388     public boolean isDdmAware() {
    389         return mIsDdmAware;
    390     }
    391 
    392     /**
    393      * Sets DDM-aware status.
    394      */
    395     void isDdmAware(boolean aware) {
    396         mIsDdmAware = aware;
    397     }
    398 
    399     /**
    400      * Returns the process ID.
    401      */
    402     public int getPid() {
    403         return mPid;
    404     }
    405 
    406     /**
    407      * Returns the Client's VM identifier.
    408      */
    409     public String getVmIdentifier() {
    410         return mVmIdentifier;
    411     }
    412 
    413     /**
    414      * Sets VM identifier.
    415      */
    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      */
    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      */
    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      */
    457     public DebuggerStatus getDebuggerConnectionStatus() {
    458         return mDebuggerInterest;
    459     }
    460 
    461     /**
    462      * Sets debugger connection status.
    463      */
    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
    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      */
    491     public HeapData getVmHeapData() {
    492         return mHeapData;
    493     }
    494 
    495     /**
    496      * Returns the {@link HeapData} object for the native code.
    497      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    552     synchronized ThreadInfo getThread(int threadId) {
    553         return mThreadMap.get(threadId);
    554     }
    555 
    556     synchronized void clearThreads() {
    557         mThreadMap.clear();
    558     }
    559 
    560     /**
    561      * Returns the list of {@link NativeAllocationInfo}.
    562      * @see Client#requestNativeHeapInformation()
    563      */
    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      */
    572     synchronized void addNativeAllocation(NativeAllocationInfo allocInfo) {
    573         mNativeAllocationList.add(allocInfo);
    574     }
    575 
    576     /**
    577      * Clear the current malloc info.
    578      */
    579     synchronized void clearNativeAllocationInfo() {
    580         mNativeAllocationList.clear();
    581     }
    582 
    583     /**
    584      * Returns the total native memory.
    585      * @see Client#requestNativeHeapInformation()
    586      */
    587     public synchronized int getTotalNativeMemory() {
    588         return mNativeTotalMemory;
    589     }
    590 
    591     synchronized void setTotalNativeMemory(int totalMemory) {
    592         mNativeTotalMemory = totalMemory;
    593     }
    594 
    595     synchronized void addNativeLibraryMapInfo(long startAddr, long endAddr, String library) {
    596         mNativeLibMapInfo.add(new NativeLibraryMapInfo(startAddr, endAddr, library));
    597     }
    598 
    599     /**
    600      * Returns an {@link Iterator} on {@link NativeLibraryMapInfo} objects.
    601      * <p/>
    602      * The caller must synchronize on the {@link ClientData} object while iterating.
    603      */
    604     public synchronized Iterator<NativeLibraryMapInfo> getNativeLibraryMapInfo() {
    605         return mNativeLibMapInfo.iterator();
    606     }
    607 
    608     synchronized void setAllocationStatus(AllocationTrackingStatus status) {
    609         mAllocationStatus = status;
    610     }
    611 
    612     /**
    613      * Returns the allocation tracking status.
    614      * @see Client#requestAllocationStatus()
    615      */
    616     public synchronized AllocationTrackingStatus getAllocationStatus() {
    617         return mAllocationStatus;
    618     }
    619 
    620     synchronized void setAllocations(AllocationInfo[] allocs) {
    621         mAllocations = allocs;
    622     }
    623 
    624     /**
    625      * Returns the list of tracked allocations.
    626      * @see Client#requestAllocationDetails()
    627      */
    628     public synchronized AllocationInfo[] getAllocations() {
    629         return mAllocations;
    630     }
    631 
    632     void addFeature(String feature) {
    633         mFeatures.add(feature);
    634     }
    635 
    636     /**
    637      * Returns true if the {@link Client} supports the given <var>feature</var>
    638      * @param feature The feature to test.
    639      * @return true if the feature is supported
    640      *
    641      * @see ClientData#FEATURE_PROFILING
    642      * @see ClientData#FEATURE_HPROF
    643      */
    644     public boolean hasFeature(String feature) {
    645         return mFeatures.contains(feature);
    646     }
    647 
    648     /**
    649      * Sets the device-side path to the hprof file being written
    650      * @param pendingHprofDump the file to the hprof file
    651      */
    652     void setPendingHprofDump(String pendingHprofDump) {
    653         mPendingHprofDump = pendingHprofDump;
    654     }
    655 
    656     /**
    657      * Returns the path to the device-side hprof file being written.
    658      */
    659     String getPendingHprofDump() {
    660         return mPendingHprofDump;
    661     }
    662 
    663     public boolean hasPendingHprofDump() {
    664         return mPendingHprofDump != null;
    665     }
    666 
    667     synchronized void setMethodProfilingStatus(MethodProfilingStatus status) {
    668         mProfilingStatus = status;
    669     }
    670 
    671     /**
    672      * Returns the method profiling status.
    673      * @see Client#requestMethodProfilingStatus()
    674      */
    675     public synchronized MethodProfilingStatus getMethodProfilingStatus() {
    676         return mProfilingStatus;
    677     }
    678 
    679     /**
    680      * Sets the device-side path to the method profile file being written
    681      * @param pendingMethodProfiling the file being written
    682      */
    683     void setPendingMethodProfiling(String pendingMethodProfiling) {
    684         mPendingMethodProfiling = pendingMethodProfiling;
    685     }
    686 
    687     /**
    688      * Returns the path to the device-side method profiling file being written.
    689      */
    690     String getPendingMethodProfiling() {
    691         return mPendingMethodProfiling;
    692     }
    693 }
    694 
    695