// Copyright (c) 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:
//  Test wrapper for Symbian ELF testing

/**
 @file
 @internalTechnology
*/

#include <crashdatasave.h>

#include "t_self.h"
#include "t_coredumpserver.h"

CSymbianElfWrapper::CSymbianElfWrapper()
	{}

CSymbianElfWrapper::~CSymbianElfWrapper()
	{
	iCoreDumpSession.Close();
	iFsSession.Close();
	}

/**
 * Two phase constructor for CSymbianElfWrapper
 * @return CSymbianElfWrapper object
 * @leave
 */
CSymbianElfWrapper* CSymbianElfWrapper::NewL()
	{
	RDebug::Printf("\ncreating test oibject");
	CSymbianElfWrapper* ret = new (ELeave) CSymbianElfWrapper();
	CleanupStack::PushL(ret);
	ret->ConstructL();
	CleanupStack::Pop(ret);
	return ret;
	}

/**
 * Safe construction
 * @leave
 */
void CSymbianElfWrapper::ConstructL()
	{
	User::LeaveIfError(iCoreDumpSession.Connect());
	User::LeaveIfError(iFsSession.Connect());
	}

/**
 * Handle a command invoked from the script
 * @param aCommand Is the name of the command for a TBuf
 * @param aSection Is the .ini file section where parameters to the command are located
 * @param aAsyncErrorIndex Is used by the TEF Block framework to handle asynchronous commands.
 * @leaves One of the system wide error codes
 */
TBool CSymbianElfWrapper::DoCommandL(const TTEFFunction& aCommand, const TTEFSectionName& aSection, const TInt aAsyncErrorIndex)
	{
	//First we need to get the parameters from the ini file
	TInt crashId;
	TPtrC crashName;
	TPtrC crashSource;
	TPtrC section;

	TBool valsOk = GetIntFromConfig(aSection, KKeyCrashId, crashId) &&
				   GetStringFromConfig(aSection, KKeyCrashName, crashName) &&
				   GetStringFromConfig(aSection, KKeyCrashSource, crashSource) &&
				   GetStringFromConfig(aSection, KKeyElfSection, section);

	if(!valsOk)
		{
		ERR_PRINTF1(_L("Unable to get the proper config option from ini file. Are you sure its valid?"));
		return EFalse;
		}

	//Look at the command to see what we need to do
	if(KCommandGenerateFile() == aCommand && KSourceSystemCrash() == crashSource)
		{
		GenerateFileFromSystemCrashL(crashName, crashId);
		}
	else if(KCommandFindSection() == aCommand)
		{
		VerifySection(section, crashName, ETrue);
		}
	else if(KCommandVerifyNoSection() == aCommand)
		{
		VerifySection(section, crashName, EFalse);
		}
	else
		{
		ERR_PRINTF1(_L("Unable to get the proper command from script file. Are you sure its valid?"));
		return EFalse;
		}

	return ETrue;
	}

/**
 * Verifies a given section in the elf file
 * @param aSectionToVerify Section to verify
 * @param aCrashName ELF file name
 * @param aExists To check if the section exists and is valid, or not
 */
void CSymbianElfWrapper::VerifySection(TDesC& aSectionToVerify, TDesC& aCrashName, TBool aExists)
	{
	RFile selfFile;

	TInt err = selfFile.Open(iFsSession, aCrashName, EFileStream|EFileRead);
	INFO_PRINTF3(_L("Trying to open crash file at [%S] and got back [%d]"), &aCrashName, err);

	T_SELF_ASSERT_TRUE(err == KErrNone, 0);
	CleanupClosePushL(selfFile);

	CacheElfHeader(selfFile);
	ValidateElfFile(selfFile);

	if(aSectionToVerify == KSectionsVariantData())
		{
		VerifyVariantData(selfFile, aExists);
		}

	CleanupStack::PopAndDestroy();
	}

/**
 * Verifies Variant data in the elf file
 * @param aElfFile File handle to elf file
 */
