crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianStackLib/Algorithms/StackAlgorithmManager.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.Collections.Generic;
using System.Text;
using SymbianDebugLib.Engine;
using SymbianStackLib.Data.Output.Entry;
using SymbianStackLib.Engine;
using SymbianStackLib.Interfaces;
using SymbianUtils;
using SymbianUtils.PluginManager;
using SymbianUtils.Range;

namespace SymbianStackLib.Algorithms
{
    internal sealed class StackAlgorithmManager : DisposableObject, IStackAlgorithmObserver, IStackAlgorithmManager
    {
        #region Delegates & events
        public enum TEvent
        {
            EAlgorithmStarted = 0,
            EAlgorithmProgress,
            EAlgorithmComplete
        }

        public delegate void AlgorithmEventHandler( StackAlgorithmManager aAlgManager, TEvent aEvent );
        public event AlgorithmEventHandler EventHandler;

        public delegate void AlgorithmExceptionHandler( StackAlgorithmManager aAlgManager, Exception aException );
        public event AlgorithmExceptionHandler ExceptionHandler;
        #endregion

        #region Constructors
        public StackAlgorithmManager( StackEngine aEngine )
		{
            iEngine = aEngine;
            
            // Make the view name based upon the stack address range
            AddressRange range = aEngine.AddressInfo.Range;
            StringBuilder viewName = new StringBuilder();
            viewName.AppendFormat( "StackReconstructor_{0}_{1}", range, DateTime.Now.Ticks );
            iView = iEngine.DebugEngine.CreateView( viewName.ToString(), iEngine.CodeSegments );
        }
		#endregion

		#region API
        public void Reconstruct( TSynchronicity aSynchronicity )
        {
            iSynchronicity = aSynchronicity;
            try
            {
                PrepareForExecution();
                ExecuteHeadAlgorithm();
            }
            catch ( Exception e )
            {
                // The only reason an exception should occur is if none of the algorithms indicate that
                // they are ready to process stack data (probably because symbols were not provided). 
                //
                // In this situation, we must report the exception to our event handler
                if ( ExceptionHandler != null && EventHandler != null )
                {
                    // Make sure we sent the 'start' and 'end' events as well - we cannot send
                    // these events twice so it's okay to try to send them (will be ignored if
                    // already sent).
                    ReportEvent( TEvent.EAlgorithmStarted );
                    
                    // Now report the exception
                    ExceptionHandler( this, e );

                    // Indicate completion since we're not going to be able to do anything anymore
                    ReportEvent( TEvent.EAlgorithmComplete );
                }
                else
                {
                    // No exception handler so just rethrow...
                    throw e;
                }
            }
        }
		#endregion

		#region Properties
        public int Progress
        {
            get
            {
                // Scale the progress back to a fraction of the total
                lock ( this )
                {
                    int totalProgressSoFar = iProgressValueCompleted + iProgressValueCurrent;
                    float prog = ( (float) totalProgressSoFar ) / ( (float) iProgressValueMax );
                    prog *= 100.0f;
                    return (int) prog;
                }
            }
        }
        #endregion

        #region From IStackAlgorithmObserver
        public void StackBuildingStarted( StackAlgorithm aAlg )
        {
            Trace( "[SBAlgManager] StackBuildingStarted() - aAlg: {0}", aAlg );
            ReportEvent( TEvent.EAlgorithmStarted );
        }

        public void StackBuldingProgress( StackAlgorithm aAlg, int aPercent )
        {
            Trace( "[SBAlgManager] StackBuldingProgress() - aAlg: {0}, aPercent: {1}", aAlg, aPercent );
            lock ( this )
            {
                iProgressValueCurrent = aPercent;
            }
            //
            ReportEvent( TEvent.EAlgorithmProgress );
        }

