sysperfana/heapanalyser/Libraries/Engine/HeapLib/Cells/HeapCell.cs
author Jussi Ryoma <ext-jussi.s.ryoma@nokia.com>
Tue, 24 Aug 2010 14:01:48 +0300
changeset 16 72f198be1c1d
parent 8 15296fd0af4a
permissions -rw-r--r--
Crash Analyser Carbide Extension 1.4.0

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
*   this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
*   this list of conditions and the following disclaimer in the documentation
*   and/or other materials provided with the distribution.
* - Neither the name of Nokia Corporation nor the names of its contributors
*   may be used to endorse or promote products derived from this software
*   without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* 
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using SymbianStructuresLib.Debug.Symbols;
using SymbianUtils.RawItems;
using SymbianUtils.Utilities;
using HeapLib.Array;
using HeapLib.Relationships;
using HeapLib.Cells.Descriptors;

namespace HeapLib.Cells
{
    public class HeapCell : IEnumerable<RawItem>, IEquatable<HeapCell>
    {
        #region Enumerations
        public enum TRegion
        {
            EHeader = 0,
            EPayload,
            EBeforeCell,
            EAfterCell
        }

        public enum TType : byte
        {
            EAllocated = 0,
            EFree
        }

        public enum TBuildType
        {
            ERelease = 0,
            EDebug = 1
        }

        public enum TDescriptorType
        {
            // These are Symbian's descriptor types
            EBufC = 0,
            EPtrC,
            EPtr,
            EBuf,
            EBufCPtr,

            // This are specific to this app
            ENullTerminatedString,
            EUnknown,
        }
        #endregion

        #region Constructors & destructor
        public HeapCell()
        {
            iRelationshipManager = new RelationshipManager(this);
        }

        internal HeapCell(HeapCell aCell)
            : this()
        {
            iTag = aCell.Tag;
            iAddress = aCell.Address;
            iType = aCell.Type;
            iFlags = aCell.iFlags;
            iSymbol[0] = aCell.iSymbol[0];
            iSymbol[1] = aCell.iSymbol[1];
            iSymbol[2] = aCell.iSymbol[2];
            iRawItems = aCell.iRawItems;
            iDescriptorInfo = aCell.iDescriptorInfo;
            iHeaderRawItems = aCell.iHeaderRawItems;
            iLength = aCell.iLength;
            //
            iRelationshipManager = aCell.RelationshipManager;
            iChecksum = new CRC32Checksum(aCell.Checksum);
        }

        internal HeapCell(uint aAddress, uint aLength, TType aType)
            : this()
        {
            iAddress = aAddress;
            iType = aType;
            //
            iHeaderRawItems.Clear();
            AddRawItemHeader(new RawItem(aAddress, aLength));
        }
        #endregion

        #region Constants
        public const uint KHeapCellSizeAllocatedUREL = 4;   // Just length
        public const uint KHeapCellSizeAllocatedUDEB = 12;  // Length + Allocation Number + Nesting Level
        public const uint KHeapCellSizeFree = 8;            // Length + Pointer to next free cell
        #endregion

        #region API
        public void AddRawItem(RawItem aItem)
        {
            RawItems.Add(aItem);
            //
            byte[] dataArray = aItem.DataArray;
            iChecksum.Checksum(dataArray);
        }

        public void AddRawItemHeader(RawItem aItem)
        {
            // Not checksummed
            iHeaderRawItems.Add(aItem);
            if (iHeaderRawItems.Count == 1)
            {
                iLength = iHeaderRawItems[0].Data;
            }
        }

        public uint Remainder(uint aAddress)
        {
            uint ret = (Address + Length) - aAddress;
            return ret;
        }

        public static uint CellHeaderSize(TType aType)
        {
            uint ret = AllocatedCellHeaderSize;
            if (aType == TType.EFree)
            {
                ret = FreeCellHeaderSize;
            }
            return ret;
        }

        public static uint AllocatedCellSizeByBuildType(TBuildType aType)
        {
            uint size = 0;
            //
            switch (aType)
            {
                case TBuildType.EDebug:
                    size = KHeapCellSizeAllocatedUDEB;
                    break;
                case TBuildType.ERelease:
                    size = KHeapCellSizeAllocatedUREL;
                    break;
            }
            //
            return size;
        }

