crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianCodeLib/DbgEnginePlugin/CodePrimer.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) 2004-2008 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 System.IO;
using SymbianUtils;
using SymbianUtils.Tracer;
using SymbianUtils.FileTypes;
using SymbianStructuresLib.Debug.Symbols;
using SymbianStructuresLib.CodeSegments;
using SymbianDebugLib.Entity;
using SymbianDebugLib.PluginAPI;
using SymbianCodeLib.SourceManagement.Source;
using SymbianCodeLib.SourceManagement.Provisioning;

namespace SymbianCodeLib.DbgEnginePlugin
{
    internal class CodePrimer : DbgPluginPrimer
    {
        #region Constructors
        public CodePrimer( CodePlugin aPlugin )
            : base( aPlugin )
		{
		}
		#endregion

        #region From DbgPluginPrimer
        public override void Add( DbgEntity aEntity )
        {
            CodeSourceProvider provider = null;
            //
            if ( aEntity.FSEntity.IsFile )
            {
                if ( aEntity.Exists && aEntity.FSEntity.IsValid )
                {
                    provider = ProvisioningManager.GetProvider( aEntity.FSEntity.FullName );
                }
                //
                if ( provider != null )
                {
                    using ( CodeSourceCollection sources = provider.CreateSources( aEntity.FullName ) )
                    {
                        // Make sure the time to read attribute is setup in alignment with
                        // whether the entity was explicitly added by the user or found implicitly
                        // by scanning.
                        if ( aEntity.WasAddedExplicitly == false )
                        {
                            foreach ( CodeSource source in sources )
                            {
                                // This means, don't read this source until it is actually
                                // referenced by the client. I.e. until the client activates
                                // a code segment that refers to this 
                                source.TimeToRead = CodeSource.TTimeToRead.EReadWhenNeeded;
                            }
                        }

                        // Ownership is transferred
                        iSources.AddRange( sources );
                        sources.Clear();
                    }
                }
                else
                {
                    throw new NotSupportedException( "Specified file type is not supported" );
                }
            }
            else
            {
                throw new ArgumentException( "SymbianCodeLib does not support directory entities" );
            }
        }

        public override void Prime( TSynchronicity aSynchronicity )
        {
            CodePlugin plugin = this.Plugin;

            // Wipe any state ready for new priming attempt
            base.OnPrepareToPrime();
            if ( base.ResetEngineBeforePriming )
            {
                plugin.Clear();
            }
    
            // Report the "priming started event" prior to adding the sources
            base.ReportEvent( TPrimeEvent.EEventPrimingStarted, null );

            // Any sources already registered with the source manager at the start of a new
            // priming request are assumed to already be complete.
            RecordAlreadyCompletedSources();

            // Initially, whilst adding the sources to the source manager, we'll
            // operate synchronously. This prevents us from triggering the read
            // operation in the SourceAdded callback below.
            iSynchronicity = TSynchronicity.ESynchronous;

            // Listen to source manager events in case any new sources are
            // created whilst reading those we already know about.
            CodeSourceManager sourceManager = this.SourceManager;
            sourceManager.SourceAdded += new CodeSourceManager.SourceEventHandler( SourceManager_SourceAdded );
            sourceManager.SourceRemoved += new CodeSourceManager.SourceEventHandler( SourceManager_SourceRemoved );

            // Tell the source manager which sources we are reading. This also will
            // call our event handler (added above) so that we can observe source events.
            CodeSourceCollection sources = iSources;
            iSources = new CodeSourceCollection();
            sourceManager.AddRange( sources );

            // If we're operating asynchronously, then the loop below will potentially
            // complete quite quickly. This means that any (new/additional) sources which 
            // are created (asynchronously) whilst reading is underway will not themselves 
            // be read. 
            // 
            // Therefore, we store the synchronisation mode as a data member, and when
            // 'SourceAdded' is called, if operating asynchronously, we'll also initiate
            // a read operation for the source (as soon as it is added).
            //
            // If we're operating synchronously, then this isn't important. Because we're
            // behaving synchronously, the loop below will only process one source at a
            // time (before waiting) and therefore even if that source adds new/additional
            // sources to the source manager, we'll catch them as soon as we move the
            // next iteration around the loop.
            iSynchronicity = aSynchronicity;

            // TODO: possibly re-write this so that it uses two separate code paths
            // for synchronous and asynchronous priming? By trying to use one path
            // this code looks rather complex and isn't terribly robust.
            try
            {
                // Now we can start the sources running.
                int count = iSourcesYetToBePrimed.Count;
                while ( count > 0 )
                {
                    // Get the head source and remove it from the pending list
                    CodeSource source = iSourcesYetToBePrimed[ 0 ];
                    iSourcesYetToBePrimed.Remove( source );
                        
                    // If the source wants to read it's data immediately, then activated
                    // it right now...
                    if ( source.TimeToRead == CodeSource.TTimeToRead.EReadWhenPriming )
                    {
                        source.Read( aSynchronicity );
                    }
                    else
                    {
                        // This source will read it's data on it's own terms so skip
                        // it to ensure that we can track when all the other sources 
                        // (that do actually read their files now..) are ready.
                        Skip( source );
                    }

                    count = iSourcesYetToBePrimed.Count;
                }
            }
            catch( Exception e )
            {
                // If priming failed, report completion before rethrowing...
                OnPrimeComplete();
                throw e;
            }
       }

