• 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 = (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