• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 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 
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 
212         @Override
compareTo(HeapSegmentElement other)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      */
HeapSegment(ByteBuffer hpsgData)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      */
isValid()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      */
canAppend(HeapSegment other)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      */
append(HeapSegment other)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 
getStartAddress()345     public long getStartAddress() {
346         return mStartAddress + mOffset;
347     }
348 
getLength()349     public int getLength() {
350         return mAllocationUnitSize * mAllocationUnitCount;
351     }
352 
getEndAddress()353     public long getEndAddress() {
354         return getStartAddress() + getLength();
355     }
356 
rewindElements()357     public void rewindElements() {
358         if (mUsageData != null) {
359             mUsageData.rewind();
360         }
361     }
362 
getNextElement(HeapSegmentElement reuse)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
equals(Object o)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
hashCode()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
toString()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
compareTo(HeapSegment other)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