        protected override void OnPrimeComplete()
        {
            System.Diagnostics.Debug.Assert( iSourcesYetToBePrimed.Count == 0 );
            try
            {
                SourceManager.SourceAdded -= new CodeSourceManager.SourceEventHandler( SourceManager_SourceAdded );
                SourceManager.SourceRemoved -= new CodeSourceManager.SourceEventHandler( SourceManager_SourceRemoved );
                //
                base.OnPrimeComplete();
            }
            finally
            {
                SourceEventsUnsubscribe();
            }
        }

        protected override int Count
        {
            get
            {
                int count = SourceManager.Count;
                return count; 
            }
        }
        #endregion

        #region API
        #endregion

		#region Properties
        internal CodePlugin Plugin
        {
            get { return base.Engine as CodePlugin; }
        }

        internal CodeSourceManager SourceManager
        {
            get { return Plugin.SourceManager; }
        }

        internal CodeSourceProviderManager ProvisioningManager
        {
            get { return Plugin.ProvisioningManager; }
        }
        #endregion

        #region Event handlers
        private void SourceManager_SourceAdded( CodeSource aSource )
        {
            base.Engine.Trace( "[CodePrimer] SourceManager_SourceAdded - aSource: {0}, time to read: {1}", aSource, aSource.TimeToRead );

            aSource.EventHandler += new CodeSource.EventHandlerFunction( Source_EventHandler );
            bool needToSave = true;

            // If we're in async mode and the source wants to be read immediately
            // then kick it off. If we're operating in sync mode, then this will
            // be done as part of the loop within 'Prime'.
            if ( iSynchronicity == TSynchronicity.EAsynchronous )
            {
                if ( aSource.TimeToRead == CodeSource.TTimeToRead.EReadWhenPriming )
                {
                    aSource.Read( iSynchronicity );
                }
                else
                {
                    // This source will read it's data on it's own terms so skip
                    // it to ensure that we can track when all the other sources 
                    // (that do actually read their files now..) are ready.
                    Skip( aSource );
                }

                // We don't need to add it to the 'yet to be primed' list because
                // it's either been 'read' or then it only supports read-on-demand.
                needToSave = false;
            }

            if ( needToSave )
            {
                lock ( iSourcesYetToBePrimed )
                {
                    iSourcesYetToBePrimed.Add( aSource );
                }
            }

            base.Engine.Trace( string.Format( "[SourceManager_SourceAdded] {0}, srcCount: {1}, yetToBePrimed: {2}", aSource.URI, SourceManager.Count, iSourcesYetToBePrimed.Count ) );
        }

        private void SourceManager_SourceRemoved( CodeSource aSource )
        {
            base.Engine.Trace( string.Format( "[SourceManager_SourceRemoved] START - {0}, srcCount: {1}, yetToBePrimed: {2}", aSource.URI, SourceManager.Count, iSourcesYetToBePrimed.Count ) );
            
            aSource.EventHandler -= new CodeSource.EventHandlerFunction( Source_EventHandler );

            base.RemoveFromCompleted( aSource );
            lock ( iSourcesYetToBePrimed )
            {
                iSourcesYetToBePrimed.Remove( aSource );
            }

            // Check for completion since removing a source might mean that we've now
            // reached completed state (if it was the last one that we were waiting for)
            bool amComplete = base.IsComplete;
            if ( amComplete )
            {
                CheckForCompletion( amComplete );
            }

            base.Engine.Trace( string.Format( "[SourceManager_SourceRemoved] END - {0}, srcCount: {1}, yetToBePrimed: {2}, amComplete: {3}", aSource.URI, SourceManager.Count, iSourcesYetToBePrimed.Count, amComplete ) );
        }

        private void Source_EventHandler( CodeSource.TEvent aEvent, CodeSource aSource, object aData )
        {
            bool primeCompleted = false;

            // Map source event onto a primer event
            if ( aEvent == CodeSource.TEvent.EReadingProgress )
            {
                base.SaveLatestProgress( aSource, (int) aData );
            }
            
            // If all sources are complete, then are we also done?
            if ( aEvent == CodeSource.TEvent.EReadingComplete )
            {
                // We don't need to listen to this source anymore
                aSource.EventHandler -= new CodeSource.EventHandlerFunction( Source_EventHandler );

                // Source is 100% complete now.
                base.SaveLatestProgress( aSource, 100 );

                // It's complete, so record as such so that we can tell when all the sources
                // are now ready.
                primeCompleted = base.AddToCompleted( aSource );
            }

            CheckForCompletion( primeCompleted );
        }
        #endregion

        #region Internal methods
        private void Skip( CodeSource aSource )
        {
            System.Diagnostics.Debug.Assert( aSource.TimeToRead == CodeSource.TTimeToRead.EReadWhenNeeded );
            bool primeCompleted = base.AddToCompleted( aSource );
            CheckForCompletion( primeCompleted );
        }

        private void SourceEventsUnsubscribe()
        {
            foreach ( CodeSource source in SourceManager )
            {
                source.EventHandler -= new CodeSource.EventHandlerFunction( Source_EventHandler );
            }
        }

        private void CheckForCompletion( bool aAmIComplete )
        {
            // Report any progress
            base.ReportProgressIfNeeded( aAmIComplete );

            // Tidy up and report completion
            if ( aAmIComplete )
            {
                OnPrimeComplete();
            }
        }

        private void RecordAlreadyCompletedSources()
        {
            CodeSourceManager sourceManager = this.SourceManager;
            foreach ( CodeSource source in sourceManager )
            {
                base.AddToCompleted( source );
            }
        }
        #endregion

        #region Data members
        private TSynchronicity iSynchronicity = TSynchronicity.ESynchronous;
        private CodeSourceCollection iSources = new CodeSourceCollection();
        private CodeSourceCollection iSourcesYetToBePrimed = new CodeSourceCollection();
        #endregion
    }
}