// Copyright (c) 2007-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:
//
#include <e32std.h>
#include <e32base.h>
#include "tdexcformatter.h"
#include "dexcformatter.h"
CDexcFormatterTest * CDexcFormatterTest::NewL(RTest &itest)
{
CDexcFormatterTest *self = CDexcFormatterTest::NewLC(itest);
CleanupStack::Pop();
return self;
}
CDexcFormatterTest * CDexcFormatterTest::NewLC(RTest &itest)
{
CDexcFormatterTest *self = new(ELeave) CDexcFormatterTest(itest);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
void CDexcFormatterTest::ConstructL()
{
server = new(ELeave) CTestCrashDataSource;
formatter = CDexcFormatter::NewL();
writer = CTestDataSave::NewL();
formatter->ConfigureDataSourceL(server);
formatter->ConfigureDataSaveL(writer);
}
CDexcFormatterTest::CDexcFormatterTest(RTest &aTest) : itest(aTest) { }
CDexcFormatterTest::~CDexcFormatterTest()
{
delete server;
delete writer;
delete formatter;
}
void CDexcFormatterTest::CleanupEComArray(TAny* aArray)
{
(static_cast<RImplInfoPtrArray*> (aArray))->ResetAndDestroy();
REComSession::FinalClose();
}
void CDexcFormatterTest::TestSuiteL(TSuite aSuite) const
{
RDebug::Print(_L("-> TestSuite(%d)\n"), aSuite);
TInt err;
TBuf<23> name(KTestName);
name.AppendNum(aSuite);
itest.Next(name);
switch(aSuite)
{
case EConfigData:
{
RDebug::Print(_L("EConfigData starting...\n"));
TRAP(err, formatter->ConfigureDataSourceL(server));
itest(err == KErrNone);
itest(server == formatter->iDataSource);
TRAP(err, formatter->ConfigureDataSaveL(writer));
itest(err == KErrNone);
itest(writer == formatter->iDataSave);
TRAP(err, formatter->ConfigureDataSourceL(NULL));
itest(err == KErrNoMemory);
TRAP(err, formatter->ConfigureDataSaveL(NULL));
itest(err == KErrNoMemory);
RDebug::Print(_L("EConfigData passed!\n"));
break;
}
case EFindImplementation:
{
RDebug::Print(_L("EFindImplementation starting...\n"));
RImplInfoPtrArray infoArray;
TCleanupItem cleanup(CDexcFormatterTest::CleanupEComArray, (TAny*)&infoArray);
CleanupStack::PushL(cleanup);
TRAPD(err, CDexcFormatter::ListAllImplementationsL(infoArray));
itest(err == KErrNone);
CCoreDumpFormatter* findImpFormatter = 0;
RBuf description;
description.CreateL(MaxPluginDescription);
CleanupClosePushL(description);
err = KErrNotFound;
for (TInt i = 0; i < infoArray.Count(); i++)
{
TUid uid = infoArray[i]->ImplementationUid();
findImpFormatter = CCoreDumpFormatter::NewL(uid);
findImpFormatter->GetDescription(description);
delete findImpFormatter;
RDebug::Print(_L("found plugin[%d]: (%S)\n"), i, &description);
if(description == KPluginDescription())
{
itest(uid == KDexcFormatterUid);
err = KErrNone;
break;
}
description.Zero();
}
CleanupStack::PopAndDestroy();
itest(err == KErrNone);
CleanupStack::PopAndDestroy(); // infoArray, results in a call to CleanupEComArray
break;
}
case EConfigParams:
{
RDebug::Print(_L("EConfigParams starting\n"));
_LIT( KTrueFalseOpt, "True,False" );
_LIT( KFalseOpt, "False" );
_LIT( KTrueOpt, "True" );
TInt err;
COptionConfig * param = NULL;
TInt paramsCount = formatter->GetNumberConfigParametersL(); //it should be non leaving call
itest(paramsCount == formatter->iConfigList.Count());
TRAP(err, param = formatter->GetConfigParameterL(0));
itest(param->Index() == CDexcFormatter::EDumpStack);
itest(param->Uid() == KDEXCFormatterUid );
itest(param->Source() == COptionConfig::EFormatterPlugin);
itest(param->Type() == COptionConfig::ETBool);
itest(param->Options() == KTrueFalseOpt);
itest(param->ValueAsBool() == 1);
param = NULL;
TRAP(err, param = formatter->GetConfigParameterL(1));
itest(param->Index() == CDexcFormatter::EAsciiStack);
itest(param->Uid() == KDEXCFormatterUid );
itest(param->Source() == COptionConfig::EFormatterPlugin);
itest(param->Type() == COptionConfig::ETBool);
itest(param->Options() == KTrueFalseOpt);
itest(param->ValueAsBool() == 0);
param = NULL;
RDebug::Printf("->TRAP(err, param = formatter->GetConfigParameterL(paramsCount))\n");
TRAP(err, param = formatter->GetConfigParameterL(paramsCount));
itest(err == KErrBadHandle);
param = NULL;
RDebug::Printf("formatter->SetConfigParameterL( 0, (TInt)EFalse, KFalseOpt );\n");
TRAP(err, formatter->SetConfigParameterL( 0, (TInt)EFalse, KFalseOpt ));
itest(err == KErrNone);
itest(formatter->iConfigList[0]->ValueAsBool() == EFalse);
TRAP(err, param = formatter->GetConfigParameterL(0));
itest(formatter->iConfigList[0]->ValueAsBool() == EFalse);
itest(param->ValueAsDesc() == KFalseOpt);
RDebug::Printf("formatter->SetConfigParameterL( 0, (TInt)ETrue, KTrueOpt );\n");
TRAP(err, formatter->SetConfigParameterL( 0, (TInt)ETrue, KNullDesC ));
itest(err == KErrNone);
TRAP(err, param = formatter->GetConfigParameterL(0));
itest(formatter->iConfigList[0]->ValueAsBool());
itest(param->ValueAsDesc() == KTrueOpt);
RDebug::Print(_L("EConfigParams finished\n"));
break;
}
case EDumpLog:
{
RDebug::Print(_L("EDumpLog starting.... \n"));
// 0123456789012345678901234567890123456789012345678901234567890123456789012345
_LIT8(KLine, "----------------------------------------------------------------------------");
// 0 12345678901234567890 1 23456789012345 6 78901234567890 1 2345678901234?
_LIT8(KLogDump, "%S\r\nEKA2 USER CRASH LOG\r\nThread Name: %S\r\nThread ID: %u\r\nUser Stack %08X-%08X\r\n");
TBuf8<65+76+KMaxName+11+16> reference; //line, thread name, id and 2x 8-digits
//add newLC
CThreadInfo *thread = CThreadInfo::NewL(KCrashTid,
KCrashThreadName,
KCrashPid,
KCrashThreadPriority,
KCrashSvcStackPtr,
KCrashSvcStackAddr,
KCrashSvcStackSize,
KCrashUsrStackAddr,
KCrashUsrStackSize);
RDebug::Print(_L(" -> writer->OpenL(_L(\"\"), handle)\n"));
writer->OpenL(_L("")); //clear writer buffer
formatter->DumpLogL(*thread);
delete thread;
itest(writer->iData != KLogDump());
reference.Format(KLogDump, &KLine, &KCrashThreadName8, KCrashTid, KCrashUsrStackAddr, KCrashUsrStackAddr + KCrashUsrStackSize);
itest(writer->iData == reference);
RDebug::Print(_L("EDumpLog finished\n"));
break;
}
case EDumpPanicInfo:
{
RDebug::Print(_L("EDumpPanicInfo starting.... \n"));
// 0123456 7 8 9 0
_LIT8(KPanicDump, "Panic: %S-%d\r\n");
RBuf8 reference;
reference.CreateL(KPanicDump().Length() + 11 + KMaxExitCategoryName);
CleanupClosePushL(reference);
TCrashInfo crash;
crash.iReason = KCrashReason;
crash.iCategory = KCrashCategory;
writer->OpenL(_L("")); //clear writer buffer
formatter->DumpPanicInfoL(crash);
itest(writer->iData != KPanicDump());
reference.Format(KPanicDump, &KCrashCategory8, KCrashReason);
itest(writer->iData == reference);
CleanupStack::PopAndDestroy(); //reference
RDebug::Print(_L("EDumpPanicInfo finished\n"));
break;
}
case EDumpExcInfo:
{
RDebug::Print(_L("EDumpExcInfo starting.... \n"));
_LIT8(KExcDump, "\r\nUNHANDLED EXCEPTION:\r\ncode=%d PC=%08x FAR=%08x FSR=%08x\r\nR13svc=%08x R14svc=%08x SPSRsvc=%08x\r\n");
// 012345 6 7890123456789012345678901234567890123 4 5
RBuf8 reference;
reference.CreateL(KExcDump().Length() + 11 + 6*4);
CleanupClosePushL(reference);
TCrashInfo crash;
crash.iType = TCrashInfo::ECrashException;
crash.iContext.iExcCode = KCrashExcCode;
crash.iContext.iFaultAddress = KCrashFaultAddress;
crash.iContext.iFaultStatus = KCrashFaultStatus;
crash.iContext.iR15 = KRegValue32 + KCrashTid;
crash.iContext.iR13Svc = crash.iContext.iR15 + 1;
crash.iContext.iR14Svc = crash.iContext.iR15 + 2;
crash.iContext.iSpsrSvc = crash.iContext.iR15 + 3;
writer->Open(_L("")); //clear writer buffer
formatter->DumpExcInfoL(crash);
RDebug::Print( _L("-> itest(writer->iData != KExcDump())\n") );
itest(writer->iData != KExcDump());
TInt32 code = KCrashExcCode;
TInt32 pc = KRegValue32 + KCrashTid; //index of r15
TInt32 far = KCrashFaultAddress;
TInt32 fsr = KCrashFaultStatus;
TInt32 r13svc = KRegValue32 + KCrashTid + 1; //index of r13svc
TInt32 r14svc = KRegValue32 + KCrashTid + 2; //index of r14svc
TInt32 spsr = KRegValue32 + KCrashTid + 3; //index of spsr
reference.Format(KExcDump, code, pc, far, fsr, r13svc, r14svc, spsr);
/*
RBuf8 referencePrint;
referencePrint.CreateL(256);
referencePrint.Copy( reference );
char *tonp = (char*) referencePrint.PtrZ();
LOG_MSG2(" KExcDump=%s\n", tonp);
referencePrint.Close();
writer->Print();
*/
RDebug::Print( _L("-> itest(writer->iData == reference)\n") );
itest(writer->iData == reference);
CleanupStack::PopAndDestroy(); //reference
RDebug::Print(_L("EDumpExcInfo finished\n"));
break;
}
case EDumpRegisters:
{
RDebug::Print(_L("EDumpRegisters starting.... \n"));
_LIT8(KCpsrDump, "\r\nUSER REGISTERS:\r\nCPSR=%08x\r\n");
_LIT8(KRegsDump, "r%02d=%08x %08x %08x %08x\r\n");
RBuf8 reference;
reference.CreateL(KCpsrDump().Length() + 4 + KCoreRegsCount * (KRegsDump().Length() + 4*4));
CleanupClosePushL(reference);
writer->Open(_L("")); //clear writer buffer
TRAP(err, formatter->DumpRegistersL(KCrashTid));
itest(err == KErrNone);
itest(writer->iData != KRegsDump());
TInt32 cpsr = KRegValue32 + KCrashTid + 16; //index of cpsr
reference.Format(KCpsrDump, cpsr);
TBuf8<41> line;
#define REG_VAL(i) ( KRegValue32 + KCrashTid + i )
for(TInt i = 0; i < KCoreRegsCount; i+=4)
{
TInt reg0 = REG_VAL(i);
TInt reg1 = REG_VAL(i+1);
TInt reg2 = REG_VAL(i+2);
TInt reg3 = REG_VAL(i+3);
line.Format(KRegsDump, i, reg0, reg1, reg2, reg3);
reference.Append(line);
}
#undef REG_VAL
itest(writer->iData == reference);
CleanupStack::PopAndDestroy(); //reference
RDebug::Print(_L("EDumpRegisters finished\n"));
break;
}
case EDumpCodeSegs:
{
RDebug::Print(_L("EDumpCodeSegs starting.... \n"));
_LIT8(KSegsLine, "%08X-%08X %S\r\n");
RBuf8 line;
CleanupClosePushL(line);
line.CreateL(KSegsLine().Length() + 2*4 + KMaxFileName);
// 0123456789012345679012
_LIT8(KSegsDump, "\r\nCODE SEGMENTS:\r\n");
RBuf8 reference;
CleanupClosePushL(reference);
reference.CreateL(KSegsDump().Length() + KCodeSegsCount * (line.MaxLength()) + 76);
writer->Open(_L("")); //clear writer buffer
formatter->DumpCodeSegsL(KCrashPid);
//writer->Print();
itest(writer->iData != KSegsDump());
RDebug::Print( _L(" writer->iData.Length() = %d\n"), writer->iData.Length() );
reference = KSegsDump();
TUint base;
TUint size;
// See CTestCrashDataSource::GetCodeSegmentsL()
base = KCrashCodeRunAddr + KCrashPid;
size= KCrashCodeSize + KCrashPid;
line.Format(KSegsLine, base, base + size, &KCrashProcessName8);
reference.Append(line);
base = KCrashCodeRunAddr + KCrashPid + 1;
size= KCrashCodeSize + KCrashPid + 1;
line.Format(KSegsLine, base, base + size, &KLibName1_8);
reference.Append(line);
base = KCrashCodeRunAddr + KCrashPid + 2;
size= KCrashCodeSize + KCrashPid + 2;
line.Format(KSegsLine, base, base + size, &KLibName2_8);
reference.Append(line);
reference.Append(_L8("\r\n"));
/*
RBuf8 referencePrint;
referencePrint.CreateL(256);
referencePrint.Copy( reference );
char *tonp = (char*) referencePrint.PtrZ();
LOG_MSG2(" KCodeSeg=%s\n", tonp);
referencePrint.Close();
writer->Print();
*/
itest(writer->iData == reference);
CleanupStack::PopAndDestroy(2);
RDebug::Print(_L("EDumpCodeSegs finished\n"));
break;
}
case EDumpStack:
{
RDebug::Print(_L("EDumpStack starting.... \n"));
_LIT( KFalseOpt, "False" );
CThreadInfo *thread = CThreadInfo::NewL(KCrashTid,
KCrashThreadName,
KCrashPid,
KCrashThreadPriority,
KCrashSvcStackPtr,
KCrashSvcStackAddr,
KCrashSvcStackSize,
KCrashUsrStackAddr,
KCrashUsrStackSize);
CleanupStack::PushL(thread);
RBuf8 stack;
stack.CreateL(KCrashUsrStackSize*5);
CleanupClosePushL(stack);
writer->Open(_L("")); //clear writer buffer
formatter->SetConfigParameterL( 1, (TInt32)EFalse, KFalseOpt ); //binary dump
formatter->DumpStackL(KCrashTid, *thread);
for( TInt i = 0; i < KCrashUsrStackSize; i++ )
{
stack.Append((TUint8*)&i, 1);
}
RDebug::Printf("itest(writer->iData == stack);");
itest(writer->iData == stack);
itest(writer->iData != _L8(""));
stack.Zero();
for(TInt i = 0; i < KCrashUsrStackSize; i+=16)
{
stack.AppendNumFixedWidth(KCrashUsrStackAddr + i, EHex, 8);
stack.Append(_L8(": "));
for(TInt j = 0; j < 16; ++j)
{
stack.AppendNumFixedWidth(i+j, EHex, 2);
stack.Append(' ');
}
for(TInt j = 0; j < 16; ++j)
{
TInt c = i+j;
if(c < 0x20 || c >= 0x7f) //something writable
c = 0x2e; //.
stack.Append(TChar(c));
}
stack.AppendFormat(_L8("\r\n"));
}
writer->OpenL(_L("")); //clear writer buffer
RDebug::Printf("formatter->SetConfigParameterL( 1, (TInt)ETrue, KNullDesC );");
formatter->SetConfigParameterL( 1, (TInt)ETrue, KNullDesC ); //ascii dump
RDebug::Printf("formatter->DumpStackL(KCrashTid, *thread);");
formatter->DumpStackL(KCrashTid, *thread);
RDebug::Printf("itest(writer->iData == stack);");
itest(writer->iData == stack);
itest(writer->iData != _L8(""));
CleanupStack::PopAndDestroy();
CleanupStack::PopAndDestroy(thread);
RDebug::Print(_L("EDumpStack finished\n"));
break;
}
default:
break;
}
RDebug::Print(_L("<- TestSuite(%d)\n"), aSuite);
}
void RunTestsL()
{
RDebug::Print(_L("-> RunTestsL()\n"));
CDexcFormatterTest::TSuite suite[] = {
CDexcFormatterTest::ENone,
CDexcFormatterTest::EConfigData,
CDexcFormatterTest::EFindImplementation,
CDexcFormatterTest::EConfigParams,
CDexcFormatterTest::EGetDescription,
CDexcFormatterTest::EDumpLog,
CDexcFormatterTest::EDumpPanicInfo,
CDexcFormatterTest::EDumpExcInfo,
CDexcFormatterTest::EDumpRegisters,
CDexcFormatterTest::EDumpCodeSegs,
CDexcFormatterTest::EDumpStack,
CDexcFormatterTest::ECrash
};
RTest rtest(KTestTitle);
rtest.Title();
rtest.Start(_L(""));
CDexcFormatterTest::TSuite *test = suite;
while(*test != CDexcFormatterTest::ECrash)
{
CDexcFormatterTest *itester = CDexcFormatterTest::NewLC(rtest);
itester->TestSuiteL(*test++);
CleanupStack::PopAndDestroy();
}
rtest.End();
rtest.Close();
RDebug::Print(_L("<- RunTestsL()\n"));
}
TInt E32Main()
{
RDebug::Print(_L("-> E32Main()\n"));
__UHEAP_MARK;
CTrapCleanup* cleanup = CTrapCleanup::New();
__ASSERT_ALWAYS(cleanup, User::Panic(_L("D_EXC_TEST-NO_CLEANUP"), KErrNoMemory));
TRAPD(err, RunTestsL());
__ASSERT_ALWAYS(!err, User::Panic(_L("D_EXC_TEST-RunTestsL trapped with error: "), err));
delete cleanup;
__UHEAP_MARKEND;
RDebug::Print(_L("<- E32Main()\n"));
return err;
}