|
1 // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 //! @file f32test\concur\t_tdebug.cpp |
|
17 |
|
18 #include <e32test.h> |
|
19 #include <f32file.h> |
|
20 #include "t_server.h" |
|
21 #include "t_tdebug.h" |
|
22 #include "cfafsdlyif.h" |
|
23 |
|
24 TThreadData TTest::iData[KMaxThreads]; |
|
25 TThreadData TTest::iDummy; |
|
26 TFullName TTest::iWhere; |
|
27 RMutex TTest::iDebugLock; |
|
28 RMutex TTest::iPrintLock; |
|
29 TBool TTest::iInit = EFalse; |
|
30 |
|
31 LOCAL_C TFileName gErrorPos; |
|
32 |
|
33 // Instance of the class to force initialisation. |
|
34 LOCAL_C TTest gTest; |
|
35 |
|
36 LOCAL_C TInt KSecond = 1000000; |
|
37 |
|
38 class TTestOverflowTruncate : public TDesOverflow |
|
39 /// |
|
40 /// Used to suppress overflow when appending formatted text to a buffer. |
|
41 /// |
|
42 { |
|
43 public: |
|
44 virtual void Overflow(TDes &/*aDes*/) {} |
|
45 }; |
|
46 |
|
47 TTest::TTest() |
|
48 // |
|
49 // Constructor, forces initialisation of variables. |
|
50 // |
|
51 { |
|
52 Init(); |
|
53 } |
|
54 |
|
55 TInt TTest::Init() |
|
56 /// |
|
57 /// Initialise stuff (currently just the locks) if it hasn't been |
|
58 /// done already. |
|
59 /// |
|
60 { |
|
61 if (!iInit) |
|
62 { |
|
63 TInt r = KErrNone; |
|
64 r = iDebugLock.CreateLocal(); |
|
65 if (r != KErrNone) |
|
66 { |
|
67 RDebug::Print(_L("ERROR %d creating iDebugLock\n"), r); |
|
68 return r; |
|
69 } |
|
70 r = iPrintLock.CreateLocal(); |
|
71 if (r != KErrNone) |
|
72 { |
|
73 RDebug::Print(_L("ERROR %d creating iPrintLock\n"), r); |
|
74 return r; |
|
75 } |
|
76 iInit = ETrue; |
|
77 } |
|
78 return KErrNone; |
|
79 } |
|
80 |
|
81 TInt TTest::Create(TInt aNum, TThreadFunction aFunction, const TDesC& aName) |
|
82 /// |
|
83 /// Create a thread, setting up the name and our data area. |
|
84 /// |
|
85 { |
|
86 if (aNum < 0 || aNum > KMaxThreads) |
|
87 { |
|
88 test.Printf(_L("Illegal thread %d\n"), aNum); |
|
89 test(EFalse); |
|
90 } |
|
91 TThreadData &d = iData[aNum]; |
|
92 // test.Printf(_L("creating thread %d (%S)\n"), aNum, &aName); |
|
93 // d.iThread.LogonCancel(d.iStat); |
|
94 // d.iThread.Close(); |
|
95 TInt r; |
|
96 r = d.iThread.Create(aName, aFunction, KDefaultStackSize+32*1024, KMinHeapSize, 0x20000, &d); |
|
97 if (r != KErrNone) |
|
98 { |
|
99 TBuf<128> buf; |
|
100 test.Printf(_L("Error creating thread %d '%S' (was %d '%S'): %S\n"), |
|
101 aNum, &aName, d.iNum, &d.iName, &TTest::ErrStr(r, buf)); |
|
102 test(0); |
|
103 } |
|
104 d.iId = d.iThread.Id(); |
|
105 d.iNum = aNum; |
|
106 d.iName = aName; |
|
107 d.iThread.Logon(d.iStat); |
|
108 return r; |
|
109 } |
|
110 |
|
111 TInt TTest::RunOnly() |
|
112 /// |
|
113 /// Resume all of the threads we have created. |
|
114 /// |
|
115 { |
|
116 TInt i; |
|
117 for (i = 0; i < KMaxThreads; i++) |
|
118 { |
|
119 if (iData[i].iId > 0) |
|
120 { |
|
121 iData[i].iThread.Resume(); |
|
122 } |
|
123 } |
|
124 return KErrNone; |
|
125 } |
|
126 |
|
127 TInt TTest::Run(TBool aExitAny, TInt aTimeout) |
|
128 /// |
|
129 /// Run until all (or any one) threads has completed, or until a timeout. |
|
130 /// @param aExitAny If true, exit when the first thread completes, otherwise |
|
131 /// wait until they have all completed. |
|
132 /// @param aTimeout if zero, no timeout, otherwise it is the timeout in microseconds. |
|
133 /// |
|
134 { |
|
135 TInt i; |
|
136 TInt status = RunOnly(); |
|
137 RTimer timer; |
|
138 TRequestStatus tstat; |
|
139 timer.CreateLocal(); |
|
140 if (aTimeout) |
|
141 timer.After(tstat, aTimeout); |
|
142 for (;;) |
|
143 { |
|
144 status = KErrNone; |
|
145 User::WaitForAnyRequest(); |
|
146 if (aTimeout > 0 && tstat != KRequestPending) |
|
147 break; |
|
148 TBool running = EFalse; |
|
149 for (i = 0; i < KMaxThreads; i++) |
|
150 { |
|
151 if (iData[i].iId > 0) |
|
152 { |
|
153 if (iData[i].iStat == KRequestPending) |
|
154 { |
|
155 running = ETrue; |
|
156 } |
|
157 else |
|
158 { |
|
159 TThreadData &d = iData[i]; |
|
160 // ignore result of LogonCancel, since we know thread has finished |
|
161 d.iThread.LogonCancel(d.iStat); |
|
162 d.iThread.Close(); |
|
163 d.iId = 0; |
|
164 if (d.iStat != KErrNone) |
|
165 { |
|
166 status = KErrAbort; |
|
167 TBuf<32> ebuf; |
|
168 test.Printf(_L("ERROR: %S in thread %S: %S\n %S"), |
|
169 &ErrStr(d.iStat.Int(), ebuf), &d.iName, &d.iMess, &iWhere); |
|
170 if (aExitAny) |
|
171 { |
|
172 running = EFalse; |
|
173 break; |
|
174 } |
|
175 } |
|
176 } |
|
177 } |
|
178 } |
|
179 if (!running) |
|
180 break; |
|
181 } |
|
182 timer.Cancel(); |
|
183 timer.Close(); |
|
184 return status; |
|
185 } |
|
186 |
|
187 void TTest::KillAll(TInt aReason) |
|
188 // |
|
189 // Kill (destroy) all of the created threads, then wait for up to 10 seconds |
|
190 // for them all to die (and just exit if any are still alive). |
|
191 // |
|
192 { |
|
193 for (TInt i = 0; i < KMaxThreads; i++) |
|
194 { |
|
195 if (iData[i].iId > 0) |
|
196 { |
|
197 TThreadData &d = iData[i]; |
|
198 d.iThread.Kill(aReason); |
|
199 } |
|
200 } |
|
201 Run(EFalse, 10*KSecond); |
|
202 } |
|
203 |
|
204 TThreadData& TTest::Self() |
|
205 /// |
|
206 /// Return a reference to the current thread; if it's not one we've created |
|
207 /// return a reference to a dummy data area indicating no thread. |
|
208 /// |
|
209 { |
|
210 RThread me; |
|
211 TInt i; |
|
212 for (i = 0; i < KMaxThreads; i++) |
|
213 { |
|
214 if (me.Id() == iData[i].iId) |
|
215 { |
|
216 return iData[i]; |
|
217 } |
|
218 } |
|
219 iDummy.iId = 0; |
|
220 iDummy.iNum = -1; |
|
221 iDummy.iName.Format(_L("#%d"), (TUint)me.Id()); |
|
222 return iDummy; |
|
223 } |
|
224 |
|
225 TThreadData& TTest::Data(TInt aIndex) |
|
226 /// |
|
227 /// Return a reference to the data area for the specified thread, or to a |
|
228 /// dummy area if it's not in the right range. |
|
229 /// |
|
230 /// @param aIndex index to the thread (ThreadData::iNum is the same number). |
|
231 /// |
|
232 { |
|
233 if (aIndex >= 0 && aIndex < KMaxThreads) |
|
234 return iData[aIndex]; |
|
235 iDummy.iId = 0; |
|
236 iDummy.iNum = -1; |
|
237 iDummy.iName = _L(""); |
|
238 return iDummy; |
|
239 } |
|
240 |
|
241 void TTest::Start(const TDesC& aStr) |
|
242 /// |
|
243 /// Output "START TEST" and the string. |
|
244 /// |
|
245 { |
|
246 Printf(_L("START TEST: %S\n"), &aStr); |
|
247 } |
|
248 |
|
249 void TTest::Next(const TDesC& aStr) |
|
250 /// |
|
251 /// Output "NEXT TEST" and the string. |
|
252 /// |
|
253 { |
|
254 Printf(_L("NEXT TEST: %S\n"), &aStr); |
|
255 } |
|
256 |
|
257 void TTest::PrintLock() |
|
258 /// |
|
259 /// Wait if another task is doing output. |
|
260 /// |
|
261 { |
|
262 iPrintLock.Wait(); |
|
263 } |
|
264 |
|
265 void TTest::PrintUnlock() |
|
266 /// |
|
267 /// Signal that output is complete so that other tasks can do output. |
|
268 /// |
|
269 { |
|
270 iPrintLock.Signal(); |
|
271 } |
|
272 |
|
273 void TTest::Printf(TRefByValue<const TDesC> aFmt, ...) |
|
274 /// |
|
275 /// Output the formatted text, prepending it with the thread name if it is one |
|
276 /// we've created. Parameters as for printf(). Note that if more than one |
|
277 /// thread tries to call it at the same time it will lock so that only one is |
|
278 /// processed at a time, the debug output isn't thread-safe (it can mix |
|
279 /// characters from different threads). |
|
280 /// |
|
281 { |
|
282 TTestOverflowTruncate overflow; |
|
283 VA_LIST list; |
|
284 VA_START(list, aFmt); |
|
285 TBuf<256> buf; |
|
286 buf.SetLength(0); |
|
287 if (Self().iNum >= 0) |
|
288 { |
|
289 buf.Append(Self().iName); |
|
290 buf.Append(_L(": ")); |
|
291 } |
|
292 buf.AppendFormatList(aFmt, list, &overflow); |
|
293 #if defined(__WINS__) |
|
294 if (buf.Right(1) != _L("\n")) |
|
295 buf.Append(_L("\n")); |
|
296 #else |
|
297 if (buf.Right(1) == _L("\n")) |
|
298 buf.SetLength(buf.Length() - 1); |
|
299 #endif |
|
300 iDebugLock.Wait(); |
|
301 RDebug::Print(_L("%S"), &buf); |
|
302 iDebugLock.Signal(); |
|
303 VA_END(list); |
|
304 } |
|
305 |
|
306 void TTest::Printf() |
|
307 /// |
|
308 /// Output a blank line, prepended with the thread name if any. |
|
309 /// |
|
310 { |
|
311 Printf(_L("\n")); |
|
312 } |
|
313 |
|
314 void TTest::Fail(TPos aPos, TRefByValue<const TDesC> aFmt, ...) |
|
315 /// |
|
316 /// Output an error message (formatted as for printf()), then exit the thread. |
|
317 /// The message is placed in the buffer associated with the thread so that |
|
318 /// the parent task can display it. |
|
319 /// |
|
320 { |
|
321 VA_LIST list; |
|
322 VA_START(list, aFmt); |
|
323 Self().iMess.FormatList(aFmt, list); |
|
324 iDebugLock.Wait(); |
|
325 TPtrC8 ptr((TUint8*)aPos.iFailFile); |
|
326 gErrorPos.Copy(ptr); |
|
327 iWhere.Format(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); |
|
328 RDebug::Print(_L("\n")); |
|
329 RDebug::Print(_L("ERROR in thread %S: %S"), &Self().iName, &Self().iMess); |
|
330 RDebug::Print(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); |
|
331 RDebug::Print(_L("\n")); |
|
332 iDebugLock.Signal(); |
|
333 User::Exit(KErrAbort); |
|
334 } |
|
335 |
|
336 void TTest::Fail(TPos aPos, TInt aErr, TRefByValue<const TDesC> aFmt, ...) |
|
337 /// |
|
338 /// Output an error message including the interpreted error value followed |
|
339 /// by the specified text (formatted as for printf()), then exit the thread. |
|
340 /// The message is placed in the buffer associated with the thread so that |
|
341 /// the parent task can display it. |
|
342 /// |
|
343 { |
|
344 VA_LIST list; |
|
345 VA_START(list, aFmt); |
|
346 TBuf<32> ebuf; |
|
347 ErrStr(aErr, ebuf); |
|
348 Self().iMess.FormatList(aFmt, list); |
|
349 iDebugLock.Wait(); |
|
350 TPtrC8 ptr((TUint8*)aPos.iFailFile); |
|
351 gErrorPos.Copy(ptr); |
|
352 iWhere.Format(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); |
|
353 RDebug::Print(_L("\n")); |
|
354 RDebug::Print(_L("%S in thread %S: %S"), &ebuf, &Self().iName, &Self().iMess); |
|
355 RDebug::Print(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); |
|
356 RDebug::Print(_L("\n")); |
|
357 iDebugLock.Signal(); |
|
358 User::Exit(aErr); |
|
359 } |
|
360 |
|
361 TDesC& TTest::ErrStr(TInt aErr, TDes& aDes) |
|
362 /// |
|
363 /// Interpret an error status value as a string in the specified buffer. |
|
364 /// If the value isn't recognised then it formats a string containing the |
|
365 /// value itself (like "Error -65"). |
|
366 /// @param aErr The error value. |
|
367 /// @param aDes Descriptor of the buffer to be used. |
|
368 /// @return Descriptor of the buffer. |
|
369 /// |
|
370 { |
|
371 switch (aErr) |
|
372 { |
|
373 case KErrNone: |
|
374 aDes = _L("KErrNone"); |
|
375 break; |
|
376 case KErrNotFound: |
|
377 aDes = _L("KErrNotFound"); |
|
378 break; |
|
379 case KErrGeneral: |
|
380 aDes = _L("KErrGeneral"); |
|
381 break; |
|
382 case KErrCancel: |
|
383 aDes = _L("KErrCancel"); |
|
384 break; |
|
385 case KErrNoMemory: |
|
386 aDes = _L("KErrNoMemory"); |
|
387 break; |
|
388 case KErrNotSupported: |
|
389 aDes = _L("KErrNotSupported"); |
|
390 break; |
|
391 case KErrArgument: |
|
392 aDes = _L("KErrArgument"); |
|
393 break; |
|
394 case KErrTotalLossOfPrecision: |
|
395 aDes = _L("KErrTotalLossOfPrecision"); |
|
396 break; |
|
397 case KErrBadHandle: |
|
398 aDes = _L("KErrBadHandle"); |
|
399 break; |
|
400 case KErrOverflow: |
|
401 aDes = _L("KErrOverflow"); |
|
402 break; |
|
403 case KErrUnderflow: |
|
404 aDes = _L("KErrUnderflow"); |
|
405 break; |
|
406 case KErrAlreadyExists: |
|
407 aDes = _L("KErrAlreadyExists"); |
|
408 break; |
|
409 case KErrPathNotFound: |
|
410 aDes = _L("KErrPathNotFound"); |
|
411 break; |
|
412 case KErrDied: |
|
413 aDes = _L("KErrDied"); |
|
414 break; |
|
415 case KErrInUse: |
|
416 aDes = _L("KErrInUse"); |
|
417 break; |
|
418 case KErrServerTerminated: |
|
419 aDes = _L("KErrServerTerminated"); |
|
420 break; |
|
421 case KErrServerBusy: |
|
422 aDes = _L("KErrServerBusy"); |
|
423 break; |
|
424 case KErrCompletion: |
|
425 aDes = _L("KErrCompletion"); |
|
426 break; |
|
427 case KErrNotReady: |
|
428 aDes = _L("KErrNotReady"); |
|
429 break; |
|
430 case KErrUnknown: |
|
431 aDes = _L("KErrUnknown"); |
|
432 break; |
|
433 case KErrCorrupt: |
|
434 aDes = _L("KErrCorrupt"); |
|
435 break; |
|
436 case KErrAccessDenied: |
|
437 aDes = _L("KErrAccessDenied"); |
|
438 break; |
|
439 case KErrLocked: |
|
440 aDes = _L("KErrLocked"); |
|
441 break; |
|
442 case KErrWrite: |
|
443 aDes = _L("KErrWrite"); |
|
444 break; |
|
445 case KErrDisMounted: |
|
446 aDes = _L("KErrDisMounted"); |
|
447 break; |
|
448 case KErrEof: |
|
449 aDes = _L("KErrEof"); |
|
450 break; |
|
451 case KErrDiskFull: |
|
452 aDes = _L("KErrDiskFull"); |
|
453 break; |
|
454 case KErrBadDriver: |
|
455 aDes = _L("KErrBadDriver"); |
|
456 break; |
|
457 case KErrBadName: |
|
458 aDes = _L("KErrBadName"); |
|
459 break; |
|
460 case KErrCommsLineFail: |
|
461 aDes = _L("KErrCommsLineFail"); |
|
462 break; |
|
463 case KErrCommsFrame: |
|
464 aDes = _L("KErrCommsFrame"); |
|
465 break; |
|
466 case KErrCommsOverrun: |
|
467 aDes = _L("KErrCommsOverrun"); |
|
468 break; |
|
469 case KErrCommsParity: |
|
470 aDes = _L("KErrCommsParity"); |
|
471 break; |
|
472 case KErrTimedOut: |
|
473 aDes = _L("KErrTimedOut"); |
|
474 break; |
|
475 case KErrCouldNotConnect: |
|
476 aDes = _L("KErrCouldNotConnect"); |
|
477 break; |
|
478 case KErrCouldNotDisconnect: |
|
479 aDes = _L("KErrCouldNotDisconnect"); |
|
480 break; |
|
481 case KErrDisconnected: |
|
482 aDes = _L("KErrDisconnected"); |
|
483 break; |
|
484 case KErrBadLibraryEntryPoint: |
|
485 aDes = _L("KErrBadLibraryEntryPoint"); |
|
486 break; |
|
487 case KErrBadDescriptor: |
|
488 aDes = _L("KErrBadDescriptor"); |
|
489 break; |
|
490 case KErrAbort: |
|
491 aDes = _L("KErrAbort"); |
|
492 break; |
|
493 case KErrTooBig: |
|
494 aDes = _L("KErrTooBig"); |
|
495 break; |
|
496 case KErrDivideByZero: |
|
497 aDes = _L("KErrDivideByZero"); |
|
498 break; |
|
499 case KErrBadPower: |
|
500 aDes = _L("KErrBadPower"); |
|
501 break; |
|
502 case KErrDirFull: |
|
503 aDes = _L("KErrDirFull"); |
|
504 break; |
|
505 case KErrHardwareNotAvailable: |
|
506 aDes = _L("KErrHardwareNotAvailable"); |
|
507 break; |
|
508 case KErrSessionClosed: |
|
509 aDes = _L("KErrSessionClosed"); |
|
510 break; |
|
511 case KErrPermissionDenied: |
|
512 aDes = _L("KErrPermissionDenied"); |
|
513 break; |
|
514 case KRequestPending: |
|
515 aDes = _L("KRequestPending"); |
|
516 break; |
|
517 default: |
|
518 aDes = _L("Error "); |
|
519 aDes.AppendNum(aErr); |
|
520 break; |
|
521 } |
|
522 return aDes; |
|
523 } |
|
524 |
|
525 TInt TTest::ParseCommandArguments(TPtrC aArgV[], TInt aArgMax) |
|
526 /// |
|
527 /// Parse command line. Put the parameters into array aArgv for |
|
528 /// use by the tests, strip out flags starting with / or - and interpret |
|
529 /// them to set debug flags. |
|
530 /// |
|
531 { |
|
532 RFs fs; |
|
533 TInt r = fs.Connect(); |
|
534 test(r == KErrNone); |
|
535 TInt flags = 0; |
|
536 TInt argc = ParseCommandArguments(aArgV, aArgMax, flags); |
|
537 fs.SetDebugRegister(flags); |
|
538 fs.Close(); |
|
539 return argc; |
|
540 } |
|
541 |
|
542 TInt TTest::ParseCommandArguments(TPtrC aArgV[], TInt aArgMax, TInt& aDebugFlags) |
|
543 /// |
|
544 /// Parse command line. Put the parameters into array aArgv for |
|
545 /// use by the tests, strip out flags starting with / or - and interpret |
|
546 /// them to set debug flags. |
|
547 /// |
|
548 { |
|
549 LOCAL_D TBuf<0x100> cmd; |
|
550 User::CommandLine(cmd); |
|
551 TLex lex(cmd); |
|
552 TPtrC token=lex.NextToken(); |
|
553 TFileName thisfile=RProcess().FileName(); |
|
554 if (token.MatchF(thisfile)==0) |
|
555 { |
|
556 token.Set(lex.NextToken()); |
|
557 } |
|
558 // set up parameter list (offset zero is the filename) |
|
559 TInt argc = 0; |
|
560 aArgV[argc++].Set(thisfile); |
|
561 while (token.Length() != 0) |
|
562 { |
|
563 TChar ch = token[0]; |
|
564 // strip out (and interpret) flags starting with - or / |
|
565 if (ch == '-' || ch == '/') |
|
566 { |
|
567 for (TInt i = 1; i < token.Length(); i++) |
|
568 { |
|
569 switch (User::UpperCase(token[i])) |
|
570 { |
|
571 case 'D': |
|
572 aDebugFlags |= KDLYFAST; |
|
573 break; |
|
574 case 'F': |
|
575 aDebugFlags |= KFSYS; |
|
576 break; |
|
577 case 'I': |
|
578 aDebugFlags |= KISO9660; |
|
579 break; |
|
580 case 'L': |
|
581 aDebugFlags |= KFLDR; |
|
582 break; |
|
583 #ifdef __CONCURRENT_FILE_ACCESS__ |
|
584 case 'M': |
|
585 aDebugFlags |= KTHRD; |
|
586 break; |
|
587 #endif |
|
588 case 'N': |
|
589 aDebugFlags |= KNTFS; |
|
590 break; |
|
591 case 'S': |
|
592 aDebugFlags |= KFSERV; |
|
593 break; |
|
594 case 'T': |
|
595 aDebugFlags |= KLFFS; |
|
596 break; |
|
597 case 'Y': |
|
598 aDebugFlags |= KDLYTRC; |
|
599 break; |
|
600 } |
|
601 } |
|
602 } |
|
603 else if (argc < aArgMax) |
|
604 aArgV[argc++].Set(token); |
|
605 token.Set(lex.NextToken()); |
|
606 } |
|
607 return argc; |
|
608 } |
|
609 |
|
610 TChar TTest::DefaultDriveChar() |
|
611 { |
|
612 TFileName session; |
|
613 RFs fs; |
|
614 fs.Connect(); |
|
615 fs.SessionPath(session); |
|
616 fs.Close(); |
|
617 TChar drvch = User::UpperCase(session[0]); |
|
618 return drvch; |
|
619 } |
|
620 |
|
621 TTest::TPos::TPos(const char *aFile, TInt aLine) |
|
622 { |
|
623 iFailFile = aFile; |
|
624 iFailLine = aLine; |
|
625 } |
|
626 |