crashanalysercmd/UI/CrashServer/Engine/CACmdLineEngine.cs
changeset 0 818e61de6cd1
child 2 0c91f0baec58
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crashanalysercmd/UI/CrashServer/Engine/CACmdLineEngine.cs	Thu Feb 11 15:50:58 2010 +0200
@@ -0,0 +1,910 @@
+/*
+* 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.Text;
+using System.IO;
+using System.Xml;
+using System.Collections.Generic;
+using CrashItemLib.Crash;
+using CrashItemLib.Crash.Container;
+using CrashItemLib.Crash.Source;
+using CrashItemLib.Crash.Processes;
+using CrashItemLib.Crash.Messages;
+using CrashItemLib.Crash.Summarisable;
+using CrashItemLib.Sink;
+using CrashItemLib.Engine;
+using CrashItemLib.Engine.Sources;
+using CrashItemLib.Engine.Interfaces;
+using CrashItemLib.PluginAPI;
+using SymbianUtils.FileSystem.Utilities;
+using SymbianXmlInputLib.Parser;
+using SymbianXmlInputLib.Parser.Nodes;
+using SymbianXmlInputLib.Elements;
+using SymbianXmlInputLib.Elements.Types.Category;
+using SymbianXmlInputLib.Elements.Types.FileSystem;
+using SymbianXmlInputLib.Elements.Types.Command;
+using SymbianDebugLib;
+using SymbianDebugLib.Engine;
+using SymbianDebugLib.Entity;
+using SymbianUtils;
+using SymbianUtils.Tracer;
+using CrashItemLib.Crash.InfoSW;
+
+namespace CrashAnalyserServerExe.Engine
+{
+    internal class CACmdLineEngine : DisposableObject, ITracer, ICIEngineUI
+	{
+		#region Constructors
+        public CACmdLineEngine( DbgEngine aDebugEngine )
+		{
+            iDebugEngine = aDebugEngine;
+            iInputs = new CACmdLineInputParameters( aDebugEngine );
+            iCrashItemEngine = new CIEngine( aDebugEngine, this as ICIEngineUI );
+		}
+		#endregion
+
+        #region API
+        public int RunCommandLineOperations()
+        {
+            Trace( "[CA Cmd] START " );
+            Trace( string.Empty );
+            Trace( "[CA Cmd] command line: " + System.Environment.CommandLine );
+            Trace( "[CA Cmd] command wd:   " + System.Environment.CurrentDirectory );
+            Trace( "[CA Cmd] proc count:   " + System.Environment.ProcessorCount );
+            Trace( "[CA Cmd] sysdir:       " + System.Environment.SystemDirectory );
+            Trace( "[CA Cmd] version:      " + System.Environment.Version.ToString() );
+            Trace( string.Empty );
+
+            int error = CACmdLineException.KErrNone;
+                        
+            //
+            try
+            {
+                if (!iInputs.ParseCommandLine())
+                {
+                    throw new CACmdLineException("Error while parsing command line", CACmdLineException.KErrCommandLineError);
+                }
+                
+                // We expect to see an "-input" parameter
+                iReportProgress = CheckForProgressParameter();
+
+                // Switch off UI output at the debug engine level
+                iDebugEngine.UiMode = TDbgUiMode.EUiDisabled;
+
+                // Next, attempt to prime the crash engine with every source we
+                // identified from the input specification. The goal is to
+                // create all the needed Crash Item Source objects for each input file.
+                // Any inputs which we don't support will not have an associated source and
+                // will be flagged accordingly within the CACmdLineFileSource object.
+                TryToPrimeSources();
+
+                // Next, prime the debug engine will all the debug meta-data inputs.
+                // Again, individual error messages will be associated with each meta-data
+                // input.
+                TryToPrimeDbgEngine();
+
+                // Next, we invoke the crash engine to process all the crash item sources we
+                // created during the prime step. Exceptions are caught and associated 
+                // messages & diagnostics are created at the input-file level.
+                TryToIdentifyCrashes();
+
+                // Next, we start the output phase. Any 'valid' crash containers are serialized
+                // to xml. Any input files which could not be processed have 'dummy' containers
+                // created for them, and these are also serialised to 'failed' CI files.
+                // If the CI Sink plugin is unavailable, then we cannot create any CI output.
+                // In this situation, we throw an exception which is caught below.
+                TryToCreateOutput();
+            }
+            catch ( CACmdLineException cmdLineException )
+            {
+                error = cmdLineException.ErrorCode;
+                //
+                Trace( "[CA Cmd] " + cmdLineException.Message + " " + cmdLineException.StackTrace );
+            }
+            catch ( Exception generalException )
+            {
+                error = CACmdLineException.KErrGeneral;
+                //
+                Trace( "[CA Cmd] " + generalException.Message + " " + generalException.StackTrace );
+            }
+            
+            Trace( "[CA Cmd] - operation complete: " + error );
+            return error;
+        }
+        #endregion
+
+		#region Properties
+        public string CommandLineArguments
+        {
+            get { return Environment.CommandLine; }
+        }
+ 
+        public CIEngine CrashItemEngine
+        {
+            get { return iCrashItemEngine; }
+        }
+        #endregion
+
+        #region Event handlers
+        private void DbgEngine_EntityPrimingStarted( DbgEngine aEngine, DbgEntity aEntity, object aContext )
+        {
+            Trace( "[CA Cmd] Priming debug meta-data: " + aEntity.FullName );
+
+            // Emit progress banner
+            if ( iReportProgress )
+            {
+                Print( "Reading debug meta-data..." );
+            }
+        }
+
+        private void DbgEngine_EntityPrimingProgress( DbgEngine aEngine, DbgEntity aEntity, object aContext )
+        {
+            if ( aContext != null )
+            {
+                if ( aContext.GetType() == typeof( int ) )
+                {
+                    int value = (int) aContext;
+                    Trace( "[CA Cmd] Priming debug meta-data progress: {0:d3}% {1}", value, aEntity.FullName );
+
+                    // If reporting progress, then output something so the carbide extension is aware
+                    // of what is going on in the background.
+                    if ( iReportProgress )
+                    {
+                        string msg = string.Format( "{1:d3}%, {0}", aEntity.FullName, value );
+                        Print( msg );
+                    }
+                }
+            }
+        }
+
+        private void DbgEngine_EntityPrimingComplete( DbgEngine aEngine, DbgEntity aEntity, object aContext )
+        {
+            Trace( "[CA Cmd] Primed debug meta-data: " + aEntity.FullName );
+        }
+
+        private void CrashItemEngine_SourceObserver( CIEngine.TSourceEvent aEvent, CIEngineSource aSource, object aParameter )
+        {
+            if ( iReportProgress )
+            {
+                string msg = string.Empty;
+                //
+                switch ( aEvent )
+                {
+                case CIEngine.TSourceEvent.EEventSourceReady:
+                    msg = string.Format( "Reading file: [{0}], progress: 100%", aSource.FileName );
+                    break;
+                case CIEngine.TSourceEvent.EEventSourceProgress:
+                    if ( aParameter != null && aParameter is int )
+                    {
+                        msg = string.Format( "Reading file: [{0}], progress: {1:d3}%", aSource.FileName, (int) aParameter );
+                    }
+                    break;
+                default:
+                    break;
+                }
+                
+                // Output a message only if we have one
+                if ( string.IsNullOrEmpty( msg ) == false )
+                {
+                    Print( msg );
+                }
+            }
+        }
+        #endregion
+
+        #region Internal constants
+        private const string KCrashItemSinkName = "CRASH INFO FILE";
+        private const string KParamProgress = "-PROGRESS";
+        #endregion
+
+        #region Internal methods
+        private bool CheckForProgressParameter()
+        {
+            bool report = this.CommandLineArguments.Contains( KParamProgress );
+            return report;
+        }
+
+        private CISink FindSink()
+        {
+            Trace( "[CA Cmd] FindSink() - START" );
+            CISink ret = null;
+            //
+            CISinkManager sinkManager = iCrashItemEngine.SinkManager;
+            foreach ( CISink sink in sinkManager )
+            {
+                Trace( "[CA Cmd] FindSink() - found sink: " + sink.Name );
+
+                if ( sink.Name.ToUpper().Contains( KCrashItemSinkName ) )
+                {
+                    ret = sink;
+                    break;
+                }
+            }
+            //
+            Trace( "[CA Cmd] FindSink() - END - ret: " + ret );
+            return ret;
+        }
+
+        private void TryToPrimeSources()
+        {
+            Trace( "[CA Cmd] TryToPrimeSources() - START" );
+            CrashItemEngine.ClearAll();
+            
+            // Prime engine with source files
+            CACmdLineFSEntityList<CACmdLineFileSource> sourceFileNames = iInputs.SourceFiles;
+            int progress = -1;
+            int count = sourceFileNames.Count;
+
+            // Emit progress banner
+            if ( iReportProgress )
+            {
+                Print( "Locating crash files..." );
+            }
+
+            for ( int i = 0; i < count; i++ )
+            {
+                CACmdLineFileSource file = sourceFileNames[ i ];
+                //
+                try
+                {
+                    // We prime each file individually. If an exception is thrown then we
+                    // record an appropriate error in the associated file object.
+                    Trace( "[CA Cmd] TryToPrimeSources() - priming: " + file );
+
+                    bool primeSuccess = CrashItemEngine.Prime( file );
+
+                    // Report progress as we work through the sources
+                    if ( iReportProgress )
+                    {
+                        float newProgress = ( ( (float) i + 1 ) / (float) count ) * 100.0f;
+                        if ( (int) newProgress != progress || i == count - 1 )
+                        {
+                            progress = (int) newProgress;
+                            Print( string.Format( "{0:d3}%", progress ) );
+                        }
+                    }
+
+                    Trace( "[CA Cmd] TryToPrimeSources() - primed result: " + primeSuccess );
+                }
+                catch ( Exception sourcePrimerException )
+                {
+                    file.AddError( "Error Identifying Source Type", "There was an error when attempting to identify the source file type. The file could not be processed." );
+                    file.AddDiagnostic( "Crash Primer Exception Message", sourcePrimerException.Message );
+                    file.AddDiagnostic( "Crash Primer Exception Stack", sourcePrimerException.StackTrace );
+                }
+            }
+
+            AssociateInputFilesWithCrashItemSources();
+            Trace( "[CA Cmd] TryToPrimeSources() - END" );
+        }
+
+        private void TryToPrimeDbgEngine()
+        {
+            DbgEngine debugEngine = iDebugEngine;
+            //
+            Exception primerException = null;
+            CACmdLineFSEntityList<CACmdLineFSEntity> metaDataFiles = iInputs.MetaDataFiles;
+            //
+            try
+            {
+                debugEngine.Clear();
+
+                foreach ( CACmdLineFSEntity entry in metaDataFiles )
+                {
+                    Trace( "[CA Cmd] Seeding debug meta engine with entry: " + entry.Name );
+                    DbgEntity entity = debugEngine.Add( entry.Name );
+                    if ( entity != null )
+                    {
+                        Trace( "[CA Cmd] Entry type detected as: [" + entity.CategoryName + "]" );
+                        entity.Tag = entry;
+                    }
+                    else
+                    {
+                        Trace( "[CA Cmd] Entry not handled: " + entry.Name );
+                        entry.AddError( "Meta-Data File Not Supported", "The file \'" + entry.Name + "\' is of unknown origin." );
+                    }
+                }
+
+                // Listen to prime events
+                try
+                {
+                    Trace( "[CA Cmd] Starting prime operation... " );
+                    debugEngine.EntityPrimingStarted += new DbgEngine.EventHandler( DbgEngine_EntityPrimingStarted );
+                    debugEngine.EntityPrimingProgress += new DbgEngine.EventHandler( DbgEngine_EntityPrimingProgress );
+                    debugEngine.EntityPrimingComplete += new DbgEngine.EventHandler( DbgEngine_EntityPrimingComplete );
+                    debugEngine.Prime( TSynchronicity.ESynchronous );
+                    Trace( "[CA Cmd] Debug meta data priming completed successfully." );
+                }
+                finally
+                {
+                    debugEngine.EntityPrimingStarted -= new DbgEngine.EventHandler( DbgEngine_EntityPrimingStarted );
+                    debugEngine.EntityPrimingProgress -= new DbgEngine.EventHandler( DbgEngine_EntityPrimingProgress );
+                    debugEngine.EntityPrimingComplete -= new DbgEngine.EventHandler( DbgEngine_EntityPrimingComplete );
+                }
+            }
+            catch ( Exception exception )
+            {
+                Trace( "[CA Cmd] Debug meta data priming exception: " + exception.Message + ", " + exception.StackTrace );
+                primerException = exception;
+            }
+
+            // Go through each debug entity and check it for errors. Add diagnostics
+            // and error messages where appropriate.
+            foreach ( DbgEntity entity in debugEngine )
+            {
+                string name = entity.FullName;
+                //
+                CACmdLineFSEntity file = metaDataFiles[ name ];
+                file.Clear();
+                //
+                if ( entity.PrimerResult.PrimedOkay )
+                {
+                    if ( !entity.Exists )
+                    {
+                        file.AddError( "Meta-Data File Missing", string.Format( "The file \'{0}\' could not be found.", file.Name ) );
+                    }
+                    else if ( entity.IsUnsupported )
+                    {
+                        file.AddError( "Meta-Data File Not Supported", string.Format( "The file \'{0}\' is of unknown origin.", file.Name ) );
+                    }
+                }
+                else
+                {
+                    // Add error
+                    file.AddError( "Meta-Data Read Error", entity.PrimerResult.PrimeErrorMessage );
+
+                    // And diagnostic information
+                    Exception exception = entity.PrimerResult.PrimeException != null ? entity.PrimerResult.PrimeException : primerException;
+                    if ( exception != null )
+                    {
+                        file.AddDiagnostic( "Meta-Data Exception Message", entity.PrimerResult.PrimeException.Message );
+                        file.AddDiagnostic( "Meta-Data Exception Stack", entity.PrimerResult.PrimeException.StackTrace );
+                    }
+                    else
+                    {
+                        file.AddDiagnostic( "Meta-Data Unknown Failure", "No exception occurred at the primer or entity level?" );
+                    }
+                }
+            }
+        }
+
+        private void TryToIdentifyCrashes()
+        {
+            Exception crashEngineException = null;
+            //
+            try
+            {
+                iCrashItemEngine.SourceObservers += new CIEngine.CIEngineSourceObserver( CrashItemEngine_SourceObserver );
+                iCrashItemEngine.IdentifyCrashes( TSynchronicity.ESynchronous );
+            }
+            catch ( Exception exception )
+            {
+                crashEngineException = exception;
+            }
+            finally
+            {
+                iCrashItemEngine.SourceObservers -= new CIEngine.CIEngineSourceObserver( CrashItemEngine_SourceObserver );
+            }
+
+            // Check each source in the engine and create messages based upon it's
+            // state at the end of processing.
+            foreach ( CACmdLineFileSource file in iInputs.SourceFiles )
+            {
+                if ( file.Source != null )
+                {
+                    CIEngineSource source = file.Source;
+                    switch ( source.State )
+                    {
+                    case CIEngineSource.TState.EStateReady:
+                        // Success case - the source resulted in the creation of at least one container
+                        file.AddDiagnostic( "Source Read Successfully", string.Format( "{0} crash container(s) created", source.ContainerCount ) );
+                        break;
+                    case CIEngineSource.TState.EStateReadyNoItems:
+                        file.AddWarning( "Source File Contains No Crashes", "The input data was read successfully but contains no crash information." );
+                        break;
+                    case CIEngineSource.TState.EStateReadyCorrupt:
+                        file.AddError( "Source File is Corrupt", "The input data is invalid or corrupt." );
+                        break;
+                    case CIEngineSource.TState.EStateUninitialised:
+                        file.AddError( "Source File not Read", "The input data was never read." );
+                        file.AddDiagnostic( "Source State Invalid", "Source is still in unitialised state, even though reading is complete?" );
+                        break;
+                    case CIEngineSource.TState.EStateProcessing:
+                        file.AddDiagnostic( "Source State Invalid", "Source is still in processing state, even though reading is complete?" );
+                        break;
+                    default:
+                        break;
+                    }
+                }
+                else
+                {
+                    file.AddError( "File is Not Supported", "There file type is not recognized and was not processed." );
+                }
+
+                // Add in details of any exception
+                if ( crashEngineException != null )
+                {
+                    file.AddDiagnostic( "Crash Identification Exception Message", crashEngineException.Message );
+                    file.AddDiagnostic( "Crash Identification Exception Stack", crashEngineException.StackTrace );
+                }
+            }
+        }
+
+        private void TryToCreateOutput()
+        {
+            CACmdLineFSEntityList<CACmdLineFileSource> inputFiles = iInputs.SourceFiles;
+            //
+            CISink sink = FindSink();
+            if ( sink == null )
+            {
+                throw new CACmdLineException( "CI Output Plugin Not Available", CACmdLineException.KErrSinkNotAvailable );
+            }
+  
+            CACmdLineFSEntityList<CACmdLineFileSource> sourceFileNames = iInputs.SourceFiles;
+            int progress = -1;
+            int count = sourceFileNames.Count;
+
+            // Emit progress banner
+            if ( iReportProgress )
+            {
+                Print( "Creating CI content..." );
+            }
+            
+            for ( int i = 0; i < count; i++ )
+            {
+                CACmdLineFileSource file = sourceFileNames[ i ];
+
+                System.Console.WriteLine("Starting to process file " +file.Name);
+
+                // If the file has a corresponding source then we know that crash item recognised it.
+                // Otherwise, we skip it.
+                if ( file.Source != null )
+                {
+                    // We copy and remove all the file-level messages. These will be added to the container
+                    // (where appropriate) or then to an output entry otherwise.
+                    CACmdLineMessage[] fileMessages = file.ToArray();
+                    file.Clear();
+
+                    // At this point, the input file is guaranteed to have an associated container. In
+                    // the current mobile crash file format, there will be a 1:1 mapping, i.e. each file
+                    // will contain a single crash report ("container") and therefore if symbols were
+                    // found for just a single container (within the file) then it's good enough to treat
+                    // the file as archivable.
+                    foreach ( CIContainer container in file.Containers )
+                    {
+                        // Firstly, add any meta-data errors/messages/warnings to this container
+                        // as crash item message entries
+                        AddMetaDataMessagesToContainer( container );
+
+                        SetArchiveFileName(file.Name, container, sink);
+
+                        foreach (CIMessage message in container.Messages)
+                        {
+                            if (message.Type == CrashItemLib.Crash.Messages.CIMessage.TType.ETypeError)
+                            {
+                                container.Status = CIContainer.TStatus.EStatusErrorContainer;
+                            }
+                        }
+                        // Now we can try to serialize the container to CI. This method will
+                        // not throw an exception.
+                        //
+                        // If the operation succeeds, then the input file will have an associated
+                        // container object (and associated xml output file name) and we need not
+                        // do anymore.
+                        //
+                        // If it fails, then the input file will not be assigned the container
+                        // object and therefore, later on, we'll invoke the CI Sink directly to 
+                        // create a stub 'FAILED' CI output file.
+                        //
+                        // NB: If Symbols were not available for the specified container, then
+                        // we don't create a CI file but instead callback to a helper that
+                        // will move the file to another temporary location for later repeat
+                        // processing
+                        bool hasSymbols = ContainsSymbols( container );
+                        
+                        if (container.Status == CIContainer.TStatus.EStatusErrorContainer) //normally don't output containers with errors
+                        {
+                            file.State = CACmdLineFileSource.TState.EStateUninitialized;
+                            if (iInputs.DecodeWithoutSymbols) //with force mode, output no matter what
+                            {
+                                TryToCreateOutput(sink, container, file, fileMessages);
+                            }
+                        }
+                        else if (hasSymbols || IsSymbollessMobileCrash( container ))
+                        {                            
+                            file.State = CACmdLineFileSource.TState.EStateProcessedAndReadyToBeArchived;
+                            TryToCreateOutput(sink, container, file, fileMessages);
+                        }
+                        else if (IsSymbollessMobileCrash(container)) //Crash api and registration files do not need symbols
+                        {
+                            
+                            file.State = CACmdLineFileSource.TState.EStateProcessedAndReadyToBeArchived;
+                            TryToCreateOutput(sink, container, file, fileMessages);
+                        }
+                        else 
+                        {                            
+                            file.State = CACmdLineFileSource.TState.EStateSkippedDueToMissingSymbols;
+                            if (iInputs.DecodeWithoutSymbols) //with force mode, output no matter what
+                            {
+                                //remove this to prevent .corrupt_ci creation!
+                                TryToCreateOutput(sink, container, file, fileMessages);
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    file.State = CACmdLineFileSource.TState.EStateSkippedDueToNotBeingRecognized;
+                }
+
+                // Move file to final location
+                MoveProcessedFile( file );
+
+                // Report progress as we work through the sources
+                if ( iReportProgress )
+                {
+                    float newProgress = ( ( (float) i + 1 ) / (float) count ) * 100.0f;
+                    if ( (int) newProgress != progress || i == count - 1 )
+                    {
+                        progress = (int) newProgress;
+                        Print( string.Format( "{0:d3}%", progress ) );
+                    }
+                }
+            }
+        }
+
+       
+        private void SetArchiveFileName(string aFileFullPath, CIContainer aContainer, CISink sink)
+        {
+            string fileName = Path.GetFileName(aFileFullPath);
+
+            //add romid to filename if not already there
+            CIInfoSW info = (CIInfoSW) aContainer.ChildByType( typeof( CIInfoSW ) );
+            if (info != null)
+            {
+                //RomID
+                if (info.ImageCheckSum != 0)
+                {
+                    string romid = info.ImageCheckSum.ToString("x8");
+
+                    if (fileName.Length < 8 || fileName.Substring(0, 8) != romid)
+                    {
+                        fileName = romid + "_" + fileName;
+                        
+                    }
+                }
+            }
+            
+            
+            string basename = Path.GetFileNameWithoutExtension(fileName);
+            string extension = Path.GetExtension(aFileFullPath);
+            
+            string archiveNamePath = Path.Combine(iInputs.ArchivePath, fileName); //Path.GetFileName(iInputs.ArchivePath));
+            int counter = 1;               
+            while (File.Exists(archiveNamePath))
+            {
+                archiveNamePath = Path.Combine(iInputs.ArchivePath, basename + "_" + counter.ToString() + extension);
+                counter++;
+            }
+
+            iInputs.SinkParameters.OperationData1 = (Object)archiveNamePath;
+            iBinFileArchivePathName = archiveNamePath;
+           
+        }
+
+       
+        private void TryToCreateOutput( CISink aXmlSink, CIContainer aContainer, CACmdLineFileSource aFile, CACmdLineMessage[] aMessagesToAdd )
+        {
+            Trace( "[CA Cmd] TryToCreateOutput() - START - container source: {0}", aContainer.Source.MasterFileName );
+
+            // By the time we are outputting a container, there should no longer be any messages
+            // associated with the file.
+            System.Diagnostics.Debug.Assert( aFile.Count == 0 );
+
+            // Check whether the file contained any errors or 
+            // messages of it own.
+            if ( aMessagesToAdd.Length > 0 )
+            {
+                // Copy warnings, messages and errors into crash item container.
+                // Diagnostic messages are not copied.
+                CACmdLineFSEntity.CopyMessagesToContainer( aMessagesToAdd, aContainer );
+            }
+
+            // This is where we will record the output attempt
+            CACmdLineFileSource.OutputEntry outputEntry = null;
+            //
+            try
+            {
+                // Finish preparing the sink parameters
+                CISinkSerializationParameters sinkParams = iInputs.SinkParameters;
+                sinkParams.Container = aContainer;
+
+                // Perform serialization
+                Trace( "[CA Cmd] TryToCreateOutput() - serializing..." );
+                object output = aXmlSink.Serialize( sinkParams );
+                Trace( "[CA Cmd] TryToCreateOutput() - serialization returned: " + output );
+
+                if ( aFile != null )
+                {
+                    // Create new output
+                    string outputFileName = output is string ? (string) output : string.Empty;
+
+                    // Save output file name
+                    outputEntry = aFile.AddOutput( aContainer, outputFileName, TOutputStatus.ESuccess );
+                }
+
+                // Merge in any diagnostic messages that were left into the output entry.
+                // This ensure we output diagnostics in the final manifest data.
+                outputEntry.AddRange( aMessagesToAdd, CACmdLineMessage.TType.ETypeDiagnostic );
+            }
+            catch ( Exception outputException )
+            {
+                Trace( "[CA Cmd] TryToCreateOutput() - outputException.Message:    " + outputException.Message );
+                Trace( "[CA Cmd] TryToCreateOutput() - outputException.StackTrace: " + outputException.StackTrace );
+
+                if ( aFile != null )
+                {
+                    // Something went wrong with CI serialisation for the specified container.
+                    outputEntry = aFile.AddOutput( aContainer, string.Empty, TOutputStatus.EFailed );
+                    //
+                    outputEntry.AddError( "Could not Create CI", "CI output could not be created" );
+                    outputEntry.AddDiagnostic( "CI Sink Exception Message", outputException.Message );
+                    outputEntry.AddDiagnostic( "CI Sink Exception Stack", outputException.StackTrace );
+                    
+                    // Since we didn't manage to sink the container to CI successfully, we must
+                    // make sure we don't lose any associated messages from the original file. 
+                    // Merge these into the output entry also.
+                    outputEntry.AddRange( aMessagesToAdd );
+                }
+            }
+        }
+
+        private void AssociateInputFilesWithCrashItemSources()
+        {
+            CACmdLineFSEntityList<CACmdLineFileSource> sourceFileNames = iInputs.SourceFiles;
+
+            // Emit progress banner
+            if ( iReportProgress )
+            {
+                Print( "Categorizing files..." );
+            }
+            
+            // Check each source in the engine and try to map it back onto an input source
+            // file name. The goal is to identify input files which have no corresponding crash engine
+            // source. These files are unsupported.
+            CIEngineSourceCollection sources = iCrashItemEngine.Sources;
+            int count = sources.Count;
+            int progress = -1;
+            for( int i=0; i<count; i++ )
+            {
+                CIEngineSource source = sources[ i ];
+                string sourceFileName = source.FileName;
+                
+                // Try to match an input file with a given source object
+                CACmdLineFileSource inputFile = sourceFileNames[ sourceFileName ];
+                if ( inputFile != null )
+                {
+                    inputFile.Source = source;
+                }
+
+                // Report progress as we work through the sources
+                if ( iReportProgress )
+                {
+                    float newProgress = ( ( (float) i+1 ) / (float) count ) * 100.0f;
+                    if ( (int) newProgress != progress || i == count - 1 )
+                    {
+                        progress = (int) newProgress;
+                        Print( string.Format( "{0:d3}%", progress ) );
+                    }
+                }
+            }
+        }
+
+        private void AddMetaDataMessagesToContainer( CIContainer aContainer )
+        {
+            // All meta-data errors, warnings & messages are added as 
+            // children of the container.
+            CACmdLineFSEntityList<CACmdLineFSEntity> metaDataFiles = iInputs.MetaDataFiles;
+            foreach ( CACmdLineFSEntity file in metaDataFiles )
+            {
+                file.CopyMessagesToContainer( aContainer );
+            }
+        }
+
+
+        private bool ContainsSymbols( CIContainer aContainer )
+        {
+            bool retval = false;
+            if (aContainer.FileNames.Length > 1)
+            {
+                retval = true;
+            }
+            return retval;
+
+        }
+
+        private bool IsSymbollessMobileCrash(CIContainer aContainer)
+        {
+            bool retval = false;
+
+
+            foreach (CIMessage message in aContainer.Messages)
+            {
+                if (message.Title == "MobileCrash content type")
+                {
+                    if (message.Description.Trim() == "registration")
+                    {
+                        retval = true;
+                    }
+                    else if (message.Description.Trim() == "alive")
+                    {
+                        retval = true;
+                    }
+                    else if (message.Description.Trim() == "report")
+                    {
+                        retval = true;
+                    }
+                }
+            }
+
+
+            return retval;
+        }
+
+
+        /*private bool ContainsSymbols( CIContainer aContainer )
+        {
+            // Symbols can be registered as the global level, or then per-process.
+            bool ret = ( aContainer.SymbolDictionary.Count > 0 );            
+            if ( ret == false )
+            {
+                // Check at process level
+                CISummarisableEntityList summaries = aContainer.Summaries;
+                foreach ( CISummarisableEntity entity in summaries.ChildrenByType<CISummarisableEntity>() )
+                {
+                    CIProcess process = entity.Process;
+                    if ( process != null )
+                    {
+                        ret = ( process.SymbolDictionary.Count > 0 );
+                        if ( ret == true )
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+            //
+            return ret;
+        }*/
+
+        private void MoveProcessedFile( CACmdLineFileSource aFile )
+        {
+            string skippedTarget = Path.Combine(iInputs.SkippedPath, Path.GetFileName(iBinFileArchivePathName));
+            string errorTarget = Path.Combine(iInputs.ErrorPath, Path.GetFileName(iBinFileArchivePathName));
+
+            switch ( aFile.State )
+            {
+            case CACmdLineFileSource.TState.EStateProcessedAndReadyToBeArchived:
+                MoveFile(aFile, iBinFileArchivePathName);
+                break;
+            case CACmdLineFileSource.TState.EStateSkippedDueToMissingSymbols:
+                MoveFile(aFile, skippedTarget);
+                break;
+            case CACmdLineFileSource.TState.EStateSkippedDueToNotBeingRecognized:                
+                MoveFile(aFile, errorTarget);
+                break;
+            default:               
+                MoveFile(aFile, errorTarget);
+                break;
+            }
+        }
+
+        private void MoveFile(CACmdLineFileSource aFile, string aTargetPath)
+        {
+            string newName = aTargetPath;             
+            
+            //Name availability has already been checked before starting decoding for archive location
+            //If file is going to skipped, its name may need changing
+            if (File.Exists(newName))
+            {
+                int counter = 1;
+                string original_name = newName;
+                while (File.Exists(newName))
+                {
+                    string basepath = Path.GetDirectoryName(original_name);
+                    string basename = Path.GetFileNameWithoutExtension(original_name);
+                    string extension = Path.GetExtension(original_name);
+                   
+                    newName = Path.Combine(basepath, basename + "_" + counter.ToString() + extension);
+                    counter++;
+                }
+                
+            } 
+
+            // Move the file.
+            System.Console.WriteLine("Moving file " + aFile.Name + " to " + newName);
+            if (!iInputs.TestWithoutMovingFiles)
+            {
+                File.Move(aFile.Name, newName);
+                if (!File.Exists(newName))
+                {
+                    System.Console.WriteLine("Error: unable to move file " +aFile.Name +" to " +newName );
+                }
+            
+            }
+
+
+        }
+
+        #endregion
+
+        #region Output methods
+        public void Print( string aMessage )
+        {
+            System.Console.WriteLine( aMessage );
+        }
+        #endregion
+
+        #region From ITracer
+        public void Trace( string aMessage )
+        {
+            System.Console.WriteLine("MANUAL TRACE:" +aMessage);
+            iDebugEngine.Trace( aMessage );
+        }
+
+        public void Trace( string aFormat, params object[] aParams )
+        {
+            string msg = string.Format( aFormat, aParams );
+            Trace( msg );
+        }
+        #endregion
+
+        #region From ICIEngineUI
+        void ICIEngineUI.CITrace( string aMessage )
+        {
+            Trace( aMessage );
+        }
+
+        void ICIEngineUI.CITrace( string aFormat, params object[] aParameters )
+        {
+            Trace( aFormat, aParameters );
+        }
+        #endregion
+
+        #region From DisposableObject
+        protected override void CleanupManagedResources()
+        {
+            try
+            {
+                base.CleanupManagedResources();
+            }
+            finally
+            {
+                iCrashItemEngine.Dispose();
+            }
+        }
+        #endregion
+
+        #region Data members
+        private readonly DbgEngine iDebugEngine;
+        private readonly CIEngine iCrashItemEngine;
+        private readonly CACmdLineInputParameters iInputs;
+        private bool iReportProgress = false;
+        private string iBinFileArchivePathName = string.Empty;
+        #endregion
+    }
+}