        public void StackBuildingComplete( StackAlgorithm aAlg )
        {
            // If the algorithm is still queued then everything went okay. If not, then we
            // had an exception and we should therefore ignore the completion as the algorithm
            // terminated unexpectedly.
            bool stillExists = iExecutionQueue.Contains( aAlg );
            Trace( "[SBAlgManager] StackBuildingComplete() - aAlg: {0}, stillExists: {1}", aAlg, stillExists );
            if ( stillExists )
            {
                iExecutionQueue.Clear();
                ReportEvent( TEvent.EAlgorithmComplete );
            }
        }

        public void StackBuildingElementConstructed( StackAlgorithm aAlg, StackOutputEntry aEntry )
        {
            lock ( this )
            {
                iEngine.DataOutput.InsertAsFirstEntry( aEntry );
            }
        }

        public void StackBuildingException( StackAlgorithm aAlg, Exception aException )
        {
            Trace( "[SBAlgManager] STACK ALG EXCEPTION: " + aException.Message );
            Trace( "[SBAlgManager] {0}", aException.StackTrace );

            StackAlgorithm alg = CurrentAlgorithm;

            // If we're executing using the fallback entry, then we're in trouble. 
            // There is nothing we can do besides report the problem upwards.
            if ( iExecutionQueue.Count == 1 )
            {
                if ( ExceptionHandler != null )
                {
                    ExceptionHandler( this, aException );
                }
            }
            else
            {
                // Report event
                string message = string.Format( LibResources.StackAlgorithmManager_FailedAlgorithmWarning + System.Environment.NewLine + "{1}", 
                                                alg.Name, aException.Message.ToString() 
                                                );
                iEngine.ReportMessage( StackEngine.TMessageType.ETypeWarning, message );

                // The primary algorithm has failed, let's roll back to the secondary
                // by dumping the primary algorithm and starting again.
                iExecutionQueue.Dequeue();
                iEngine.DataOutput.Clear();

                // Reset progress.
                iProgressValueCompleted += 100;
                iProgressValueCurrent = 0;

                // Start next algorithm...
                ExecuteHeadAlgorithm();
            }
        }
        #endregion

        #region From IStackAlgorithmManager
        public StackEngine Engine
        {
            get { return iEngine; }
        }

        public DbgEngineView DebugEngineView
        {
            get { return iView; }
        }

        public void Trace( string aMessage )
        {
            iEngine.Trace( aMessage );
        }

        public void Trace( string aFormat, params object[] aParams )
        {
            iEngine.Trace( aFormat, aParams );
        }
        #endregion

        #region From DisposableObject
        protected override void CleanupManagedResources()
        {
            try
            {
                base.CleanupManagedResources();
            }
            finally
            {
                if ( iMasterAlgorithmTable != null )
                {
                    iMasterAlgorithmTable.Dispose();
                    iMasterAlgorithmTable = null;
                }
                
                if ( iView != null )
                {
                    iView.Dispose();
                    iView = null;
                }
            }
        }
        #endregion

        #region Internal properties
        private StackAlgorithm CurrentAlgorithm
        {
            get
            {
                if ( iExecutionQueue.Count == 0 )
                {
                    throw new Exception( "No stack algorithms available" );
                }
                //
                return iExecutionQueue.Peek();
            }
        }
		#endregion

        #region Internal methods
        private void PrepareForExecution()
        {
            Trace( "[SBAlgManager] PrepareForExecution() - START" );

            // Validate address info
            iEngine.AddressInfo.Validate();

            // Clear data output
            iEngine.DataOutput.Clear();

            // Reset master list
            IStackAlgorithmManager manager = (IStackAlgorithmManager) this;
            IStackAlgorithmObserver observer = (IStackAlgorithmObserver) this;
            iMasterAlgorithmTable.Load( new object[] { manager, observer } );
            SortAlgorithms();

            // Build list of algorithms we'll try to use
            PrepareExecutionQueue();

            // Work out maximum progress value for this operation
            int numberOfPendingAlgs = iExecutionQueue.Count;
            iProgressValueMax = numberOfPendingAlgs * 100;

            // Reset current progress
            iProgressValueCurrent = 0;
            iProgressValueCompleted = 0;

            Trace( "[SBAlgManager] PrepareForExecution() - END" );
        }

