// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
// Example CTestStep derived implementation
//
/**
@file ParametersStep.cpp
@internalTechnology
*/
#include "parameterstep.h"
#include "tcoredumpserversuitedefs.h"
#include <e32property.h>
#include <crashdatasave.h>
#include <e32std.h>
#include <coredumpserverapi.h>
CParametersStep::~CParametersStep()
/**
* Destructor
*/
{
}
CParametersStep::CParametersStep()
/**
* Constructor
*/
{
// **MUST** call SetTestStepName in the constructor as the controlling
// framework uses the test step name immediately following construction to set
// up the step's unique logging ID.
SetTestStepName(KParametersStep);
}
TVerdict CParametersStep::doTestStepPreambleL()
/**
* @return - TVerdict code
* Override of base class virtual
*/
{
TInt ret = KErrNone;
ret = iSess.Connect();
if(ret != KErrNone)
{
SetTestStepResult( EFail );
ERR_PRINTF2(_L("Error %d from iSess->Connect()"), ret);
}
else
{
SetTestStepResult(EPass);
}
INFO_PRINTF1(_L("Successfully connected session to the Core Dump Server"));
ret = iSecSess.Connect(securityServerVersion);
if(ret != KErrNone)
{
SetTestStepResult( EFail );
ERR_PRINTF2(_L("Error %d from iSecSess->Connect()"), ret);
}
else
{
SetTestStepResult(EPass);
}
INFO_PRINTF1(_L("Successfully connected session to the debug security server"));
ret = iFs.Connect();
if(ret != KErrNone)
{
SetTestStepResult( EFail );
ERR_PRINTF2(_L("Error %d from iFs->Connect()"), ret);
}
else
{
SetTestStepResult(EPass);
}
INFO_PRINTF1(_L("Successfully connected session to the file server"));
return TestStepResult();
}
TVerdict CParametersStep::doTestStepL()
/**
* @return - TVerdict code
* Override of base class pure virtual
* Our implementation only gets called if the base class doTestStepPreambleL() did
* not leave. That being the case, the current test result value will be EPass.
*/
{
if (TestStepResult()==EPass)
{
TInt ret = KErrNone;
__UHEAP_MARK;
TRAP(ret, ClientAppL());
if(KErrNone != ret)
{
SetTestStepResult(EFail);
INFO_PRINTF2(_L("Error %d from CParametersStep->ClientAppL()"), ret);
}
//Make sure its dead
_LIT(KCrashApp, "crash");
KillCrashAppL(KCrashApp);
__UHEAP_MARKEND;
}
return TestStepResult();
}
void CParametersStep::ClientAppL()
/**
* @return void
* This calls each stage of the test
*/
{
INFO_PRINTF1(_L("Starting CDS Parameters Test Suite") );
INFO_PRINTF1(_L("This tests two scenarios: When the main thread crashes and when a child thread crashes"));
INFO_PRINTF1(_L("In each case we test all possible CDS config values to make sure that the correct things are happening \n\n"));
//Here we are testing the post processing conditions when a main thread crashes and when a child thread crashes
CrashChildThreadAndTestPostProcL();
CrashMainThreadAndTestPostProcL();
//Now we test the preprocessing values
CrashChildThreadAndTestPreProcL();
}
void CParametersStep::CrashChildThreadAndTestPreProcL()
/**
* @return void
* Tests the pre processing values do the right thing when we crash a child thread
*/
{
if (TestStepResult()==EPass)
{
INFO_PRINTF1(_L("Testing all pre proccessing options for when child thread crashes"));
_LIT(KCrashAppParams, "-m1 -d2 -c2"); //Will have one child thread that crashes after 2 sec
//1 = suspend thread, 2 = suspend process
for(TInt preVal = 1; preVal <3; preVal++ )
{
INFO_PRINTF3(_L("************* New Crash (Main thread) CDS PreProc val = %d Post Proc val = %d ************* "), preVal, 0);
//Call crash app
GenerateAndHandleCrashL(KCrashAppParams, 0, preVal);
TProcessId pid = IsCrashAppAliveL();
if(preVal == 1)
{
//Here the child should be frozen and main thread still running
if(IsCrashAppMainThreadRunningL())
{
INFO_PRINTF1(_L("Main thread running as expected"));
}
else
{
SetTestStepError(EFail);
ERR_PRINTF1(_L("Main thread is expected to be running and it is not"));
User::Leave(KErrGeneral);
}
}
else if(preVal == 2)
{
if(!IsCrashAppMainThreadRunningL())
{
INFO_PRINTF1(_L("Main thread frozen as expected"));
}
else
{
SetTestStepError(EFail);
ERR_PRINTF1(_L("Main thread is expected to be frozen and is not")) ;
User::Leave(KErrGeneral);
}
}
//Disconnect the CDS' handle on this exe
if(pid)
{
iSess.ObservationRequestL( KCrashAppFileName, KCrashAppFileName, EFalse);
INFO_PRINTF1(_L("Attaching DSS to executable"));
User::LeaveIfError(iSecSess.AttachExecutable(KCrashAppFileName, EFalse));
ResumeProcessL(pid);
User::LeaveIfError(iSecSess.DetachExecutable(KCrashAppFileName));
_LIT(KCrashApp, "crash");
KillCrashAppL(KCrashApp);
User::After(2000000);
if(IsCrashAppAliveL())
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Failed to kill app"));
User::Leave(KErrGeneral);
}
}
}
}
}
void CParametersStep::CrashMainThreadAndTestPostProcL()
/**
* @return void
* Tests the CDS post processing when the main thread of the crash app dies.
* In all cases the app should be dead except when we do not resume the process (option 0)
*/
{
if (TestStepResult()==EPass)
{
TInt cdsPreConfigVal = 2; //Suspend process (this is irrelevant really, we will check this seperately)
INFO_PRINTF1(_L("Testing all post proccessing options for when main thread crashes (in all cases crash app should be gone)"));
INFO_PRINTF1(KPostCrashEventActionPrompt);
_LIT(KCrashAppParams, "-s -d1 -c2"); //Will have a second thread to main, and main will crash after 1 second
TInt cdsPostConfigVal = 8;
do
{
//Right shift 1 to cycle 4,2,1,0
cdsPostConfigVal>>=1;
INFO_PRINTF3(_L("************* New Crash (Main thread) CDS PreProc val = %d Post Proc val = %d ************* "), cdsPreConfigVal, cdsPostConfigVal);
//Call crash app and handling with range of post processing values (0,1,2,4 inc)
GenerateAndHandleCrashL(KCrashAppParams, cdsPostConfigVal, cdsPreConfigVal);
//Let the process be resumed or killed or whatever
User::After( 4500000 );
//Now we validate the post processing values
TProcessId pid = IsCrashAppAliveL();
if(pid)
{
//If post proc = 0 (suspend) it should still be alive. we need to resume it all so we can kill it
if(cdsPostConfigVal == 0)
{
INFO_PRINTF1(_L("CDS configured not to resume process and it is still alive as expected"));
//Disconnect the CDS' handle on this exe
iSess.ObservationRequestL( KCrashAppFileName, KCrashAppFileName, EFalse);
INFO_PRINTF1(_L("Attaching DSS to executable"));
User::LeaveIfError(iSecSess.AttachExecutable(KCrashAppFileName, EFalse));
ResumeProcessL(pid);
User::LeaveIfError(iSecSess.DetachExecutable(KCrashAppFileName));
}
else
{
RDebug::Printf("Error is here ****************");
SetTestStepError(EFail);
ERR_PRINTF2(_L("Expected crash app to be dead and it wasn't. Crash was in main thread and CDS post processing was configured at %d"), cdsPostConfigVal);
User::Leave(KErrGeneral);
}
}
else if(cdsPostConfigVal ==0)
{
SetTestStepError(EFail);
ERR_PRINTF2(_L("Expected crash app to be alive and it wasn't. Crash was in main thread and CDS post processing was configured at %d"), cdsPostConfigVal);
User::Leave(KErrGeneral);
}
else
{
INFO_PRINTF2(_L("Crash app died as expected with post proc param %d"), cdsPostConfigVal);
}
_LIT(KCrashApp, "crash");
KillCrashAppL(KCrashApp);
User::After(3000000 );
if(IsCrashAppAliveL())
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Failed to kill app"));
User::Leave(KErrGeneral);
}
}
while(cdsPostConfigVal > 0);
}
}
void CParametersStep::CrashChildThreadAndTestPostProcL()
/**
* @return void
* Tests the CDS post processing when the child thread of the crash app dies. The pre processing
* step is suspend process
*/
{
if (TestStepResult()==EPass)
{
TInt cdsPreConfigVal = 2; //Suspend process (this is irrelevant really, we will check this seperately)
INFO_PRINTF1(_L("Testing all post proccessing options for when main thread crashes (in all cases crash app should be gone)"));
INFO_PRINTF1(KPostCrashEventActionPrompt);
_LIT(KCrashAppParams, "-m1 -d1 -c2"); //Will have one child thread that crashes after 1 sec
TInt cdsPostConfigVal = 8;
do
{
//Right shift 1 to cycle 4,2,1,0
cdsPostConfigVal>>=1;
INFO_PRINTF3(_L("************* New Crash (Child thread) CDS PreProc val = %d Post Proc val = %d ************* "),cdsPreConfigVal, cdsPostConfigVal);
//Call crash app and handling with range of post processing values (0,1,2,4 inc)
GenerateAndHandleCrashL(KCrashAppParams, cdsPostConfigVal, cdsPreConfigVal);
//Wait for crashapp to be resumed or killed or whatever
User::After( 1000000 );
TProcessId pid = IsCrashAppAliveL();
if(cdsPostConfigVal == 0)//do nothing
{
//In this case the whole process is suspended
if(pid && !IsCrashAppMainThreadRunningL())
{
INFO_PRINTF1(_L("Crash app alive and main thread suspended as expected"));
}
else
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Crash app was dead - not expected"));
User::Leave(KErrGeneral);
}
}
else if(cdsPostConfigVal == 1)//resume thread
{
//Only thread is resumed, not whole process
if(pid && !IsCrashAppMainThreadRunningL())
{
INFO_PRINTF1(_L("Crash app alive and main thread suspended as expected"));
}
else
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Crash app was dead - not expected"));
User::Leave(KErrGeneral);
}
}
else if(cdsPostConfigVal == 2)//resume proc
{
//Only thread is resumed, not whole process
if(pid && IsCrashAppMainThreadRunningL())
{
INFO_PRINTF1(_L("Crash app alive and main thread running as expected"));
}
else
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Crash app main thread not running - not expected"));
User::Leave(KErrGeneral);
}
}
else if(cdsPostConfigVal == 4)//kill proc
{
if(pid)
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Process is still alive but should be dead"));
User::Leave(KErrGeneral);
}
else
{
INFO_PRINTF1(_L("Process dead as expected"));
}
}
//Kill the process if its still running
if(pid)
{
//Disconnect the CDS' handle on this exe
iSess.ObservationRequestL( KCrashAppFileName, KCrashAppFileName, EFalse);
INFO_PRINTF1(_L("Attaching DSS to executable"));
User::LeaveIfError(iSecSess.AttachExecutable(KCrashAppFileName, EFalse));
ResumeProcessL(pid);
User::LeaveIfError(iSecSess.DetachExecutable(KCrashAppFileName));
_LIT(KCrashApp, "crash");
KillCrashAppL(KCrashApp);
User::After(2000000);
if(IsCrashAppAliveL())
{
SetTestStepResult( EFail );
ERR_PRINTF1(_L("Failed to kill app"));
User::Leave(KErrGeneral);
}
}
}
while(cdsPostConfigVal > 0);
}
}
void CParametersStep::ResumeProcessL(TUint64 aPid)
/**
Called to resume all the threads of the specified process. CCoreCrashHandler executes it as a post-processing action.
@param aPid ID of process to be resumed
@leave err one of the system wide error codes
iSecSess must be attached to the executable in question
*/
{
INFO_PRINTF1(_L("Resuming process"));
//Get the threads we wish to resume
RThreadPointerList threadList;
TCleanupItem cleanupProcesses(CTe_coredumpserverSuite::CleanupThreadList, (TAny*)&threadList);
CleanupStack::PushL(cleanupProcesses);
iSess.GetThreadsL(threadList, aPid);
TInt err = KErrNone;
//first resume child threads
TInt looperr = KErrNone;
for(TInt j = 1; j < threadList.Count(); j++)
{
looperr = iSecSess.ResumeThread( threadList[j]->Id() );
if( (looperr != KErrNone) && (looperr != KErrNotFound) )
{
err = looperr;
ERR_PRINTF2(_L("CParametersStep::ResumeProcessL - unable to resume thread! err:%d"), err);
}
}
looperr = KErrNone;
//then resume the main thread
if(threadList.Count())
{
INFO_PRINTF2(_L("resuming main thread:%Ld\n"), threadList[0]->Id());
looperr = iSecSess.ResumeThread(threadList[0]->Id());
}
if( (looperr != KErrNone) && (looperr != KErrNotFound) )
{
err = looperr;
ERR_PRINTF2(_L("CCoreDumpSession::ResumeProcessL - unable to resume main thread! err:%d"), err);
}
CleanupStack::PopAndDestroy(&threadList);
User::LeaveIfError(err);
}
TProcessId CParametersStep::IsCrashAppAliveL()
/**
* @return TInt PID of process if alive, null if not
* Scans the process list looking for crash app. Returns true if its found
*/
{
TProcessId pid = NULL;
RProcessPointerList procList;
TCleanupItem cleanupProcesses(CTe_coredumpserverSuite::CleanupProcessList, (TAny*)&procList);
CleanupStack::PushL(cleanupProcesses);
iSess.GetProcessesL( procList );
for(TInt i=0; i < procList.Count(); i++ )
{
if(procList[i]->Name() == KCrashAppFileName)
{
INFO_PRINTF1(_L("Found the crash app"));
pid = procList[i]->Id();
}
}
CleanupStack::PopAndDestroy(&procList);
return pid;
}
void CParametersStep::KillCrashAppL(const TDesC& aProcessName)
/**
* @return void
* Kills specified process
*/
{
_LIT(KAsterisk, "*");
HBufC* buf = HBufC::NewLC(1+aProcessName.Length());
TPtr bufPtr = buf->Des();
bufPtr.Append(aProcessName);
bufPtr.Append(KAsterisk);
TFindProcess finder(bufPtr);
TFullName fullname;
RProcess process;
while(finder.Next(fullname) == KErrNone)
{
TInt err = process.Open(finder);
if(err == KErrNone)
{
INFO_PRINTF1(_L("Killing process"));
INFO_PRINTF1(process.Name());
process.Kill(KErrNone);
process.Close();
}
else
{
ERR_PRINTF2(_L("Failed to kill process. Error = %d "), err);
}
}
CleanupStack::PopAndDestroy(buf);
}
void CParametersStep::GenerateAndHandleCrashL(const TDesC& aCrashAppParameters,
const TUint& aCDSPostProcParam,
const TUint& aCDSPreProcParam)
/**
* @return void
* This will:
* 1. Load Formatter
* 2. Load writer
* 3. Configure CDS to post process after crash
* 4. Observe the crash process
* 5. Start the crash process
* 6. Monitor the crash progress until its dead
*/
{
//Start the process that we intend to crash....
RProcess crashProcess;
CleanupClosePushL(crashProcess);
TInt ret = crashProcess.Create( KCrashAppFileName, aCrashAppParameters);
if(ret != KErrNone)
{
ERR_PRINTF2(_L("Error %d from RProcess .Create(z:\\sys\\bin\\crashapp.exe)/n"), ret);
SetTestStepResult(EFail);
User::Leave(ret);
}
//Now we configure CDS and writer
INFO_PRINTF2(_L("Configuring pre processing of CDS with %d"), aCDSPreProcParam);
DoConfigureL(EPreCrashEventAction, CDS_UID.iUid, COptionConfig::ECoreDumpServer, COptionConfig::ETUInt, KNullDesC,
1, KNullDesC, aCDSPreProcParam, KNullDesC);
INFO_PRINTF2(_L("Configuring post processing of CDS with %d"), aCDSPostProcParam);
DoConfigureL(EPostCrashEventAction, CDS_UID.iUid, COptionConfig::ECoreDumpServer, COptionConfig::ETUInt, KNullDesC,
1, KNullDesC, aCDSPostProcParam, KNullDesC );
INFO_PRINTF1(_L("Observing process"));
iSess.ObservationRequestL( KCrashAppFileName, KCrashAppFileName, ETrue);
crashProcess.Resume();
CleanupStack::PopAndDestroy(&crashProcess);
//This waits for the formatting to complete
MonitorProgressL();
}
void CParametersStep::MonitorProgressL()
/**
* @return void
* This method monitors the RProperty for the crash progress. There are several states of this but we are
* only interested in the start and finish of these properties
*/
{
RProperty crashProgress;
User::LeaveIfError(crashProgress.Attach(KCoreDumpServUid, ECrashProgress));
TBuf<50> crashProg;
TRequestStatus status;
crashProgress.Subscribe(status);
INFO_PRINTF1(_L("CDS has started processing"));
//Now we wait until its finished
do
{
User::WaitForRequest(status);
crashProgress.Subscribe(status);
User::LeaveIfError(crashProgress.Get(crashProg ));
LOG_MSG("Found property and it was:");
}
while(crashProg != KEndOfProcessing);
INFO_PRINTF1(_L("CDS has finished processing"));
}
TInt CParametersStep::GetNumberOfProccessesL()
/**
* @return TInt
* Utility function to get the number of proccesses running
*/
{
RProcessPointerList procList;
TCleanupItem cleanupProcesses(CTe_coredumpserverSuite::CleanupProcessList, (TAny*)&procList);
CleanupStack::PushL(cleanupProcesses);
iSess.GetProcessesL(procList);
TInt numProcs = procList.Count();
CleanupStack::PopAndDestroy();
return numProcs;
}
TInt CParametersStep::GetNumberThreadsL()
/**
* @return TInt
* Utility function to get the number of proccesses running
*/
{
RThreadPointerList threadList;
TCleanupItem cleanupProcesses(CTe_coredumpserverSuite::CleanupThreadList, (TAny*)&threadList);
CleanupStack::PushL(cleanupProcesses);
iSess.GetThreadsL(threadList);
TInt numThreads = threadList.Count();
CleanupStack::PopAndDestroy();
return numThreads;
}
void CParametersStep::DoConfigureL(const TUint32& aIndex,
const TUint32& aUID,
const COptionConfig::TParameterSource& aSource,
const COptionConfig::TOptionType& aType,
const TDesC& aPrompt,
const TUint32& aNumOptions,
const TDesC& aOptions,
const TInt32& aVal,
const TDesC& aStrValue )
/**
* @return void
* @param aIndex Internal index to the component that owns the object
* @param aUID UID of the component that owns the object
* @param aSource Type of component that owns the object
* @param aType Type of parameter
* @param aPrompt Prompt to present to user
* @param aNumOptions Number of options that the parameter can be set to. Only applies if type is ETMultiEntryEnum.
* @param aOptions Comma separated list of options. Applies to ETMultiEntryEnum and ETBool
* @param aVal Integer value. Applies to ETInt, ETUInt, ETBool
* @param aStrValue String value. Applies to ETString, ETFileName, ETMultiEntry, ETBool
*/
{
COptionConfig * config;
config = COptionConfig::NewL( aIndex,
aUID,
aSource,
aType,
aPrompt,
aNumOptions,
aOptions,
aVal,
aStrValue);
CleanupStack::PushL(config);
//Configure now...
TRAPD(ret, iSess.SetConfigParameterL(*config) );
if(ret != KErrNone)
{
ERR_PRINTF2(_L("Error %d changing param"), ret );
SetTestStepResult(EFail);
User::Leave(ret);
}
CleanupStack::PopAndDestroy(config);
}
TBool CParametersStep::IsCrashAppMainThreadRunningL()
/**
* Reads the main threads property to make sure its being updated
*/
{
TInt mainThreadCounter1 = 0;
TInt mainThreadCounter2 = 0;
User::LeaveIfError(RProperty::Get(KCrashAppUid, 0, mainThreadCounter1));
User::After(1500000);
User::LeaveIfError(RProperty::Get(KCrashAppUid, 0, mainThreadCounter2));
return (mainThreadCounter2 > mainThreadCounter1);
}
TVerdict CParametersStep::doTestStepPostambleL()
/**
* @return - TVerdict code
* Override of base class virtual
*/
{
iSess.Disconnect();
iSecSess.Close();
iFs.Close();
User::After(5000000); //ensure we give enough time for session to close
return TestStepResult();
}