com.android.ddmlib.HeapSegment Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ddmlib Show documentation
Show all versions of ddmlib Show documentation
Library providing APIs to talk to Android devices
/*
* 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 java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.ParseException;
/**
* Describes the types and locations of objects in a segment of a heap.
*/
public final class HeapSegment implements Comparable {
/**
* Describes an object/region encoded in the HPSG data.
*/
public static class HeapSegmentElement implements Comparable {
/*
* Solidity values, which must match the values in
* the HPSG data.
*/
/** The element describes a free block. */
public static final int SOLIDITY_FREE = 0;
/** The element is strongly-reachable. */
public static final int SOLIDITY_HARD = 1;
/** The element is softly-reachable. */
public static final int SOLIDITY_SOFT = 2;
/** The element is weakly-reachable. */
public static final int SOLIDITY_WEAK = 3;
/** The element is phantom-reachable. */
public static final int SOLIDITY_PHANTOM = 4;
/** The element is pending finalization. */
public static final int SOLIDITY_FINALIZABLE = 5;
/** The element is not reachable, and is about to be swept/freed. */
public static final int SOLIDITY_SWEEP = 6;
/** The reachability of the object is unknown. */
public static final int SOLIDITY_INVALID = -1;
/*
* Kind values, which must match the values in
* the HPSG data.
*/
/** The element describes a data object. */
public static final int KIND_OBJECT = 0;
/** The element describes a class object. */
public static final int KIND_CLASS_OBJECT = 1;
/** The element describes an array of 1-byte elements. */
public static final int KIND_ARRAY_1 = 2;
/** The element describes an array of 2-byte elements. */
public static final int KIND_ARRAY_2 = 3;
/** The element describes an array of 4-byte elements. */
public static final int KIND_ARRAY_4 = 4;
/** The element describes an array of 8-byte elements. */
public static final int KIND_ARRAY_8 = 5;
/** The element describes an unknown type of object. */
public static final int KIND_UNKNOWN = 6;
/** The element describes a native object. */
public static final int KIND_NATIVE = 7;
/** The object kind is unknown or unspecified. */
public static final int KIND_INVALID = -1;
/**
* A bit in the HPSG data that indicates that an element should
* be combined with the element that follows, typically because
* an element is too large to be described by a single element.
*/
private static final int PARTIAL_MASK = 1 << 7;
/**
* Describes the reachability/solidity of the element. Must
* be set to one of the SOLIDITY_* values.
*/
private int mSolidity;
/**
* Describes the type/kind of the element. Must be set to one
* of the KIND_* values.
*/
private int mKind;
/**
* Describes the length of the element, in bytes.
*/
private int mLength;
/**
* Creates an uninitialized element.
*/
public HeapSegmentElement() {
setSolidity(SOLIDITY_INVALID);
setKind(KIND_INVALID);
setLength(-1);
}
/**
* Create an element describing the entry at the current
* position of hpsgData.
*
* @param hs The heap segment to pull the entry from.
* @throws BufferUnderflowException if there is not a whole entry
* following the current position
* of hpsgData.
* @throws ParseException if the provided data is malformed.
*/
public HeapSegmentElement(HeapSegment hs)
throws BufferUnderflowException, ParseException {
set(hs);
}
/**
* Replace the element with the entry at the current position of
* hpsgData.
*
* @param hs The heap segment to pull the entry from.
* @return this object.
* @throws BufferUnderflowException if there is not a whole entry
* following the current position of
* hpsgData.
* @throws ParseException if the provided data is malformed.
*/
public HeapSegmentElement set(HeapSegment hs)
throws BufferUnderflowException, ParseException {
/* TODO: Maybe keep track of the virtual address of each element
* so that they can be examined independently.
*/
ByteBuffer data = hs.mUsageData;
int eState = data.get() & 0x000000ff;
int eLen = (data.get() & 0x000000ff) + 1;
while ((eState & PARTIAL_MASK) != 0) {
/* If the partial bit was set, the next byte should describe
* the same object as the current one.
*/
int nextState = data.get() & 0x000000ff;
if ((nextState & ~PARTIAL_MASK) != (eState & ~PARTIAL_MASK)) {
throw new ParseException("State mismatch", data.position());
}
eState = nextState;
eLen += (data.get() & 0x000000ff) + 1;
}
setSolidity(eState & 0x7);
setKind((eState >> 3) & 0x7);
setLength(eLen * hs.mAllocationUnitSize);
return this;
}
public int getSolidity() {
return mSolidity;
}
public void setSolidity(int solidity) {
this.mSolidity = solidity;
}
public int getKind() {
return mKind;
}
public void setKind(int kind) {
this.mKind = kind;
}
public int getLength() {
return mLength;
}
public void setLength(int length) {
this.mLength = length;
}
@Override
public int compareTo(HeapSegmentElement other) {
if (mLength != other.mLength) {
return mLength < other.mLength ? -1 : 1;
}
return 0;
}
}
//* The ID of the heap that this segment belongs to.
protected int mHeapId;
//* The size of an allocation unit, in bytes. (e.g., 8 bytes)
protected int mAllocationUnitSize;
//* The virtual address of the start of this segment.
protected long mStartAddress;
//* The offset of this pices from mStartAddress, in bytes.
protected int mOffset;
//* The number of allocation units described in this segment.
protected int mAllocationUnitCount;
//* The raw data that describes the contents of this segment.
protected ByteBuffer mUsageData;
//* mStartAddress is set to this value when the segment becomes invalid.
private static final long INVALID_START_ADDRESS = -1;
/**
* Create a new HeapSegment based on the raw contents
* of an HPSG chunk.
*
* @param hpsgData The raw data from an HPSG chunk.
* @throws BufferUnderflowException if hpsgData is too small
* to hold the HPSG chunk header data.
*/
public HeapSegment(ByteBuffer hpsgData) throws BufferUnderflowException {
/* Read the HPSG chunk header.
* These get*() calls may throw a BufferUnderflowException
* if the underlying data isn't big enough.
*/
hpsgData.order(ByteOrder.BIG_ENDIAN);
mHeapId = hpsgData.getInt();
mAllocationUnitSize = hpsgData.get();
mStartAddress = hpsgData.getInt() & 0x00000000ffffffffL;
mOffset = hpsgData.getInt();
mAllocationUnitCount = hpsgData.getInt();
// Hold onto the remainder of the data.
mUsageData = hpsgData.slice();
mUsageData.order(ByteOrder.BIG_ENDIAN); // doesn't actually matter
// Validate the data.
//xxx do it
//xxx make sure the number of elements matches mAllocationUnitCount.
//xxx make sure the last element doesn't have P set
}
/**
* See if this segment still contains data, and has not been
* appended to another segment.
*
* @return true if this segment has not been appended to
* another segment.
*/
public boolean isValid() {
return mStartAddress != INVALID_START_ADDRESS;
}
/**
* See if other
comes immediately after this segment.
*
* @param other The HeapSegment to check.
* @return true if other
comes immediately after this
* segment.
*/
public boolean canAppend(HeapSegment other) {
return isValid() && other.isValid() && mHeapId == other.mHeapId &&
mAllocationUnitSize == other.mAllocationUnitSize &&
getEndAddress() == other.getStartAddress();
}
/**
* Append the contents of other
to this segment
* if it describes the segment immediately after this one.
*
* @param other The segment to append to this segment, if possible.
* If appended, other
will be invalid
* when this method returns.
* @return true if other
was successfully appended to
* this segment.
*/
public boolean append(HeapSegment other) {
if (canAppend(other)) {
/* Preserve the position. The mark is not preserved,
* but we don't use it anyway.
*/
int pos = mUsageData.position();
// Guarantee that we have enough room for the new data.
if (mUsageData.capacity() - mUsageData.limit() <
other.mUsageData.limit()) {
/* Grow more than necessary in case another append()
* is about to happen.
*/
int newSize = mUsageData.limit() + other.mUsageData.limit();
ByteBuffer newData = ByteBuffer.allocate(newSize * 2);
mUsageData.rewind();
newData.put(mUsageData);
mUsageData = newData;
}
// Copy the data from the other segment and restore the position.
other.mUsageData.rewind();
mUsageData.put(other.mUsageData);
mUsageData.position(pos);
// Fix this segment's header to cover the new data.
mAllocationUnitCount += other.mAllocationUnitCount;
// Mark the other segment as invalid.
other.mStartAddress = INVALID_START_ADDRESS;
other.mUsageData = null;
return true;
} else {
return false;
}
}
public long getStartAddress() {
return mStartAddress + mOffset;
}
public int getLength() {
return mAllocationUnitSize * mAllocationUnitCount;
}
public long getEndAddress() {
return getStartAddress() + getLength();
}
public void rewindElements() {
if (mUsageData != null) {
mUsageData.rewind();
}
}
public HeapSegmentElement getNextElement(HeapSegmentElement reuse) {
try {
if (reuse != null) {
return reuse.set(this);
} else {
return new HeapSegmentElement(this);
}
} catch (BufferUnderflowException ex) {
/* Normal "end of buffer" situation.
*/
} catch (ParseException ex) {
/* Malformed data.
*/
//TODO: we should catch this in the constructor
}
return null;
}
/*
* Method overrides for Comparable
*/
@Override
public boolean equals(Object o) {
if (o instanceof HeapSegment) {
return compareTo((HeapSegment) o) == 0;
}
return false;
}
@Override
public int hashCode() {
return mHeapId * 31 +
mAllocationUnitSize * 31 +
(int) mStartAddress * 31 +
mOffset * 31 +
mAllocationUnitCount * 31 +
mUsageData.hashCode();
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("HeapSegment { heap ").append(mHeapId)
.append(", start 0x")
.append(Integer.toHexString((int) getStartAddress()))
.append(", length ").append(getLength())
.append(" }");
return str.toString();
}
@Override
public int compareTo(HeapSegment other) {
if (mHeapId != other.mHeapId) {
return mHeapId < other.mHeapId ? -1 : 1;
}
if (getStartAddress() != other.getStartAddress()) {
return getStartAddress() < other.getStartAddress() ? -1 : 1;
}
/* If two segments have the same start address, the rest of
* the fields should be equal. Go through the motions, though.
* Note that we re-check the components of getStartAddress()
* (mStartAddress and mOffset) to make sure that all fields in
* an equal segment are equal.
*/
if (mAllocationUnitSize != other.mAllocationUnitSize) {
return mAllocationUnitSize < other.mAllocationUnitSize ? -1 : 1;
}
if (mStartAddress != other.mStartAddress) {
return mStartAddress < other.mStartAddress ? -1 : 1;
}
if (mOffset != other.mOffset) {
return mOffset < other.mOffset ? -1 : 1;
}
if (mAllocationUnitCount != other.mAllocationUnitCount) {
return mAllocationUnitCount < other.mAllocationUnitCount ? -1 : 1;
}
if (mUsageData != other.mUsageData) {
return mUsageData.compareTo(other.mUsageData);
}
return 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy