/**
Allocates and constructs a CProcessInfo object.
@param aId Kernel process id
@param aName Kernel process name
@see CProcessInfo::CProcessInfo
*/
EXPORT_C CProcessInfo* CProcessInfo::NewL( 
										const TUint64	  aId,
										const TDesC		& aName )
	{

	const TUint size = 
		  sizeof( TUint32 )		// iId Low
		+ sizeof( TUint32 )		// iId High
		+ sizeof( TUint32 )		// When externalized, we send the name length, so must include this
		+ 2						// When externalized, the << operator writes 2 bytes for the descriptor size
		+ aName.Size()			// iName Size, in bytes.
		+ sizeof( TUint32 ) 	// Observed
		+ sizeof( TUint32 );	// iSize itself

	if( size >= MaxSize() )
		{
		LOG_MSG3( "CProcessInfo::NewL() : Descriptorized object = 0x%X bytes would exceed the maximum of 0x%X\n", 
			size, MaxSize() );

		User::Leave( KErrTooBig );
		}

	CProcessInfo * data = new (ELeave) CProcessInfo( aId );

	CleanupStack::PushL( data );
	
	data->ConstructL( aName );

	CleanupStack::Pop(data);

	return (data);

	}
/**
Purpose:
Read memory from the security server. If the length of the data is greater
than the maximum length supported by the security server (as reported debug
functionality block tag EMemoryMaxBlockSize), break up the read into reads 
of this maximum size.

@pre Must be connected to the debug security server
@pre Must be attached to the process that owns aThreadId

@param aThreadId - The id of the thread relative to which the read should take place
@param aAddress - The virtual address to read from
@param aLength - The number of bytes to read
@param aData - The buffer to read into
@return Any error which may be returned by Security Server::ReadMemory()
*/
void CServerCrashDataSource::ReadMemoryL(
                   const TUint64    aThreadId,
                   const TUint32    aAddress,
                   const TUint32    aLength,
                   TDes8          & aData )
	{
    LOG_MSG4("->CServerCrashDataSource::ReadMemoryL(aThreadId=0x%X, aAddress=0x%X, aLength=%d)\n",
            I64LOW(aThreadId), aAddress, aLength);	
    TInt err;

	if( aLength <= iMaxMemReadSize )
		{
		err = iSecSess.ReadMemory( aThreadId, aAddress, aLength, aData, EAccess32, EEndLE8);
		}
    else
        {
        // If aLength is greater than iMaxMemReadSize, then break up the read into 
        // smaller packets and append them to aData

        RBuf8 readData;
        readData.CreateL( iMaxMemReadSize );

        TInt readLength = iMaxMemReadSize;
        TInt readSoFar = 0;
        do
            {
            LOG_MSG3( "  iSecSess->ReadMemory(addr=0x%X, length=%d)\n", aAddress+readSoFar, readLength );
            err = iSecSess.ReadMemory( aThreadId, aAddress+readSoFar, readLength, readData, EAccess32, EEndLE8 );
            if(err != KErrNone)
                break;

            readSoFar += readLength;
            aData.Append( readData );
            if( (aLength - readSoFar) < iMaxMemReadSize )
                {
                readLength = aLength - readSoFar;
                }
            }
        while( readSoFar < aLength );
        readData.Close();
        }
    User::LeaveIfError(err);
	}
