All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.ddmlib.ClientData Maven / Gradle / Ivy

There is a newer version: 25.3.0
Show newest version
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ddmlib;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ddmlib.HeapSegment.HeapSegmentElement;

import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;


/**
 * Contains the data of a {@link Client}.
 */
public class ClientData {
    /* This is a place to stash data associated with a Client, such as thread
    * states or heap data.  ClientData maps 1:1 to Client, but it's a little
    * cleaner if we separate the data out.
    *
    * Message handlers are welcome to stash arbitrary data here.
    *
    * IMPORTANT: The data here is written by HandleFoo methods and read by
    * FooPanel methods, which run in different threads.  All non-trivial
    * access should be synchronized against the ClientData object.
    */


    /** Temporary name of VM to be ignored. */
    private static final String PRE_INITIALIZED = ""; //$NON-NLS-1$

    public static enum DebuggerStatus {
        /** Debugger connection status: not waiting on one, not connected to one, but accepting
         * new connections. This is the default value. */
        DEFAULT,
        /**
         * Debugger connection status: the application's VM is paused, waiting for a debugger to
         * connect to it before resuming. */
        WAITING,
        /** Debugger connection status : Debugger is connected */
        ATTACHED,
        /** Debugger connection status: The listening port for debugger connection failed to listen.
         * No debugger will be able to connect. */
        ERROR
    }

