crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianCodeLib/DbgEnginePlugin/CodePrimer.cs
changeset 0 818e61de6cd1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianCodeLib/DbgEnginePlugin/CodePrimer.cs	Thu Feb 11 15:50:58 2010 +0200
@@ -0,0 +1,360 @@
+/*
+* 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
+    }
+}