        public TRegion RegionForAddress(uint aAddress)
        {
            uint address = Address;
            //
            TRegion ret = TRegion.EBeforeCell;
            //
            if (aAddress < address)
            {
                ret = TRegion.EBeforeCell;
            }
            else
            {
                uint endAddress = EndAddress;
                //
                if (aAddress > endAddress)
                {
                    ret = TRegion.EAfterCell;
                }
                else
                {
                    uint startOfPayload = StartOfPayloadAddress;
                    //
                    if (aAddress >= startOfPayload)
                    {
                        ret = TRegion.EPayload;
                    }
                    else
                    {
                        ret = TRegion.EHeader;
                    }
                }
            }
            //
            return ret;
        }

        public TRegion RegionForAddress(uint aAddress, out int aRemainingUntilNextBoundary)
        {
            TRegion region = RegionForAddress(aAddress);
            //
            switch (region)
            {
                default:
                    aRemainingUntilNextBoundary = -1;
                    break;
                case TRegion.EHeader:
                case TRegion.EPayload:
                    if (region == TRegion.EHeader)
                    {
                        uint startOfPayload = StartOfPayloadAddress;
                        aRemainingUntilNextBoundary = (int)(startOfPayload - aAddress);
                    }
                    else
                    {
                        uint endAddress = EndAddress;
                        aRemainingUntilNextBoundary = (int)(endAddress - aAddress + 1);
                    }
                    break;
            }
            //
            return region;
        }

        public bool IsIdentical(HeapCell aCell)
        {
            bool identical = false;
            //
            if (aCell.Length == this.Length &&
                 aCell.PossibleVTableAddress == this.PossibleVTableAddress &&
                 aCell.AllocationNumber == this.AllocationNumber &&
                 aCell.NestingLevel == this.NestingLevel &&
                 aCell.RawItems.Count == this.RawItems.Count)
            {
                identical = (aCell.Checksum == this.Checksum);
            }
            //
            return identical;
        }

        internal void ConstructionComplete(Statistics.HeapStatistics aStats)
        {
            if (Type == TType.EAllocated)
            {
                iDescriptorInfo = Descriptors.DescriptorAlgorithmManager.DescriptorInfo(this, aStats);
            }
        }
        #endregion

        #region Properties
        public HeapCell.TType Type
        {
            get { return iType; }
            set { iType = value; }
        }

        public string TypeString
        {
            get
            {
                string ret = string.Empty;
                //
                switch (Type)
                {
                    case TType.EAllocated:
                        ret = "Allocated";
                        break;
                    case TType.EFree:
                        ret = "Free";
                        break;
                }
                //
                return ret;
            }
        }

        // <summary>
        // The index is the unique number assigned to each cell as it is
        // added to the heap data for a given heap. This operation is 
        // performed by the reconstructor during parsing.
        // </summary>
        public uint Index
        {
            get { return iIndex; }
            set { iIndex = value; }
        }

        public uint Address
        {
            get { return iAddress; }
            set { iAddress = value; }
        }

        public uint StartOfPayloadAddress
        {
            get
            {
                uint ret = Address;
                //
                if (Type == TType.EAllocated)
                {
                    ret += HeaderSize;
                }
                else if (Type == TType.EFree)
                {
                    ret += KHeapCellSizeFree;
                }
                else
                {
                    System.Diagnostics.Debug.Assert(false);
                }
                //
                return ret;
            }
        }

        public uint EndAddress
        {
            get { return iAddress + iLength - 1; }
        }

        public uint Length
        {
            // Optimisation: we cache the length when adding the first
            // header raw item to avoid having to repeatedly access
            // the raw items array during initial heap preparation.
            get { return iLength; }
        }

        public uint NestingLevel
        {
            get
            {
                // Only applicable to allocated cells in debug builds
                uint ret = 0;
                //
                if (Type == TType.EAllocated && IsDebugAllocator)
                {
                    System.Diagnostics.Debug.Assert(HeaderRawItems.Count >= 2);
                    RawItem item = HeaderRawItems[1]; // Always 2nd raw item in header
                    ret = item.Data;
                }
                //
                return ret;
            }
        }

