sl@0: // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: //! @file f32test\concur\t_tdebug.cpp sl@0: sl@0: #include sl@0: #include sl@0: #include "t_server.h" sl@0: #include "t_tdebug.h" sl@0: #include "cfafsdlyif.h" sl@0: sl@0: TThreadData TTest::iData[KMaxThreads]; sl@0: TThreadData TTest::iDummy; sl@0: TFullName TTest::iWhere; sl@0: RMutex TTest::iDebugLock; sl@0: RMutex TTest::iPrintLock; sl@0: TBool TTest::iInit = EFalse; sl@0: sl@0: LOCAL_C TFileName gErrorPos; sl@0: sl@0: // Instance of the class to force initialisation. sl@0: LOCAL_C TTest gTest; sl@0: sl@0: LOCAL_C TInt KSecond = 1000000; sl@0: sl@0: class TTestOverflowTruncate : public TDesOverflow sl@0: /// sl@0: /// Used to suppress overflow when appending formatted text to a buffer. sl@0: /// sl@0: { sl@0: public: sl@0: virtual void Overflow(TDes &/*aDes*/) {} sl@0: }; sl@0: sl@0: TTest::TTest() sl@0: // sl@0: // Constructor, forces initialisation of variables. sl@0: // sl@0: { sl@0: Init(); sl@0: } sl@0: sl@0: TInt TTest::Init() sl@0: /// sl@0: /// Initialise stuff (currently just the locks) if it hasn't been sl@0: /// done already. sl@0: /// sl@0: { sl@0: if (!iInit) sl@0: { sl@0: TInt r = KErrNone; sl@0: r = iDebugLock.CreateLocal(); sl@0: if (r != KErrNone) sl@0: { sl@0: RDebug::Print(_L("ERROR %d creating iDebugLock\n"), r); sl@0: return r; sl@0: } sl@0: r = iPrintLock.CreateLocal(); sl@0: if (r != KErrNone) sl@0: { sl@0: RDebug::Print(_L("ERROR %d creating iPrintLock\n"), r); sl@0: return r; sl@0: } sl@0: iInit = ETrue; sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt TTest::Create(TInt aNum, TThreadFunction aFunction, const TDesC& aName) sl@0: /// sl@0: /// Create a thread, setting up the name and our data area. sl@0: /// sl@0: { sl@0: if (aNum < 0 || aNum > KMaxThreads) sl@0: { sl@0: test.Printf(_L("Illegal thread %d\n"), aNum); sl@0: test(EFalse); sl@0: } sl@0: TThreadData &d = iData[aNum]; sl@0: // test.Printf(_L("creating thread %d (%S)\n"), aNum, &aName); sl@0: // d.iThread.LogonCancel(d.iStat); sl@0: // d.iThread.Close(); sl@0: TInt r; sl@0: r = d.iThread.Create(aName, aFunction, KDefaultStackSize+32*1024, KMinHeapSize, 0x20000, &d); sl@0: if (r != KErrNone) sl@0: { sl@0: TBuf<128> buf; sl@0: test.Printf(_L("Error creating thread %d '%S' (was %d '%S'): %S\n"), sl@0: aNum, &aName, d.iNum, &d.iName, &TTest::ErrStr(r, buf)); sl@0: test(0); sl@0: } sl@0: d.iId = d.iThread.Id(); sl@0: d.iNum = aNum; sl@0: d.iName = aName; sl@0: d.iThread.Logon(d.iStat); sl@0: return r; sl@0: } sl@0: sl@0: TInt TTest::RunOnly() sl@0: /// sl@0: /// Resume all of the threads we have created. sl@0: /// sl@0: { sl@0: TInt i; sl@0: for (i = 0; i < KMaxThreads; i++) sl@0: { sl@0: if (iData[i].iId > 0) sl@0: { sl@0: iData[i].iThread.Resume(); sl@0: } sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt TTest::Run(TBool aExitAny, TInt aTimeout) sl@0: /// sl@0: /// Run until all (or any one) threads has completed, or until a timeout. sl@0: /// @param aExitAny If true, exit when the first thread completes, otherwise sl@0: /// wait until they have all completed. sl@0: /// @param aTimeout if zero, no timeout, otherwise it is the timeout in microseconds. sl@0: /// sl@0: { sl@0: TInt i; sl@0: TInt status = RunOnly(); sl@0: RTimer timer; sl@0: TRequestStatus tstat; sl@0: timer.CreateLocal(); sl@0: if (aTimeout) sl@0: timer.After(tstat, aTimeout); sl@0: for (;;) sl@0: { sl@0: status = KErrNone; sl@0: User::WaitForAnyRequest(); sl@0: if (aTimeout > 0 && tstat != KRequestPending) sl@0: break; sl@0: TBool running = EFalse; sl@0: for (i = 0; i < KMaxThreads; i++) sl@0: { sl@0: if (iData[i].iId > 0) sl@0: { sl@0: if (iData[i].iStat == KRequestPending) sl@0: { sl@0: running = ETrue; sl@0: } sl@0: else sl@0: { sl@0: TThreadData &d = iData[i]; sl@0: // ignore result of LogonCancel, since we know thread has finished sl@0: d.iThread.LogonCancel(d.iStat); sl@0: d.iThread.Close(); sl@0: d.iId = 0; sl@0: if (d.iStat != KErrNone) sl@0: { sl@0: status = KErrAbort; sl@0: TBuf<32> ebuf; sl@0: test.Printf(_L("ERROR: %S in thread %S: %S\n %S"), sl@0: &ErrStr(d.iStat.Int(), ebuf), &d.iName, &d.iMess, &iWhere); sl@0: if (aExitAny) sl@0: { sl@0: running = EFalse; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: if (!running) sl@0: break; sl@0: } sl@0: timer.Cancel(); sl@0: timer.Close(); sl@0: return status; sl@0: } sl@0: sl@0: void TTest::KillAll(TInt aReason) sl@0: // sl@0: // Kill (destroy) all of the created threads, then wait for up to 10 seconds sl@0: // for them all to die (and just exit if any are still alive). sl@0: // sl@0: { sl@0: for (TInt i = 0; i < KMaxThreads; i++) sl@0: { sl@0: if (iData[i].iId > 0) sl@0: { sl@0: TThreadData &d = iData[i]; sl@0: d.iThread.Kill(aReason); sl@0: } sl@0: } sl@0: Run(EFalse, 10*KSecond); sl@0: } sl@0: sl@0: TThreadData& TTest::Self() sl@0: /// sl@0: /// Return a reference to the current thread; if it's not one we've created sl@0: /// return a reference to a dummy data area indicating no thread. sl@0: /// sl@0: { sl@0: RThread me; sl@0: TInt i; sl@0: for (i = 0; i < KMaxThreads; i++) sl@0: { sl@0: if (me.Id() == iData[i].iId) sl@0: { sl@0: return iData[i]; sl@0: } sl@0: } sl@0: iDummy.iId = 0; sl@0: iDummy.iNum = -1; sl@0: iDummy.iName.Format(_L("#%d"), (TUint)me.Id()); sl@0: return iDummy; sl@0: } sl@0: sl@0: TThreadData& TTest::Data(TInt aIndex) sl@0: /// sl@0: /// Return a reference to the data area for the specified thread, or to a sl@0: /// dummy area if it's not in the right range. sl@0: /// sl@0: /// @param aIndex index to the thread (ThreadData::iNum is the same number). sl@0: /// sl@0: { sl@0: if (aIndex >= 0 && aIndex < KMaxThreads) sl@0: return iData[aIndex]; sl@0: iDummy.iId = 0; sl@0: iDummy.iNum = -1; sl@0: iDummy.iName = _L(""); sl@0: return iDummy; sl@0: } sl@0: sl@0: void TTest::Start(const TDesC& aStr) sl@0: /// sl@0: /// Output "START TEST" and the string. sl@0: /// sl@0: { sl@0: Printf(_L("START TEST: %S\n"), &aStr); sl@0: } sl@0: sl@0: void TTest::Next(const TDesC& aStr) sl@0: /// sl@0: /// Output "NEXT TEST" and the string. sl@0: /// sl@0: { sl@0: Printf(_L("NEXT TEST: %S\n"), &aStr); sl@0: } sl@0: sl@0: void TTest::PrintLock() sl@0: /// sl@0: /// Wait if another task is doing output. sl@0: /// sl@0: { sl@0: iPrintLock.Wait(); sl@0: } sl@0: sl@0: void TTest::PrintUnlock() sl@0: /// sl@0: /// Signal that output is complete so that other tasks can do output. sl@0: /// sl@0: { sl@0: iPrintLock.Signal(); sl@0: } sl@0: sl@0: void TTest::Printf(TRefByValue aFmt, ...) sl@0: /// sl@0: /// Output the formatted text, prepending it with the thread name if it is one sl@0: /// we've created. Parameters as for printf(). Note that if more than one sl@0: /// thread tries to call it at the same time it will lock so that only one is sl@0: /// processed at a time, the debug output isn't thread-safe (it can mix sl@0: /// characters from different threads). sl@0: /// sl@0: { sl@0: TTestOverflowTruncate overflow; sl@0: VA_LIST list; sl@0: VA_START(list, aFmt); sl@0: TBuf<256> buf; sl@0: buf.SetLength(0); sl@0: if (Self().iNum >= 0) sl@0: { sl@0: buf.Append(Self().iName); sl@0: buf.Append(_L(": ")); sl@0: } sl@0: buf.AppendFormatList(aFmt, list, &overflow); sl@0: #if defined(__WINS__) sl@0: if (buf.Right(1) != _L("\n")) sl@0: buf.Append(_L("\n")); sl@0: #else sl@0: if (buf.Right(1) == _L("\n")) sl@0: buf.SetLength(buf.Length() - 1); sl@0: #endif sl@0: iDebugLock.Wait(); sl@0: RDebug::Print(_L("%S"), &buf); sl@0: iDebugLock.Signal(); sl@0: VA_END(list); sl@0: } sl@0: sl@0: void TTest::Printf() sl@0: /// sl@0: /// Output a blank line, prepended with the thread name if any. sl@0: /// sl@0: { sl@0: Printf(_L("\n")); sl@0: } sl@0: sl@0: void TTest::Fail(TPos aPos, TRefByValue aFmt, ...) sl@0: /// sl@0: /// Output an error message (formatted as for printf()), then exit the thread. sl@0: /// The message is placed in the buffer associated with the thread so that sl@0: /// the parent task can display it. sl@0: /// sl@0: { sl@0: VA_LIST list; sl@0: VA_START(list, aFmt); sl@0: Self().iMess.FormatList(aFmt, list); sl@0: iDebugLock.Wait(); sl@0: TPtrC8 ptr((TUint8*)aPos.iFailFile); sl@0: gErrorPos.Copy(ptr); sl@0: iWhere.Format(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); sl@0: RDebug::Print(_L("\n")); sl@0: RDebug::Print(_L("ERROR in thread %S: %S"), &Self().iName, &Self().iMess); sl@0: RDebug::Print(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); sl@0: RDebug::Print(_L("\n")); sl@0: iDebugLock.Signal(); sl@0: User::Exit(KErrAbort); sl@0: } sl@0: sl@0: void TTest::Fail(TPos aPos, TInt aErr, TRefByValue aFmt, ...) sl@0: /// sl@0: /// Output an error message including the interpreted error value followed sl@0: /// by the specified text (formatted as for printf()), then exit the thread. sl@0: /// The message is placed in the buffer associated with the thread so that sl@0: /// the parent task can display it. sl@0: /// sl@0: { sl@0: VA_LIST list; sl@0: VA_START(list, aFmt); sl@0: TBuf<32> ebuf; sl@0: ErrStr(aErr, ebuf); sl@0: Self().iMess.FormatList(aFmt, list); sl@0: iDebugLock.Wait(); sl@0: TPtrC8 ptr((TUint8*)aPos.iFailFile); sl@0: gErrorPos.Copy(ptr); sl@0: iWhere.Format(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); sl@0: RDebug::Print(_L("\n")); sl@0: RDebug::Print(_L("%S in thread %S: %S"), &ebuf, &Self().iName, &Self().iMess); sl@0: RDebug::Print(_L(" %S line %d\n"), &gErrorPos, aPos.iFailLine); sl@0: RDebug::Print(_L("\n")); sl@0: iDebugLock.Signal(); sl@0: User::Exit(aErr); sl@0: } sl@0: sl@0: TDesC& TTest::ErrStr(TInt aErr, TDes& aDes) sl@0: /// sl@0: /// Interpret an error status value as a string in the specified buffer. sl@0: /// If the value isn't recognised then it formats a string containing the sl@0: /// value itself (like "Error -65"). sl@0: /// @param aErr The error value. sl@0: /// @param aDes Descriptor of the buffer to be used. sl@0: /// @return Descriptor of the buffer. sl@0: /// sl@0: { sl@0: switch (aErr) sl@0: { sl@0: case KErrNone: sl@0: aDes = _L("KErrNone"); sl@0: break; sl@0: case KErrNotFound: sl@0: aDes = _L("KErrNotFound"); sl@0: break; sl@0: case KErrGeneral: sl@0: aDes = _L("KErrGeneral"); sl@0: break; sl@0: case KErrCancel: sl@0: aDes = _L("KErrCancel"); sl@0: break; sl@0: case KErrNoMemory: sl@0: aDes = _L("KErrNoMemory"); sl@0: break; sl@0: case KErrNotSupported: sl@0: aDes = _L("KErrNotSupported"); sl@0: break; sl@0: case KErrArgument: sl@0: aDes = _L("KErrArgument"); sl@0: break; sl@0: case KErrTotalLossOfPrecision: sl@0: aDes = _L("KErrTotalLossOfPrecision"); sl@0: break; sl@0: case KErrBadHandle: sl@0: aDes = _L("KErrBadHandle"); sl@0: break; sl@0: case KErrOverflow: sl@0: aDes = _L("KErrOverflow"); sl@0: break; sl@0: case KErrUnderflow: sl@0: aDes = _L("KErrUnderflow"); sl@0: break; sl@0: case KErrAlreadyExists: sl@0: aDes = _L("KErrAlreadyExists"); sl@0: break; sl@0: case KErrPathNotFound: sl@0: aDes = _L("KErrPathNotFound"); sl@0: break; sl@0: case KErrDied: sl@0: aDes = _L("KErrDied"); sl@0: break; sl@0: case KErrInUse: sl@0: aDes = _L("KErrInUse"); sl@0: break; sl@0: case KErrServerTerminated: sl@0: aDes = _L("KErrServerTerminated"); sl@0: break; sl@0: case KErrServerBusy: sl@0: aDes = _L("KErrServerBusy"); sl@0: break; sl@0: case KErrCompletion: sl@0: aDes = _L("KErrCompletion"); sl@0: break; sl@0: case KErrNotReady: sl@0: aDes = _L("KErrNotReady"); sl@0: break; sl@0: case KErrUnknown: sl@0: aDes = _L("KErrUnknown"); sl@0: break; sl@0: case KErrCorrupt: sl@0: aDes = _L("KErrCorrupt"); sl@0: break; sl@0: case KErrAccessDenied: sl@0: aDes = _L("KErrAccessDenied"); sl@0: break; sl@0: case KErrLocked: sl@0: aDes = _L("KErrLocked"); sl@0: break; sl@0: case KErrWrite: sl@0: aDes = _L("KErrWrite"); sl@0: break; sl@0: case KErrDisMounted: sl@0: aDes = _L("KErrDisMounted"); sl@0: break; sl@0: case KErrEof: sl@0: aDes = _L("KErrEof"); sl@0: break; sl@0: case KErrDiskFull: sl@0: aDes = _L("KErrDiskFull"); sl@0: break; sl@0: case KErrBadDriver: sl@0: aDes = _L("KErrBadDriver"); sl@0: break; sl@0: case KErrBadName: sl@0: aDes = _L("KErrBadName"); sl@0: break; sl@0: case KErrCommsLineFail: sl@0: aDes = _L("KErrCommsLineFail"); sl@0: break; sl@0: case KErrCommsFrame: sl@0: aDes = _L("KErrCommsFrame"); sl@0: break; sl@0: case KErrCommsOverrun: sl@0: aDes = _L("KErrCommsOverrun"); sl@0: break; sl@0: case KErrCommsParity: sl@0: aDes = _L("KErrCommsParity"); sl@0: break; sl@0: case KErrTimedOut: sl@0: aDes = _L("KErrTimedOut"); sl@0: break; sl@0: case KErrCouldNotConnect: sl@0: aDes = _L("KErrCouldNotConnect"); sl@0: break; sl@0: case KErrCouldNotDisconnect: sl@0: aDes = _L("KErrCouldNotDisconnect"); sl@0: break; sl@0: case KErrDisconnected: sl@0: aDes = _L("KErrDisconnected"); sl@0: break; sl@0: case KErrBadLibraryEntryPoint: sl@0: aDes = _L("KErrBadLibraryEntryPoint"); sl@0: break; sl@0: case KErrBadDescriptor: sl@0: aDes = _L("KErrBadDescriptor"); sl@0: break; sl@0: case KErrAbort: sl@0: aDes = _L("KErrAbort"); sl@0: break; sl@0: case KErrTooBig: sl@0: aDes = _L("KErrTooBig"); sl@0: break; sl@0: case KErrDivideByZero: sl@0: aDes = _L("KErrDivideByZero"); sl@0: break; sl@0: case KErrBadPower: sl@0: aDes = _L("KErrBadPower"); sl@0: break; sl@0: case KErrDirFull: sl@0: aDes = _L("KErrDirFull"); sl@0: break; sl@0: case KErrHardwareNotAvailable: sl@0: aDes = _L("KErrHardwareNotAvailable"); sl@0: break; sl@0: case KErrSessionClosed: sl@0: aDes = _L("KErrSessionClosed"); sl@0: break; sl@0: case KErrPermissionDenied: sl@0: aDes = _L("KErrPermissionDenied"); sl@0: break; sl@0: case KRequestPending: sl@0: aDes = _L("KRequestPending"); sl@0: break; sl@0: default: sl@0: aDes = _L("Error "); sl@0: aDes.AppendNum(aErr); sl@0: break; sl@0: } sl@0: return aDes; sl@0: } sl@0: sl@0: TInt TTest::ParseCommandArguments(TPtrC aArgV[], TInt aArgMax) sl@0: /// sl@0: /// Parse command line. Put the parameters into array aArgv for sl@0: /// use by the tests, strip out flags starting with / or - and interpret sl@0: /// them to set debug flags. sl@0: /// sl@0: { sl@0: RFs fs; sl@0: TInt r = fs.Connect(); sl@0: test(r == KErrNone); sl@0: TInt flags = 0; sl@0: TInt argc = ParseCommandArguments(aArgV, aArgMax, flags); sl@0: fs.SetDebugRegister(flags); sl@0: fs.Close(); sl@0: return argc; sl@0: } sl@0: sl@0: TInt TTest::ParseCommandArguments(TPtrC aArgV[], TInt aArgMax, TInt& aDebugFlags) sl@0: /// sl@0: /// Parse command line. Put the parameters into array aArgv for sl@0: /// use by the tests, strip out flags starting with / or - and interpret sl@0: /// them to set debug flags. sl@0: /// sl@0: { sl@0: LOCAL_D TBuf<0x100> cmd; sl@0: User::CommandLine(cmd); sl@0: TLex lex(cmd); sl@0: TPtrC token=lex.NextToken(); sl@0: TFileName thisfile=RProcess().FileName(); sl@0: if (token.MatchF(thisfile)==0) sl@0: { sl@0: token.Set(lex.NextToken()); sl@0: } sl@0: // set up parameter list (offset zero is the filename) sl@0: TInt argc = 0; sl@0: aArgV[argc++].Set(thisfile); sl@0: while (token.Length() != 0) sl@0: { sl@0: TChar ch = token[0]; sl@0: // strip out (and interpret) flags starting with - or / sl@0: if (ch == '-' || ch == '/') sl@0: { sl@0: for (TInt i = 1; i < token.Length(); i++) sl@0: { sl@0: switch (User::UpperCase(token[i])) sl@0: { sl@0: case 'D': sl@0: aDebugFlags |= KDLYFAST; sl@0: break; sl@0: case 'F': sl@0: aDebugFlags |= KFSYS; sl@0: break; sl@0: case 'I': sl@0: aDebugFlags |= KISO9660; sl@0: break; sl@0: case 'L': sl@0: aDebugFlags |= KFLDR; sl@0: break; sl@0: #ifdef __CONCURRENT_FILE_ACCESS__ sl@0: case 'M': sl@0: aDebugFlags |= KTHRD; sl@0: break; sl@0: #endif sl@0: case 'N': sl@0: aDebugFlags |= KNTFS; sl@0: break; sl@0: case 'S': sl@0: aDebugFlags |= KFSERV; sl@0: break; sl@0: case 'T': sl@0: aDebugFlags |= KLFFS; sl@0: break; sl@0: case 'Y': sl@0: aDebugFlags |= KDLYTRC; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: else if (argc < aArgMax) sl@0: aArgV[argc++].Set(token); sl@0: token.Set(lex.NextToken()); sl@0: } sl@0: return argc; sl@0: } sl@0: sl@0: TChar TTest::DefaultDriveChar() sl@0: { sl@0: TFileName session; sl@0: RFs fs; sl@0: fs.Connect(); sl@0: fs.SessionPath(session); sl@0: fs.Close(); sl@0: TChar drvch = User::UpperCase(session[0]); sl@0: return drvch; sl@0: } sl@0: sl@0: TTest::TPos::TPos(const char *aFile, TInt aLine) sl@0: { sl@0: iFailFile = aFile; sl@0: iFailLine = aLine; sl@0: } sl@0: