diff -r 000000000000 -r a41df078684a userlibandfileserver/fileserver/runtests/runtests.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/runtests/runtests.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,786 @@ +// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// f32\runtests\runtests.cpp +// +// + +#include +#include +#include +#include +#include +#include +#include "f32file.h" +#include "f32dbg.h" +#include "runtests.h" +#include + +#define __PANIC(r) Panic(__LINE__,r) + +//#define _RUN_FOREVER_ +//#define _PANIC_ON_FAILURE_ + +const TInt KDefaultTimeout=1200; // 20 minutes +const TInt KBackgroundTimeout=3000000; // 3 seconds +const TInt KDestroyTimeout=1000000; // 1 second + +_LIT(KLitBackslash,"\\"); +_LIT(KDebugMessage, "Testing finished, and the system will be panicked"); +#ifdef __EPOC32__ +_LIT(KLitDefaultTestPath,"Z:\\TEST"); +#else +_LIT(KLitDefaultTestPath,""); +#endif + +#ifdef _DEBUG +_LIT(KBuildType, "UDEB"); +#else +_LIT(KBuildType, "UREL"); +#endif + +typedef RArray RProcIdList; + +GLDEF_D TPath TheTestPath = KLitDefaultTestPath(); +GLDEF_D RFs TheFs; +GLDEF_D RLoader TheLoaderSession; +GLDEF_D TDesC8* TheTestList; +GLDEF_D RTimer TheTimer; +GLDEF_D TBuf<256> TheProcessCommand=KNullDesC(); +GLDEF_D TInt TheTimeOut = KDefaultTimeout; +GLDEF_D TInt TheCurrentProcessList; +GLDEF_D RProcIdList ProcLists[2]; +GLDEF_D RTimer IdleWaitTimer; +GLDEF_D RTimer DestructionTimer; +GLDEF_D RProperty CurrTest; +TBool ShowTimings = 0; +TInt TickPeriod = 15625; +TBool CleanUpProcesses = EFalse; + +#ifdef _ENABLE_BTRACE_ANALYSIS_ + +// BTrace analysis forward declarations +const TInt KDefaultBTraceLevel = 0; +TBool BtraceAnalysis = EFalse; +TInt BTraceAnalysisLevel; +TInt BTraceAnalyseSetup(); +void BTraceAnalyseEnd(); +void BTraceAnalyse(TInt aAnalysisLevel); + +#endif //_ENABLE_BTRACE_ANALYSIS_ + +_LIT(KLitPanicCategory,"RUNTESTS-"); +_LIT(KLitLogPreamble,"RUNTESTS: "); + +void LogMsg(TRefByValue aFmt,...); + +void DisableSimulatedFailure() + { + // Turn off simulated failure mechanisms for all base servers + TheFs.SetAllocFailure(KAllocFailureOff); // F32 heap failure + TheFs.SetErrorCondition(KErrNone, 0); // F32 other failure + TheLoaderSession.DebugFunction(ELoaderDebug_SetHeapFail, 0, 0, 0); // Loader heap failure + TheLoaderSession.DebugFunction(ELoaderDebug_SetRFsFail, KErrNone, 0, 0); // Loader RFs failure + + // make sure kernel heap debug is off + __KHEAP_TOTAL_RESET; + } + +GLDEF_C void Panic(TInt aLine, TInt aReason) + { + TBuf<16> cat=KLitPanicCategory(); + cat.AppendNum(aLine); + User::Panic(cat,aReason); + } + +TInt CloseAndWait(RHandleBase aH, TRequestStatus *aN = NULL) + { + TRequestStatus tempS; + if(!aN) + { + // Create a destruction notifier if none was supplied. + aH.NotifyDestruction(tempS); + aN = &tempS; + } + if (*aN!=KRequestPending) + { + User::WaitForRequest(*aN); + aH.Close(); + return KErrNoMemory; + } + TRequestStatus t; + DestructionTimer.After(t, KDestroyTimeout); + aH.Close(); + User::WaitForRequest(*aN, t); + if (*aN != KRequestPending) + { + DestructionTimer.Cancel(); + User::WaitForRequest(t); + return KErrNone; + } + User::CancelMiscNotifier(*aN); + User::WaitForRequest(*aN); + return KErrTimedOut; + } + +void CloseWaitAndWarn(RHandleBase aH, TRequestStatus *aN = NULL) + { + TFullName fn(aH.FullName()); + TInt r = CloseAndWait(aH, aN); + if (r == KErrNoMemory) + LogMsg(_L("WARNING OOM checking destruction of %S"), &fn); + else if (r == KErrTimedOut) + LogMsg(_L("ERROR Destruction of %S timed out"), &fn); + } + +TInt InitIdleWait() + { + TInt r = IdleWaitTimer.CreateLocal(); + if (r!=KErrNone) + return r; + return KErrNone; + } + +void WaitForIdle() + { + TRequestStatus idle_req; + TRequestStatus timer_req; + IdleWaitTimer.After(timer_req, KBackgroundTimeout); + User::NotifyOnIdle(idle_req); + User::WaitForRequest(idle_req, timer_req); + if (idle_req != KRequestPending) + { + IdleWaitTimer.Cancel(); + User::WaitForRequest(timer_req); + } + else + { + User::CancelMiscNotifier(idle_req); + User::WaitForRequest(idle_req); + LogMsg(_L("WARNING Excessive Background Activity Detected")); + } + } + +TBool IntentionallyPersistent(RProcess aProcess) + { + TInt v; + TInt r = RProperty::Get(aProcess.SecureId(), KRuntestsIntentionalPersistenceKey, v); + if (r==KErrNone && TUint(v)==KRuntestsIntentionalPersistenceValue) + return ETrue; + return EFalse; + } + +TInt GetProcessListThread(TAny* a) + { + RProcIdList& pl = *(RProcIdList*)a; + TFindProcess fp(_L("*")); + TFullName fn; + TInt r = KErrNone; + while (r==KErrNone && fp.Next(fn)==KErrNone) + { + RProcess p; + r = p.Open(fp, EOwnerThread); + if (r==KErrNone) + { + TUint id = (TUint)p.Id(); + r = pl.Append(id); + p.Close(); + } + } + return r; + } + +TInt GetProcessList(RProcIdList& aList) + { + aList.Reset(); + RThread t; + TRequestStatus s; + TInt r = t.Create(KNullDesC, &GetProcessListThread, 0x1000, NULL, &aList); + if (r==KErrNone) + { + t.Logon(s); + t.SetPriority(EPriorityAbsoluteHigh); + if (s==KRequestPending) + t.Resume(); + User::WaitForRequest(s); + r=s.Int(); + if (t.ExitType()==EExitPending) + { + t.Kill(0); + WaitForIdle(); + } + else if (t.ExitType()!=EExitKill) + { + r = -99; + } + CloseWaitAndWarn(t); + } + aList.Sort(); + return r; + } + +TBool ParseNumber(TLex& aLex, TUint& aNumber, TBool isTime) + { + TPtrC numberDes = aLex.NextToken(); + TInt len = numberDes.Length(); + if (len == 0) + { + return EFalse; + } + + aNumber = 0; + TInt magnitude = 1; + TChar c = numberDes[len-1]; + if (isTime) + { + switch (c) + { + case 'h': + case 'H': + len -= 1; + magnitude = 3600; + break; + + case 'm': + case 'M': + len -= 1; + /*FALLTHRU*/ + default: + magnitude = 60; + break; + + case 's': + case 'S': + len -= 1; + magnitude = 1; + break; + } + } + + for (TInt i = len-1; i >= 0; --i) + { + c = numberDes[i]; + if (c < '0' || c > '9') + __PANIC(KErrArgument); + aNumber += ((TInt)c-'0')*magnitude; + magnitude *= 10; + } + + return ETrue; + } + +void GetTimeOut(TLex& aLex) +// +// +// + { + TheTimeOut = KDefaultTimeout; + TUint timeOut = 0; + if (ParseNumber(aLex, timeOut, ETrue)) + { + TheTimeOut = timeOut; + } + } + +#ifdef _ENABLE_BTRACE_ANALYSIS_ + +void GetAnalysisLevel(TLex& aLex) + { + BTraceAnalysisLevel = KDefaultBTraceLevel; + TUint level; + if (ParseNumber(aLex, level, EFalse)) + { + BTraceAnalysisLevel = level; + } + } +#endif //_ENABLE_BTRACE_ANALYSIS_ + +void LogMsg(TRefByValue aFmt,...) + { + VA_LIST list; + VA_START(list,aFmt); + TBuf<0x100> buf=KLitLogPreamble(); + buf.AppendFormatList(aFmt,list); + RDebug::Print(_L("%S"),&buf); + } + +_LIT(KLitError, "Error "); +TBool LogProcess(TUint aId, TBool aInit) + { + TFullName pn; + TFileName fn; + RProcess p; + TBool killed = EFalse; + TInt r = p.Open(TProcessId(aId)); + if (r==KErrNone) + { + if (IntentionallyPersistent(p)) + { + p.Close(); + return killed; + } + pn = p.FullName(); + fn = p.FileName(); + if (!aInit && CleanUpProcesses && p.ExitType()==EExitPending) + {// p is a left over process so terminate it. + killed = ETrue; + TRequestStatus status; + p.Logon(status); + p.Kill(KErrNone); // Kill with KErrNone to suppress extra debug output from kernel. + User::WaitForRequest(status); + CloseAndWait(p); + } + else + { + p.Close(); + } + } + else + { + pn = KLitError; + pn.AppendNum(r); + } + if (aInit) + LogMsg(_L("Running process id=%d: %S (%S)"),aId,&pn,&fn); + else + { + if(killed) + LogMsg(_L("ERROR Leftover process was killed id=%d: %S (%S)"),aId,&pn,&fn); + else + LogMsg(_L("ERROR Leftover process id=%d: %S (%S)"),aId,&pn,&fn); + } + return killed; + } + +void ListProcesses() + { + RProcIdList& cur_list = ProcLists[TheCurrentProcessList]; + TInt cc = cur_list.Count(); + TInt ci; + for (ci=0; ci=2 && testname[0]=='/' && testname[1]=='/') + return; + // ignore this line if it begins with rem or @rem + if (testname.CompareF(KLitRemark) == 0 || testname.CompareF(KLitAtRemark) == 0) + return; + TFileName testnameU; + testnameU.Copy(testname); + TFileName fullpathname; + if (testnameU.Locate(TChar('\\'))==KErrNotFound) + fullpathname=TheTestPath; + fullpathname+=testnameU; + if (testname.Locate(TChar('.'))==KErrNotFound) + fullpathname+=_L(".EXE"); + TInt r; + + RFile file; + r=file.Open(TheFs,fullpathname,EFileRead); + if (r!=KErrNone) + { + // Remove path to let loader locate exe + fullpathname = fullpathname.Mid(fullpathname.LocateReverse('\\')+1); + } + else + file.Close(); + + RProcess p; + if(TheProcessCommand==KNullDesC) + { + TheProcessCommand.Copy(lex.Remainder()); + r=p.Create(fullpathname, TheProcessCommand); + TheProcessCommand=KNullDesC(); + } + else + r=p.Create(fullpathname, TheProcessCommand); + if (r!=KErrNone) + { + LogMsg(_L("Test %S ERROR Could not load file, error %d"),&fullpathname,r); + return; + } + TRequestStatus ds; + p.NotifyDestruction(ds); // allocate the destruction notifier early so that it doesn't get flagged as a leak by kernel heap checking in e.g., efile (DEF133800) + CurrTest.Set(p.FileName()); + User::After(100000); // allow latency measurements to be output + p.SetJustInTime(EFalse); // we don't want the automatic test run to be halted by the debugger + TRequestStatus ps; + p.Rendezvous(ps); + TInt time_remain = TheTimeOut; + TRequestStatus ts; + TUint start = User::TickCount(); + p.Resume(); + TBool persist = EFalse; + TBool timer_running = EFalse; + FOREVER + { + TInt nsec = Min(time_remain, 1800); + if (!timer_running) + TheTimer.After(ts, nsec*1000000); + timer_running = ETrue; + User::WaitForRequest(ps,ts); + if (ps!=KRequestPending) + { + if (p.ExitType()==EExitPending) + { + // rendezvous completed but process not terminated + if (!IntentionallyPersistent(p)) + { + // not persistent - wait for process to terminate + p.Logon(ps); + continue; + } + persist = ETrue; + } + break; + } + timer_running = EFalse; + time_remain -= nsec; + if (time_remain==0) + { + LogMsg(_L("Going to kill test %S: it's taken %u seconds, which is too long"),&fullpathname,TheTimeOut); + p.Kill(0); + User::WaitForRequest(ps); + p.Logon(ps); + User::WaitForRequest(ps); + + CloseWaitAndWarn(p, &ds); + RDebug::Print(_L("\n")); + LogMsg(_L("Test %S TIMEOUT"),&fullpathname); + return; + } + else + { + LogMsg(_L("Taken %u seconds so far"),TheTimeOut-time_remain); + } + } + TUint end = User::TickCount(); + if(timer_running) + { + TheTimer.Cancel(); + User::WaitForRequest(ts); + } + +#ifdef _ENABLE_BTRACE_ANALYSIS_ + // + // + // + if (BtraceAnalysis) + {// Analyse BTrace buffer + BTraceAnalyse(BTraceAnalysisLevel); + } +#endif //_ENABLE_BTRACE_ANALYSIS_ + + TBuf<32> exitCat=p.ExitCategory(); + TExitType exitType=p.ExitType(); + TInt exitReason=p.ExitReason(); + if (persist || (exitType==EExitKill && exitReason==KErrNone)) + { + TUint time = TUint((TUint64)(end-start)*(TUint64)TickPeriod/(TUint64)1000000); + if(ShowTimings) + { + LogMsg(_L("Test %S OK - Seconds Taken: %u"),&fullpathname, time); + } + else + { + LogMsg(_L("Test %S OK"),&fullpathname); + } + if (persist) + { + // We do not need this destruction notifier so cancel it. + User::CancelMiscNotifier(ds); + User::WaitForRequest(ds); + + p.Close(); + } + else + { + CloseWaitAndWarn(p, &ds); + } + return; + } + LogMsg(_L("Test %S FAIL - Exit code %d,%d,%S"),&fullpathname,exitType,exitReason,&exitCat); + CloseWaitAndWarn(p, &ds); +#if defined(_PANIC_ON_FAILURE_) + __PANIC(KErrGeneral); +#endif + } + +void ProcessTestList() + { + TUint start = User::TickCount(); + + TLex8 llex(*TheTestList); + while(!llex.Eos()) + { + llex.SkipSpace(); + llex.Mark(); + while(!llex.Eos() && llex.Peek()!='\n' && llex.Peek()!='\r') + llex.Inc(); + TPtrC8 line=llex.MarkedToken(); + if (line.Length()!=0) + ProcessLine(line); + + // allow cleanup to complete before starting the next test + WaitForIdle(); + + // make sure simulated failure is off + DisableSimulatedFailure(); + + // check for leftover processes + CheckProcesses(); + + // Reset the demand paging cache to its default size. + UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize, 0, 0); + } + + TUint end = User::TickCount(); + TUint time = TUint((TUint64)(end-start)*(TUint64)TickPeriod/(TUint64)1000000); + LogMsg(_L("Elapsed Seconds: %u"), time); + } + +void Help() + { + RDebug::Print(_L("Runtests test list [-x where tests are] [-d drive letter to test] [-t timeout] [-p] [-st] [-c]")); + RDebug::Print(_L("where -p sets runtests to be system permanent, -st enables timing information,")); + RDebug::Print(_L("-c enables left over processes to be cleaned up")); + } + +GLDEF_C TInt E32Main() + { + HAL::Get(HAL::ESystemTickPeriod, TickPeriod); + RThread().SetPriority(EPriorityAbsoluteHigh); + TBuf<0x100> cmd; + User::CommandLine(cmd); + TFileName thisfile=RProcess().FileName(); + TLex lex(cmd); + TPtrC token=lex.NextToken(); + if (token.MatchF(thisfile)==0) + { + token.Set(lex.NextToken()); + } + if (token.Length()==0) + {// __PANIC(0); + Help(); + return 0; + } + TFileName listfilename=token; + while (!lex.Eos()) + { + token.Set(lex.NextToken()); + if (token.Length()==0) + break; // ignore trailing whitespace + else if (token==_L("-x")) + { + token.Set(lex.NextToken()); + TheTestPath = token; + } + else if (token==_L("-d")) + { + token.Set(lex.NextToken()); + TheProcessCommand = token; + } + else if (token==_L("-t")) + { + GetTimeOut(lex); + } + else if (token==_L("-p")) + { + User::SetCritical(User::ESystemPermanent); + } + else if (token==_L("-st")) + ShowTimings = 1; + +#ifdef _ENABLE_BTRACE_ANALYSIS_ + else if (token == _L("-a")) + { + BtraceAnalysis = ETrue; + GetAnalysisLevel(lex); + TInt r = BTraceAnalyseSetup(); + if (r != KErrNone) + { + RDebug::Print(_L("ERROR - Couldn't open BTrace driver (Code %d)"), r); + return 0; + } + } +#endif //_ENABLE_BTRACE_ANALYSIS_ + + else if (token == _L("-c")) + CleanUpProcesses = ETrue; + else + { + RDebug::Print(_L("Unknown option %S"), &token); + Help(); + return 0; + } + } + + RDebug::Print(_L("TPTT= %S \n"), &TheProcessCommand); + RDebug::Print(_L("TTL= %S \n"), &listfilename); + RDebug::Print(_L("TTP= %S \n"), &TheTestPath); + RDebug::Print(_L("TO= %d seconds\n"), TheTimeOut); + + TInt l=TheTestPath.Length(); + if (l > 0 && TheTestPath[l-1]!='\\') + TheTestPath+=KLitBackslash; + if (listfilename.Locate(TChar('\\'))==KErrNotFound) + listfilename.Insert(0,TheTestPath); + TInt r=TheFs.Connect(); + if (r!=KErrNone) + __PANIC(r); + r = TheLoaderSession.Connect(); + if (r!=KErrNone) + __PANIC(r); + DisableSimulatedFailure(); + r=TheFs.SetSessionPath(_L("Z:\\test\\")); + if (r!=KErrNone) + __PANIC(r); + r=TheTimer.CreateLocal(); + if (r!=KErrNone) + __PANIC(r); + RFile listfile; + r=listfile.Open(TheFs,listfilename,EFileRead|EFileShareAny); + if (r!=KErrNone) + __PANIC(r); + TInt listfilesize; + r=listfile.Size(listfilesize); + if (r!=KErrNone) + __PANIC(r); + HBufC8* pL=HBufC8::New(listfilesize); + if (!pL) + __PANIC(KErrNoMemory); + TPtr8 ptr=pL->Des(); + TheTestList=pL; + r=listfile.Read(ptr); + if (r!=KErrNone) + __PANIC(r); + listfile.Close(); + LogMsg(_L("Running test script %S"),&listfilename); + LogMsg(_L("Build %S"),&KBuildType); + LogMsg(_L("Path to test %S"),&TheProcessCommand); + + r = RProperty::Define( KRuntestsCurrentTestKey, + RProperty::EText, + TSecurityPolicy(TSecurityPolicy::EAlwaysPass), + TSecurityPolicy(RProcess().SecureId()), + 512 + ); + if (r!=KErrNone && r!=KErrAlreadyExists) + __PANIC(r); + r = CurrTest.Attach(RProcess().SecureId(), KRuntestsCurrentTestKey); + if (r!=KErrNone) + __PANIC(r); + r = CurrTest.Set(KNullDesC); + if (r!=KErrNone) + __PANIC(r); + + r = DestructionTimer.CreateLocal(); + if (r!=KErrNone) + __PANIC(r); + TheCurrentProcessList = 0; + r = GetProcessList(ProcLists[0]); + if (r!=KErrNone) + __PANIC(r); + ListProcesses(); + r = InitIdleWait(); + if (r!=KErrNone) + __PANIC(r); +#if defined(_RUN_FOREVER_) + FOREVER +#endif + ProcessTestList(); + r = CurrTest.Set(KNullDesC); + if (r!=KErrNone) + __PANIC(r); + CurrTest.Close(); + User::After(1000000); // allow latency measurements to be output before exiting + LogMsg(_L("Completed test script %S"),&listfilename); + TheLoaderSession.Close(); + TheFs.Close(); + TheTimer.Close(); + IdleWaitTimer.Close(); + DestructionTimer.Close(); + +#ifdef _ENABLE_BTRACE_ANALYSIS_ + BTraceAnalyseEnd(); +#endif //_ENABLE_BTRACE_ANALYSIS_ + if(User::Critical()==User::ESystemPermanent) + RDebug::Print(KDebugMessage); + return KErrNone; + }