        public uint AllocationNumber
        {
            get
            {
                // Only applicable to allocated cells in debug builds
                uint ret = 0;
                //
                if (Type == TType.EAllocated && IsDebugAllocator)
                {
                    System.Diagnostics.Debug.Assert(HeaderRawItems.Count >= 3);
                    RawItem item = HeaderRawItems[2]; // Always 3rd raw item in header
                    ret = item.Data;
                }
                //
                return ret;
            }
        }

        public uint PossibleVTableAddress
        {
            get
            {
                // This is always the first raw item
                uint ret = 0;
                //
                if (RawItems.Count > 0)
                {
                    ret = RawItems[0].Data;
                }
                //
                return ret;
            }
        }

        public Symbol Symbol
        {
            get { return iSymbol[0]; }
            set { iSymbol[0] = value; }
        }

        public Symbol Symbol2
        {
            get { return iSymbol[1]; }
            set { iSymbol[1] = value; }
        }

        public Symbol Symbol3
        {
            get { return iSymbol[2]; }
            set { iSymbol[2] = value; }
        }

        public string SymbolString
        {
            get
            {
                return SymbolStringRef(false);
            }
        }

        private string SymbolStringRef(bool reference)
        {
            StringBuilder ret = new StringBuilder();

            if (Type == TType.EAllocated)
            {
                if (Symbol != null)
                {
                    ret.Append(Symbol.NameWithoutVTablePrefix);
                }
                else if (IsDescriptor)
                {
                    ret.Append("[Descriptor] " + DescriptorTextBeautified);
                }
            }
            else if (Type == TType.EFree)
            {
                ret.Append("[Free]");
                if (Symbol != null)
                {
                    ret.Append(" ");
                    ret.Append(Symbol.NameWithoutVTablePrefix);
                }
            }

            // If no text as yet identified, then return [Unknown], if there is not unique referrer.
            // If the referrer is unique, or if all referrers are of the same known type,
            // instead of [Unknown], you can see [Part of XXX], where XXX is the type of the referrer. 
            if (ret.Length == 0)
            {
                if (!reference)
                {
                    if (iRelationshipManager.ReferencedByUnique != null)
                    {
                        string symbolString = iRelationshipManager.ReferencedByUnique.SymbolStringRef(true);

                        if (!symbolString.Equals("[Unknown]"))
                        {
                            ret.Append("[Part of ");
                            ret.Append(symbolString);
                            ret.Append("]");
                        }
                        else
                        {
                            ret.Append("[Unknown]");
                        }
                    }
                    else
                    {
                        ret.Append("[Unknown]");
                    }
                }
                else
                {
                    ret.Append("[Unknown]");
                }
            }
            return ret.ToString();
        }

        public string SymbolStringWithoutDescriptorPrefix
        {
            get
            {
                string ret = SymbolString;
                //
                if (IsDescriptor)
                {
                    ret = "[" + DescriptorLength.ToString("d4") + "] " + DescriptorTextBeautifiedWithoutLength;
                }
                //
                return ret;
            }
        }

        public uint HeaderSize
        {
            get
            {
                uint size = 0;
                //
                switch (Type)
                {
                    case TType.EAllocated:
                        size = HeapCell.AllocatedCellHeaderSize;
                        break;
                    case TType.EFree:
                        size = KHeapCellSizeFree;
                        break;
                }
                //
                return size;
            }
        }

        public uint PayloadLength
        {
            get { return (EndAddress - StartOfPayloadAddress) + 1; }
        }

        public RawItem this[uint aAddress]
        {
            get
            {
                RawItem ret = null;
                //
                TRegion region = RegionForAddress(aAddress);
                if (region == TRegion.EPayload || region == TRegion.EHeader)
                {
                    if (region == TRegion.EHeader)
                    {
                        uint offset = aAddress - Address;
                        int index = System.Convert.ToInt32(offset / RawItem.KSizeOfOneRawItemInBytes);
                        //
                        if (index < 0 || index >= HeaderRawItems.Count)
                        {
                            throw new ArgumentException("Address 0x" + aAddress.ToString("x8") + " is beyond this cell's header");
                        }
                        //
                        ret = HeaderRawItems[index];
                    }
                    else
                    {
                        // Payload
                        uint offset = aAddress - StartOfPayloadAddress;
                        int index = System.Convert.ToInt32(offset / RawItem.KSizeOfOneRawItemInBytes);
                        //
                        if (index < 0 || index >= RawItems.Count)
                        {
                            throw new ArgumentException("Address 0x" + aAddress.ToString("x8") + " is beyond this cell's payload");
                        }
                        //
                        ret = this[index];
                    }
                }
                else
                {
                    throw new ArgumentException("Address 0x" + aAddress.ToString("x8") + " is not within this cell's extent");
                }
                return ret;
            }
        }

