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