void CSymbianElfWrapper::VerifyVariantData(RFile& aElfFile, TBool aExists)
	{
	INFO_PRINTF1(_L("Looking at the size of variant data section."));

	TUint sectionOffset = GetSectionOffset(aElfFile, ESYM_NOTE_VARIANT_DATA);
	T_SELF_ASSERT_TRUE(sectionOffset > 0, 0);

	TUint sectionSize = GetSectionSize(aElfFile, ESYM_NOTE_VARIANT_DATA);
	if(aExists)
		{
		T_SELF_ASSERT_TRUE(sectionSize > 0, 0);
		}
	else
		{
		T_SELF_ASSERT_TRUE(sectionSize == 0, 0);
		}

	//Now look at the actual structure
	Sym32_variant_spec_data varData;
	TPtr8 varDataDes((TUint8*)&varData, sizeof(Sym32_variant_spec_data));

	T_SELF_ASSERT_TRUE(aElfFile.Read(sectionOffset, varDataDes) == KErrNone, 0);
	
	INFO_PRINTF1(_L("Looking at the details of variant data structure. Must be > 0"));
	T_SELF_ASSERT_TRUE(varData.es_data > 0, 0);

	if(aExists)
		{
		T_SELF_ASSERT_TRUE(varData.es_size > 0, 0);
		}
	else
		{
		T_SELF_ASSERT_TRUE(varData.es_size == 0, 0);
		}

	INFO_PRINTF2(_L("Variant data lives in the file at [0x%X]"), varData.es_data);
	INFO_PRINTF2(_L("Variant data is [0x%X] in size"), varData.es_size);
	}

/**
 * Looks at elf signature to see if its ok
 * @param aElfFile File to check
 */
void CSymbianElfWrapper::ValidateElfFile(RFile& aElfFile)
	{
	INFO_PRINTF1(_L("Verifying Elf Header..."));
	T_SELF_ASSERT_TRUE(iElfHeader.e_ident[EI_MAG0] == 0x7f, 1);
	T_SELF_ASSERT_TRUE(iElfHeader.e_ident[EI_MAG1] == 0x45, 1);
	T_SELF_ASSERT_TRUE(iElfHeader.e_ident[EI_MAG2] == 0x4c, 1);
	T_SELF_ASSERT_TRUE(iElfHeader.e_ident[EI_MAG3] == 0x46, 1);
	}

/**
 * Caches the ELF header in the object
 * @param aElfFile file to look at
 */
void CSymbianElfWrapper::CacheElfHeader(RFile& aElfFile)
	{
	INFO_PRINTF1(_L("Reading Elf Header..."));
	TUint fileLocation = 0;
	TPtr8 hdrDes((TUint8*)&iElfHeader, sizeof(Elf32_Ehdr));
	T_SELF_ASSERT_TRUE(aElfFile.Read(fileLocation, hdrDes) == KErrNone, 0);
	}

/**
 * Gets the size of a given section in the elf file.
 * @param aElfFile File to look at
 * @param aNoteType Note of interest
 * @return Section size, 0 if not found
 */
TUint CSymbianElfWrapper::GetSectionSize(RFile& aElfFile, ESYM_NOTE_TYPE aNoteType)
	{
	INFO_PRINTF2(_L("Getting section size for section [0x%X]..."), aNoteType);
	TUint32 sectionOffset = GetSectionOffset(aElfFile, aNoteType);

	T_SELF_ASSERT_TRUE_RET(sectionOffset != 0, 0, 0);

	Sym32_dhdr dhdr;
	TPtr8 dhdrDes((TUint8*)&dhdr, sizeof(Sym32_dhdr));

	T_SELF_ASSERT_TRUE_RET(aElfFile.Read(sectionOffset, dhdrDes) == KErrNone, 0, 0);

	return dhdr.d_descrsz;
	}

/**
 * Searches the elf file for a given section and returns the offset of that section
 * @return Section offset - 0 if not found
 */
