crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianStructuresLib/Debug/Symbols/Symbols/SymbolCollectionList.cs
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:50:58 +0200
changeset 0 818e61de6cd1
permissions -rw-r--r--
Add initial version of Crash Analyser cmdline under EPL

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
* 
* Description:
*
*/
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using SymbianUtils;
using SymbianUtils.Range;
using SymbianStructuresLib.CodeSegments;
using SymbianStructuresLib.Debug.Common.FileName;

namespace SymbianStructuresLib.Debug.Symbols
{
    public class SymbolCollectionList : IEnumerable<SymbolCollection>
    {
        #region Constructors
        public SymbolCollectionList()
        {
        }
        #endregion

        #region API
        public void Clear()
        {
            lock ( iCollections )
            {
                iCollections.Clear();
            }
            lock ( iLookupCache )
            {
                iLookupCache.Clear();
            }
            lock ( iFileNameDictionary )
            {
                iFileNameDictionary.Clear();
            }
        }

        public void Add( SymbolCollection aCollection )
        {
            // Check not already added
            PlatformFileName name = aCollection.FileName;
            lock ( iFileNameDictionary )
            {
                if ( iFileNameDictionary.ContainsKey( name ) )
                {
                    throw new ArgumentException( string.Format( "Collection \'{0}\' already exists", name ) );
                }
            }

            // Add to file name dictionary
            iFileNameDictionary.Add( name, aCollection );

            // Add to non-optimised collection
            iCollections.Add( aCollection );
        }

        public void AddAndBuildCache( SymbolCollection aCollection )
        {
            Add( aCollection );
            UpdateCacheForCollection( aCollection );
        }

        public void Remove( SymbolCollection aCollection )
        {
            Predicate<SymbolCollection> predicate = delegate( SymbolCollection collection )
            {
                return collection.Equals( aCollection );
            };
            //
            lock ( iCollections )
            {
                iCollections.RemoveAll( predicate );
            }
            //
            RemoveFromCache( aCollection );
        }

        public void RemoveUntagged()
        {
            Predicate<SymbolCollection> predicate = delegate( SymbolCollection collection )
            {
                return collection.Tagged == false;
            };
            //
            lock ( iCollections )
            {
                iCollections.RemoveAll( predicate );
            }

            // MRU can be left intact since implicitly it should only contained tagged
            // collections
        }

        public void Serialize( Stream aStream )
        {
            using ( StreamWriter writer = new StreamWriter( aStream ) )
            {
                Action<SymbolCollection> action = delegate( SymbolCollection collection )
                {
                    if ( collection.Tagged )
                    {
                        collection.Serialize( writer );
                    }
                };
                //
                lock ( this )
                {
                    iCollections.ForEach( action );
                }
            }
        }

        public bool Contains( uint aAddress )
        {
            WaitForLookupCache();
            //
            bool ret = false;
            //
            AddressCollectionPair temp = new AddressCollectionPair( new AddressRange( aAddress, aAddress ), null );
            AddressCollectionPairComparer comparer = new AddressCollectionPairComparer();
            //
            lock ( iLookupCache )
            {
                int pos = iLookupCache.BinarySearch( temp, comparer );
                ret = ( pos >= 0 );
            }
            //
            return ret;
        }

        public bool Contains( SymbolCollection aCollection )
        {
            bool ret = false;

            // Check not already added
            PlatformFileName name = aCollection.FileName;
            lock ( iFileNameDictionary )
            {
                ret = iFileNameDictionary.ContainsKey( name );
            }
            //
            return ret;
        }

        public Symbol Lookup( uint aAddress, out SymbolCollection aCollection )
        {
            WaitForLookupCache();
            //
            Symbol ret = null;
            aCollection = null;
            //
            AddressCollectionPair temp = new AddressCollectionPair( new AddressRange( aAddress, aAddress ), null );
            AddressCollectionPairComparer comparer = new AddressCollectionPairComparer();
            //
            lock ( iLookupCache )
            {
                int pos = iLookupCache.BinarySearch( temp, comparer );
                if ( pos >= 0 )
                {
                    temp = iLookupCache[ pos ];
                    aCollection = temp.Collection;
                    ret = aCollection[ aAddress ];
                }
            }
            //
            return ret;
        }

