// 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 <e32base.h> //base stuff
#include <e32debug.h> //logging
#include <e32cons.h> //console
#include <e32property.h>
#include <e32utrace.h>
#include <d32btrace.h>
#include <uloggerclient.h>
#include "t_crashdriver_drv.h"
#include "crash.h"
#include "crashdefs.h"//definitions included
_LIT(KAlloc, "-a <num_of_kbytes> : memory to be allocated on the heap; default is zero\n");
_LIT(KChunks, "-n <num_of_chunks> : first global - 1k, the rest 0.5k are local to the process; default is none\n");
_LIT(KDelay, "-d <num_of_seconds> : time before the app crashes; default is 60s\n");
_LIT(KFault, "-c <fault_type> : crash type; default is null pointer dereference\n");
_LIT(KFaultTypes, "None=0, PrefetchAbort=1, DataRead=2\n DataWrite=3, UndefInstr=4, UserPanic=5\n UserException=6, EStackOverflow=7, UserExit=8\n UserLeave=9, ThreadKill=10, ThreadPanic=11\n ThreadTerminate=12, DivByZero=13\n");
_LIT(KLoad, "-l : load esock.dll library into process address space; default is no\n");
_LIT(KSimon, "-s : to create a dummy child thread that is not going to crash; default is no\n");
_LIT(KThreads, "-m <num_of_threads> : that are going to be created and then crashed; if not specified the main thread is going to crash\n");
_LIT(KTrace, "-t: includes test trace data\n");
_LIT(KKernelCrash, "-k: causes the crash to occur in a device driver\n");
_LIT(KCrashDriverLddFileName, "crashdriverldd");
//crash app trace filter
#define CrashAppTraceFilter 200
/** Crash App UID */
const TUid KCrashAppUid = { 0x102831E5 };
using namespace Ulogger;
enum TFaultType
{
ENone =0,
EPrefetchAbort =1,
EDataRead =2,
EDataWrite =3,
EUndefInstr =4,
EUserPanic =5,
EUserException =6,
EStackOverflow =7,
EUserExit =8,
EUserLeave =9,
EThreadKill =10,
EThreadPanic =11,
EThreadTerminate=12,
EDivByZero =13,
};
enum TProgress
{
EMainThread,
EChildThread
};
CConsoleBase *console;
_LIT(KPanicCategory, "example panic category");
TInt gAlloc = 0;
TInt gDelay = 60;
TBool gLibrary = EFalse;
TInt gChunks = 0;
TInt gMultikill = 0;
TBool gScreamer = EFalse;
TFaultType gFault = ENone;
TBool gTrace = EFalse;
TBool gKernelSide = EFalse;
void WriteTraceData()
{
RBTrace trace;
TInt e = trace.Open();
if(e != KErrNone)
{
RDebug::Printf("crashapp: opening trace buffer failed: %d\n", e);
trace.Close();
return;
}
RDebug::Printf("crashapp: opening trace buffer succesful\n");
trace.SetFilter(200, ETrue);
trace.SetFilter2(1);
trace.SetMode(RBTrace::EEnable);
//do some tracing
TPrimaryFilter prim = KTraceCategory; //arbitrary once between 192 and 253
RDebug::Printf("crashapp: making trace calls");
User::After(15000000);
for(TInt i=0; i<KTRACENUMBER; i++)
{
TUTrace::PrintfPrimary(prim, ETrue, ETrue, "crashapp trace call number %d", i);
}
trace.Close();
}
typedef void (*Tfunc)();
void PrefetchAbort()
{
Tfunc f = NULL;
f();
}
void DataRead()
{
TInt* r = (TInt*) 0x1000;
TInt rr = (TInt)*r;
// Stop compilation warning. Should not get here anyway.
rr++;
}
void DataWrite()
{
TInt* r = (TInt*) 0x1000;
*r = 0x42;
}
void UndefInstr()
{
TUint32 undef = 0xE6000010;
Tfunc f = (Tfunc) &undef;
f();
}
void DivByZero()
{
int i = 1;
int j = 10;
for(j=10; j>0; --j)
{
//this is here to avoid compiler div by zero warnings
}
i = i/j;
}
void UserPanic()
{
User::Panic(KPanicCategory, KErrGeneral);
}
void UserException()
{
User::RaiseException(EExcGeneral);
}
void UserExit()
{
User::Exit(KErrGeneral);
}
void UserLeaveL()
{
User::Leave(KErrGeneral);
}
void ThreadKill()
{
RThread thread;
thread.Kill(KErrNone);
}
void ThreadPanic()
{
RThread thread;
thread.Panic(KPanicCategory, KErrGeneral);
}
void ThreadTerminate()
{
RThread thread;
thread.Terminate(KErrGeneral);
}
static TUint recurseCount;
void StackOverflow()
{
TUint32 array[128];
array[0] = ++recurseCount;
StackOverflow();
TUint warnRem = array[0];
array[0] = warnRem;
}
void ParseCommandlineArgsL()
{
TInt argc = User::CommandLineLength();
if(argc > 0)
{
HBufC* args = HBufC::NewLC(User::CommandLineLength());
TPtr argv = args->Des();
User::CommandLine(argv);
TLex lex(*args);
while(!lex.Eos())
{
if(lex.Get() == '-')
{
TChar c = lex.Get();
if(c == '-')
{
TPtrC16 token = lex.NextToken();
c = token[0];
}
lex.SkipSpace();
switch(c)
{
case 'a':
lex.Val(gAlloc);
break;
case 'd':
lex.Val(gDelay);
break;
case 'n':
lex.Val(gChunks);
break;
case 'c':
lex.Val((TInt&)gFault);
break;
case 'l':
gLibrary = ETrue;
break;
case 'm':
lex.Val(gMultikill);
break;
case 's':
gScreamer = ETrue;
break;
case 't':
gTrace = ETrue;
break;
case 'k':
gKernelSide = ETrue;
break;
case 'h':
default:
_LIT(KParams, "full parameter list:\n");
console->Printf(KParams);
console->Printf(KAlloc);
console->Printf(KChunks);
console->Printf(KDelay);
console->Printf(KFault);
console->Printf(KFaultTypes);
console->Printf(KLoad);
console->Printf(KSimon);
console->Printf(KThreads);
console->Printf(KTrace);
console->Printf(KKernelCrash);
_LIT(KPressAnyKey,"[press any key]");
console->Printf(KPressAnyKey);
console->Getch();
User::Leave(KErrNone);
}
}
lex.SkipSpace();
}
CleanupStack::PopAndDestroy(args);
}
}
TInt ThreadSimon(TAny* /*aCall*/)
{
RThread thread;
TUint simonCounter = 1;
while(simonCounter++)
{
User::After(1000000);
RDebug::Printf("crashapp.exe - Simon[%Lu] says...%d\n", thread.Id().Id(), simonCounter);
}
return 0;
}
TInt ThreadCrash(TAny* aCall)
{
User::After(gDelay*1000000);
Tfunc call = (Tfunc)(aCall);
RThread thread;
RDebug::Printf("crashapp.exe - time to die[%Lu]!\n", thread.Id().Id());
call();
return 0;
}
void MainL()
{
//Setup a property to monitor main thread
static _LIT_SECURITY_POLICY_PASS(KAllowAllPolicy);
TInt err = RProperty::Define(KCrashAppUid, EMainThread, RProperty::EInt, KAllowAllPolicy, KAllowAllPolicy);
if (err != KErrAlreadyExists)
{
User::LeaveIfError(err);
}
Tfunc call = NULL;
recurseCount = 0;
_LIT(KConsole, "crash console");
console = Console::NewL(KConsole, TSize(KConsFullScreen, KConsFullScreen));
CleanupStack::PushL(console);
ParseCommandlineArgsL();
if(gTrace)
{
WriteTraceData();
}
CleanupStack::PopAndDestroy(console);
//Load crash driver
RKernelCrashDrv crashDriver;
if(gKernelSide)
{
err = User::LoadLogicalDevice(KCrashDriverLddFileName);
if(KErrNone != err && KErrAlreadyExists != err )
{
RDebug::Printf("Unable to load kernel crash driver (%d)", err);
User::Leave(err);
}
CleanupClosePushL(crashDriver);
RDebug::Printf("Crash Driver loaded");
err = crashDriver.Open();
if(KErrNone != err)
{
RDebug::Printf("Unable to open kernel crash driver (%d)", err);
User::Leave(err);
}
RDebug::Printf("Crash Driver opened");
}
TInt32 array[3];
array[0] = 0xDEADDEAD;
array[1] = 0xF000BEEF;
array[2] = 0xBEEBEE00;
RDebug::Printf("crashapp.exe will crash after %d secs, crash type: ", gDelay);
TRequestStatus status;
switch(gFault)
{
default:
case ENone:
{
RDebug::Printf("null pointer\n");
break;
}
case EPrefetchAbort:
{
RDebug::Printf("prefetch abort\n");
if(gKernelSide)
{
User::After(gDelay*1000000);
#ifdef __MARM__
SetRegs();
#endif
crashDriver.SendPrefetchAbortFault(status);
User::WaitForRequest(status);
err = status.Int();
if(KErrNone != err)
{
RDebug::Printf("Send request failed (%d)", err);
User::Leave(err);
}
break;
}
call = PrefetchAbort;
break;
}
case EDataRead:
{
RDebug::Printf("data read");
if(gKernelSide)
{
User::After(gDelay*1000000);
#ifdef __MARM__
SetRegs();
#endif
crashDriver.SendDataReadFault(status);
User::WaitForRequest(status);
err = status.Int();
if(KErrNone != err)
{
RDebug::Printf("Send request failed (%d)", err);
User::Leave(err);
}
break;
}
call = DataRead;
break;
}
case EDataWrite:
{
RDebug::Printf("data write");
if(gKernelSide)
{
User::After(gDelay*1000000);
#ifdef __MARM__
SetRegs();
#endif
crashDriver.SendDataWriteFault(status);
User::WaitForRequest(status);
err = status.Int();
if(KErrNone != err)
{
RDebug::Printf("Send request failed (%d)", err);
User::Leave(err);
}
break;
}
call = DataWrite;
break;
}
case EUndefInstr:
{
RDebug::Printf("Undefined instruction");
if(gKernelSide)
{
User::After(gDelay*1000000);
#ifdef __MARM__
SetRegs();
#endif
crashDriver.SendUndefInstructionFault(status);
User::WaitForRequest(status);
err = status.Int();
if(KErrNone != err)
{
RDebug::Printf("Send request failed (%d)", err);
User::Leave(err);
}
break;
}
RDebug::Printf("undefined instruction");
call = UndefInstr;
break;
}
case EDivByZero:
{
RDebug::Printf("Division by zero");
if(gKernelSide)
{
User::After(gDelay*1000000);
#ifdef __MARM__
SetRegs();
#endif
crashDriver.SendDivByZeroFault(status);
User::WaitForRequest(status);
err = status.Int();
if(KErrNone != err)
{
RDebug::Printf("Send request failed (%d)", err);
User::Leave(err);
}
break;
}
RDebug::Printf("division by zero");
call = DivByZero;
break;
}
case EUserPanic:
{
RDebug::Printf("user panic");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = UserPanic;
break;
}
case EUserException:
{
RDebug::Printf("user exception\n");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = UserException;
break;
}
case EUserExit:
{
RDebug::Printf("user exit\n");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = UserExit;
break;
}
case EUserLeave:
{
RDebug::Printf("user leave\n");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = UserLeaveL;
break;
}
case EThreadKill:
{
RDebug::Printf("thread kill\n");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = ThreadKill;
break;
}
case EThreadPanic:
{
RDebug::Printf("thread panic\n");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = ThreadPanic;
break;
}
case EThreadTerminate:
{
RDebug::Printf("thread terminate\n");
if(gKernelSide)
{
RDebug::Printf("This crash is not supported kernel side");
User::Leave(KErrNotSupported);
}
call = ThreadTerminate;
break;
}
case EStackOverflow:
{
RDebug::Printf("stack overflow");
if(gKernelSide)
{
#ifdef __MARM__
SetRegs();
#endif
crashDriver.SendStackOverFlowFault(status);
User::WaitForRequest(status);
err = status.Int();
if(KErrNone != err)
{
RDebug::Printf("Send request failed (%d)", err);
User::Leave(err);
}
break;
}
call = StackOverflow;
break;
}
}
if(gKernelSide)
{
CleanupStack::PopAndDestroy(&crashDriver);
err = User::FreeLogicalDevice(RKernelCrashDrv::Name());
if(KErrNone != err)
{
RDebug::Printf("Unable to free kernel crash driver");
User::Leave(err);
}
}
if(gLibrary)
{
_LIT(KDllName, "esock.dll");
RLibrary dll;
TInt err = dll.Load(KDllName);
}
for(TInt i = 0; i < gChunks; i++)
{
RChunk chunk;
if(!i)
{
_LIT(KCrashChunk, "crashchunk");
RDebug::Printf("crashapp.exe - creating global chunk\n");
TInt err = chunk.CreateGlobal(KCrashChunk, 1024, 4096);
TUint8 data = 0xCA;
TUint8 *ptr = chunk.Base();
for(TInt i = 0; i < 1024; i++)
ptr[i] = data;
}
else
{
RDebug::Printf("crashapp.exe - creating global chunk:%d\n", i);
TInt err = chunk.CreateLocal(512, 1024);
TUint8 data = 0xBA;
TUint8 *ptr = chunk.Base();
for(TInt i = 0; i < 512; i++)
ptr[i] = data;
}
}
RDebug::Printf("crashapp.exe - allocating %d kbytes of heap space", gAlloc);
if(gAlloc)
{
TAny *memory = User::Alloc(gAlloc*1024);
if(!memory)
{
RDebug::Printf("crashapp.exe - unable to allocate memory on the heap!!!\n");
}
else
{
TUint8 data = 0xDA;
TUint8 *ptr = (TUint8*)memory;
for(TInt i = 0; i < gAlloc*1024; i++)
ptr[i] = data;
}
}
if(gScreamer)
{
//screaming thread just
_LIT(KThreadSimon, "SimonTheFirst");
RThread threadSimon;
threadSimon.Create(KThreadSimon(), ThreadSimon, KDefaultStackSize, NULL, (TAny*)NULL);
threadSimon.Resume();
threadSimon.Close();
}
if(gMultikill == 0) //main thread crashes, no child threads
{
User::After(gDelay*1000000); //crash timeout
RDebug::Printf("crashapp.exe - main thread says...\n");
call();
}
else //child threads crashing
{
RDebug::Printf("crashapp.exe - child threads crashing every second...\n");
_LIT(KCrashThread, "crashthread%d");
TBuf<36> threadName;
for(TInt i = 0; i < gMultikill; i++)
{
RThread crashThread;
threadName.Format(KCrashThread, i);
crashThread.Create(threadName, ThreadCrash, KDefaultStackSize, NULL, (TAny*)call);
crashThread.Resume();
crashThread.Close();
User::After(1000000);
}
}
RThread thread;
TUint mainCounter = 1;
while(mainCounter++) //main thread keep alive signals
{
User::After(300000);
RDebug::Printf("main thread[%Lu] loop:%d\n", thread.Id().Id(), mainCounter);
RDebug::Printf("setting property to %d: ", mainCounter);
err = RProperty::Set(KCrashAppUid, EMainThread, mainCounter);
if(err != KErrNone)
{
RDebug::Printf("Failed to set RProperty in main thread! err:%d\n", err);
}
}
delete console;
if(array[0] > 1) //remove warning
return;
}
TInt E32Main()
{
__UHEAP_MARK;
CTrapCleanup* cleanup = CTrapCleanup::New();
_LIT(KPanicCleanup, "CRASH-NO-CLEANUP");
__ASSERT_ALWAYS(cleanup, User::Panic(KPanicCleanup, KErrNoMemory));
TRAPD(err, MainL());
_LIT(KPanicLeave, "CRASH-LEAVE");
__ASSERT_ALWAYS(err == KErrNone, User::Panic(KPanicLeave, err));
delete cleanup;
__UHEAP_MARKEND;
return err;
}