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 17package com.android.ddmlib; 18 19import java.nio.BufferUnderflowException; 20import java.nio.ByteBuffer; 21import java.nio.ByteOrder; 22import java.text.ParseException; 23 24/** 25 * Describes the types and locations of objects in a segment of a heap. 26 */ 27public final class HeapSegment implements Comparable<HeapSegment> { 28 29 /** 30 * Describes an object/region encoded in the HPSG data. 31 */ 32 public static class HeapSegmentElement implements Comparable<HeapSegmentElement> { 33 34 /* 35 * Solidity values, which must match the values in 36 * the HPSG data. 37 */ 38 39 /** The element describes a free block. */ 40 public static int SOLIDITY_FREE = 0; 41 42 /** The element is strongly-reachable. */ 43 public static int SOLIDITY_HARD = 1; 44 45 /** The element is softly-reachable. */ 46 public static int SOLIDITY_SOFT = 2; 47 48 /** The element is weakly-reachable. */ 49 public static int SOLIDITY_WEAK = 3; 50 51 /** The element is phantom-reachable. */ 52 public static int SOLIDITY_PHANTOM = 4; 53 54 /** The element is pending finalization. */ 55 public static int SOLIDITY_FINALIZABLE = 5; 56 57 /** The element is not reachable, and is about to be swept/freed. */ 58 public static int SOLIDITY_SWEEP = 6; 59 60 /** The reachability of the object is unknown. */ 61 public static int SOLIDITY_INVALID = -1; 62 63 64 /* 65 * Kind values, which must match the values in 66 * the HPSG data. 67 */ 68 69 /** The element describes a data object. */ 70 public static int KIND_OBJECT = 0; 71 72 /** The element describes a class object. */ 73 public static int KIND_CLASS_OBJECT = 1; 74 75 /** The element describes an array of 1-byte elements. */ 76 public static int KIND_ARRAY_1 = 2; 77 78 /** The element describes an array of 2-byte elements. */ 79 public static int KIND_ARRAY_2 = 3; 80 81 /** The element describes an array of 4-byte elements. */ 82 public static int KIND_ARRAY_4 = 4; 83 84 /** The element describes an array of 8-byte elements. */ 85 public static int KIND_ARRAY_8 = 5; 86 87 /** The element describes an unknown type of object. */ 88 public static int KIND_UNKNOWN = 6; 89 90 /** The element describes a native object. */ 91 public static int KIND_NATIVE = 7; 92 93 /** The object kind is unknown or unspecified. */ 94 public static int KIND_INVALID = -1; 95 96 97 /** 98 * A bit in the HPSG data that indicates that an element should 99 * be combined with the element that follows, typically because 100 * an element is too large to be described by a single element. 101 */ 102 private static int PARTIAL_MASK = 1 << 7; 103 104 105 /** 106 * Describes the reachability/solidity of the element. Must 107 * be set to one of the SOLIDITY_* values. 108 */ 109 private int mSolidity; 110 111 /** 112 * Describes the type/kind of the element. Must be set to one 113 * of the KIND_* values. 114 */ 115 private int mKind; 116 117 /** 118 * Describes the length of the element, in bytes. 119 */ 120 private int mLength; 121 122 123 /** 124 * Creates an uninitialized element. 125 */ 126 public HeapSegmentElement() { 127 setSolidity(SOLIDITY_INVALID); 128 setKind(KIND_INVALID); 129 setLength(-1); 130 } 131 132 /** 133 * Create an element describing the entry at the current 134 * position of hpsgData. 135 * 136 * @param hs The heap segment to pull the entry from. 137 * @throws BufferUnderflowException if there is not a whole entry 138 * following the current position 139 * of hpsgData. 140 * @throws ParseException if the provided data is malformed. 141 */ 142 public HeapSegmentElement(HeapSegment hs) 143 throws BufferUnderflowException, ParseException { 144 set(hs); 145 } 146 147 /** 148 * Replace the element with the entry at the current position of 149 * hpsgData. 150 * 151 * @param hs The heap segment to pull the entry from. 152 * @return this object. 153 * @throws BufferUnderflowException if there is not a whole entry 154 * following the current position of 155 * hpsgData. 156 * @throws ParseException if the provided data is malformed. 157 */ 158 public HeapSegmentElement set(HeapSegment hs) 159 throws BufferUnderflowException, ParseException { 160 161 /* TODO: Maybe keep track of the virtual address of each element 162 * so that they can be examined independently. 163 */ 164 ByteBuffer data = hs.mUsageData; 165 int eState = data.get() & 0x000000ff; 166 int eLen = (data.get() & 0x000000ff) + 1; 167 168 while ((eState & PARTIAL_MASK) != 0) { 169 170 /* If the partial bit was set, the next byte should describe 171 * the same object as the current one. 172 */ 173 int nextState = data.get() & 0x000000ff; 174 if ((nextState & ~PARTIAL_MASK) != (eState & ~PARTIAL_MASK)) { 175 throw new ParseException("State mismatch", data.position()); 176 } 177 eState = nextState; 178 eLen += (data.get() & 0x000000ff) + 1; 179 } 180 181 setSolidity(eState & 0x7); 182 setKind((eState >> 3) & 0x7); 183 setLength(eLen * hs.mAllocationUnitSize); 184 185 return this; 186 } 187 188 public int getSolidity() { 189 return mSolidity; 190 } 191 192 public void setSolidity(int solidity) { 193 this.mSolidity = solidity; 194 } 195 196 public int getKind() { 197 return mKind; 198 } 199 200 public void setKind(int kind) { 201 this.mKind = kind; 202 } 203 204 public int getLength() { 205 return mLength; 206 } 207 208 public void setLength(int length) { 209 this.mLength = length; 210 } 211 212 @Override 213 public int compareTo(HeapSegmentElement other) { 214 if (mLength != other.mLength) { 215 return mLength < other.mLength ? -1 : 1; 216 } 217 return 0; 218 } 219 } 220 221 //* The ID of the heap that this segment belongs to. 222 protected int mHeapId; 223 224 //* The size of an allocation unit, in bytes. (e.g., 8 bytes) 225 protected int mAllocationUnitSize; 226 227 //* The virtual address of the start of this segment. 228 protected long mStartAddress; 229 230 //* The offset of this pices from mStartAddress, in bytes. 231 protected int mOffset; 232 233 //* The number of allocation units described in this segment. 234 protected int mAllocationUnitCount; 235 236 //* The raw data that describes the contents of this segment. 237 protected ByteBuffer mUsageData; 238 239 //* mStartAddress is set to this value when the segment becomes invalid. 240 private final static long INVALID_START_ADDRESS = -1; 241 242 /** 243 * Create a new HeapSegment based on the raw contents 244 * of an HPSG chunk. 245 * 246 * @param hpsgData The raw data from an HPSG chunk. 247 * @throws BufferUnderflowException if hpsgData is too small 248 * to hold the HPSG chunk header data. 249 */ 250 public HeapSegment(ByteBuffer hpsgData) throws BufferUnderflowException { 251 /* Read the HPSG chunk header. 252 * These get*() calls may throw a BufferUnderflowException 253 * if the underlying data isn't big enough. 254 */ 255 hpsgData.order(ByteOrder.BIG_ENDIAN); 256 mHeapId = hpsgData.getInt(); 257 mAllocationUnitSize = hpsgData.get(); 258 mStartAddress = hpsgData.getInt() & 0x00000000ffffffffL; 259 mOffset = hpsgData.getInt(); 260 mAllocationUnitCount = hpsgData.getInt(); 261 262 // Hold onto the remainder of the data. 263 mUsageData = hpsgData.slice(); 264 mUsageData.order(ByteOrder.BIG_ENDIAN); // doesn't actually matter 265 266 // Validate the data. 267//xxx do it 268//xxx make sure the number of elements matches mAllocationUnitCount. 269//xxx make sure the last element doesn't have P set 270 } 271 272 /** 273 * See if this segment still contains data, and has not been 274 * appended to another segment. 275 * 276 * @return true if this segment has not been appended to 277 * another segment. 278 */ 279 public boolean isValid() { 280 return mStartAddress != INVALID_START_ADDRESS; 281 } 282 283 /** 284 * See if <code>other</code> comes immediately after this segment. 285 * 286 * @param other The HeapSegment to check. 287 * @return true if <code>other</code> comes immediately after this 288 * segment. 289 */ 290 public boolean canAppend(HeapSegment other) { 291 return isValid() && other.isValid() && mHeapId == other.mHeapId && 292 mAllocationUnitSize == other.mAllocationUnitSize && 293 getEndAddress() == other.getStartAddress(); 294 } 295 296 /** 297 * Append the contents of <code>other</code> to this segment 298 * if it describes the segment immediately after this one. 299 * 300 * @param other The segment to append to this segment, if possible. 301 * If appended, <code>other</code> will be invalid 302 * when this method returns. 303 * @return true if <code>other</code> was successfully appended to 304 * this segment. 305 */ 306 public boolean append(HeapSegment other) { 307 if (canAppend(other)) { 308 /* Preserve the position. The mark is not preserved, 309 * but we don't use it anyway. 310 */ 311 int pos = mUsageData.position(); 312 313 // Guarantee that we have enough room for the new data. 314 if (mUsageData.capacity() - mUsageData.limit() < 315 other.mUsageData.limit()) { 316 /* Grow more than necessary in case another append() 317 * is about to happen. 318 */ 319 int newSize = mUsageData.limit() + other.mUsageData.limit(); 320 ByteBuffer newData = ByteBuffer.allocate(newSize * 2); 321 322 mUsageData.rewind(); 323 newData.put(mUsageData); 324 mUsageData = newData; 325 } 326 327 // Copy the data from the other segment and restore the position. 328 other.mUsageData.rewind(); 329 mUsageData.put(other.mUsageData); 330 mUsageData.position(pos); 331 332 // Fix this segment's header to cover the new data. 333 mAllocationUnitCount += other.mAllocationUnitCount; 334 335 // Mark the other segment as invalid. 336 other.mStartAddress = INVALID_START_ADDRESS; 337 other.mUsageData = null; 338 339 return true; 340 } else { 341 return false; 342 } 343 } 344 345 public long getStartAddress() { 346 return mStartAddress + mOffset; 347 } 348 349 public int getLength() { 350 return mAllocationUnitSize * mAllocationUnitCount; 351 } 352 353 public long getEndAddress() { 354 return getStartAddress() + getLength(); 355 } 356 357 public void rewindElements() { 358 if (mUsageData != null) { 359 mUsageData.rewind(); 360 } 361 } 362 363 public HeapSegmentElement getNextElement(HeapSegmentElement reuse) { 364 try { 365 if (reuse != null) { 366 return reuse.set(this); 367 } else { 368 return new HeapSegmentElement(this); 369 } 370 } catch (BufferUnderflowException ex) { 371 /* Normal "end of buffer" situation. 372 */ 373 } catch (ParseException ex) { 374 /* Malformed data. 375 */ 376//TODO: we should catch this in the constructor 377 } 378 return null; 379 } 380 381 /* 382 * Method overrides for Comparable 383 */ 384 @Override 385 public boolean equals(Object o) { 386 if (o instanceof HeapSegment) { 387 return compareTo((HeapSegment) o) == 0; 388 } 389 return false; 390 } 391 392 @Override 393 public int hashCode() { 394 return mHeapId * 31 + 395 mAllocationUnitSize * 31 + 396 (int) mStartAddress * 31 + 397 mOffset * 31 + 398 mAllocationUnitCount * 31 + 399 mUsageData.hashCode(); 400 } 401 402 @Override 403 public String toString() { 404 StringBuilder str = new StringBuilder(); 405 406 str.append("HeapSegment { heap ").append(mHeapId) 407 .append(", start 0x") 408 .append(Integer.toHexString((int) getStartAddress())) 409 .append(", length ").append(getLength()) 410 .append(" }"); 411 412 return str.toString(); 413 } 414 415 @Override 416 public int compareTo(HeapSegment other) { 417 if (mHeapId != other.mHeapId) { 418 return mHeapId < other.mHeapId ? -1 : 1; 419 } 420 if (getStartAddress() != other.getStartAddress()) { 421 return getStartAddress() < other.getStartAddress() ? -1 : 1; 422 } 423 424 /* If two segments have the same start address, the rest of 425 * the fields should be equal. Go through the motions, though. 426 * Note that we re-check the components of getStartAddress() 427 * (mStartAddress and mOffset) to make sure that all fields in 428 * an equal segment are equal. 429 */ 430 431 if (mAllocationUnitSize != other.mAllocationUnitSize) { 432 return mAllocationUnitSize < other.mAllocationUnitSize ? -1 : 1; 433 } 434 if (mStartAddress != other.mStartAddress) { 435 return mStartAddress < other.mStartAddress ? -1 : 1; 436 } 437 if (mOffset != other.mOffset) { 438 return mOffset < other.mOffset ? -1 : 1; 439 } 440 if (mAllocationUnitCount != other.mAllocationUnitCount) { 441 return mAllocationUnitCount < other.mAllocationUnitCount ? -1 : 1; 442 } 443 if (mUsageData != other.mUsageData) { 444 return mUsageData.compareTo(other.mUsageData); 445 } 446 return 0; 447 } 448} 449