        public RawItem this[int aIndex]
        {
            get
            {
                return iRawItems[aIndex];
            }
        }

        public RawItemCollection RawItems
        {
            get { return iRawItems; }
        }

        public RawItemCollection HeaderRawItems
        {
            get { return iHeaderRawItems; }
        }

        public long CombinedLinkedCellPayloadLengths
        {
            get
            {
                int depth = 0;
                long ret = DoGetCombinedLinkedCellPayloadLengths(ref depth);
                return ret;
            }
        }

        public long PayloadLengthIncludingLinkedCells
        {
            get
            {
                long ret = PayloadLength;
                ret += RelationshipManager.PayloadLengthOfEmbeddedCells;
                //
                return ret;
            }
        }

        public object Tag
        {
            get { return iTag; }
            set { iTag = value; }
        }

        public uint Checksum
        {
            get { return iChecksum.Value; }
        }

        public bool IsUnknown
        {
            get { return Symbol == null && Type == TType.EAllocated; }
        }
        #endregion

        #region Descriptor related functionality
        public TDescriptorType DescriptorType
        {
            get
            {
                TDescriptorType ret = TDescriptorType.EUnknown;
                //
                if (IsDescriptor && iDescriptorInfo != null)
                {
                    ret = iDescriptorInfo.Type;
                }
                //
                return ret;
            }
        }

        public bool IsDescriptorUnicode
        {
            get
            {
                bool ret = false;
                //
                if (IsDescriptor && iDescriptorInfo != null)
                {
                    ret = iDescriptorInfo.IsUnicode;
                }
                //
                return ret;
            }
        }

        public bool IsDescriptor
        {
            get
            {
                return (iDescriptorInfo != null);
            }
        }

        public int DescriptorLength
        {
            get
            {
                int length = 0;
                //
                if (IsDescriptor && iDescriptorInfo != null)
                {
                    length = iDescriptorInfo.Length;
                }
                //
                return length;
            }
        }

        public string DescriptorText
        {
            get
            {
                string ret = string.Empty;
                //
                if (IsDescriptor && iDescriptorInfo != null)
                {
                    ret = iDescriptorInfo.Text;
                }
                //
                return ret;
            }
        }

        public string DescriptorTextBeautifiedWithoutLength
        {
            get
            {
                StringBuilder ret = new StringBuilder();
                //
                string des = DescriptorText;
                if (des.Length > KMaxSymbolStringDescriptorLength)
                {
                    ret.Append(" \"");
                    ret.Append(des.Substring(0, KMaxSymbolStringDescriptorLength));
                    ret.Append("...\"");
                }
                else
                {
                    ret.Append(" \"");
                    ret.Append(des);
                    ret.Append("\"");
                }
                //
                return ret.ToString();
            }
        }

        public string DescriptorTextBeautified
        {
            get
            {
                StringBuilder ret = new StringBuilder();
                //
                ret.Append("{");
                ret.Append(DescriptorLength.ToString("d4"));
                ret.Append("}");
                //
                ret.Append(DescriptorTextBeautifiedWithoutLength);
                //
                return ret.ToString();
            }
        }
        #endregion

        #region Static properties
        public static bool IsDebugAllocator
        {
            get
            {
                return AllocatedCellHeaderSize == AllocatedCellSizeByBuildType(TBuildType.EDebug);
            }
        }

        public static uint FreeCellHeaderSize
        {
            get { return KFreeCellHeaderSize; }
        }

        public static uint AllocatedCellHeaderSize
        {
            get { return iAllocatedCellHeaderSize; }
            set { iAllocatedCellHeaderSize = value; }
        }
        #endregion

        #region Relationships
        public RelationshipManager RelationshipManager
        {
            get { return iRelationshipManager; }
        }
        #endregion

        #region IEnumerable Members
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new HeapCellRawItemEnumerator(this);
        }