        private void PrepareExecutionQueue()
        {
            iExecutionQueue.Clear();
            
            // Find most appropriate algorithm...
            StackAlgorithm primary = FindPrimaryAlgorithm();
            if ( primary != null )
            {
                iExecutionQueue.Enqueue( primary );

                // Find backup algorithms
                EnqueueBackupAlgorithms( primary );
            }
            else
            {
                throw new Exception( "No valid stack algorithms available" );
            }
        }

        private StackAlgorithm FindPrimaryAlgorithm()
        {
            StackAlgorithm ret = null;
            //
            foreach ( StackAlgorithm alg in iMasterAlgorithmTable )
            {
                if ( alg.IsAvailable() )
                {
                    ret = alg;
                    break;
                }
            }
            //
            Trace( "[SBAlgManager] FindPrimaryAlgorithm() - ret: {0}", ret );
            return ret;
        }

        private void EnqueueBackupAlgorithms( StackAlgorithm aExclude )
        {
            foreach ( StackAlgorithm alg in iMasterAlgorithmTable )
            {
                string name = alg.Name;
                bool available = alg.IsAvailable();
                Trace( "[SBAlgManager] EnqueueBackupAlgorithms() - name: {0}, available: {1}", name, available );

                if ( available && name != aExclude.Name )
                {
                    iExecutionQueue.Enqueue( alg );
                }
            }
        }

        private void ExecuteHeadAlgorithm()
        {
            Trace( "[SBAlgManager] ExecuteHeadAlgorithm() - iSynchronicity: {0}", iSynchronicity );
            StackAlgorithm alg = CurrentAlgorithm;
            alg.BuildStack( iSynchronicity );
        }

        private void SortAlgorithms()
        {
            Comparison<StackAlgorithm> sorter = delegate( StackAlgorithm aLeft, StackAlgorithm aRight )
            {
                int ret = 1;
                //
                if ( aLeft == null )
                {
                    ret = -1;
                }
                else if ( aRight == null )
                {
                }
                else
                {
                    ret = aLeft.Priority.CompareTo( aRight.Priority );
                }
                //
                return ret;
            };
            iMasterAlgorithmTable.Sort( sorter );
        }

        private void ReportEvent( TEvent aEvent )
        {
            // Ensure we only report significant events once
            if ( EventHandler != null )
            {
                switch ( aEvent )
                {
                case TEvent.EAlgorithmStarted:
                    if ( ( iFlags & TFlags.EFlagsReportedStarted ) != TFlags.EFlagsReportedStarted )
                    {
                        iFlags |= TFlags.EFlagsReportedStarted;
                        EventHandler( this, aEvent );
                    }
                    break;
                case TEvent.EAlgorithmComplete:
                    if ( ( iFlags & TFlags.EFlagsReportedComplete ) != TFlags.EFlagsReportedComplete )
                    {
                        iFlags |= TFlags.EFlagsReportedComplete;
                        EventHandler( this, aEvent );
                    }
                    break;
                default:
                    EventHandler( this, aEvent );
                    break;
                }
            }
        }
        #endregion

        #region Internal enumerations
        [Flags]
        private enum TFlags
        {
            EFlagsNone = 0,
            EFlagsReportedStarted = 1,
            EFlagsReportedComplete = 2
        }
        #endregion

        #region Data members
        private readonly StackEngine iEngine;
        private DbgEngineView iView;
        private PluginManager<StackAlgorithm> iMasterAlgorithmTable = new PluginManager<StackAlgorithm>( 2 );
        
        // Transient variables
        private TSynchronicity iSynchronicity = TSynchronicity.EAsynchronous;
        private Queue<StackAlgorithm> iExecutionQueue = new Queue<StackAlgorithm>();
        private int iProgressValueMax = 0;
        private int iProgressValueCompleted = 0;
        private int iProgressValueCurrent = 0;
        private TFlags iFlags = TFlags.EFlagsNone;
        #endregion
    }
}