sysperfana/heapanalyser/Libraries/Engine/HeapComparisonLib/Data/ComparsionEngine.cs
author Matti Laitinen <matti.t.laitinen@nokia.com>
Tue, 15 Jun 2010 12:47:20 +0300
changeset 8 15296fd0af4a
permissions -rw-r--r--
HeapAnalyser 1.1.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.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using SymbianUtils;
using SymbianUtils.Range;
using SymbianUtils.RawItems;
using HeapLib.Cells;
using HeapLib.Array;
using HeapLib.Reconstructor;
using SymbianUtils.TextUtilities.Readers.Types.Array;

namespace HeapComparisonLib.Data
{
    public class ComparsionEngine : AsyncArrayReader<HeapCell>
    {
        #region Constructors & destructor
        public ComparsionEngine( HeapReconstructor aReconstructor1, HeapReconstructor aReconstructor2 )
            : base( new ComparsionCellSupplierProxy( aReconstructor1 ) )
        {
            iReconstructor1 = aReconstructor1;
            iReconstructor2 = aReconstructor2;
        }
        #endregion

        #region API
        public void Compare()
        {
            AsyncRead();
        }
        #endregion

        #region Properties
        public HeapReconstructor Reconstructor1
        {
            get { return iReconstructor1; }
        }

        public HeapReconstructor Reconstructor2
        {
            get { return iReconstructor2; }
        }

        public HeapCellArrayWithStatistics ResultsUnchanged
        {
            get { return iUnchanged; }
        }

        public HeapCellArrayWithStatistics ResultsUnchangedLengthButDifferentContents
        {
            get { return iUnchangedLengthButDifferentContents; }
        }

        public HeapCellArrayWithStatistics ResultsChanged
        {
            get { return iChanged; }
        }

        public HeapCellArrayWithStatistics ResultsUniqueInHeap1
        {
            get { return iUniqueInReconstructor1; }
        }

        public HeapCellArrayWithStatistics ResultsUniqueInHeap2
        {
            get { return iUniqueInReconstructor2; }
        }
        #endregion

        #region From AsyncArrayReader
        protected override void HandleObject( HeapCell aCell, int aIndex, int aCount )
        {
            if ( aCell.Address == 0xc8023e5c || 
                 aCell.Address == 0xc8024064 ||
                 aCell.Address == 0xc80276d4 ||
                 aCell.Address == 0xc82100b4
                )
            {
                int x = 0;
                x++;
            }

            uint address = aCell.Address;
            HeapCell locatedCell = iReconstructor2.Data.CellByExactAddress( address );
            //
            if ( locatedCell != null )
            {
                System.Diagnostics.Debug.Assert( aCell.Tag == null );

                // Remove sucessfully located cells from the 2nd list
                iReconstructor2.Data.Remove( locatedCell );

                // Check vtables to detect if the same
                HeapCell.TType origType = aCell.Type;
                HeapCell.TType locatedType = locatedCell.Type;

                uint origVTable = aCell.PossibleVTableAddress;
                uint locatedVTable = locatedCell.PossibleVTableAddress;

                // Compare length, type and vTable
                if ( origType == locatedType )
                {
                    // Type is the same...
                    if ( aCell.Length == locatedCell.Length )
                    {
                        CompareCellsWithSameLengths( aCell, locatedCell );
                    }
                    else
                    {
                        CompareCellsWithDifferentLengths( aCell, locatedCell );
                    }
                }
                else
                {
                    // The cell is not of the same type, so they cannot have
                    // any commonality between them. Therefore they are unique in their
                    // respective heaps.
                    iUniqueInReconstructor1.Add( aCell );
                    iUniqueInReconstructor2.Add( locatedCell );
                }
            }
            else
            {
                // There wasn't any corresponding cell in the second list, so it's got to be
                // unique to the first heap
                iUniqueInReconstructor1.Add( aCell );
            }
        }

        protected override void HandleReadCompleted()
        {
            try
            {
                base.HandleReadCompleted();
            }
            finally
            {
                // Anything left behind in reconstructor 2 was unique to it.
                int count = iReconstructor2.Data.Count;
                int alreadyHave = iUniqueInReconstructor2.Count;
                foreach ( HeapCell leftover in iReconstructor2.Data )
                {
                    iUniqueInReconstructor2.Add( leftover );
                }
                System.Diagnostics.Debug.Assert( alreadyHave + count == iUniqueInReconstructor2.Count );
            }

            /*
            int unchanged = CountBySymbol( "CFsDisconnectRequest", iUnchanged );
            int diffContents = CountBySymbol( "CFsDisconnectRequest", iUnchangedLengthButDifferentContents );
            int changed = CountBySymbol( "CFsDisconnectRequest", iChanged );
            int unique1 = 0;// CountBySymbol( "CFsDisconnectRequest", iUniqueInReconstructor1 );
            int unique2 = CountBySymbol( "CFsDisconnectRequest", iUniqueInReconstructor2 );

            int total = unchanged + diffContents + changed + unique1 + unique2;

            int x = total;
            */
        }
        #endregion

