diff -r 8e12a575a9b5 -r 15296fd0af4a sysperfana/heapanalyser/Libraries/Engine/HeapLib/Cells/HeapCell.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sysperfana/heapanalyser/Libraries/Engine/HeapLib/Cells/HeapCell.cs Tue Jun 15 12:47:20 2010 +0300 @@ -0,0 +1,940 @@ +/* +* 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, IEquatable + { + #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; + } + } + + // + // 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. + // + 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 IEnumerable.GetEnumerator() + { + return new HeapCellRawItemEnumerator(this); + } + #endregion + + #region IEquatable 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 + } +} \ No newline at end of file