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