        #region Internal methods
        private void CompareCellsWithSameLengths( HeapCell aFromRecon1, HeapCell aFromRecon2 )
        {
            System.Diagnostics.Debug.Assert( aFromRecon1.Tag == null );
            System.Diagnostics.Debug.Assert( aFromRecon1.Address == aFromRecon2.Address );
            System.Diagnostics.Debug.Assert( aFromRecon2.Type == aFromRecon2.Type );
            System.Diagnostics.Debug.Assert( aFromRecon1.Length == aFromRecon2.Length );

            // Cells are the same, at least at face value. We also compare
            // contents to spot those that are entirely [unchanged].
            bool equal = ( aFromRecon1.IsIdentical( aFromRecon2 ) );
            if ( equal )
            {
                // Absolutely identical to one another
                aFromRecon1.Tag = aFromRecon2;
                iUnchanged.Add( aFromRecon1 );
            }
            else
            {
                // Same length, same type, but otherwise the contents are different
                //
                // If it's an allocated cell and the vTables are different, then 
                // the cell is treated as [unique].
                //
                // If it's a free cell, we don't care.
                if ( aFromRecon2.Type == HeapCell.TType.EAllocated )
                {
                    uint vt1 = aFromRecon1.IsUnknown ? 0 : aFromRecon1.PossibleVTableAddress;
                    uint vt2 = aFromRecon2.IsUnknown ? 0 : aFromRecon2.PossibleVTableAddress;

                    // If there was no associated symbol then the comparison will be zero vs
                    // zero, in which case we'll still treat it as the [same length, diff content],
                    // rather than unique. This might happen for blob cells, e.g. those that
                    // are the payload for RArray or CBufFlat etc.
                    if ( vt1 == vt2 )
                    {
                        // VTables are the same, but somehow the remaining content is different.
                        // Must also check whether one is a descriptor an the other isn't!
                        if ( aFromRecon1.IsDescriptor != aFromRecon2.IsDescriptor )
                        {
                            // One is a descriptor, the other isn't => [unique]
                            iUniqueInReconstructor1.Add( aFromRecon1 );
                            iUniqueInReconstructor2.Add( aFromRecon2 );
                        }
                        else
                        {
                            // [same length, diff content]
                            aFromRecon1.Tag = aFromRecon2;
                            iUnchangedLengthButDifferentContents.Add( aFromRecon1 );
                        }
                    }
                    else
                    {
                        // VTables are different, lengths and types are the same => [unique]
                        iUniqueInReconstructor1.Add( aFromRecon1 );
                        iUniqueInReconstructor2.Add( aFromRecon2 );
                    }
                }
                else
                {
                    // Must be a free cell, and since the length is the same we
                    // really don't care => [same length, diff content]
                    aFromRecon1.Tag = aFromRecon2;
                    iUnchangedLengthButDifferentContents.Add( aFromRecon1 );
                }
            }
        }

        private void CompareCellsWithDifferentLengths( HeapCell aFromRecon1, HeapCell aFromRecon2 )
        {
            System.Diagnostics.Debug.Assert( aFromRecon1.Tag == null );
            System.Diagnostics.Debug.Assert( aFromRecon1.Address == aFromRecon2.Address );
            System.Diagnostics.Debug.Assert( aFromRecon2.Type == aFromRecon2.Type );
            System.Diagnostics.Debug.Assert( aFromRecon1.Length != aFromRecon2.Length );

            if ( aFromRecon1.Type == HeapCell.TType.EFree )
            {
                // Free cells, same address, different lengths => [changed]
                aFromRecon1.Tag = aFromRecon2;
                iChanged.Add( aFromRecon1 );
            }
            else if ( aFromRecon1.Type == HeapCell.TType.EAllocated )
            {
                if ( aFromRecon1.IsUnknown && aFromRecon2.IsUnknown )
                {
                    // Check if both are descriptors. If they are not, then
                    // assume unique.
                    if ( aFromRecon1.IsDescriptor != aFromRecon2.IsDescriptor )
                    {
                        // One is a descriptor, one isn't => [unique]
                        iUniqueInReconstructor1.Add( aFromRecon1 );
                        iUniqueInReconstructor2.Add( aFromRecon2 );
                    }
                    else
                    {
                        // Cells with unknown vTables, but at the same address are just assumed to be blobs that
                        // have grown or shrunk => [changed]
                        aFromRecon1.Tag = aFromRecon2;
                        iChanged.Add( aFromRecon1 );
                    }
                }
                else
                {
                    uint vt1 = aFromRecon1.IsUnknown ? 0 : aFromRecon1.PossibleVTableAddress;
                    uint vt2 = aFromRecon2.IsUnknown ? 0 : aFromRecon2.PossibleVTableAddress;

                    // If either vTable is different, then they are [unique]. If the vTables
                    // are the same, then this is odd - classes with known vTables shouldn't change
                    // size dynamically?
                    if ( vt1 == vt2 )
                    {
                        // VTables are the same, but somehow the length has
                        // changed. I don't know what to do about this at the moment so
                        // am filing the cells in the unique bucket!
                        iUniqueInReconstructor1.Add( aFromRecon1 );
                        iUniqueInReconstructor2.Add( aFromRecon2 );
                    }
                    else
                    {
                        // VTables are different, lengths not the same, types is the same => [unique]
                        iUniqueInReconstructor1.Add( aFromRecon1 );
                        iUniqueInReconstructor2.Add( aFromRecon2 );
                    }
                }
            }
        }

        private static bool CompareCommonContents( HeapCell aLeft, HeapCell aRight )
        {
            HeapCell smaller = aLeft;
            HeapCell larger = aRight;
            if ( aLeft.RawItems.Count > aRight.RawItems.Count )
            {
                smaller = aRight;
                larger = aLeft;
            }

            // The payload length is in bytes, the raw item count is in 32 bit words.
            // They should agree, once multipled by 4 - if not, something is badly borked.
            // We should probably throw an exception, but try to be tolerant for now...
            if ( smaller.RawItems.Count * 4 != smaller.PayloadLength )
            {
                return false;
            }
            else if ( larger.RawItems.Count * 4 != larger.PayloadLength )
            {
                return false;
            }

            int smallerSize = smaller.RawItems.Count;
            for ( int i = 0; i < smallerSize; i++ )
            {
                RawItem itemS = smaller[ i ];
                RawItem itemL = larger[ i ];
                //
                if ( itemS.Data != itemL.Data )
                {
                    return false;
                }
            }

            return true;
        }

        private static int CountBySymbol( string aText, HeapCellArray aArray )
        {
            int ret = 0;
            int count = aArray.Count;
            for ( int i = 0; i < count; i++ )
            {
                HeapCell cell = aArray[ i ];
                string sym = cell.SymbolStringWithoutDescriptorPrefix;
                if ( sym.Contains( aText ) )
                {
                    ++ret;
                }
            }
            return ret;
        }
        #endregion

        #region Data members
        private readonly HeapReconstructor iReconstructor1;
        private readonly HeapReconstructor iReconstructor2;
        //
        private HeapCellArrayWithStatistics iUnchanged = new HeapCellArrayWithStatistics();
        private HeapCellArrayWithStatistics iUnchangedLengthButDifferentContents = new HeapCellArrayWithStatistics();
        private HeapCellArrayWithStatistics iChanged = new HeapCellArrayWithStatistics();
        private HeapCellArrayWithStatistics iUniqueInReconstructor1 = new HeapCellArrayWithStatistics();
        private HeapCellArrayWithStatistics iUniqueInReconstructor2 = new HeapCellArrayWithStatistics();
        #endregion
    }

    internal class ComparsionCellSupplierProxy : AsyncArrayObjectSupplier<HeapCell>
    {
        #region Constructors & destructor
        public ComparsionCellSupplierProxy( HeapReconstructor aReconstructor )
        {
            iReconstructor = aReconstructor;
        }
        #endregion

        #region AsyncArrayObjectSupplier<HeapCell> Members
        public int ObjectCount
        {
            get { return iReconstructor.Data.Count; }
        }

        public HeapCell this[ int aIndex ]
        {
            get { return iReconstructor.Data[ aIndex ]; }
        }
        #endregion

        #region Data members
        private readonly HeapReconstructor iReconstructor;
        #endregion
    }
}