TUint CSymbianElfWrapper::GetSectionOffset(RFile& aElfFile, ESYM_NOTE_TYPE aNoteType)
	{
	INFO_PRINTF2(_L("Getting section offset for section [0x%X]..."), aNoteType);

	//Look through descriptor notes for this type
	for(TUint j = 0; j < iElfHeader.e_phnum; j++)
		{
		Elf32_Phdr phdr;
		TPtr8 phdrDes((TUint8*)&phdr, sizeof(Elf32_Phdr));
		T_SELF_ASSERT_TRUE_RET(aElfFile.Read(iElfHeader.e_phoff + j*sizeof(Elf32_Phdr), phdrDes) == KErrNone, 0, 0);

		if(phdr.p_type == PT_NOTE)
			{
			Sym32_dhdr dhdr;
			TPtr8 dhdrDes((TUint8*)&dhdr, sizeof(Sym32_dhdr));
			T_SELF_ASSERT_TRUE_RET(aElfFile.Read(phdr.p_offset, dhdrDes) == KErrNone, 0, 0);

			if(dhdr.d_type == aNoteType)
				{
				return phdr.p_offset + sizeof(Sym32_dhdr);
				}
			}
		}

	return 0;
	}

/**
 * Looks at a given system crash and generates a symbian elf file describing it at
 * specified file
 * @param aCrashName Name of ELF file to generate
 * @param aCrashId ID of crash to analyse
 */
void CSymbianElfWrapper::GenerateFileFromSystemCrashL(TDesC& aCrashName, TInt aCrashId)
	{
	INFO_PRINTF1(_L("Generating ELF file from System crash"));

	//Load SELF and filewriter and configure them to dump where required
	TPluginRequest loadSELFReq;
	loadSELFReq.iPluginType = TPluginRequest::EFormatter;
	loadSELFReq.iLoad = ETrue;
	loadSELFReq.iUid = KUidELFFormatterV2;

	TPluginRequest loadWriterReq;
	loadWriterReq.iPluginType = TPluginRequest::EWriter;
	loadWriterReq.iLoad = ETrue;
	loadWriterReq.iUid = KUidFileWriter;

	INFO_PRINTF1(_L("Loading the file writer and symbian elf..."));
	iCoreDumpSession.PluginRequestL(loadWriterReq);
	iCoreDumpSession.PluginRequestL(loadSELFReq);

	COptionConfig * config = COptionConfig::NewL((TInt)(CCrashDataSave::ECoreFilePath),
		   KUidFileWriter.iUid, COptionConfig::EWriterPlugin, COptionConfig::ETFileName, KFilePathPrompt,
			1, KNullDesC, 0, aCrashName);

	CleanupStack::PushL(config);
	config->Instance(0);

	INFO_PRINTF2(_L("Configuring the file writer to dump file at [%S]..."), &aCrashName);
	iCoreDumpSession.SetConfigParameterL(*config);
	CleanupStack::PopAndDestroy(config);

	//Get the crashes from the flash partition
	RCrashInfoPointerList* crashList = new(ELeave)RCrashInfoPointerList;
	TCleanupItem listCleanup(CCoreDumpServerWrapper::CleanupCrashList, (TAny*)crashList);
	CleanupStack::PushL(listCleanup);

	INFO_PRINTF1(_L("Looking at crashes in flash to find ours..."));
	iCoreDumpSession.ListCrashesInFlashL(*crashList);
	T_SELF_ASSERT_TRUE(crashList->Count() != 0, 1);
	T_SELF_ASSERT_TRUE((*crashList)[0]->iCrashId == 0, 1);

	CleanupStack::PopAndDestroy(1);

	TRequestStatus status;
    iCoreDumpSession.ProcessCrashLog(aCrashId, status);
	User::WaitForRequest(status);

	T_SELF_ASSERT_TRUE(KErrNone == status.Int(), 0);
	}

//eof