    public static enum AllocationTrackingStatus {
        /**
         * Allocation tracking status: unknown.
         * 

This happens right after a {@link Client} is discovered * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query * regarding its allocation tracking status. * @see Client#requestAllocationStatus() */ UNKNOWN, /** Allocation tracking status: the {@link Client} is not tracking allocations. */ OFF, /** Allocation tracking status: the {@link Client} is tracking allocations. */ ON } public static enum MethodProfilingStatus { /** * Method profiling status: unknown. *

This happens right after a {@link Client} is discovered * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query * regarding its method profiling status. * @see Client#requestMethodProfilingStatus() */ UNKNOWN, /** Method profiling status: the {@link Client} is not profiling method calls. */ OFF, /** Method profiling status: the {@link Client} is tracing method calls. */ TRACER_ON, /** Method profiling status: the {@link Client} is being profiled via sampling. */ SAMPLER_ON } /** * String for feature enabling starting/stopping method profiling * @see #hasFeature(String) */ public static final String FEATURE_PROFILING = "method-trace-profiling"; //$NON-NLS-1$ /** * String for feature enabling direct streaming of method profiling data * @see #hasFeature(String) */ public static final String FEATURE_PROFILING_STREAMING = "method-trace-profiling-streaming"; //$NON-NLS-1$ /** * String for feature enabling sampling profiler. * @see #hasFeature(String) */ public static final String FEATURE_SAMPLING_PROFILER = "method-sample-profiling"; //$NON-NLS-1$ /** * String for feature indicating support for tracing OpenGL calls. * @see #hasFeature(String) */ public static final String FEATURE_OPENGL_TRACING = "opengl-tracing"; //$NON-NLS-1$ /** * String for feature indicating support for providing view hierarchy. * @see #hasFeature(String) */ public static final String FEATURE_VIEW_HIERARCHY = "view-hierarchy"; //$NON-NLS-1$ /** * String for feature allowing to dump hprof files * @see #hasFeature(String) */ public static final String FEATURE_HPROF = "hprof-heap-dump"; //$NON-NLS-1$ /** * String for feature allowing direct streaming of hprof dumps * @see #hasFeature(String) */ public static final String FEATURE_HPROF_STREAMING = "hprof-heap-dump-streaming"; //$NON-NLS-1$ private static IHprofDumpHandler sHprofDumpHandler; private static IMethodProfilingHandler sMethodProfilingHandler; private static IAllocationTrackingHandler sAllocationTrackingHandler; // is this a DDM-aware client? private boolean mIsDdmAware; // the client's process ID private final int mPid; // Java VM identification string private String mVmIdentifier; // client's self-description private String mClientDescription; // client's user id (on device in a multi user environment) private int mUserId; // client's user id is valid private boolean mValidUserId; // client's ABI private String mAbi; // jvm flag: currently only indicates whether checkJni is enabled private String mJvmFlags; // how interested are we in a debugger? private DebuggerStatus mDebuggerInterest; // List of supported features by the client. private final HashSet mFeatures = new HashSet(); // Thread tracking (THCR, THDE). private TreeMap mThreadMap; /** VM Heap data */ private final HeapData mHeapData = new HeapData(); /** Native Heap data */ private final HeapData mNativeHeapData = new HeapData(); private HashMap mHeapInfoMap = new HashMap(); /** library map info. Stored here since the backtrace data * is computed on a need to display basis. */ private ArrayList mNativeLibMapInfo = new ArrayList(); /** Native Alloc info list */ private ArrayList mNativeAllocationList = new ArrayList(); private int mNativeTotalMemory; private AllocationInfo[] mAllocations; private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN; private String mPendingHprofDump; private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN; private String mPendingMethodProfiling; /** * Heap Information. *

The heap is composed of several {@link HeapSegment} objects. *

A call to {@link #isHeapDataComplete()} will indicate if the segments (available through * {@link #getHeapSegments()}) represent the full heap. */ public static class HeapData { private TreeSet mHeapSegments = new TreeSet(); private boolean mHeapDataComplete = false; private byte[] mProcessedHeapData; private Map> mProcessedHeapMap; /** * Abandon the current list of heap segments. */ public synchronized void clearHeapData() { /* Abandon the old segments instead of just calling .clear(). * This lets the user hold onto the old set if it wants to. */ mHeapSegments = new TreeSet(); mHeapDataComplete = false; } /** * Add raw HPSG chunk data to the list of heap segments. * * @param data The raw data from an HPSG chunk. */ synchronized void addHeapData(ByteBuffer data) { HeapSegment hs; if (mHeapDataComplete) { clearHeapData(); } try { hs = new HeapSegment(data); } catch (BufferUnderflowException e) { System.err.println("Discarding short HPSG data (length " + data.limit() + ")"); return; } mHeapSegments.add(hs); } /** * Called when all heap data has arrived. */ synchronized void sealHeapData() { mHeapDataComplete = true; } /** * Returns whether the heap data has been sealed. */ public boolean isHeapDataComplete() { return mHeapDataComplete; } /** * Get the collected heap data, if sealed. * * @return The list of heap segments if the heap data has been sealed, or null if it hasn't. */ public Collection getHeapSegments() { if (isHeapDataComplete()) { return mHeapSegments; } return null; } /** * Sets the processed heap data. * * @param heapData The new heap data (can be null) */ public void setProcessedHeapData(byte[] heapData) { mProcessedHeapData = heapData; } /** * Get the processed heap data, if present. * * @return the processed heap data, or null. */ public byte[] getProcessedHeapData() { return mProcessedHeapData; } public void setProcessedHeapMap(Map> heapMap) { mProcessedHeapMap = heapMap; } public Map> getProcessedHeapMap() { return mProcessedHeapMap; } } public static class HeapInfo { public long maxSizeInBytes; public long sizeInBytes; public long bytesAllocated; public long objectsAllocated; public long timeStamp; public byte reason; public HeapInfo(long maxSizeInBytes, long sizeInBytes, long bytesAllocated, long objectsAllocated, long timeStamp, byte reason) { this.maxSizeInBytes = maxSizeInBytes; this.sizeInBytes = sizeInBytes; this.bytesAllocated = bytesAllocated; this.objectsAllocated = objectsAllocated; this.timeStamp = timeStamp; this.reason = reason; } } /** * Handlers able to act on HPROF dumps. */ public interface IHprofDumpHandler { /** * Called when a HPROF dump succeeded. * @param remoteFilePath the device-side path of the HPROF file. * @param client the client for which the HPROF file was. */ void onSuccess(String remoteFilePath, Client client); /** * Called when a HPROF dump was successful. * @param data the data containing the HPROF file, streamed from the VM * @param client the client that was profiled. */ void onSuccess(byte[] data, Client client); /** * Called when a hprof dump failed to end on the VM side * @param client the client that was profiled. * @param message an optional (null ok) error message to be displayed. */ void onEndFailure(Client client, String message); } /** * Handlers able to act on Method profiling info */ public interface IMethodProfilingHandler { /** * Called when a method tracing was successful. * @param remoteFilePath the device-side path of the trace file. * @param client the client that was profiled. */ void onSuccess(String remoteFilePath, Client client); /** * Called when a method tracing was successful. * @param data the data containing the trace file, streamed from the VM * @param client the client that was profiled. */ void onSuccess(byte[] data, Client client); /** * Called when method tracing failed to start * @param client the client that was profiled. * @param message an optional (null ok) error message to be displayed. */ void onStartFailure(Client client, String message); /** * Called when method tracing failed to end on the VM side * @param client the client that was profiled. * @param message an optional (null ok) error message to be displayed. */ void onEndFailure(Client client, String message); } /* * Handlers able to act on allocation tracking info */ public interface IAllocationTrackingHandler { /** * Called when an allocation tracking was successful. * @param data the data containing the encoded allocations. * See {@link AllocationsParser#parse(java.nio.ByteBuffer)} for parsing this data. * @param client the client for which allocations were tracked. */ void onSuccess(@NonNull byte[] data, @NonNull Client client); } /** * Sets the handler to receive notifications when an HPROF dump succeeded or failed. */ public static void setHprofDumpHandler(IHprofDumpHandler handler) { sHprofDumpHandler = handler; } static IHprofDumpHandler getHprofDumpHandler() { return sHprofDumpHandler; } /** * Sets the handler to receive notifications when an HPROF dump succeeded or failed. */ public static void setMethodProfilingHandler(IMethodProfilingHandler handler) { sMethodProfilingHandler = handler; } static IMethodProfilingHandler getMethodProfilingHandler() { return sMethodProfilingHandler; } public static void setAllocationTrackingHandler(@NonNull IAllocationTrackingHandler handler) { sAllocationTrackingHandler = handler; } @Nullable static IAllocationTrackingHandler getAllocationTrackingHandler() { return sAllocationTrackingHandler; } /** * Generic constructor. */ ClientData(int pid) { mPid = pid; mDebuggerInterest = DebuggerStatus.DEFAULT; mThreadMap = new TreeMap(); } /** * Returns whether the process is DDM-aware. */ public boolean isDdmAware() { return mIsDdmAware; } /** * Sets DDM-aware status. */ void isDdmAware(boolean aware) { mIsDdmAware = aware; } /** * Returns the process ID. */ public int getPid() { return mPid; } /** * Returns the Client's VM identifier. */ public String getVmIdentifier() { return mVmIdentifier; } /** * Sets VM identifier. */ void setVmIdentifier(String ident) { mVmIdentifier = ident; } /** * Returns the client description. *

This is generally the name of the package defined in the * AndroidManifest.xml. * * @return the client description or null if not the description was not yet * sent by the client. */ public String getClientDescription() { return mClientDescription; } /** * Returns the client's user id. * @return user id if set, -1 otherwise */ public int getUserId() { return mUserId; } /** * Returns true if the user id of this client was set. Only devices that support multiple * users will actually return the user id to ddms. For other/older devices, this will not * be set. */ public boolean isValidUserId() { return mValidUserId; } /** Returns the abi flavor (32-bit or 64-bit) of the application, null if unknown or not set. */ @Nullable public String getAbi() { return mAbi; } /** Returns the VM flags in use, or null if unknown. */ public String getJvmFlags() { return mJvmFlags; } /** * Sets client description. * * There may be a race between HELO and APNM. Rather than try * to enforce ordering on the device, we just don't allow an empty * name to replace a specified one. */ void setClientDescription(String description) { if (mClientDescription == null && !description.isEmpty()) { /* * The application VM is first named before being assigned * its real name. * Depending on the timing, we can get an APNM chunk setting this name before * another one setting the final actual name. So if we get a SetClientDescription * with this value we ignore it. */ if (!PRE_INITIALIZED.equals(description)) { mClientDescription = description; } } } void setUserId(int id) { mUserId = id; mValidUserId = true; } void setAbi(String abi) { mAbi = abi; } void setJvmFlags(String jvmFlags) { mJvmFlags = jvmFlags; } /** * Returns the debugger connection status. */ public DebuggerStatus getDebuggerConnectionStatus() { return mDebuggerInterest; } /** * Sets debugger connection status. */ void setDebuggerConnectionStatus(DebuggerStatus status) { mDebuggerInterest = status; } /** * Sets the current heap info values for the specified heap. * @param heapId The heap whose info to update * @param sizeInBytes The size of the heap, in bytes * @param bytesAllocated The number of bytes currently allocated in the heap * @param objectsAllocated The number of objects currently allocated in * @param timeStamp * @param reason */ synchronized void setHeapInfo(int heapId, long maxSizeInBytes, long sizeInBytes, long bytesAllocated, long objectsAllocated, long timeStamp, byte reason) { mHeapInfoMap.put(heapId, new HeapInfo(maxSizeInBytes, sizeInBytes, bytesAllocated, objectsAllocated, timeStamp, reason)); } /** * Returns the {@link HeapData} object for the VM. */ public HeapData getVmHeapData() { return mHeapData; } /** * Returns the {@link HeapData} object for the native code. */ HeapData getNativeHeapData() { return mNativeHeapData; } /** * Returns an iterator over the list of known VM heap ids. *

* The caller must synchronize on the {@link ClientData} object while iterating. * * @return an iterator over the list of heap ids */ public synchronized Iterator getVmHeapIds() { return mHeapInfoMap.keySet().iterator(); } /** * Returns the most-recent info values for the specified VM heap. * * @param heapId The heap whose info should be returned * @return a map containing the info values for the specified heap. * Returns null if the heap ID is unknown. */ public synchronized HeapInfo getVmHeapInfo(int heapId) { return mHeapInfoMap.get(heapId); } /** * Adds a new thread to the list. */ synchronized void addThread(int threadId, String threadName) { ThreadInfo attr = new ThreadInfo(threadId, threadName); mThreadMap.put(threadId, attr); } /** * Removes a thread from the list. */ synchronized void removeThread(int threadId) { mThreadMap.remove(threadId); } /** * Returns the list of threads as {@link ThreadInfo} objects. *

The list is empty until a thread update was requested with * {@link Client#requestThreadUpdate()}. */ public synchronized ThreadInfo[] getThreads() { Collection threads = mThreadMap.values(); return threads.toArray(new ThreadInfo[threads.size()]); } /** * Returns the {@link ThreadInfo} by thread id. */ synchronized ThreadInfo getThread(int threadId) { return mThreadMap.get(threadId); } synchronized void clearThreads() { mThreadMap.clear(); } /** * Returns the list of {@link NativeAllocationInfo}. * @see Client#requestNativeHeapInformation() */ public synchronized List getNativeAllocationList() { return Collections.unmodifiableList(mNativeAllocationList); } /** * adds a new {@link NativeAllocationInfo} to the {@link Client} * @param allocInfo The {@link NativeAllocationInfo} to add. */ synchronized void addNativeAllocation(NativeAllocationInfo allocInfo) { mNativeAllocationList.add(allocInfo); } /** * Clear the current malloc info. */ synchronized void clearNativeAllocationInfo() { mNativeAllocationList.clear(); } /** * Returns the total native memory. * @see Client#requestNativeHeapInformation() */ public synchronized int getTotalNativeMemory() { return mNativeTotalMemory; } synchronized void setTotalNativeMemory(int totalMemory) { mNativeTotalMemory = totalMemory; } synchronized void addNativeLibraryMapInfo(long startAddr, long endAddr, String library) { mNativeLibMapInfo.add(new NativeLibraryMapInfo(startAddr, endAddr, library)); } /** * Returns the list of native libraries mapped in memory for this client. */ public synchronized List getMappedNativeLibraries() { return Collections.unmodifiableList(mNativeLibMapInfo); } synchronized void setAllocationStatus(AllocationTrackingStatus status) { mAllocationStatus = status; } /** * Returns the allocation tracking status. * @see Client#requestAllocationStatus() */ public synchronized AllocationTrackingStatus getAllocationStatus() { return mAllocationStatus; } synchronized void setAllocations(AllocationInfo[] allocs) { mAllocations = allocs; } /** * Returns the list of tracked allocations. * @see Client#requestAllocationDetails() */ @Nullable public synchronized AllocationInfo[] getAllocations() { return mAllocations; } void addFeature(String feature) { mFeatures.add(feature); } /** * Returns true if the {@link Client} supports the given feature * @param feature The feature to test. * @return true if the feature is supported * * @see ClientData#FEATURE_PROFILING * @see ClientData#FEATURE_HPROF */ public boolean hasFeature(String feature) { return mFeatures.contains(feature); } /** * Sets the device-side path to the hprof file being written * @param pendingHprofDump the file to the hprof file */ void setPendingHprofDump(String pendingHprofDump) { mPendingHprofDump = pendingHprofDump; } /** * Returns the path to the device-side hprof file being written. */ String getPendingHprofDump() { return mPendingHprofDump; } public boolean hasPendingHprofDump() { return mPendingHprofDump != null; } synchronized void setMethodProfilingStatus(MethodProfilingStatus status) { mProfilingStatus = status; } /** * Returns the method profiling status. * @see Client#requestMethodProfilingStatus() */ public synchronized MethodProfilingStatus getMethodProfilingStatus() { return mProfilingStatus; } /** * Sets the device-side path to the method profile file being written * @param pendingMethodProfiling the file being written */ void setPendingMethodProfiling(String pendingMethodProfiling) { mPendingMethodProfiling = pendingMethodProfiling; } /** * Returns the path to the device-side method profiling file being written. */ String getPendingMethodProfiling() { return mPendingMethodProfiling; } }