|
1 /* |
|
2 * Copyright (c) 2010 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 |
|
18 #include "CStressWorker.h" |
|
19 #include "RSearchServerSession.h" |
|
20 #include "CCPixIndexer.h" |
|
21 #include "CCPixSearcher.h" |
|
22 #include "CSearchDocument.h" |
|
23 #include <e32math.h> |
|
24 #include "CLog.h" |
|
25 #include "MCPixSearcherObserver.h" |
|
26 #include "robustnesstest.pan" |
|
27 |
|
28 #include "CWorker.h" |
|
29 #include "CIndexingWorker.h" |
|
30 #include "CSearchingWorker.h" |
|
31 |
|
32 #define KLockWaitSeconds 5 // wait for some good time |
|
33 |
|
34 #define KLockWaitMicroSeconds KLockWaitSeconds * 1000 * 1000 // wait for some good time |
|
35 |
|
36 #define KWaitAfterCancellingSeconds 10 // wait for some good time |
|
37 |
|
38 #define KWaitFinishSeconds 10 // wait for some good time |
|
39 |
|
40 #define KWaitFinishMicroSeconds KWaitFinishSeconds * 1000 * 1000 // wait for some good time |
|
41 |
|
42 |
|
43 CStressWorker::CStressWorker( MLog& aLog, |
|
44 TInt aPreIndex, |
|
45 TInt aIndexAverageItems, |
|
46 TBool aIndexingEnabled, |
|
47 TBool aDeletesEnabled, |
|
48 TBool aSearchersEnabled, |
|
49 TBool aCancellingEnabled, |
|
50 TInt aIndexerSleep, |
|
51 TInt aSearcherSleep ) |
|
52 : iLog( aLog ), |
|
53 iWorkers(), |
|
54 iIsActive( EFalse ), |
|
55 iPreIndex ( aPreIndex ), |
|
56 iIndexAverageItems( aIndexAverageItems ), |
|
57 iIndexingEnabled ( aIndexingEnabled ), |
|
58 iDeletesEnabled ( aDeletesEnabled ), |
|
59 iSearchersEnabled ( aSearchersEnabled ), |
|
60 iCancellingEnabled( aCancellingEnabled ), |
|
61 iIndexerSleep ( aIndexerSleep), |
|
62 iSearcherSleep ( aSearcherSleep ) |
|
63 { |
|
64 } |
|
65 |
|
66 CStressWorker::~CStressWorker() |
|
67 { |
|
68 iWorkers.ResetAndDestroy(); |
|
69 iWorkers.Close(); |
|
70 } |
|
71 |
|
72 void CStressWorker::ConstructL() |
|
73 { |
|
74 } |
|
75 |
|
76 void CStressWorker::StartWorkersL() |
|
77 { |
|
78 |
|
79 // |
|
80 // Indexers |
|
81 // |
|
82 |
|
83 |
|
84 if ( iIndexingEnabled ) |
|
85 { |
|
86 // Indexer working with normal index |
|
87 iWorkers.Append( CIndexingWorker::NewL( KNormalVolume, iDeletesEnabled, iIndexAverageItems, iIndexerSleep ) ); |
|
88 |
|
89 // 3 indexers working with busy index |
|
90 iWorkers.Append( CIndexingWorker::NewL( KBusyVolume, iDeletesEnabled, iIndexAverageItems, iIndexerSleep ) ); |
|
91 iWorkers.Append( CIndexingWorker::NewL( KBusyVolume, iDeletesEnabled, iIndexAverageItems, iIndexerSleep ) ); |
|
92 iWorkers.Append( CIndexingWorker::NewL( KBusyVolume, iDeletesEnabled, iIndexAverageItems, iIndexerSleep ) ); |
|
93 |
|
94 // = 4 indexers in total |
|
95 } |
|
96 |
|
97 if ( iSearchersEnabled ) |
|
98 { |
|
99 // |
|
100 // Searchers |
|
101 // |
|
102 |
|
103 // Multisearcher |
|
104 iWorkers.Append( CSearchingWorker::NewL( KTestBaseAppClass, iSearcherSleep ) ); |
|
105 |
|
106 // Search normal index |
|
107 iWorkers.Append( CSearchingWorker::NewL( KNormalVolume, iSearcherSleep, iCancellingEnabled ) ); |
|
108 |
|
109 // Search big the busy index |
|
110 iWorkers.Append( CSearchingWorker::NewL( KBusyVolume, iSearcherSleep ) ); |
|
111 iWorkers.Append( CSearchingWorker::NewL( KBusyVolume, iSearcherSleep, EFalse, iCancellingEnabled ) ); |
|
112 iWorkers.Append( CSearchingWorker::NewL( KBusyBaseAppClass, iSearcherSleep ) ); |
|
113 |
|
114 // = 5 searchers in total |
|
115 } |
|
116 |
|
117 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
118 { |
|
119 iWorkers[i]->StartL(); |
|
120 } |
|
121 } |
|
122 |
|
123 void CStressWorker::PrepareIndexL() |
|
124 { |
|
125 RSearchServerSession session; |
|
126 User::LeaveIfError( session.Connect() ); |
|
127 CleanupClosePushL( session ); |
|
128 |
|
129 // First database has one reader and one or two searchers, what |
|
130 // is something that can be expected in the actual device |
|
131 User::LeaveIfError |
|
132 ( |
|
133 session.DefineVolume( KNormalVolume, |
|
134 KNormalIndexDirectory ) |
|
135 ); |
|
136 |
|
137 // Second database has a number of searchers and indexers. |
|
138 User::LeaveIfError |
|
139 ( |
|
140 session.DefineVolume( KBusyVolume, |
|
141 KBusyIndexDirectory ) |
|
142 ); |
|
143 |
|
144 if ( iPreIndex ) iLog.LogL( _L( "preparing index") ); |
|
145 |
|
146 CCPixIndexer* indexer = CCPixIndexer::NewLC( session ); |
|
147 indexer->OpenDatabaseL( KNormalVolume ); |
|
148 indexer->ResetL(); |
|
149 |
|
150 HBufC* buf = HBufC::NewLC( 4 * 1024 ); |
|
151 TPtr pbuf( buf->Des() ); |
|
152 |
|
153 for ( TInt i = 0; i < iPreIndex; i++ ) |
|
154 { |
|
155 indexer->AddL( *GenerateDocumentLC( iIndexAverageItems, |
|
156 KNormalBaseAppClass, |
|
157 pbuf ) ); |
|
158 CleanupStack::PopAndDestroy(); |
|
159 } |
|
160 |
|
161 indexer->FlushL(); |
|
162 |
|
163 indexer->OpenDatabaseL( KBusyVolume ); |
|
164 indexer->ResetL(); |
|
165 |
|
166 for ( TInt i = 0; i < iPreIndex; i++ ) |
|
167 { |
|
168 indexer->AddL( *GenerateDocumentLC( iIndexAverageItems, |
|
169 KBusyBaseAppClass, |
|
170 pbuf ) ); |
|
171 CleanupStack::PopAndDestroy(); |
|
172 } |
|
173 |
|
174 CleanupStack::PopAndDestroy( buf ); |
|
175 |
|
176 indexer->FlushL(); |
|
177 CleanupStack::PopAndDestroy( indexer ); |
|
178 CleanupStack::PopAndDestroy(); // session |
|
179 } |
|
180 |
|
181 void CStressWorker::StartL() |
|
182 { |
|
183 |
|
184 PrepareIndexL(); |
|
185 |
|
186 TRAPD( err, StartWorkersL() ); |
|
187 |
|
188 if ( err != KErrNone ) |
|
189 { |
|
190 iWorkers.ResetAndDestroy(); |
|
191 User::Leave( err ); |
|
192 } |
|
193 else |
|
194 { |
|
195 iIsActive = ETrue; |
|
196 } |
|
197 } |
|
198 |
|
199 void CStressWorker::DoFinishL() |
|
200 { |
|
201 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
202 { |
|
203 iWorkers[i]->Cancel(); |
|
204 } |
|
205 |
|
206 User::After( KWaitAfterCancellingSeconds * 1000 * 1000 ); |
|
207 |
|
208 int running = iWorkers.Count(); |
|
209 TInt round = 0; |
|
210 for (; round < 3 && running ; round++ ) |
|
211 { |
|
212 switch (round) |
|
213 { |
|
214 case 1: |
|
215 case 2: |
|
216 iLog.LogL( _L( "Retrying to stop workers." ) ); |
|
217 break; |
|
218 } |
|
219 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
220 { |
|
221 if ( iWorkers[i]->IsActive() ) |
|
222 { |
|
223 if ( iWorkers[i]->TryJoin( KWaitFinishMicroSeconds ) == KErrNone ) |
|
224 { |
|
225 running--; |
|
226 if ( round > 0 ) |
|
227 { |
|
228 iLog.LogL( _L( "worker '%S' finished" ), |
|
229 &iWorkers[i]->Name() ); |
|
230 } |
|
231 } |
|
232 else |
|
233 { |
|
234 iLog.LogL( _L( "worker '%S' failed to exit within %d s" ), |
|
235 &iWorkers[i]->Name(), |
|
236 KWaitFinishSeconds ); |
|
237 } |
|
238 } |
|
239 } |
|
240 } |
|
241 |
|
242 if ( running ) |
|
243 { |
|
244 iLog.LogL( _L( "Deadlock? %d worker(s) failed to exit." ), |
|
245 running ); |
|
246 |
|
247 iLog.LogL( _L( "Terminating workers forcefully (may cause leaks)" ) ); |
|
248 |
|
249 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
250 { |
|
251 if ( iWorkers[i]->IsActive() ) |
|
252 { |
|
253 iWorkers[i]->Terminate(); |
|
254 iWorkers[i]->Unlock(); // So that we can check the records. |
|
255 iLog.LogL( _L( "terminated worker '%S'." ), |
|
256 &iWorkers[i]->Name() ); |
|
257 } |
|
258 } |
|
259 |
|
260 } |
|
261 else if (round > 1) |
|
262 { |
|
263 iLog.LogL( _L( "All workers finished successfully." ) ); |
|
264 } |
|
265 |
|
266 ReportL(); |
|
267 |
|
268 } |
|
269 |
|
270 TBool CStressWorker::ReportL() |
|
271 { |
|
272 TBool error = EFalse; |
|
273 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
274 { |
|
275 if ( iWorkers[i]->Error() != KErrNone && |
|
276 iWorkers[i]->IsReported() == EFalse ) |
|
277 { |
|
278 TDateTime time = iWorkers[i]->ExitTime().DateTime(); |
|
279 |
|
280 TBuf<64> buf; |
|
281 buf.AppendNumFixedWidth( (TUint)time.Hour(), EDecimal, 2 ); |
|
282 buf.Append(_L(":")); |
|
283 buf.AppendNumFixedWidth( (TUint)time.Minute(), EDecimal, 2 ); |
|
284 buf.Append(_L(":")); |
|
285 buf.AppendNumFixedWidth( (TUint)time.Second(), EDecimal, 2 ); |
|
286 buf.Append(_L(".")); |
|
287 buf.AppendNumFixedWidth( (TUint)time.MicroSecond(), EDecimal, 3 ); |
|
288 |
|
289 iLog.LogL( _L( "worker '%S' failed with %d at %S" ), |
|
290 &iWorkers[i]->Name(), |
|
291 iWorkers[i]->Error(), |
|
292 &buf ); |
|
293 iWorkers[i]->SetReported(); |
|
294 error = ETrue; |
|
295 } |
|
296 } |
|
297 return error; |
|
298 } |
|
299 |
|
300 |
|
301 void CStressWorker::AppendOpStatsL( RPointerArray<HBufC8>& aStats, TInt aOperations, TInt64 aOperationsMicroSeconds ) |
|
302 { |
|
303 HBufC8* buf = HBufC8::NewL( 64 ); |
|
304 buf->Des().AppendNum( aOperations ); |
|
305 aStats.Append( buf ); |
|
306 buf = HBufC8::NewL( 64 ); |
|
307 if ( aOperations > 0 ) |
|
308 { |
|
309 buf->Des().AppendNum( (aOperationsMicroSeconds / 1000) / aOperations ); |
|
310 } |
|
311 else |
|
312 { |
|
313 buf->Des().Append( _L8( "NaN") ); |
|
314 } |
|
315 aStats.Append( buf ); |
|
316 } |
|
317 |
|
318 void CStressWorker::AppendOpStatsL( RPointerArray<HBufC8>& aStats, TInt aOperations, TInt64 aOperationsMicroSeconds, TInt64 aOperationsPeakMicroSeconds ) |
|
319 { |
|
320 HBufC8* buf = HBufC8::NewL( 64 ); |
|
321 AppendOpStatsL( aStats, aOperations, aOperationsMicroSeconds ); |
|
322 |
|
323 if ( aOperations > 0 ) |
|
324 { |
|
325 buf->Des().AppendNum( aOperationsPeakMicroSeconds / 1000 ); |
|
326 } |
|
327 else |
|
328 { |
|
329 buf->Des().Append( _L8( "NaN") ); |
|
330 } |
|
331 aStats.Append( buf ); |
|
332 } |
|
333 |
|
334 TInt CStressWorker::DirectorySizeL( const TDesC& aDirectory ) |
|
335 { |
|
336 |
|
337 RFs fs; |
|
338 User::LeaveIfError( fs.Connect() ); |
|
339 CleanupClosePushL( fs ); |
|
340 |
|
341 TBuf<256> buf; |
|
342 buf.Append( aDirectory ); |
|
343 buf.Append( _L( "\\*" ) ); |
|
344 |
|
345 // Make sure, that the index is not under merging or optimization operations |
|
346 |
|
347 CDir* dir; |
|
348 TInt err = fs.GetDir( buf, |
|
349 KEntryAttMaskSupported, |
|
350 ESortByName, |
|
351 dir ); |
|
352 TInt size = 0; |
|
353 if ( err == KErrNone ) |
|
354 { |
|
355 for (TInt i=0;i<dir->Count();i++) |
|
356 { |
|
357 size += (*dir)[i].iSize; |
|
358 } |
|
359 |
|
360 delete dir; |
|
361 } |
|
362 else |
|
363 { |
|
364 // It is ok, if directory was not found. |
|
365 } |
|
366 |
|
367 CleanupStack::PopAndDestroy(); // fs |
|
368 |
|
369 return size; |
|
370 } |
|
371 |
|
372 _LIT( KIndexZero, "\\_0" ); |
|
373 _LIT( KIndexOne, "\\_1" ); |
|
374 |
|
375 TInt CStressWorker::IndexSizeL( const TDesC& aDirectory ) |
|
376 { |
|
377 TInt size = 0; |
|
378 TBuf<256> buf; |
|
379 buf.Append( aDirectory ); |
|
380 buf.Append( KIndexZero ); |
|
381 size += DirectorySizeL( buf ); |
|
382 buf.Zero(); |
|
383 buf.Append( aDirectory ); |
|
384 buf.Append( KIndexOne ); |
|
385 size += DirectorySizeL( buf ); |
|
386 return size; |
|
387 } |
|
388 |
|
389 void CStressWorker::AppendStatsLabelsL( RPointerArray<HBufC8>& aStats ) |
|
390 { |
|
391 aStats.Append( _L8( "norm sz").AllocL() ); |
|
392 aStats.Append( _L8( "busy sz").AllocL() ); |
|
393 |
|
394 if ( iIndexingEnabled ) |
|
395 { |
|
396 aStats.Append( _L8( "adds").AllocL() ); |
|
397 aStats.Append( _L8( "ms/op").AllocL() ); |
|
398 aStats.Append( _L8( "deletes").AllocL() ); |
|
399 aStats.Append( _L8( "ms/op").AllocL() ); |
|
400 } |
|
401 if ( iSearchersEnabled ) |
|
402 { |
|
403 aStats.Append( _L8( "searches").AllocL() ); |
|
404 aStats.Append( _L8( "ms/op").AllocL() ); |
|
405 aStats.Append( _L8( "peak").AllocL() ); |
|
406 |
|
407 aStats.Append( _L8( "getdocs").AllocL() ); |
|
408 aStats.Append( _L8( "ms/op").AllocL() ); |
|
409 aStats.Append( _L8( "peak").AllocL() ); |
|
410 |
|
411 aStats.Append( _L8( "terms").AllocL() ); |
|
412 aStats.Append( _L8( "ms/op").AllocL() ); |
|
413 aStats.Append( _L8( "peak").AllocL() ); |
|
414 |
|
415 aStats.Append( _L8( "getterms").AllocL() ); |
|
416 aStats.Append( _L8( "ms/op").AllocL() ); |
|
417 aStats.Append( _L8( "peak").AllocL() ); |
|
418 } |
|
419 } |
|
420 |
|
421 |
|
422 void CStressWorker::AppendStatsInsideLocksL( RPointerArray<HBufC8>& aStats, const RArray<TBool>& aLocked ) |
|
423 { |
|
424 TInt additions = 0; |
|
425 TInt64 additionsMicroSeconds = 0; |
|
426 |
|
427 TInt deletes = 0; |
|
428 TInt64 deletesMicroSeconds = 0; |
|
429 |
|
430 TInt searches = 0; |
|
431 TInt64 searchesMicroSeconds = 0; |
|
432 TInt64 searchesPeakMicroSeconds = 0; |
|
433 |
|
434 TInt termSearches = 0; |
|
435 TInt64 termSearchesMicroSeconds = 0; |
|
436 TInt64 termSearchesPeakMicroSeconds = 0; |
|
437 |
|
438 TInt docs = 0; |
|
439 TInt64 docsMicroSeconds = 0; |
|
440 TInt64 docsPeakMicroSeconds = 0; |
|
441 |
|
442 TInt terms = 0; |
|
443 TInt64 termsMicroSeconds = 0; |
|
444 TInt64 termsPeakMicroSeconds = 0; |
|
445 |
|
446 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
447 { |
|
448 if ( aLocked[i] ) |
|
449 { |
|
450 // Extract statistics only, if worker was successfully locked. |
|
451 if ( dynamic_cast<CIndexingWorker*>( iWorkers[i] ) ) |
|
452 { |
|
453 CIndexingWorker* worker = dynamic_cast<CIndexingWorker*>( iWorkers[i] ); |
|
454 additions += worker->ConsumeAdditions(); |
|
455 additionsMicroSeconds += worker->ConsumeAdditionsMicroSeconds(); |
|
456 deletes += worker->ConsumeDeletes(); |
|
457 deletesMicroSeconds += worker->ConsumeDeletesMicroSecond(); |
|
458 } |
|
459 if ( dynamic_cast<CSearchingWorker*>( iWorkers[i] ) ) |
|
460 { |
|
461 CSearchingWorker* worker = dynamic_cast<CSearchingWorker*>( iWorkers[i] ); |
|
462 searches += worker->ConsumeSearches(); |
|
463 searchesMicroSeconds += worker->ConsumeSearchesMicroSeconds(); |
|
464 searchesPeakMicroSeconds += worker->ConsumeSearchesPeakMicroSeconds(); |
|
465 |
|
466 termSearches += worker->ConsumeTermSearches(); |
|
467 termSearchesMicroSeconds += worker->ConsumeTermSearchesMicroSeconds(); |
|
468 termSearchesPeakMicroSeconds+= worker->ConsumeTermSearchesPeakMicroSeconds(); |
|
469 |
|
470 docs += worker->ConsumeDocs(); |
|
471 docsMicroSeconds += worker->ConsumeDocsMicroSeconds(); |
|
472 docsPeakMicroSeconds += worker->ConsumeDocsPeakMicroSeconds(); |
|
473 |
|
474 terms += worker->ConsumeTerms(); |
|
475 termsMicroSeconds += worker->ConsumeTermsMicroSeconds(); |
|
476 termsPeakMicroSeconds += worker->ConsumeTermsPeakMicroSeconds(); |
|
477 } |
|
478 } |
|
479 } |
|
480 HBufC8* buf = HBufC8::NewL( 64 ); |
|
481 buf->Des().AppendNum( IndexSizeL( KNormalIndexDirectory ) ); |
|
482 aStats.Append( buf ); |
|
483 |
|
484 buf = HBufC8::NewL( 64 ); |
|
485 buf->Des().AppendNum( IndexSizeL( KBusyIndexDirectory ) ); |
|
486 aStats.Append( buf ); |
|
487 |
|
488 if ( iIndexingEnabled ) |
|
489 { |
|
490 AppendOpStatsL( aStats, additions, additionsMicroSeconds ); |
|
491 AppendOpStatsL( aStats, deletes, deletesMicroSeconds ); |
|
492 } |
|
493 |
|
494 if ( iSearchersEnabled ) |
|
495 { |
|
496 AppendOpStatsL( aStats, searches, searchesMicroSeconds, searchesPeakMicroSeconds ); |
|
497 AppendOpStatsL( aStats, docs, docsMicroSeconds, docsPeakMicroSeconds ); |
|
498 AppendOpStatsL( aStats, termSearches, termSearchesMicroSeconds, termSearchesPeakMicroSeconds ); |
|
499 AppendOpStatsL( aStats, terms, termsMicroSeconds, termsPeakMicroSeconds ); |
|
500 } |
|
501 |
|
502 } |
|
503 |
|
504 void CStressWorker::AppendStatsL( RPointerArray<HBufC8>& aStats ) |
|
505 { |
|
506 RArray<TBool> locked; |
|
507 CleanupClosePushL( locked ); |
|
508 |
|
509 // First of first: lock all workers that can be locked and request |
|
510 // their entries |
|
511 |
|
512 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
513 { |
|
514 if ( iWorkers[i]->TryLock( KLockWaitMicroSeconds ) == KErrNone ) |
|
515 { |
|
516 locked.Append( ETrue ); |
|
517 } |
|
518 else |
|
519 { |
|
520 TRAP_IGNORE |
|
521 ( |
|
522 iLog.LogL( _L( "worker '%S' failed to respond within %d s" ), |
|
523 &iWorkers[i]->Name(), |
|
524 KLockWaitSeconds ); |
|
525 ) |
|
526 locked.Append( EFalse ); |
|
527 } |
|
528 } |
|
529 |
|
530 TRAPD( err, AppendStatsInsideLocksL( aStats, locked ) ); |
|
531 |
|
532 for ( TInt i = 0; i < iWorkers.Count(); i++ ) |
|
533 { |
|
534 if ( locked[i] ) |
|
535 { |
|
536 iWorkers[i]->Unlock(); |
|
537 } |
|
538 } |
|
539 |
|
540 CleanupStack::PopAndDestroy(); // locked |
|
541 |
|
542 User::LeaveIfError( err ); |
|
543 } |
|
544 |
|
545 TInt CStressWorker::Finish() |
|
546 { |
|
547 if ( iIsActive ) |
|
548 { |
|
549 TRAPD( err, DoFinishL() ); |
|
550 if ( err != KErrNone ) |
|
551 { |
|
552 iWorkers.ResetAndDestroy(); |
|
553 } |
|
554 iIsActive = EFalse; |
|
555 return err; |
|
556 } |
|
557 return KErrNone; |
|
558 } |
|
559 |
|
560 TBool CStressWorker::IsActive() |
|
561 { |
|
562 return iIsActive; |
|
563 } |