        public void SortByCollectionAddress()
        {
            Comparison<SymbolCollection> comparer = delegate( SymbolCollection aCol1, SymbolCollection aCol2 )
            {
                int ret = aCol1.BaseAddress.CompareTo( aCol2.BaseAddress );
                return ret;
            };
            //
            iCollections.Sort( comparer );
        }

        public void BuildLookupCache()
        {
            WaitForLookupCache();
            //
            lock ( iLookupCache )
            {
                iLookupCache.Clear();
            }
            
            // Build the cache in a separate thread.
            // Must take the lock to either create or destroy waiter.
            lock ( iWaiterSyncRoot )
            {
                if ( iLookUpCacheWaiter == null )
                {
                    iLookUpCacheWaiter = new AutoResetEvent( false );
                    ThreadPool.QueueUserWorkItem( new WaitCallback( BackgroundThreadBuildLookupCache ), null );
                }
            }
        }
        #endregion

        #region Properties
        public int Count
        {
            get
            {
                lock ( iCollections )
                {
                    return iCollections.Count;
                }
            }
        }

        public bool IsEmpty
        {
            get { return Count == 0; } 
        }

        public bool Tagged
        {
            set
            {
                lock ( this )
                {
                    Action<SymbolCollection> action = delegate( SymbolCollection collection )
                    {
                        collection.Tagged = value;
                    };
                    iCollections.ForEach( action );
                }
            }
        }

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

        public SymbolCollection this[ int aIndex ]
        {
            get
            {
                lock ( iCollections )
                {
                    return iCollections[ aIndex ];
                }
            }
        }

        public SymbolCollection this[ PlatformFileName aFileName ]
        {
            get
            {
                Predicate<SymbolCollection> predicate = delegate( SymbolCollection collection )
                {
                    bool same = collection.FileName.Equals( aFileName );
                    return same;
                };
                //
                SymbolCollection ret = null;
                //
                lock ( iCollections )
                {
                    ret = iCollections.Find( predicate );
                }
                //
                return ret;
            }
        }

        public SymbolCollection this[ CodeSegDefinition aCodeSegment ]
        {
            get
            {
                Predicate<SymbolCollection> predicate = delegate( SymbolCollection collection )
                {
                    bool same = collection.IsMatchingCodeSegment( aCodeSegment );
                    return same;
                };
                //
                SymbolCollection ret = null;
                //
                lock ( iCollections )
                {
                    ret = iCollections.Find( predicate );
                }
                //
                return ret;
            }
        }
        #endregion