        IEnumerator<RawItem> IEnumerable<RawItem>.GetEnumerator()
        {
            return new HeapCellRawItemEnumerator(this);
        }
        #endregion

        #region IEquatable<HeapCell> Members
        public bool Equals(HeapCell aOther)
        {
            bool ret = (aOther.Address == this.Address);
            return ret;
        }
        #endregion

        #region Operators
        public static bool operator ==(HeapCell aLeft, HeapCell aRight)
        {
            bool ret = false;

            // If both are null, or both are same instance, return true.
            if (System.Object.ReferenceEquals(aLeft, aRight))
            {
                ret = true;
            }
            else if (((object)aLeft == null) || ((object)aRight == null))
            {
                // If one is null, but not both, return false.
                ret = false;
            }
            else
            {
                // Return true if the fields match:
                ret = (aLeft.Address == aRight.Address);
            }
            //
            return ret;
        }

        public static bool operator !=(HeapCell aLeft, HeapCell aRight)
        {
            return !(aLeft == aRight);
        }
        #endregion

        #region From System.Object
        public override int GetHashCode()
        {
            return Address.GetHashCode();
        }

        public override bool Equals(object aObject)
        {
            bool ret = false;
            //
            if (aObject is HeapCell)
            {
                HeapCell otherCell = (HeapCell)aObject;
                ret = Equals(otherCell);
            }
            //
            return ret;
        }

        public override string ToString()
        {
            string ret = "[0x" + Address.ToString("x8") + " " + TypeString + "]";
            return ret;
        }

        public string ToStringExtended()
        {
            StringBuilder ret = new StringBuilder();

            // Type
            if (Type == HeapCell.TType.EAllocated)
            {
                if (IsDescriptor)
                {
                    ret.Append("[D]");
                }
                else
                {
                    ret.Append("[A]");
                }
            }
            else
            {
                ret.Append("[F]");
            }

            // Address
            ret.Append(" 0x" + Address.ToString("x8"));

            // Symbol (if present)
            if (Symbol != null)
            {
                ret.Append(" - " + SymbolString);
            }
            else if (IsDescriptor)
            {
                ret.Append(" - " + DescriptorTextBeautified);
            }

            return ret.ToString();
        }
        #endregion

        #region Internal flags
        [Flags]
        private enum TFlags
        {
            EFlagsNone = 0,
            EFlagsInCombiningCheck = 1
        }
        #endregion

        #region Internal constants
        private const int KMaxRecursiveLinkedCellDepth = 5; // levels
        private const int KMaxSymbolStringDescriptorLength = 128;
        private const int KFreeCellHeaderSize = 8; // always
        #endregion

        #region Internal methods
        private long DoGetCombinedLinkedCellPayloadLengths(ref int aDepth)
        {
            long ret = PayloadLength;
            //
            if (aDepth <= KMaxRecursiveLinkedCellDepth)
            {
                iFlags |= TFlags.EFlagsInCombiningCheck;
                //
                foreach (RelationshipInfo relInfo in RelationshipManager.EmbeddedReferencesTo)
                {
                    HeapCell linkedCell = relInfo.ToCell;
                    bool isInLinkCheck = ((linkedCell.iFlags & TFlags.EFlagsInCombiningCheck) == TFlags.EFlagsInCombiningCheck);
                    if (isInLinkCheck == false)
                    {
                        ++aDepth;
                        ret += linkedCell.DoGetCombinedLinkedCellPayloadLengths(ref aDepth);
                    }
                    else
                    {
                    }
                }
                //
                iFlags &= ~TFlags.EFlagsInCombiningCheck;
            }
            //
            return ret;
        }
        #endregion

        #region Data members
        private static uint iAllocatedCellHeaderSize = 0;
        private object iTag;
        private uint iIndex;
        private uint iAddress;
        private uint iLength;
        private TType iType = TType.EAllocated;
        private TFlags iFlags = TFlags.EFlagsNone;
        private Symbol[] iSymbol = new Symbol[3] { null, null, null };
        private CRC32Checksum iChecksum = new CRC32Checksum();
        private DescriptorInfo iDescriptorInfo = null;
        private RawItemCollection iRawItems = new RawItemCollection();
        private RawItemCollection iHeaderRawItems = new RawItemCollection();
        private readonly RelationshipManager iRelationshipManager;
        #endregion
    }
}