void CServerCrashDataSource::GetCodeSegmentsL( const TUint64 aTid, RCodeSegPointerList &aCodeSegList, TUint & aTotalCodeSegListDescSize )
	{
	LOG_MSG2("->CServerCrashDataSource::GetCodeSegmentsL(aTid=%Lu)\n", aTid );

    aCodeSegList.ResetAndDestroy();
    aTotalCodeSegListDescSize = 0;

	TUint32 size = KMaxFileName;
	RBuf8 buffer;
    buffer.CreateL(KMaxFileName);
    CleanupClosePushL(buffer);

	DoGetListL( ECodeSegs, aTid, (TUint)-1, buffer, size );
	LOG_MSG2( " DoGetListL( ECodeSegs ) returned buffer.Size()=0x%X\n", buffer.Size() );

	TUint8* ptr = (TUint8*)buffer.Ptr();
	const TUint8* ptrEnd = ptr + size;

	while(ptr < ptrEnd)
		{
		TCodeSegListEntry* entry = (TCodeSegListEntry*)ptr;

		LOG_MSG4( "  entry->CodeBase=0x%X, CodeSize=0x%X, ConstDataSize=0x%X\n", 
			entry->iCodeBase, entry->iCodeSize, entry->iConstDataSize );
		LOG_MSG4( "  InitDataBase=0x%X, InitDataSize=0x%X, UnintDataSize=0x%X\n", 
			entry->iInitialisedDataBase, entry->iInitialisedDataSize, entry->iUninitialisedDataSize );
		LOG_MSG3( "  IsXip=0x%X, CodeSegType=0x%X\n", entry->iIsXip, entry->iCodeSegType );
		
        TCodeSegInfo *codeSeg = new(ELeave) TCodeSegInfo;

		TPtr name(&(entry->iName[0]), entry->iNameLength, entry->iNameLength);
        codeSeg->iName = name;	

        codeSeg->iType = entry->iCodeSegType;
        codeSeg->iXIP = entry->iIsXip;

        codeSeg->iCodeSize = entry->iCodeSize;
        codeSeg->iCodeRunAddr = entry->iCodeBase;
        if( codeSeg->iXIP )
            {
            codeSeg->iCodeLoadAddr = codeSeg->iCodeRunAddr;
            }
        else
            {
            codeSeg->iCodeLoadAddr = 0; //TODO
            }

        codeSeg->iRoDataSize = entry->iConstDataSize;
        codeSeg->iRoDataRunAddr = entry->iCodeBase + entry->iCodeSize;
        if( codeSeg->iXIP )
            {
            codeSeg->iRoDataLoadAddr = codeSeg->iRoDataRunAddr;
            }
        else
            {
            codeSeg->iRoDataLoadAddr = 0; //TODO
            }

        codeSeg->iDataSize = entry->iInitialisedDataSize + entry->iUninitialisedDataSize;
        codeSeg->iDataRunAddr = entry->iInitialisedDataBase;
        if( codeSeg->iXIP )
            {
            codeSeg->iDataLoadAddr = codeSeg->iDataRunAddr;
            }
        else
            {
            codeSeg->iDataLoadAddr = 0; //TODO
            }

        TInt err = aCodeSegList.Append(codeSeg);
        if(err != KErrNone)
            {
            delete codeSeg;
            User::Leave(err);
            }

		aTotalCodeSegListDescSize += sizeof(TCodeSegInfo);
        ptr += Align4(entry->GetSize());
		}

    CleanupStack::PopAndDestroy(&buffer);
	}
/**
 * Read command line parameters and control the launching of the agents. 
 */
