|
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 } |