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 java.nio.BufferUnderflowException; 20 import java.nio.ByteBuffer; 21 import java.nio.ByteOrder; 22 import java.text.ParseException; 23 24 /** 25 * Describes the types and locations of objects in a segment of a heap. 26 */ 27 public 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 */ HeapSegmentElement()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 */ HeapSegmentElement(HeapSegment hs)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 */ set(HeapSegment hs)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 = (int)data.get() & 0x000000ff; 166 int eLen = ((int)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 = (int)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 += ((int)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 getSolidity()188 public int getSolidity() { 189 return mSolidity; 190 } 191 setSolidity(int solidity)192 public void setSolidity(int solidity) { 193 this.mSolidity = solidity; 194 } 195 getKind()196 public int getKind() { 197 return mKind; 198 } 199 setKind(int kind)200 public void setKind(int kind) { 201 this.mKind = kind; 202 } 203 getLength()204 public int getLength() { 205 return mLength; 206 } 207 setLength(int length)208 public void setLength(int length) { 209 this.mLength = length; 210 } 211 compareTo(HeapSegmentElement other)212 public int compareTo(HeapSegmentElement other) { 213 if (mLength != other.mLength) { 214 return mLength < other.mLength ? -1 : 1; 215 } 216 return 0; 217 } 218 } 219 220 //* The ID of the heap that this segment belongs to. 221 protected int mHeapId; 222 223 //* The size of an allocation unit, in bytes. (e.g., 8 bytes) 224 protected int mAllocationUnitSize; 225 226 //* The virtual address of the start of this segment. 227 protected long mStartAddress; 228 229 //* The offset of this pices from mStartAddress, in bytes. 230 protected int mOffset; 231 232 //* The number of allocation units described in this segment. 233 protected int mAllocationUnitCount; 234 235 //* The raw data that describes the contents of this segment. 236 protected ByteBuffer mUsageData; 237 238 //* mStartAddress is set to this value when the segment becomes invalid. 239 private final static long INVALID_START_ADDRESS = -1; 240 241 /** 242 * Create a new HeapSegment based on the raw contents 243 * of an HPSG chunk. 244 * 245 * @param hpsgData The raw data from an HPSG chunk. 246 * @throws BufferUnderflowException if hpsgData is too small 247 * to hold the HPSG chunk header data. 248 */ HeapSegment(ByteBuffer hpsgData)249 public HeapSegment(ByteBuffer hpsgData) throws BufferUnderflowException { 250 /* Read the HPSG chunk header. 251 * These get*() calls may throw a BufferUnderflowException 252 * if the underlying data isn't big enough. 253 */ 254 hpsgData.order(ByteOrder.BIG_ENDIAN); 255 mHeapId = hpsgData.getInt(); 256 mAllocationUnitSize = (int) hpsgData.get(); 257 mStartAddress = (long) hpsgData.getInt() & 0x00000000ffffffffL; 258 mOffset = hpsgData.getInt(); 259 mAllocationUnitCount = hpsgData.getInt(); 260 261 // Hold onto the remainder of the data. 262 mUsageData = hpsgData.slice(); 263 mUsageData.order(ByteOrder.BIG_ENDIAN); // doesn't actually matter 264 265 // Validate the data. 266 //xxx do it 267 //xxx make sure the number of elements matches mAllocationUnitCount. 268 //xxx make sure the last element doesn't have P set 269 } 270 271 /** 272 * See if this segment still contains data, and has not been 273 * appended to another segment. 274 * 275 * @return true if this segment has not been appended to 276 * another segment. 277 */ isValid()278 public boolean isValid() { 279 return mStartAddress != INVALID_START_ADDRESS; 280 } 281 282 /** 283 * See if <code>other</code> comes immediately after this segment. 284 * 285 * @param other The HeapSegment to check. 286 * @return true if <code>other</code> comes immediately after this 287 * segment. 288 */ canAppend(HeapSegment other)289 public boolean canAppend(HeapSegment other) { 290 return isValid() && other.isValid() && mHeapId == other.mHeapId && 291 mAllocationUnitSize == other.mAllocationUnitSize && 292 getEndAddress() == other.getStartAddress(); 293 } 294 295 /** 296 * Append the contents of <code>other</code> to this segment 297 * if it describes the segment immediately after this one. 298 * 299 * @param other The segment to append to this segment, if possible. 300 * If appended, <code>other</code> will be invalid 301 * when this method returns. 302 * @return true if <code>other</code> was successfully appended to 303 * this segment. 304 */ append(HeapSegment other)305 public boolean append(HeapSegment other) { 306 if (canAppend(other)) { 307 /* Preserve the position. The mark is not preserved, 308 * but we don't use it anyway. 309 */ 310 int pos = mUsageData.position(); 311 312 // Guarantee that we have enough room for the new data. 313 if (mUsageData.capacity() - mUsageData.limit() < 314 other.mUsageData.limit()) { 315 /* Grow more than necessary in case another append() 316 * is about to happen. 317 */ 318 int newSize = mUsageData.limit() + other.mUsageData.limit(); 319 ByteBuffer newData = ByteBuffer.allocate(newSize * 2); 320 321 mUsageData.rewind(); 322 newData.put(mUsageData); 323 mUsageData = newData; 324 } 325 326 // Copy the data from the other segment and restore the position. 327 other.mUsageData.rewind(); 328 mUsageData.put(other.mUsageData); 329 mUsageData.position(pos); 330 331 // Fix this segment's header to cover the new data. 332 mAllocationUnitCount += other.mAllocationUnitCount; 333 334 // Mark the other segment as invalid. 335 other.mStartAddress = INVALID_START_ADDRESS; 336 other.mUsageData = null; 337 338 return true; 339 } else { 340 return false; 341 } 342 } 343 getStartAddress()344 public long getStartAddress() { 345 return mStartAddress + mOffset; 346 } 347 getLength()348 public int getLength() { 349 return mAllocationUnitSize * mAllocationUnitCount; 350 } 351 getEndAddress()352 public long getEndAddress() { 353 return getStartAddress() + getLength(); 354 } 355 rewindElements()356 public void rewindElements() { 357 if (mUsageData != null) { 358 mUsageData.rewind(); 359 } 360 } 361 getNextElement(HeapSegmentElement reuse)362 public HeapSegmentElement getNextElement(HeapSegmentElement reuse) { 363 try { 364 if (reuse != null) { 365 return reuse.set(this); 366 } else { 367 return new HeapSegmentElement(this); 368 } 369 } catch (BufferUnderflowException ex) { 370 /* Normal "end of buffer" situation. 371 */ 372 } catch (ParseException ex) { 373 /* Malformed data. 374 */ 375 //TODO: we should catch this in the constructor 376 } 377 return null; 378 } 379 380 /* 381 * Method overrides for Comparable 382 */ 383 @Override equals(Object o)384 public boolean equals(Object o) { 385 if (o instanceof HeapSegment) { 386 return compareTo((HeapSegment) o) == 0; 387 } 388 return false; 389 } 390 391 @Override hashCode()392 public int hashCode() { 393 return mHeapId * 31 + 394 mAllocationUnitSize * 31 + 395 (int) mStartAddress * 31 + 396 mOffset * 31 + 397 mAllocationUnitCount * 31 + 398 mUsageData.hashCode(); 399 } 400 401 @Override toString()402 public String toString() { 403 StringBuilder str = new StringBuilder(); 404 405 str.append("HeapSegment { heap ").append(mHeapId) 406 .append(", start 0x") 407 .append(Integer.toHexString((int) getStartAddress())) 408 .append(", length ").append(getLength()) 409 .append(" }"); 410 411 return str.toString(); 412 } 413 compareTo(HeapSegment other)414 public int compareTo(HeapSegment other) { 415 if (mHeapId != other.mHeapId) { 416 return mHeapId < other.mHeapId ? -1 : 1; 417 } 418 if (getStartAddress() != other.getStartAddress()) { 419 return getStartAddress() < other.getStartAddress() ? -1 : 1; 420 } 421 422 /* If two segments have the same start address, the rest of 423 * the fields should be equal. Go through the motions, though. 424 * Note that we re-check the components of getStartAddress() 425 * (mStartAddress and mOffset) to make sure that all fields in 426 * an equal segment are equal. 427 */ 428 429 if (mAllocationUnitSize != other.mAllocationUnitSize) { 430 return mAllocationUnitSize < other.mAllocationUnitSize ? -1 : 1; 431 } 432 if (mStartAddress != other.mStartAddress) { 433 return mStartAddress < other.mStartAddress ? -1 : 1; 434 } 435 if (mOffset != other.mOffset) { 436 return mOffset < other.mOffset ? -1 : 1; 437 } 438 if (mAllocationUnitCount != other.mAllocationUnitCount) { 439 return mAllocationUnitCount < other.mAllocationUnitCount ? -1 : 1; 440 } 441 if (mUsageData != other.mUsageData) { 442 return mUsageData.compareTo(other.mUsageData); 443 } 444 return 0; 445 } 446 } 447