|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions are met: |
|
7 * |
|
8 * - Redistributions of source code must retain the above copyright notice, |
|
9 * this list of conditions and the following disclaimer. |
|
10 * - Redistributions in binary form must reproduce the above copyright notice, |
|
11 * this list of conditions and the following disclaimer in the documentation |
|
12 * and/or other materials provided with the distribution. |
|
13 * - Neither the name of Nokia Corporation nor the names of its contributors |
|
14 * may be used to endorse or promote products derived from this software |
|
15 * without specific prior written permission. |
|
16 * |
|
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 * POSSIBILITY OF SUCH DAMAGE. |
|
28 * |
|
29 * Initial Contributors: |
|
30 * Nokia Corporation - initial contribution. |
|
31 * |
|
32 * Contributors: |
|
33 * |
|
34 * Description: |
|
35 * |
|
36 */ |
|
37 |
|
38 using System; |
|
39 using System.IO; |
|
40 using System.Text; |
|
41 using System.Collections; |
|
42 using System.Collections.Generic; |
|
43 using SymbianUtils; |
|
44 |
|
45 namespace HeapComparisonLib.CSV |
|
46 { |
|
47 public class CSVComparisonEngine |
|
48 { |
|
49 #region Events |
|
50 public enum TEvent |
|
51 { |
|
52 // Reported as events |
|
53 EEventOperationStarted = 0, |
|
54 EEventOperationComplete, |
|
55 |
|
56 // Reported as percentage progress |
|
57 EEventSplittingProgress, |
|
58 EEventComparingProgress, |
|
59 |
|
60 // Reported as indexed progress |
|
61 EEventSplittingMovedToNewFile, |
|
62 EEventComparingMovedToNewDataSet, |
|
63 } |
|
64 |
|
65 public delegate void PercentageProgressHandler( TEvent aEvent, CSVComparisonEngine aSender, int aProgressPercent ); |
|
66 public event PercentageProgressHandler ePercentageProgressHandler; |
|
67 |
|
68 public delegate void IndexedProgressHandler( TEvent aEvent, CSVComparisonEngine aSender, int aCurrentIndex, int aMaxIndex ); |
|
69 public event IndexedProgressHandler eIndexedProgressHandler; |
|
70 |
|
71 public delegate void EventHandler( TEvent aEvent, CSVComparisonEngine aSender ); |
|
72 public event EventHandler eEventHandler; |
|
73 |
|
74 public delegate void ExceptionHandler( string aTitle, string aBody ); |
|
75 public event ExceptionHandler eExceptionHandler; |
|
76 #endregion |
|
77 |
|
78 #region Constructors & destructor |
|
79 public CSVComparisonEngine( List<string> aSource, string aDestinationPath ) |
|
80 { |
|
81 iSource.AddRange( aSource ); |
|
82 iDestinationPath = aDestinationPath; |
|
83 } |
|
84 #endregion |
|
85 |
|
86 #region API |
|
87 public void CompareAsync() |
|
88 { |
|
89 iIndex = 0; |
|
90 iState = TState.EStateIdle; |
|
91 |
|
92 // Start things off... |
|
93 OnStateSplitting(); |
|
94 } |
|
95 #endregion |
|
96 |
|
97 #region Properties |
|
98 public int Count |
|
99 { |
|
100 get { return iDataSets.Count; } |
|
101 } |
|
102 #endregion |
|
103 |
|
104 #region Internal state related |
|
105 private enum TState |
|
106 { |
|
107 EStateIdle = 0, |
|
108 EStateSplittingLogs, |
|
109 EStateComparingAndExportingIndividualDataSets, |
|
110 EStateComparingAllDataSets, |
|
111 EStateFinished |
|
112 } |
|
113 |
|
114 private void OnStateSplitting() |
|
115 { |
|
116 // If we have more files to split, then kick off |
|
117 // a new splitter. |
|
118 if ( iIndex < iSource.Count ) |
|
119 { |
|
120 string fileName = iSource[ iIndex ]; |
|
121 if ( eIndexedProgressHandler != null ) |
|
122 { |
|
123 eIndexedProgressHandler( TEvent.EEventSplittingMovedToNewFile, this, iIndex + 1, iSource.Count ); |
|
124 } |
|
125 |
|
126 // Move to next file |
|
127 iIndex++; |
|
128 |
|
129 // Set up next worker |
|
130 iWorkerSplitter = new CSVWorkerLogSplitter( fileName, iDataSets ); |
|
131 iWorkerSplitter.iObserver += new AsyncReaderBase.Observer( WorkerSplitter_Observer ); |
|
132 iWorkerSplitter.SplitAsync(); |
|
133 // |
|
134 ChangeState( TState.EStateSplittingLogs ); |
|
135 } |
|
136 else |
|
137 { |
|
138 // Done with splitting phase, move on to comparing. |
|
139 // We must have at least two sets, or else there's nothing |
|
140 // to compare. |
|
141 if ( iDataSets.Count <= 1 ) |
|
142 { |
|
143 if ( eExceptionHandler != null ) |
|
144 { |
|
145 eExceptionHandler( "Nothing to compare", "There are insufficient data sets to compare." ); |
|
146 } |
|
147 |
|
148 ChangeState( TState.EStateFinished ); |
|
149 } |
|
150 else |
|
151 { |
|
152 // Next, find the shared chunks |
|
153 IdentifySharedHeaps(); |
|
154 |
|
155 // And now start to export |
|
156 iIndex = 0; |
|
157 if ( iWorkerSplitter != null ) |
|
158 { |
|
159 iWorkerSplitter.Dispose(); |
|
160 iWorkerSplitter = null; |
|
161 } |
|
162 |
|
163 ChangeState( TState.EStateComparingAndExportingIndividualDataSets ); |
|
164 OnStateComparingAndExportingIndividualDataSets(); |
|
165 } |
|
166 } |
|
167 } |
|
168 |
|
169 private void OnStateComparingAndExportingIndividualDataSets() |
|
170 { |
|
171 // If there are only two sets to compare, then we generate a single output file and that's it. |
|
172 // If we have 3 (or more) data sets, then we will generate three (or more) comparison files, e.g.: |
|
173 // |
|
174 // [File 1] 0 vs 1 |
|
175 // [File 2] 1 vs 2 |
|
176 // |
|
177 // and also |
|
178 // |
|
179 // [File 3] 0 vs 2 |
|
180 // |
|
181 // Etc. |
|
182 int numberOfComparisons = ( iDataSets.Count > 2 ? iDataSets.Count : 1 ); |
|
183 |
|
184 if ( iIndex + 1 < iDataSets.Count ) |
|
185 { |
|
186 if ( eIndexedProgressHandler != null ) |
|
187 { |
|
188 eIndexedProgressHandler( TEvent.EEventComparingMovedToNewDataSet, this, iIndex + 1, numberOfComparisons ); |
|
189 } |
|
190 |
|
191 // Get sets to compare |
|
192 CSVDataSet set1 = iDataSets[ iIndex + 0 ]; |
|
193 CSVDataSet set2 = iDataSets[ iIndex + 1 ]; |
|
194 |
|
195 // Build excel file name |
|
196 string excelFileName = BuildExcelFileNameForTwoSetComparison(); |
|
197 |
|
198 // Since we've now built the filename it's okay to increment the index |
|
199 ++iIndex; |
|
200 |
|
201 // Create and initiate comparison |
|
202 iWorkerComparatorTwo = new CSVWorkerTwoDataSetComparator( set1, set2, excelFileName ); |
|
203 iWorkerComparatorTwo.eObserver += new CSVWorkerTwoDataSetComparator.Observer( WorkerComparatorTwo_Observer ); |
|
204 iWorkerComparatorTwo.CompareAndSaveAsync(); |
|
205 |
|
206 ChangeState( TState.EStateComparingAndExportingIndividualDataSets ); |
|
207 } |
|
208 else |
|
209 { |
|
210 // Special comparison of first vs last set when comparing |
|
211 // more than two data sets |
|
212 if ( iDataSets.Count > 2 ) |
|
213 { |
|
214 if ( eIndexedProgressHandler != null ) |
|
215 { |
|
216 eIndexedProgressHandler( TEvent.EEventComparingMovedToNewDataSet, this, numberOfComparisons, numberOfComparisons ); |
|
217 } |
|
218 |
|
219 // Special value to indicate first vs last comparison |
|
220 iIndex = KSpecialFirstVsLastComparisonIndex; |
|
221 |
|
222 // Get sets to compare |
|
223 CSVDataSet set1 = iDataSets[ 0 ]; |
|
224 CSVDataSet set2 = iDataSets[ iDataSets.Count - 1 ]; |
|
225 |
|
226 // Build excel file name |
|
227 string excelFileName = BuildExcelFileNameForTwoSetComparison(); |
|
228 |
|
229 // Create and initiate comparison |
|
230 if ( iWorkerComparatorTwo != null ) |
|
231 { |
|
232 iWorkerComparatorTwo.Dispose(); |
|
233 iWorkerComparatorTwo = null; |
|
234 } |
|
235 |
|
236 iWorkerComparatorTwo = new CSVWorkerTwoDataSetComparator( set1, set2, excelFileName ); |
|
237 iWorkerComparatorTwo.eObserver += new CSVWorkerTwoDataSetComparator.Observer( WorkerComparatorTwo_Observer ); |
|
238 iWorkerComparatorTwo.CompareAndSaveAsync(); |
|
239 |
|
240 ChangeState( TState.EStateComparingAndExportingIndividualDataSets ); |
|
241 } |
|
242 else |
|
243 { |
|
244 if ( iWorkerComparatorTwo != null ) |
|
245 { |
|
246 iWorkerComparatorTwo.Dispose(); |
|
247 iWorkerComparatorTwo = null; |
|
248 } |
|
249 // |
|
250 ChangeState( TState.EStateComparingAllDataSets ); |
|
251 OnStateComparingAndExportingAllDataSets(); |
|
252 } |
|
253 } |
|
254 } |
|
255 |
|
256 private void OnStateComparingAndExportingAllDataSets() |
|
257 { |
|
258 // Create and initiate comparison |
|
259 string excelFileName = BuildExcelFileNameForSummarySpreadsheet(); |
|
260 iWorkerComparatorAll = new CSVWorkerAllDataSetComparator( iDataSets, excelFileName ); |
|
261 iWorkerComparatorAll.eObserver += new CSVWorkerAllDataSetComparator.Observer( WorkerComparatorAll_Observer ); |
|
262 iWorkerComparatorAll.CompareAndSaveAsync(); |
|
263 |
|
264 ChangeState( TState.EStateComparingAllDataSets ); |
|
265 } |
|
266 #endregion |
|
267 |
|
268 #region Event handlers |
|
269 void WorkerSplitter_Observer( AsyncReaderBase.TEvent aEvent, AsyncReaderBase aSender ) |
|
270 { |
|
271 switch ( aEvent ) |
|
272 { |
|
273 case AsyncReaderBase.TEvent.EReadingStarted: |
|
274 break; |
|
275 case AsyncReaderBase.TEvent.EReadingProgress: |
|
276 if ( ePercentageProgressHandler != null ) |
|
277 { |
|
278 ePercentageProgressHandler( TEvent.EEventSplittingProgress, this, aSender.Progress ); |
|
279 } |
|
280 break; |
|
281 case AsyncReaderBase.TEvent.EReadingComplete: |
|
282 OnStateSplitting(); |
|
283 break; |
|
284 } |
|
285 } |
|
286 |
|
287 void WorkerComparatorTwo_Observer( CSVWorkerTwoDataSetComparator.TEvent aEvent, string aThreadName ) |
|
288 { |
|
289 switch ( aEvent ) |
|
290 { |
|
291 case CSVWorkerTwoDataSetComparator.TEvent.EExportingStarted: |
|
292 break; |
|
293 case CSVWorkerTwoDataSetComparator.TEvent.EExportingProgress: |
|
294 if ( ePercentageProgressHandler != null ) |
|
295 { |
|
296 ePercentageProgressHandler( TEvent.EEventComparingProgress, this, iWorkerComparatorTwo.Progress ); |
|
297 } |
|
298 break; |
|
299 case CSVWorkerTwoDataSetComparator.TEvent.EExportingComplete: |
|
300 if ( iIndex >= iDataSets.Count ) |
|
301 { |
|
302 OnStateComparingAndExportingAllDataSets(); |
|
303 } |
|
304 else |
|
305 { |
|
306 OnStateComparingAndExportingIndividualDataSets(); |
|
307 } |
|
308 break; |
|
309 } |
|
310 } |
|
311 |
|
312 void WorkerComparatorAll_Observer( CSVWorkerAllDataSetComparator.TEvent aEvent ) |
|
313 { |
|
314 switch ( aEvent ) |
|
315 { |
|
316 case CSVWorkerAllDataSetComparator.TEvent.EExportingStarted: |
|
317 break; |
|
318 case CSVWorkerAllDataSetComparator.TEvent.EExportingProgress: |
|
319 if ( ePercentageProgressHandler != null ) |
|
320 { |
|
321 ePercentageProgressHandler( TEvent.EEventComparingProgress, this, iWorkerComparatorAll.Progress ); |
|
322 } |
|
323 break; |
|
324 case CSVWorkerAllDataSetComparator.TEvent.EExportingComplete: |
|
325 ChangeState( TState.EStateFinished ); |
|
326 break; |
|
327 } |
|
328 } |
|
329 #endregion |
|
330 |
|
331 #region Internal methods |
|
332 private void ChangeState( TState aNewState ) |
|
333 { |
|
334 if ( eEventHandler != null ) |
|
335 { |
|
336 if ( iState == TState.EStateIdle ) |
|
337 { |
|
338 eEventHandler( TEvent.EEventOperationStarted, this ); |
|
339 } |
|
340 else if ( aNewState == TState.EStateFinished ) |
|
341 { |
|
342 eEventHandler( TEvent.EEventOperationComplete, this ); |
|
343 } |
|
344 } |
|
345 |
|
346 iState = aNewState; |
|
347 } |
|
348 |
|
349 private string BuildExcelFileNameForTwoSetComparison() |
|
350 { |
|
351 StringBuilder temp = new StringBuilder( iDestinationPath ); |
|
352 if ( iDestinationPath[ iDestinationPath.Length - 1 ] != Path.DirectorySeparatorChar ) |
|
353 { |
|
354 temp.Append( Path.DirectorySeparatorChar ); |
|
355 } |
|
356 |
|
357 // Special case for first vs last |
|
358 if ( iIndex == KSpecialFirstVsLastComparisonIndex ) |
|
359 { |
|
360 temp.AppendFormat( "Comparison ({0:d3} vs {1:d3}).xls", 1, iDataSets.Count ); |
|
361 } |
|
362 else |
|
363 { |
|
364 temp.AppendFormat( "Comparison ({0:d3} vs {1:d3}).xls", iIndex + 1, iIndex + 2 ); |
|
365 } |
|
366 |
|
367 return temp.ToString(); |
|
368 } |
|
369 |
|
370 private string BuildExcelFileNameForSummarySpreadsheet() |
|
371 { |
|
372 StringBuilder temp = new StringBuilder( iDestinationPath ); |
|
373 if ( iDestinationPath[ iDestinationPath.Length - 1 ] != Path.DirectorySeparatorChar ) |
|
374 { |
|
375 temp.Append( Path.DirectorySeparatorChar ); |
|
376 } |
|
377 temp.Append( "Summary.xls" ); |
|
378 return temp.ToString(); |
|
379 } |
|
380 |
|
381 private int CalculateProgressPercentage( int aCurrent, int aMax ) |
|
382 { |
|
383 float ret = (float) aCurrent / (float) aMax; |
|
384 ret *= 100.0f; |
|
385 return (int) ret; |
|
386 } |
|
387 |
|
388 private void IdentifySharedHeaps() |
|
389 { |
|
390 // MemSpy should (and could) do this, at least eventually, but for now |
|
391 // we'll work this out on the PC if MemSpy supplied us with the chunk handles for |
|
392 // each thread. |
|
393 int count = iDataSets.Count; |
|
394 for ( int i = 0; i < count; i++ ) |
|
395 { |
|
396 CSVDataSet dataSet = iDataSets[ i ]; |
|
397 dataSet.FindSharedHeaps(); |
|
398 } |
|
399 } |
|
400 #endregion |
|
401 |
|
402 #region Internal constants |
|
403 private const int KSpecialFirstVsLastComparisonIndex = int.MaxValue; |
|
404 #endregion |
|
405 |
|
406 #region Data members |
|
407 private readonly string iDestinationPath; |
|
408 private List<string> iSource = new List<string>(); |
|
409 private CSVDataSetCollection iDataSets = new CSVDataSetCollection(); |
|
410 private int iIndex = 0; |
|
411 private TState iState = TState.EStateIdle; |
|
412 private CSVWorkerLogSplitter iWorkerSplitter; |
|
413 private CSVWorkerTwoDataSetComparator iWorkerComparatorTwo; |
|
414 private CSVWorkerAllDataSetComparator iWorkerComparatorAll; |
|
415 #endregion |
|
416 } |
|
417 } |