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