crashanalysercmd/UI/Plugins/CAPluginCrashAnalyser/CommandLine/CACmdLineEngine.cs
changeset 0 818e61de6cd1
equal deleted inserted replaced
-1:000000000000 0:818e61de6cd1
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 * 
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 using System;
       
    18 using System.Text;
       
    19 using System.IO;
       
    20 using System.Xml;
       
    21 using System.Collections.Generic;
       
    22 using CrashItemLib.Crash;
       
    23 using CrashItemLib.Crash.Container;
       
    24 using CrashItemLib.Crash.Source;
       
    25 using CrashItemLib.Crash.Messages;
       
    26 using CrashItemLib.Sink;
       
    27 using CrashItemLib.Engine;
       
    28 using CrashItemLib.Engine.Sources;
       
    29 using CrashItemLib.PluginAPI;
       
    30 using CrashAnalyserEngine.Engine;
       
    31 using CrashAnalyserEngine.Plugins;
       
    32 using CrashAnalyserEngine.Interfaces;
       
    33 using SymbianUtils.FileSystem.Utilities;
       
    34 using SymbianXmlInputLib.Parser;
       
    35 using SymbianXmlInputLib.Parser.Nodes;
       
    36 using SymbianXmlInputLib.Elements;
       
    37 using SymbianXmlInputLib.Elements.Types.Category;
       
    38 using SymbianXmlInputLib.Elements.Types.FileSystem;
       
    39 using SymbianXmlInputLib.Elements.Types.Command;
       
    40 using CAPCrashAnalysis.Plugin;
       
    41 using CAPCrashAnalysis.CommandLine.Progress;
       
    42 using SymbianDebugLib;
       
    43 using SymbianDebugLib.Engine;
       
    44 using SymbianDebugLib.Entity;
       
    45 using SymbianUtils;
       
    46 
       
    47 namespace CAPCrashAnalysis.CommandLine
       
    48 {
       
    49 	internal class CACmdLineEngine
       
    50 	{
       
    51 		#region Constructors
       
    52         public CACmdLineEngine( CAPluginCrashAnalysis aEngine )
       
    53 		{
       
    54             iEngine = aEngine;
       
    55             iInputs = new CACmdLineInputParameters( aEngine );
       
    56 		}
       
    57 		#endregion
       
    58 
       
    59         #region API
       
    60         public bool IsCommandLineHandler( string aName )
       
    61         {
       
    62             bool ret = ( aName.ToUpper() == KPluginCommandLineName );
       
    63             return ret;
       
    64         }
       
    65 
       
    66         public int RunCommandLineOperations()
       
    67         {
       
    68             UITrace( "[CA Cmd] START " );
       
    69             UITrace( string.Empty );
       
    70             UITrace( "[CA Cmd] command line: " + System.Environment.CommandLine );
       
    71             UITrace( "[CA Cmd] command wd:   " + System.Environment.CurrentDirectory );
       
    72             UITrace( "[CA Cmd] proc count:   " + System.Environment.ProcessorCount );
       
    73             UITrace( "[CA Cmd] sysdir:       " + System.Environment.SystemDirectory );
       
    74             UITrace( "[CA Cmd] version:      " + System.Environment.Version.ToString() );
       
    75             UITrace( string.Empty );
       
    76 
       
    77             int error = CAPlugin.KErrCommandLineNone;
       
    78             //
       
    79             try
       
    80             {
       
    81                 // We expect to see an "-input" parameter
       
    82                 string inputFileName = ExtractCommandLineInputParameter( CommandLineArguments );
       
    83                 bool generateReport = CheckForReportParameter( CommandLineArguments );
       
    84                 iProgressReporter.Enabled = CheckForProgressParameter( CommandLineArguments );
       
    85                 iProgressReporter.Detailed = CheckForProgressDetailedParameter(CommandLineArguments);
       
    86 
       
    87                 // If no file was found then inputFileName will be an empty string.
       
    88                 if ( string.IsNullOrEmpty( inputFileName ) )
       
    89                 {
       
    90                     throw new CACmdLineException( "Input file parameter missing", CAPlugin.KErrCommandLinePluginArgumentsMissing );
       
    91                 }
       
    92                 else if ( !FSUtilities.Exists( inputFileName ) )
       
    93                 {
       
    94                     throw new CACmdLineException( "Input file not found", CAPlugin.KErrCommandLinePluginArgumentsFileNotFound );
       
    95                 }
       
    96                 else
       
    97                 {
       
    98                     // Switch off UI output at the debug engine level
       
    99                     iEngine.DebugEngine.UiMode = TDbgUiMode.EUiDisabled;
       
   100 
       
   101                     // Reading the input file will throw an exception upon error.
       
   102                     // This is caught below and mapped onto an error code. There's nothing
       
   103                     // else we can do in this situation.
       
   104                     ReadInputFile( inputFileName );
       
   105 
       
   106                     // At this point we have enough information to identify the exact total
       
   107                     // number of progress reporting steps that will follow during the
       
   108                     // rest of the processing operation.
       
   109                     CalculateNumberOfOperationSteps();
       
   110 
       
   111                     // Next, attempt to prime the crash engine with every source we
       
   112                     // identified from the input specification. The goal is to
       
   113                     // create all the needed Crash Item Source objects for each input file.
       
   114                     // Any inputs which we don't support will not have an associated source and
       
   115                     // will be flagged accordingly within the CACmdLineFileSource object.
       
   116                     TryToPrimeSources();
       
   117 
       
   118                     // Now link the input files with crash source objects
       
   119                     AssociateInputFilesWithCrashItemSources();
       
   120 
       
   121                     // Next, prime the debug engine will all the debug meta-data inputs.
       
   122                     // Again, individual error messages will be associated with each meta-data
       
   123                     // input.
       
   124                     //
       
   125                     // We don't need to do this for summary operations
       
   126                     if ( iInputs.SinkParameters.DetailLevel != CISinkSerializationParameters.TDetailLevel.ESummary )
       
   127                     {
       
   128                         TryToPrimeDbgEngine();
       
   129                     }
       
   130 
       
   131                     // Next we print out progress steps for skipped files, because those 
       
   132                     // files are count to total steps.
       
   133                     PrintOutSkippepFiles();
       
   134 
       
   135                     // Next, we invoke the crash engine to process all the crash item sources we
       
   136                     // created during the prime step. Exceptions are caught and associated 
       
   137                     // messages & diagnostics are created at the input-file level.
       
   138                     TryToIdentifyCrashes();
       
   139 
       
   140                     // Next, we start the output phase. Any 'valid' crash containers are serialized
       
   141                     // to xml. Any input files which could not be processed have 'dummy' containers
       
   142                     // created for them, and these are also serialised to 'failed' XML files.
       
   143                     // If the XML Sink plugin is unavailable, then we cannot create any XML output.
       
   144                     // In this situation, we throw an exception which is caught below.
       
   145                     TryToCreateXmlOutput();
       
   146 
       
   147                     // Finally, we want to create the XML manifest/report data, which we'll emit 
       
   148                     // via standard output.
       
   149                     if ( generateReport )
       
   150                     {
       
   151                         CreateAndEmitXmlReport();
       
   152                     }
       
   153                 }
       
   154             }
       
   155             catch ( CACmdLineException cmdLineException )
       
   156             {
       
   157                 error = cmdLineException.ErrorCode;
       
   158                 //
       
   159                 UITrace( "[CA Cmd] " + cmdLineException.Message + " " + cmdLineException.StackTrace );
       
   160             }
       
   161             catch ( Exception generalException )
       
   162             {
       
   163                 error = CAPlugin.KErrCommandLineGeneral;
       
   164                 //
       
   165                 UITrace( "[CA Cmd] " + generalException.Message + " " + generalException.StackTrace );
       
   166             }
       
   167             
       
   168             UITrace( "[CA Cmd] - operation complete: " + error );
       
   169             return error;
       
   170         }
       
   171         #endregion
       
   172 
       
   173 		#region Properties
       
   174         public string[] CommandLineArguments
       
   175         {
       
   176             get { return iEngine.UIEngine.CommandLineArguments; }
       
   177         }
       
   178  
       
   179         public CIEngine CrashItemEngine
       
   180         {
       
   181             get { return iEngine.CrashItemEngine; }
       
   182         }
       
   183         #endregion
       
   184 
       
   185         #region Event handlers
       
   186         private void DbgEngine_EntityPrimingStarted( DbgEngine aEngine, DbgEntity aEntity, object aContext )
       
   187         {
       
   188             UITrace( "[CA Cmd] Priming debug meta-data: " + aEntity.FullName );
       
   189 
       
   190             iProgressReporter.StepBegin("Priming debug meta-data: " + aEntity.FullName, aEntity.FullName, 100);
       
   191         }
       
   192 
       
   193         private void DbgEngine_EntityPrimingProgress( DbgEngine aEngine, DbgEntity aEntity, object aContext )
       
   194         {
       
   195             if ( aContext != null )
       
   196             {
       
   197                 if ( aContext.GetType() == typeof( int ) )
       
   198                 {
       
   199                     int value = (int) aContext;
       
   200                     UITrace( "[CA Cmd] Priming debug meta-data progress: {0:d3}% {1}", value, aEntity.FullName );
       
   201 
       
   202                     // If reporting progress, then output something so the carbide extension is aware
       
   203                     // of what is going on in the background.
       
   204                     iProgressReporter.StepProgress(string.Empty, value, aEntity.FullName);
       
   205                 }
       
   206             }
       
   207         }
       
   208 
       
   209         private void DbgEngine_EntityPrimingComplete( DbgEngine aEngine, DbgEntity aEntity, object aContext )
       
   210         {
       
   211             iProgressReporter.StepEnd(string.Empty, aEntity.FullName);
       
   212             UITrace( "[CA Cmd] Primed debug meta-data: " + aEntity.FullName );
       
   213         }
       
   214 
       
   215         private void CrashItemEngine_SourceObserver( CIEngine.TSourceEvent aEvent, CIEngineSource aSource, object aParameter )
       
   216         {
       
   217             string msg = string.Empty;
       
   218             //
       
   219             switch ( aEvent )
       
   220             {
       
   221             case CIEngine.TSourceEvent.EEventSourceStateChanged:
       
   222                 if ( aSource.State == CIEngineSource.TState.EStateProcessing )
       
   223                 {
       
   224                     iProgressReporter.StepBegin("Processing crash file: " + aSource.FileName, aSource.FileName, 100);
       
   225                 }
       
   226                 break;
       
   227             case CIEngine.TSourceEvent.EEventSourceReady:
       
   228                 iProgressReporter.StepEnd(string.Empty, aSource.FileName);
       
   229                 break;
       
   230             case CIEngine.TSourceEvent.EEventSourceProgress:
       
   231                 if ( aParameter != null && aParameter is int )
       
   232                 {
       
   233                     iProgressReporter.StepProgress(string.Empty, (int)aParameter, aSource.FileName);
       
   234                 }
       
   235                 break;
       
   236             default:
       
   237                 break;
       
   238             }
       
   239         }
       
   240         #endregion
       
   241 
       
   242         #region Internal constants
       
   243         private const string KParamReport = "-REPORT";
       
   244         private const string KParamProgress = "-PROGRESS";
       
   245         private const string KParamProgressDetailed = "-PROGRESS_DETAILS";
       
   246         private const string KPluginCommandLineName = "CRASH_ANALYSIS";
       
   247         private const string KPluginInputParameter = "-INPUT";
       
   248         private const string KPluginInputFileDocumentRootNode = "crash_analysis";
       
   249 
       
   250         // Step keys for progress reporting
       
   251         private const string KStepKeyReadingInputXml = "READING_INPUT_XML";
       
   252         private const string KStepKeyPrimingSources = "PRIMING_SOURCES";
       
   253         private const string KStepKeyWritingOutputXml = "WRITING_OUTPUT_XML";
       
   254         private const string KStepKeyCategorizingInputFiles = "CATEGORIZING_INPUT_FILES";
       
   255         #endregion
       
   256 
       
   257         #region Internal methods
       
   258         private string ExtractCommandLineInputParameter( string[] aArgs )
       
   259         {
       
   260             string ret = string.Empty;
       
   261 
       
   262             // -nogui -plugin CRASH_ANALYSIS -input d:\ca_fullsummary.xml
       
   263             for( int i=0; i<aArgs.Length; i++ )
       
   264             {
       
   265                 string cmd = aArgs[ i ].Trim().ToUpper();
       
   266                 string nextArg = ( i < aArgs.Length - 1 ? aArgs[ i + 1 ].Trim().ToUpper() : string.Empty );
       
   267                 //
       
   268                 if ( cmd == KPluginInputParameter && nextArg != string.Empty )
       
   269                 {
       
   270                     ret = nextArg;
       
   271                 }
       
   272             }
       
   273 
       
   274             return ret;
       
   275         }
       
   276 
       
   277         private bool CheckForReportParameter( string[] aArgs )
       
   278         {
       
   279             bool report = false;
       
   280             //
       
   281             string[] args = aArgs;
       
   282             for ( int i = 0; i < args.Length; i++ )
       
   283             {
       
   284                 string cmd = args[ i ].Trim().ToUpper();
       
   285                 string nextArg = ( i < args.Length - 1 ? args[ i + 1 ].Trim().ToUpper() : string.Empty );
       
   286                 //
       
   287                 try
       
   288                 {
       
   289                     if ( cmd == KParamReport )
       
   290                     {
       
   291                         UITrace( "[CA Cmd] XML report requested." );
       
   292                         report = true;
       
   293                         break;
       
   294                     }
       
   295                 }
       
   296                 catch ( Exception )
       
   297                 {
       
   298                 }
       
   299             }
       
   300             //
       
   301             return report;
       
   302         }
       
   303 
       
   304         private bool CheckForProgressParameter( string[] aArgs )
       
   305         {
       
   306             bool report = false;
       
   307             //
       
   308             string[] args = aArgs;
       
   309             for ( int i = 0; i < args.Length; i++ )
       
   310             {
       
   311                 string cmd = args[ i ].Trim().ToUpper();
       
   312                 string nextArg = ( i < args.Length - 1 ? args[ i + 1 ].Trim().ToUpper() : string.Empty );
       
   313                 //
       
   314                 try
       
   315                 {
       
   316                     if ( cmd == KParamProgress )
       
   317                     {
       
   318                         UITrace( "[CA Cmd] progress requested." );
       
   319                         report = true;
       
   320                         break;
       
   321                     }
       
   322                 }
       
   323                 catch ( Exception )
       
   324                 {
       
   325                 }
       
   326             }
       
   327             //
       
   328             return report;
       
   329         }
       
   330 
       
   331         private bool CheckForProgressDetailedParameter(string[] aArgs)
       
   332         {
       
   333             bool report = false;
       
   334             //
       
   335             string[] args = aArgs;
       
   336             for (int i = 0; i < args.Length; i++)
       
   337             {
       
   338                 string cmd = args[i].Trim().ToUpper();
       
   339                 string nextArg = (i < args.Length - 1 ? args[i + 1].Trim().ToUpper() : string.Empty);
       
   340                 //
       
   341                 try
       
   342                 {
       
   343                     if (cmd == KParamProgressDetailed)
       
   344                     {
       
   345                         UITrace("[CA Cmd] detailed progress requested.");
       
   346                         report = true;
       
   347                         break;
       
   348                     }
       
   349                 }
       
   350                 catch (Exception)
       
   351                 {
       
   352                 }
       
   353             }
       
   354             //
       
   355             return report;
       
   356         }
       
   357 
       
   358         private CISink FindXmlSink()
       
   359         {
       
   360             UITrace( "[CA Cmd] FindXmlSink() - START" );
       
   361             CISink ret = null;
       
   362             //
       
   363             CISinkManager sinkManager = iEngine.CrashItemEngine.SinkManager;
       
   364             foreach ( CISink sink in sinkManager )
       
   365             {
       
   366                 UITrace( "[CA Cmd] FindXmlSink() - found sink: " + sink.Name );
       
   367 
       
   368                 if ( sink.Name.ToUpper().Contains( "CRASH XML" ) )
       
   369                 {
       
   370                     ret = sink;
       
   371                     break;
       
   372                 }
       
   373             }
       
   374             //
       
   375             UITrace( "[CA Cmd] FindXmlSink() - END - ret: " + ret );
       
   376             return ret;
       
   377         }
       
   378 
       
   379         private void ReadInputFile( string aFileName )
       
   380         {
       
   381             UITrace( "[CA Cmd] ReadInputFile() - START - aFileName: " + aFileName );
       
   382 
       
   383             iProgressReporter.StepBegin( "Reading XML input file...", KStepKeyReadingInputXml );
       
   384             iInputs.Read( aFileName );
       
   385 
       
   386             iProgressReporter.PrintProgress( string.Format( "{0} META DATA FILES", iInputs.MetaDataFiles.Count ), 1 );
       
   387             iProgressReporter.PrintProgress( string.Format( "{0} CRASH FILES", iInputs.SourceFiles.Count ), 1 );
       
   388             
       
   389             iProgressReporter.StepEnd( "Read XML input file.", KStepKeyReadingInputXml );
       
   390 
       
   391             UITrace( "[CA Cmd] ReadInputFile() - END - read okay: " + aFileName );
       
   392         }
       
   393 
       
   394         private void CalculateNumberOfOperationSteps()
       
   395         {
       
   396             // We start at one, since reading the input file is the first step
       
   397             int totalStepCount = 1;
       
   398             
       
   399             // 2) Prime sources
       
   400             ++totalStepCount;
       
   401 
       
   402             // 3) Cross reference/associate sources with inputs
       
   403             ++totalStepCount;
       
   404             
       
   405             // 4) Debug meta data priming - one step for each debug meta data entity.
       
   406             if ( iInputs.SinkParameters.DetailLevel != CISinkSerializationParameters.TDetailLevel.ESummary )
       
   407             {
       
   408                 totalStepCount += iInputs.MetaDataFiles.Count;
       
   409             }
       
   410 
       
   411             // 5) Reading source files - one step for each file
       
   412             totalStepCount += iInputs.SourceFiles.Count;
       
   413 
       
   414             // 6) Outputting XML - treated as one entire step, with sub-step progress reporting.
       
   415             // We can't do any better than this because the number of XML files depends on the number
       
   416             // of crash containers that are created when the files are read (step 5).
       
   417             ++totalStepCount;
       
   418 
       
   419             // All future operations will include an accurate step number prefix
       
   420             iProgressReporter.TotalNumberOfSteps = totalStepCount;
       
   421         }
       
   422 
       
   423         public void UITrace( string aMessage )
       
   424         {
       
   425             iEngine.UIManager.UITrace( aMessage );
       
   426         }
       
   427 
       
   428         public void UITrace( string aFormat, params object[] aParams )
       
   429         {
       
   430             string msg = string.Format( aFormat, aParams );
       
   431             UITrace( msg );
       
   432         }
       
   433 
       
   434         private void TryToPrimeSources()
       
   435         {
       
   436             UITrace( "[CA Cmd] TryToPrimeSources() - START" );
       
   437             CrashItemEngine.ClearAll();
       
   438             
       
   439             // Prime engine with source files
       
   440             CACmdLineFSEntityList<CACmdLineFileSource> sourceFileNames = iInputs.SourceFiles;
       
   441             int count = sourceFileNames.Count;
       
   442 
       
   443             // Emit progress banner
       
   444             iProgressReporter.StepBegin( "Locating crash files...", KStepKeyPrimingSources, count );
       
   445             skippedFiles.Clear();
       
   446             for ( int i = 0; i < count; i++ )
       
   447             {
       
   448                 CACmdLineFileSource file = sourceFileNames[ i ];
       
   449                 //
       
   450                 try
       
   451                 {
       
   452                     // We prime each file individually. If an exception is thrown then we
       
   453                     // record an appropriate error in the associated file object.
       
   454                     UITrace( "[CA Cmd] TryToPrimeSources() - priming: " + file );
       
   455 
       
   456                     bool primeSuccess = CrashItemEngine.Prime( file );
       
   457                     if ( primeSuccess == false )
       
   458                     {
       
   459                         skippedFiles.Add(file.Name);
       
   460                     }
       
   461 
       
   462  
       
   463                     UITrace( "[CA Cmd] TryToPrimeSources() - primed result: " + primeSuccess );
       
   464                 }
       
   465                 catch ( Exception sourcePrimerException )
       
   466                 {
       
   467                     file.AddError( "Error Identifying Source Type", "There was an error when attempting to identify the source file type. The file could not be processed." );
       
   468                     file.AddDiagnostic( "Crash Primer Exception Message", sourcePrimerException.Message );
       
   469                     file.AddDiagnostic( "Crash Primer Exception Stack", sourcePrimerException.StackTrace );
       
   470                 }
       
   471 
       
   472                 // Report progress as we work through the sources
       
   473                 iProgressReporter.StepProgress(string.Empty, i, KStepKeyPrimingSources);
       
   474             }
       
   475 
       
   476             iProgressReporter.StepEnd( string.Empty, KStepKeyPrimingSources );
       
   477             
       
   478             UITrace( "[CA Cmd] TryToPrimeSources() - END" );
       
   479         }
       
   480 
       
   481         private void TryToPrimeDbgEngine()
       
   482         {
       
   483             DbgEngine debugEngine = iEngine.DebugEngine;
       
   484             //
       
   485             Exception primerException = null;
       
   486             CACmdLineFSEntityList<CACmdLineFSEntity> metaDataFiles = iInputs.MetaDataFiles;
       
   487             //
       
   488             try
       
   489             {
       
   490                 debugEngine.Clear();
       
   491 
       
   492                 foreach ( CACmdLineFSEntity entry in metaDataFiles )
       
   493                 {
       
   494                     UITrace( "[CA Cmd] Seeding debug meta engine with entry: " + entry.Name );
       
   495                     DbgEntity entity = debugEngine.Add( entry.Name );
       
   496                     if ( entity != null )
       
   497                     {
       
   498                         UITrace( "[CA Cmd] Entry type detected as: [" + entity.CategoryName + "]" );
       
   499                         entity.Tag = entry;
       
   500                     }
       
   501                     else
       
   502                     {
       
   503                         UITrace( "[CA Cmd] Entry not handled: " + entry.Name );
       
   504                         entry.AddError( "Meta-Data File Not Supported", "The file \'" + entry.Name + "\' is of unknown origin." );
       
   505                     }
       
   506                 }
       
   507 
       
   508                 // Listen to prime events
       
   509                 try
       
   510                 {
       
   511                     UITrace( "[CA Cmd] Starting prime operation... " );
       
   512                     debugEngine.EntityPrimingStarted += new DbgEngine.EventHandler( DbgEngine_EntityPrimingStarted );
       
   513                     debugEngine.EntityPrimingProgress += new DbgEngine.EventHandler( DbgEngine_EntityPrimingProgress );
       
   514                     debugEngine.EntityPrimingComplete += new DbgEngine.EventHandler( DbgEngine_EntityPrimingComplete );
       
   515                     debugEngine.Prime( TSynchronicity.ESynchronous );
       
   516                     UITrace( "[CA Cmd] Debug meta data priming completed successfully." );
       
   517                 }
       
   518                 finally
       
   519                 {
       
   520                     debugEngine.EntityPrimingStarted -= new DbgEngine.EventHandler( DbgEngine_EntityPrimingStarted );
       
   521                     debugEngine.EntityPrimingProgress -= new DbgEngine.EventHandler( DbgEngine_EntityPrimingProgress );
       
   522                     debugEngine.EntityPrimingComplete -= new DbgEngine.EventHandler( DbgEngine_EntityPrimingComplete );
       
   523                 }
       
   524             }
       
   525             catch ( Exception exception )
       
   526             {
       
   527                 UITrace( "[CA Cmd] Debug meta data priming exception: " + exception.Message + ", " + exception.StackTrace );
       
   528                 primerException = exception;
       
   529             }
       
   530 
       
   531             // Go through each debug entity and check it for errors. Add diagnostics
       
   532             // and error messages where appropriate.
       
   533             foreach ( DbgEntity entity in debugEngine )
       
   534             {
       
   535                 string name = entity.FullName;
       
   536                 //
       
   537                 CACmdLineFSEntity file = metaDataFiles[ name ];
       
   538                 file.Clear();
       
   539                 //
       
   540                 if ( entity.PrimerResult.PrimedOkay )
       
   541                 {
       
   542                     if ( !entity.Exists )
       
   543                     {
       
   544                         file.AddError( "Meta-Data File Missing", string.Format( "The file \'{0}\' could not be found.", file.Name ) );
       
   545                     }
       
   546                     else if ( entity.IsUnsupported )
       
   547                     {
       
   548                         file.AddError( "Meta-Data File Not Supported", string.Format( "The file \'{0}\' is of unknown origin.", file.Name ) );
       
   549                     }
       
   550                 }
       
   551                 else
       
   552                 {
       
   553                     // Add error
       
   554                     file.AddError( "Meta-Data Read Error", entity.PrimerResult.PrimeErrorMessage );
       
   555 
       
   556                     // And diagnostic information
       
   557                     Exception exception = entity.PrimerResult.PrimeException != null ? entity.PrimerResult.PrimeException : primerException;
       
   558                     if ( exception != null )
       
   559                     {
       
   560                         file.AddDiagnostic( "Meta-Data Exception Message", entity.PrimerResult.PrimeException.Message );
       
   561                         file.AddDiagnostic( "Meta-Data Exception Stack", entity.PrimerResult.PrimeException.StackTrace );
       
   562                     }
       
   563                     else
       
   564                     {
       
   565                         file.AddDiagnostic( "Meta-Data Unknown Failure", "No exception occurred at the primer or entity level?" );
       
   566                     }
       
   567                 }
       
   568             }
       
   569         }
       
   570 
       
   571         private void TryToIdentifyCrashes()
       
   572         {
       
   573             Exception crashEngineException = null;
       
   574             //
       
   575             try
       
   576             {
       
   577                 iEngine.CrashItemEngine.SourceObservers += new CIEngine.CIEngineSourceObserver( CrashItemEngine_SourceObserver );
       
   578                 iEngine.IdentifyCrashes( TSynchronicity.ESynchronous );
       
   579             }
       
   580             catch ( Exception exception )
       
   581             {
       
   582                 crashEngineException = exception;
       
   583             }
       
   584             finally
       
   585             {
       
   586                 iEngine.CrashItemEngine.SourceObservers -= new CIEngine.CIEngineSourceObserver( CrashItemEngine_SourceObserver );
       
   587             }
       
   588 
       
   589             // Check each source in the engine and create messages based upon it's
       
   590             // state at the end of processing.
       
   591             foreach ( CACmdLineFileSource file in iInputs.SourceFiles )
       
   592             {
       
   593                 if ( file.Source != null )
       
   594                 {
       
   595                     CIEngineSource source = file.Source;
       
   596                     switch ( source.State )
       
   597                     {
       
   598                     case CIEngineSource.TState.EStateReady:
       
   599                         // Success case - the source resulted in the creation of at least one container
       
   600                         file.AddDiagnostic( "Source Read Successfully", string.Format( "{0} crash container(s) created", source.ContainerCount ) );
       
   601                         break;
       
   602                     case CIEngineSource.TState.EStateReadyNoItems:
       
   603                         file.AddWarning( "Source File Contains No Crashes", "The input data was read successfully but contains no crash information." );
       
   604                         break;
       
   605                     case CIEngineSource.TState.EStateReadyCorrupt:
       
   606                         file.AddError( "Source File is Corrupt", "The input data is invalid or corrupt." );
       
   607                         break;
       
   608                     case CIEngineSource.TState.EStateUninitialised:
       
   609                         file.AddError( "Source File not Read", "The input data was never read." );
       
   610                         file.AddDiagnostic( "Source State Invalid", "Source is still in unitialised state, even though reading is complete?" );
       
   611                         break;
       
   612                     case CIEngineSource.TState.EStateProcessing:
       
   613                         file.AddDiagnostic( "Source State Invalid", "Source is still in processing state, even though reading is complete?" );
       
   614                         break;
       
   615                     default:
       
   616                         break;
       
   617                     }
       
   618                 }
       
   619                 else
       
   620                 {
       
   621                     file.AddError( "File is Not Supported", "There file type is not recognized and was not processed." );
       
   622                 }
       
   623 
       
   624                 // Add in details of any exception
       
   625                 if ( crashEngineException != null )
       
   626                 {
       
   627                     file.AddDiagnostic( "Crash Identification Exception Message", crashEngineException.Message );
       
   628                     file.AddDiagnostic( "Crash Identification Exception Stack", crashEngineException.StackTrace );
       
   629                 }
       
   630             }
       
   631         }
       
   632 
       
   633         private void TryToCreateXmlOutput()
       
   634         {
       
   635             CACmdLineFSEntityList<CACmdLineFileSource> inputFiles = iInputs.SourceFiles;
       
   636             //
       
   637             CISink xmlSink = FindXmlSink();
       
   638             if ( xmlSink == null )
       
   639             {
       
   640                 throw new CACmdLineException( "XML Output Plugin Not Available", CAPlugin.KErrCommandLinePluginSinkNotAvailable );
       
   641             }
       
   642   
       
   643             CACmdLineFSEntityList<CACmdLineFileSource> sourceFileNames = iInputs.SourceFiles;
       
   644             int count = sourceFileNames.Count;
       
   645 
       
   646             // Emit progress banner
       
   647             iProgressReporter.StepBegin( "Creating crash XML content...", KStepKeyWritingOutputXml, count );
       
   648             
       
   649             for ( int i = 0; i < count; i++ )
       
   650             {
       
   651                  CACmdLineFileSource file = sourceFileNames[ i ];
       
   652 
       
   653                 // If the file has a corresponding source then we know that crash item recognised it.
       
   654                 if ( file.Source == null )
       
   655                 {
       
   656                     // File is not supported by crash item engine. Create dummy container which we'll
       
   657                     // serialize below.
       
   658                     CACmdLineSource cmdLineSource = new CACmdLineSource( file.File );
       
   659                     CIContainer failedContainer = CIContainer.NewErrorContainer( CrashItemEngine, cmdLineSource );
       
   660                     file.Add( failedContainer );
       
   661                 }
       
   662 
       
   663                 // We copy and remove all the file-level messages. These will be added to the container
       
   664                 // (where appropriate) or then to an output entry otherwise.
       
   665                 CACmdLineMessage[] fileMessages = file.ToArray();
       
   666                 file.Clear();
       
   667 
       
   668                 // At this point, the input file is guaranteed to have associated containers. Either
       
   669                 // valid ones (created by crash item engine) or a single 'FAILED' one which we just 
       
   670                 // added above.
       
   671                 foreach ( CIContainer container in file.Containers )
       
   672                 {
       
   673                     // Firstly, add any meta-data errors/messages/warnings to this container
       
   674                     // as crash item message entries
       
   675                     AddMetaDataMessagesToContainer( container );
       
   676 
       
   677                     // Now we can try to serialize the container to XML. This method will
       
   678                     // not throw an exception.
       
   679                     //
       
   680                     // If the operation succeeds, then the input file will have an associated
       
   681                     // container object (and associated xml output file name) and we need not
       
   682                     // do anymore.
       
   683                     //
       
   684                     // If it fails, then the input file will not be assigned the container
       
   685                     // object and therefore, later on, we'll invoke the XML Sink directly to 
       
   686                     // create a stub 'FAILED' XML output file.
       
   687                     TryToCreateXmlOutput( xmlSink, container, file, fileMessages );
       
   688                 }
       
   689 
       
   690                 // Report progress as we work through the sources
       
   691                 iProgressReporter.StepProgress(string.Empty, i, KStepKeyWritingOutputXml);
       
   692             }
       
   693 
       
   694             iProgressReporter.StepEnd( string.Empty, KStepKeyWritingOutputXml );
       
   695         }
       
   696 
       
   697         private void TryToCreateXmlOutput( CISink aXmlSink, CIContainer aContainer, CACmdLineFileSource aFile, CACmdLineMessage[] aMessagesToAdd )
       
   698         {
       
   699             UITrace( "[CA Cmd] TryToCreateXmlOutput() - START - container source: {0}", aContainer.Source.MasterFileName );
       
   700 
       
   701             // By the time we are outputting a container, there should no longer be any messages
       
   702             // associated with the file.
       
   703             System.Diagnostics.Debug.Assert( aFile.Count == 0 );
       
   704 
       
   705             // Check whether the file contained any errors or 
       
   706             // messages of it own.
       
   707             if ( aMessagesToAdd.Length > 0 )
       
   708             {
       
   709                 // Copy warnings, messages and errors into crash item container.
       
   710                 // Diagnostic messages are not copied.
       
   711                 CACmdLineFSEntity.CopyMessagesToContainer( aMessagesToAdd, aContainer );
       
   712             }
       
   713 
       
   714             // This is where we will record the output attempt
       
   715             CACmdLineFileSource.OutputEntry outputEntry = null;
       
   716             //
       
   717             try
       
   718             {
       
   719                 // Finish preparing the sink parameters
       
   720                 CISinkSerializationParameters sinkParams = iInputs.SinkParameters;
       
   721                 sinkParams.Container = aContainer;
       
   722 
       
   723                 // Perform serialization
       
   724                 UITrace( "[CA Cmd] TryToCreateXmlOutput() - serializing..." );
       
   725                 object output = aXmlSink.Serialize( sinkParams );
       
   726                 UITrace( "[CA Cmd] TryToCreateXmlOutput() - serialization returned: " + output );
       
   727 
       
   728                 if ( aFile != null )
       
   729                 {
       
   730                     // Create new output
       
   731                     string outputFileName = output is string ? (string) output : string.Empty;
       
   732 
       
   733                     // Save output file name
       
   734                     outputEntry = aFile.AddOutput( aContainer, outputFileName, TOutputStatus.ESuccess );
       
   735                 }
       
   736 
       
   737                 // Merge in any diagnostic messages that were left into the output entry.
       
   738                 // This ensure we output diagnostics in the final manifest data.
       
   739                 outputEntry.AddRange( aMessagesToAdd, CACmdLineMessage.TType.ETypeDiagnostic );
       
   740             }
       
   741             catch ( Exception outputException )
       
   742             {
       
   743                 UITrace( "[CA Cmd] TryToCreateXmlOutput() - outputException.Message:    " + outputException.Message );
       
   744                 UITrace( "[CA Cmd] TryToCreateXmlOutput() - outputException.StackTrace: " + outputException.StackTrace );
       
   745 
       
   746                 if ( aFile != null )
       
   747                 {
       
   748                     // Something went wrong with XML serialisation for the specified container.
       
   749                     outputEntry = aFile.AddOutput( aContainer, string.Empty, TOutputStatus.EFailed );
       
   750                     //
       
   751                     outputEntry.AddError( "Could not Create XML", "XML output could not be created" );
       
   752                     outputEntry.AddDiagnostic( "XML Sink Exception Message", outputException.Message );
       
   753                     outputEntry.AddDiagnostic( "XML Sink Exception Stack", outputException.StackTrace );
       
   754                     
       
   755                     // Since we didn't manage to sink the container to XML successfully, we must
       
   756                     // make sure we don't lose any associated messages from the original file. 
       
   757                     // Merge these into the output entry also.
       
   758                     outputEntry.AddRange( aMessagesToAdd );
       
   759                 }
       
   760             }
       
   761         }
       
   762 
       
   763         private void CreateAndEmitXmlReport()
       
   764         {
       
   765             CACmdLineManifestWriter writer = new CACmdLineManifestWriter( iInputs.SourceFiles );
       
   766             string xml = writer.Create();
       
   767             //
       
   768             using( StringReader reader = new StringReader( xml ) )
       
   769             {
       
   770                 string line = reader.ReadLine();
       
   771                 while ( line != null )
       
   772                 {
       
   773                     System.Console.WriteLine( line );
       
   774                     line = reader.ReadLine();
       
   775                 }
       
   776             }
       
   777         }
       
   778 
       
   779         private void AssociateInputFilesWithCrashItemSources()
       
   780         {
       
   781             CACmdLineFSEntityList<CACmdLineFileSource> sourceFileNames = iInputs.SourceFiles;
       
   782             CIEngineSourceCollection sources = iEngine.CrashItemEngine.Sources;
       
   783             int count = sources.Count;
       
   784 
       
   785             // Emit progress banner
       
   786             iProgressReporter.StepBegin( "Categorizing files...", KStepKeyCategorizingInputFiles, count );
       
   787             
       
   788             // Check each source in the engine and try to map it back onto an input source
       
   789             // file name. The goal is to identify input files which have no corresponding crash engine
       
   790             // source. These files are unsupported.
       
   791             for( int i=0; i<count; i++ )
       
   792             {
       
   793                 CIEngineSource source = sources[ i ];
       
   794                 string sourceFileName = source.FileName;
       
   795                 
       
   796                 // Try to match an input file with a given source object
       
   797                 CACmdLineFileSource inputFile = sourceFileNames[ sourceFileName ];
       
   798                 if ( inputFile != null )
       
   799                 {
       
   800                     inputFile.Source = source;
       
   801                 }
       
   802 
       
   803                 // Report progress as we work through the sources
       
   804                 iProgressReporter.StepProgress( string.Empty, i, KStepKeyCategorizingInputFiles );
       
   805             }
       
   806 
       
   807             iProgressReporter.StepEnd( string.Empty, KStepKeyCategorizingInputFiles );
       
   808         }
       
   809 
       
   810         private void PrintOutSkippepFiles()
       
   811         {
       
   812             if ( skippedFiles.Count > 0 )
       
   813             {
       
   814                 foreach ( string skippedFile in skippedFiles )
       
   815                 {
       
   816                     iProgressReporter.StepBegin( "Skipped non-crash file: " + skippedFile, skippedFile, 100 );
       
   817                     iProgressReporter.StepEnd( "", skippedFile );
       
   818                 }
       
   819             }
       
   820         }
       
   821 
       
   822         private void AddMetaDataMessagesToContainer( CIContainer aContainer )
       
   823         {
       
   824             // All meta-data errors, warnings & messages are added as 
       
   825             // children of the container.
       
   826             CACmdLineFSEntityList<CACmdLineFSEntity> metaDataFiles = iInputs.MetaDataFiles;
       
   827             foreach ( CACmdLineFSEntity file in metaDataFiles )
       
   828             {
       
   829                 file.CopyMessagesToContainer( aContainer );
       
   830             }
       
   831         }
       
   832         #endregion
       
   833 
       
   834         #region Data members
       
   835         private readonly CAPluginCrashAnalysis iEngine;
       
   836         private readonly CACmdLineInputParameters iInputs;
       
   837         private CACmdLineProgressReporter iProgressReporter = new CACmdLineProgressReporter();
       
   838         List<string> skippedFiles = new List<string>();
       
   839         #endregion
       
   840 	}
       
   841 }