diff -r 000000000000 -r 818e61de6cd1 crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianCodeLib/DbgEnginePlugin/CodePrimer.cs --- /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 + } +}