void MainL()
	{	
	LOG_MSG( "ENTER: t_multi_agent_launcher MainL()");

	TInt ret = KErrNone;
	TInt numAgents = KNumAgents;
	TInt numTargets = KNumTargets;
	TInt numTestRuns = KNumTestRuns;

	TInt argc = User::CommandLineLength();
	HBufC* commandLine = NULL;
	LOG_MSG2("t_multi_agent_launcher: MainL(): argc=%d", argc);
    
	if(argc)
		{
		commandLine = HBufC::NewLC(argc);
		TPtr commandLineBuffer = commandLine->Des();
		User::CommandLine(commandLineBuffer);

		RBuf printCommandLine;
		CleanupClosePushL( printCommandLine );
		printCommandLine.CreateL( commandLine->Des().Length() );
		printCommandLine.Copy( commandLine->Des() );
		printCommandLine.Collapse();
		LOG_MSG2("t_multi_agent_launcher: command line = %S", &printCommandLine);
		CleanupStack::PopAndDestroy( &printCommandLine );
 
		// create a lexer and read through the command line
		TLex lex(*commandLine);
		while (!lex.Eos())
			{
			// only look for options with first character '-'
			if (lex.Get() == '-')
				{
					TChar arg = lex.Get();
					switch ( arg )
						{
						case 'n':
							lex.Val( numAgents );
							LOG_MSG2("t_multi_agent_launcher: parsed numAgents as %d", numAgents);
							break;
						case 'm':
							lex.Val( numTargets );
							LOG_MSG2("t_multi_agent_launcher: parsed numTargets as %d", numTargets);                        
							break;  
						case 't':
							lex.Val( numTestRuns );
							LOG_MSG2("t_multi_agent_launcher: parsed numTestRuns as %d", numTestRuns);                        
							break;                    
						default:
							LOG_MSG("t_multi_agent_launcher: unknown argument ignoring it");
							break;                 
						}
				}
			}
		}

	// Note: below is a workaround to overcome an issue with RTest server crashing 
	// when writing to the windows console from different agents (on different CPUs 
	// at the same time). To overcome this we get signaled by the agents when they have 
	// completed their tests so that we can do a RTest complete
	RSemaphore launchSemaphore;
	CleanupClosePushL(launchSemaphore);
	ret = launchSemaphore.CreateGlobal(KLaunchSemaphoreName, 0);
	LOG_MSG2( ">Target Launcher : RSemaphore.CreateGlobal ret %d", ret);
	User::LeaveIfError( ret );

	ret = launchSemaphore.OpenGlobal(KLaunchSemaphoreName);
	LOG_MSG2( ">Target Launcher : RSemaphore.OpenGlobal ret %d", ret);
	User::LeaveIfError( ret );

	//Now launch the requested number of apps for the requested number of test runs
	for( TInt j = 0; j < numTestRuns; j++ )
		{ 
			for( TInt i = 0; i < numAgents; i++ )  
				{
					RBuf targetName;
					targetName.CleanupClosePushL();
					targetName.CreateL(KAgentExe());

					RProcess aProc;
					CleanupClosePushL(aProc); 
					RBuf launcherOptions;
					CleanupClosePushL(launcherOptions);
				    const TInt additionalWords = 2;	
					launcherOptions.CreateL( KAgentOptions().Length() + additionalWords );
		
					// Apply offset: launcherOptions.Format( .., .., i * numTargets, ..)
					// workaround to ensure we have the same binary for multiple agents. 
					// e.g. So if offset = 0, agent attaches to app1, app2, app3, app4, app5
					// if offset = 5, agent attached to app6, app7, app8, app9, app10 etc.
					// Note: apps need to be in rom otherwise the agent will fail on an assert 
					// (with KErrNotFound)
					launcherOptions.Format( KAgentOptions(), (TUint)numTargets, i * numTargets, 0);
			
					ret = LaunchProcess( aProc, targetName, launcherOptions );	
					CleanupStack::PopAndDestroy(3,&targetName);
					User::LeaveIfError(ret);
				}
		}

	// Wait for all agents to do their testing before checking the semaphore
	User::After(12000000);

	LOG_MSG( ">Target Launcher:  Semaphore wait");

	for (TInt i = 0; i < numAgents; i ++)
		{
		//We need this delay just in case an agent crashes and never signals the sem
		ret = launchSemaphore.Wait(100000);
		if( ret != KErrNone )
			{
			LOG_MSG3("launchSemaphore.Wait ret %d for agent %d", ret, i);
			break;
			}
		}

	LOG_MSG2( "testing for Semaphore ret %d", ret);

	// We only want to have one RTest instance at any one time since otherwise RTest can panic
	RTest test(_L("T_MULTI_AGENT_LAUNCHER"));
	test.Start(_L("t_multi_agent_launcher Check for agents finishing correctly"));
	test(ret == KErrNone);
	test.End();
	test.Close();

	CleanupStack::PopAndDestroy(&launchSemaphore); // launchSemaphore

	if( commandLine )
	CleanupStack::PopAndDestroy(commandLine);
	
	LOG_MSG("EXIT: t_multi_agent_launcher MainL()");
	}
void CTargetObserver::SetCrashEventL(TEventType aType, TKernelEventAction aAction)
{
	LOG_MSG3("->CTargetObserver::SetCrashEventL(type=%d, action=%d)\n", aType, aAction );
    User::LeaveIfError( iSecSess.SetEventAction( iTargetName, aType, aAction ));
}