        #region From IEnumerable<SymbolCollection>
        IEnumerator<SymbolCollection> IEnumerable<SymbolCollection>.GetEnumerator()
        {
            foreach ( SymbolCollection col in iCollections )
            {
                yield return col;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            foreach ( SymbolCollection col in iCollections )
            {
                yield return col;
            }
        }
        #endregion

        #region Internal methods
        private void WaitForLookupCache()
        {
            if ( iLookUpCacheWaiter != null )
            {
                // Wait for the cache to become ready
                iLookUpCacheWaiter.WaitOne();

                // Must take the lock to either create or destroy waiter
                lock ( iWaiterSyncRoot )
                {
                    iLookUpCacheWaiter.Close();
                    iLookUpCacheWaiter = null;
                }
            }
        }

        private void RemoveFromCache( SymbolCollection aCollection )
        {
            // Must wait if it's being built already
            WaitForLookupCache();

            // Must take the lock to either create or destroy waiter
            lock ( iWaiterSyncRoot )
            {
                System.Diagnostics.Debug.Assert( iLookUpCacheWaiter == null );
                //
                iLookUpCacheWaiter = new AutoResetEvent( false );
                ThreadPool.QueueUserWorkItem( new WaitCallback( BackgroundThreadRemoveFromCache ), aCollection );
            }
        }

        private void BackgroundThreadBuildLookupCache( object aNotUsed )
        {
            System.Diagnostics.Debug.Assert( iLookUpCacheWaiter != null );

            // This comparer will help us sort the ranges
            AddressCollectionPairComparer comparer = new AddressCollectionPairComparer();

            // Make sorted list entries
            lock ( iCollections )
            {
                int colCount = iCollections.Count;
                for ( int colIndex = 0; colIndex < colCount; colIndex++ )
                {
                    SymbolCollection collection = iCollections[ colIndex ];
                    //
                    UpdateCacheForCollection( collection );
                }
            }

            // Done building cache
            iLookUpCacheWaiter.Set();
        }

        private void BackgroundThreadRemoveFromCache( object aCollection )
        {
            System.Diagnostics.Debug.Assert( iLookUpCacheWaiter != null );
            SymbolCollection collection = (SymbolCollection) aCollection;
            //
            Predicate<AddressCollectionPair> predicate = delegate( AddressCollectionPair pair )
            {
                bool match = ( pair.Collection == collection );
                return match;
            };
            //
            lock ( iLookupCache )
            {
                AddressCollectionPair temp = new AddressCollectionPair( new AddressRange(), collection );
                iLookupCache.RemoveAll( predicate );
            }
            //
            iLookUpCacheWaiter.Set();
        }

        private void UpdateCacheForCollection( SymbolCollection aCollection )
        {
            bool isEmpty = aCollection.IsEmptyApartFromDefaultSymbol;
            if ( isEmpty == false )
            {
                AddressRangeCollection ranges = aCollection.AddressRangeCollection;
                if ( ranges != null )
                {
                    // This comparer will help us sort the ranges
                    AddressCollectionPairComparer comparer = new AddressCollectionPairComparer();

                    int rangeCount = ranges.Count;
                    for ( int rangeIndex = 0; rangeIndex < rangeCount; rangeIndex++ )
                    {
                        AddressRange range = ranges[ rangeIndex ];
                        AddressCollectionPair pair = new AddressCollectionPair( range, aCollection );
                        //
                        lock ( iLookupCache )
                        {
                            int pos = iLookupCache.BinarySearch( pair, comparer );
                            if ( pos >= 0 )
                            {
                                AddressCollectionPair overlapsWith = iLookupCache[ pos ];
                                System.Diagnostics.Debug.WriteLine( string.Format( "Collection {0} [{1}] overlaps with existing collection: {2} [{3}]", pair.Collection.FileName, pair.Range, overlapsWith.Collection, overlapsWith.Range ) );
                            }
                            else
                            {
                                pos = ~pos;
                                iLookupCache.Insert( pos, pair );
                            }
                        }
                    }
                }
            }
        }
        #endregion

        #region Internal classes
        private class AddressCollectionPair
        {
            #region Constructors
            public AddressCollectionPair( AddressRange aRange, SymbolCollection aCollection )
            {
                iRange = aRange;
                iCollection = aCollection;
            }
            #endregion

            #region Properties
            public AddressRange Range
            {
                get { return iRange; }
            }

            public SymbolCollection Collection
            {
                get { return iCollection; }
            }
            #endregion

            #region From System.Object
            public override string ToString()
            {
                string ret = string.Format( "{0} {1}", iRange, iCollection.FileName.FileNameInDevice );
                return ret;
            }
            #endregion

            #region Data members
            private readonly AddressRange iRange;
            private readonly SymbolCollection iCollection;
            #endregion
        }

        private class AddressCollectionPairComparer : IComparer<AddressCollectionPair>
        {
            public int Compare( AddressCollectionPair aLeft, AddressCollectionPair aRight )
            {
                int ret = -1;
                //
                AddressRange lr = aLeft.Range;
                AddressRange rr = aRight.Range;
                //
                if ( lr.Contains( rr ) || rr.Contains( lr ) )
                {
                    ret = 0;
                }
                else
                {
                    ret = lr.CompareTo( rr );
                }
                //
                return ret;
            }
        }
        #endregion

        #region Data members
        private object iTag = null;
        private object iWaiterSyncRoot = new object();
        private AutoResetEvent iLookUpCacheWaiter = null;
        private List<SymbolCollection> iCollections = new List<SymbolCollection>();
        private List<AddressCollectionPair> iLookupCache = new List<AddressCollectionPair>();
        private Dictionary<PlatformFileName, SymbolCollection> iFileNameDictionary = new Dictionary<PlatformFileName, SymbolCollection>();
        #endregion
    }
}