os/kernelhwsrv/kerneltest/e32test/debug/d_schedhook.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/kernelhwsrv/kerneltest/e32test/debug/d_schedhook.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,470 @@
     1.4 +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of the License "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +// e32test\debug\d_schedhook.cpp
    1.18 +// 
    1.19 +//
    1.20 +
    1.21 +#include <kernel/kernel.h>
    1.22 +#include <kernel/kern_priv.h>
    1.23 +#include "platform.h"
    1.24 +#include <kernel/cache.h>
    1.25 +#include <arm.h>
    1.26 +#include "d_schedhook.h"
    1.27 +
    1.28 +_LIT(KLddName,"D_SCHEDHOOK");
    1.29 +
    1.30 +NThread* TestThread;
    1.31 +TInt TestCount;
    1.32 +
    1.33 +const TInt KMajorVersionNumber = 0;
    1.34 +const TInt KMinorVersionNumber = 1;
    1.35 +const TInt KBuildVersionNumber = 1;
    1.36 +
    1.37 +class DSchedhookTest;
    1.38 +
    1.39 +class DSchedhookTestFactory : public DLogicalDevice
    1.40 +	{
    1.41 +public:
    1.42 +	DSchedhookTestFactory();
    1.43 +	virtual TInt Install();
    1.44 +	virtual void GetCaps(TDes8& aDes) const;
    1.45 +	virtual TInt Create(DLogicalChannelBase*& aChannel);
    1.46 +	};
    1.47 +
    1.48 +class DHwExcHandler : public DKernelEventHandler
    1.49 +	{
    1.50 +public:
    1.51 +	DHwExcHandler() : DKernelEventHandler(HandleEvent, this) {}
    1.52 +private:
    1.53 +	static TUint HandleEvent(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis);
    1.54 +	};
    1.55 +
    1.56 +class DSchedhookTest : public DLogicalChannelBase
    1.57 +	{
    1.58 +public:
    1.59 +	virtual ~DSchedhookTest();
    1.60 +protected:
    1.61 +	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
    1.62 +	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
    1.63 +public:
    1.64 +	DHwExcHandler* iHwExcHandler;
    1.65 +	};
    1.66 +
    1.67 +DECLARE_STANDARD_LDD()
    1.68 +	{
    1.69 +	return new DSchedhookTestFactory;
    1.70 +	}
    1.71 +
    1.72 +//
    1.73 +// DSchedhookTestFactory
    1.74 +//
    1.75 +
    1.76 +DSchedhookTestFactory::DSchedhookTestFactory()
    1.77 +	{
    1.78 +	iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
    1.79 +	}
    1.80 +
    1.81 +TInt DSchedhookTestFactory::Create(DLogicalChannelBase*& aChannel)
    1.82 +/**
    1.83 + Create a new DSchedhookTest on this logical device
    1.84 +*/
    1.85 +	{
    1.86 +	aChannel=new DSchedhookTest;
    1.87 +	return aChannel ? KErrNone : KErrNoMemory;
    1.88 +	}
    1.89 +
    1.90 +TInt DSchedhookTestFactory::Install()
    1.91 +/**
    1.92 + Install the LDD - overriding pure virtual
    1.93 +*/
    1.94 +	{
    1.95 +	return SetName(&KLddName);
    1.96 +	}
    1.97 +
    1.98 +void DSchedhookTestFactory::GetCaps(TDes8& aDes) const
    1.99 +/**
   1.100 + Get capabilities - overriding pure virtual
   1.101 +*/
   1.102 +	{
   1.103 +	TCapsTestV01 b;
   1.104 +	b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
   1.105 +    Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
   1.106 +	}
   1.107 +
   1.108 +//
   1.109 +// DHwExcHandler
   1.110 +//
   1.111 +
   1.112 +/**
   1.113 + Handle exceptions on our test thread by suspending it
   1.114 +*/
   1.115 +TUint DHwExcHandler::HandleEvent(TKernelEvent aEvent, TAny* a1, TAny* /*a2*/, TAny* /*aThis*/)
   1.116 +	{
   1.117 +	if (aEvent == EEventHwExc)
   1.118 +		{
   1.119 +		if(&Kern::CurrentThread().iNThread!=TestThread)
   1.120 +			return ERunNext;
   1.121 +		TArmExcInfo *pR=(TArmExcInfo*)a1;
   1.122 +		pR->iR15+=4;
   1.123 +		Kern::ThreadSuspend(Kern::CurrentThread(),1);
   1.124 +		return (TUint)EExcHandled;
   1.125 +		}
   1.126 +	return (TUint)ERunNext;
   1.127 +	}
   1.128 +
   1.129 +//
   1.130 +// DSchedhookTest
   1.131 +//
   1.132 +
   1.133 +TInt DSchedhookTest::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
   1.134 +/**
   1.135 + Create channel
   1.136 +*/
   1.137 +	{
   1.138 +	if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
   1.139 +		return KErrNotSupported;
   1.140 +	iHwExcHandler = new DHwExcHandler;
   1.141 +	if (! iHwExcHandler)
   1.142 +		return KErrNoMemory;
   1.143 +	return iHwExcHandler->Add();
   1.144 +	}
   1.145 +
   1.146 +DSchedhookTest::~DSchedhookTest()
   1.147 +	{
   1.148 +	if (iHwExcHandler)
   1.149 +		iHwExcHandler->Close();
   1.150 +	}
   1.151 +
   1.152 +
   1.153 +
   1.154 +//
   1.155 +// Scheduler Hook functions
   1.156 +//
   1.157 +
   1.158 +NThread* CurrentThread=NULL;
   1.159 +
   1.160 +void DisableRescheduleCallback()
   1.161 +/**
   1.162 + Stops the Scheduler calling us on every context switch
   1.163 +*/
   1.164 +	{
   1.165 +	// Prevent rescheduling whilst we disable the callback
   1.166 +	NKern::Lock();
   1.167 +	// Disable Callback
   1.168 +	NKern::SetRescheduleCallback(NULL);
   1.169 +	// Invalidate CurrentThread
   1.170 +	CurrentThread = NULL;
   1.171 +	// Callback now disabled...
   1.172 +	NKern::Unlock();
   1.173 +	}
   1.174 +
   1.175 +
   1.176 +
   1.177 +void RemoveSchedulerHooks()
   1.178 +/**
   1.179 + Removes the patches added to the Scheduler code.
   1.180 +*/
   1.181 +	{
   1.182 +	// Make sure callback is disabled (required before removing hooks)
   1.183 +	DisableRescheduleCallback();
   1.184 +	// Get range of memory used by hooks
   1.185 +	TLinAddr start,end;
   1.186 +	NKern::Lock();
   1.187 +	NKern::SchedulerHooks(start,end);
   1.188 +	NKern::Unlock();
   1.189 +	// Free shadow pages which cover hooks
   1.190 +	TUint32 pageSize=Kern::RoundToPageSize(1);
   1.191 +	NKern::ThreadEnterCS();
   1.192 +	for(TLinAddr a=start; a<end; a+=pageSize)
   1.193 +		Epoc::FreeShadowPage(a);   // Ignore errors because we're trying to clean up anyway
   1.194 +	NKern::ThreadLeaveCS();
   1.195 +	}
   1.196 +
   1.197 +
   1.198 +
   1.199 +TInt InsertSchedulerHooks()
   1.200 +/**
   1.201 + Enables use of the Scheduler callback by using shadow pages to patch the Scheduler code.
   1.202 +*/
   1.203 +	{
   1.204 +	// Get range of memory used by hooks	
   1.205 +	TLinAddr start=0,end=0;
   1.206 +	NKern::Lock();
   1.207 +	NKern::SchedulerHooks(start,end);
   1.208 +	NKern::Unlock();
   1.209 +	if (start==0 && end==0) return KErrNotSupported;
   1.210 +
   1.211 +	// Create shadow pages for hooks
   1.212 +	TUint32 pageSize=Kern::RoundToPageSize(1);
   1.213 +	for(TLinAddr a=start; a<end; a+=pageSize)
   1.214 +		{
   1.215 +		NKern::ThreadEnterCS();
   1.216 +		TInt r=Epoc::AllocShadowPage(a);
   1.217 +		NKern::ThreadLeaveCS();
   1.218 +		if(r!=KErrNone && r!=KErrAlreadyExists)
   1.219 +			{
   1.220 +			RemoveSchedulerHooks();
   1.221 +			return r;
   1.222 +			}
   1.223 +		}
   1.224 +	// Put hooks in
   1.225 +	NKern::Lock();
   1.226 +	NKern::InsertSchedulerHooks();
   1.227 +	NKern::Unlock();
   1.228 +	// Make I and D caches consistant for hook region
   1.229 +	Cache::IMB_Range(start,end-start);
   1.230 +
   1.231 +	return KErrNone;
   1.232 +	}
   1.233 +
   1.234 +
   1.235 +
   1.236 +void RescheduleTestFunction()
   1.237 +/**
   1.238 + Test function to be called on each thread reschedule
   1.239 +*/
   1.240 +	{
   1.241 +	// Count rechedules to the test thread
   1.242 +	if(CurrentThread==TestThread)
   1.243 +		++TestCount;
   1.244 +	}
   1.245 +
   1.246 +
   1.247 +
   1.248 +void RescheduleCallback(NThread* aNThread)
   1.249 +/**
   1.250 + Main scheduler callback.
   1.251 + Called with the Kernel Lock (Preemption Lock) held.
   1.252 +*/
   1.253 +	{
   1.254 +#ifndef __SMP__
   1.255 +	// The 'CurrentThread' is now unscheduled and has become the 'previous thread'
   1.256 +	// Set this thread 'UserContextType'...
   1.257 +	CurrentThread->SetUserContextType();
   1.258 +#endif
   1.259 +	// Make the newly scheduled thread the CurrentThread
   1.260 +	CurrentThread = aNThread;
   1.261 +	// Test function
   1.262 +	RescheduleTestFunction();
   1.263 +	}
   1.264 +
   1.265 +
   1.266 +
   1.267 +void RescheduleCallbackFirst(NThread* aNThread)
   1.268 +/**
   1.269 + Scheduler callback used once for initialisation.
   1.270 + Called with the Kernel Lock (Preemption Lock) held.
   1.271 +*/
   1.272 +	{
   1.273 +	// Initialise CurrentThread
   1.274 +	CurrentThread = aNThread;
   1.275 +	// Switch future callbacks to the main RescheduleCallback
   1.276 +	NKern::SetRescheduleCallback(RescheduleCallback);
   1.277 +	// Test function
   1.278 +	RescheduleTestFunction();
   1.279 +	}
   1.280 +
   1.281 +
   1.282 +
   1.283 +void EnableRescheduleCallback()
   1.284 +/**
   1.285 + Sets the Scheduler to call us back on every thread reschedule
   1.286 +*/
   1.287 +	{
   1.288 +	// Reset the User Context Type for all threads, because these values
   1.289 +	// will be out-of-date. (It is our Rescheduler callback which set's them.
   1.290 +	// and we're just about enable that.)
   1.291 +
   1.292 +	NKern::ThreadEnterCS();  // Prevent us from dying or suspending whilst holding a DMutex
   1.293 +	DObjectCon& threads=*Kern::Containers()[EThread];  // Get containing holding threads
   1.294 +	threads.Wait();  // Obtain the container mutex so the list does get changed under us
   1.295 +
   1.296 +#ifndef __SMP__
   1.297 +	// For each thread...
   1.298 +	TInt c=threads.Count();
   1.299 +	for (TInt i=0; i<c; i++)
   1.300 +		((DThread*)threads[i])->iNThread.ResetUserContextType();
   1.301 +#endif
   1.302 +
   1.303 +	threads.Signal();  // Release the container mutex
   1.304 +	NKern::ThreadLeaveCS();  // End of critical section
   1.305 +
   1.306 +	// Ask for callback
   1.307 +	NKern::SetRescheduleCallback(RescheduleCallbackFirst);
   1.308 +	}
   1.309 +
   1.310 +
   1.311 +
   1.312 +TInt GetThreadUserContext(NThread* aThread,TArmRegSet& aContext)
   1.313 +/**
   1.314 + Test function to get a threads User Context
   1.315 +*/
   1.316 +	{
   1.317 +	// Get the User Context Type which our Reschedule hook set
   1.318 +	TInt type = aThread->iSpare3; /*iUserContextType*/
   1.319 +
   1.320 +	// Get the table corresponding to the User Context Type
   1.321 +	const TArmContextElement* c = NThread::UserContextTables()[type];
   1.322 +
   1.323 +	// Set the User Context by using the table we got
   1.324 +	TUint32* sp  = (TUint32*)aThread->iSavedSP;
   1.325 +	TUint32* st  = (TUint32*)((TUint32)aThread->iStackBase+(TUint32)aThread->iStackSize);
   1.326 +	TArmReg* out = (TArmReg*)(&aContext);
   1.327 +	TArmReg* end = (TArmReg*)(&aContext+1);
   1.328 +	do
   1.329 +		{
   1.330 +		TInt v = c->iValue;
   1.331 +		TInt t = c->iType;
   1.332 +		++c;
   1.333 +		if(t==TArmContextElement::EOffsetFromSp)
   1.334 +			v = sp[v];
   1.335 +		else if(t==TArmContextElement::EOffsetFromStackTop)
   1.336 +			v = st[-v];
   1.337 +		*out++ = v;
   1.338 +		}
   1.339 +	while(out<end);
   1.340 +
   1.341 +	return type;
   1.342 +	}
   1.343 +
   1.344 +
   1.345 +
   1.346 +void DumpContext(TArmRegSet& aContext,TInt aType)
   1.347 +	{
   1.348 +	Kern::Printf("  Context type %d",aType);
   1.349 +	Kern::Printf("  r0 =%08x r1 =%08x r2 =%08x r3 =%08x",aContext.iR0,aContext.iR1,aContext.iR2,aContext.iR3);
   1.350 +	Kern::Printf("  r4 =%08x r5 =%08x r6 =%08x r7 =%08x",aContext.iR4,aContext.iR5,aContext.iR6,aContext.iR7);
   1.351 +	Kern::Printf("  r8 =%08x r9 =%08x r10=%08x r11=%08x",aContext.iR8,aContext.iR9,aContext.iR10,aContext.iR11);
   1.352 +	Kern::Printf("  r12=%08x r13=%08x r14=%08x r15=%08x",aContext.iR12,aContext.iR13,aContext.iR14,aContext.iR15);
   1.353 +	Kern::Printf("  cpsr=%08x dacr=%08x",aContext.iFlags, aContext.iDacr);
   1.354 +	}
   1.355 +
   1.356 +
   1.357 +
   1.358 +TInt TestGetThreadContext(DThread* aThread,TDes8* aContext)
   1.359 +	{
   1.360 +	TArmRegSet context1;
   1.361 +	TArmRegSet context2;
   1.362 +
   1.363 +	memclr(&context1,sizeof(context1));
   1.364 +	memclr(&context2,sizeof(context2));
   1.365 +	NKern::Lock();
   1.366 +	TUint32 unused;
   1.367 +	NKern::ThreadGetUserContext(&aThread->iNThread,&context1,unused);
   1.368 +	TInt r=GetThreadUserContext(&aThread->iNThread,context2);
   1.369 +	NKern::Unlock();
   1.370 +	DumpContext(context1,-1);
   1.371 +	DumpContext(context2,r);
   1.372 +
   1.373 +	TInt len,maxLen;
   1.374 +	Kern::KUDesInfo(*aContext,len,maxLen);
   1.375 +	if(maxLen>KMaxThreadContext)
   1.376 +		maxLen = KMaxThreadContext;
   1.377 +	TPtr8 ptr((TUint8*)&context1,maxLen,maxLen);
   1.378 +	Kern::KUDesPut(*aContext,ptr);
   1.379 +
   1.380 +	for(TUint i=0; i<sizeof(context1); i++)
   1.381 +		if(((TUint8*)&context1)[i]!=((TUint8*)&context2)[i])
   1.382 +			return KErrGeneral;
   1.383 +	return r;
   1.384 +	}
   1.385 +
   1.386 +
   1.387 +DThread* ThreadFromId(TUint aId)
   1.388 +	{
   1.389 +	// This is risky because the thread could die on us an the DThread* become invalid.
   1.390 +	// We are relying on this never happening in our test code.
   1.391 +	NKern::ThreadEnterCS();  // Prevent us from dying or suspending whilst holding a DMutex
   1.392 +	DObjectCon& threads=*Kern::Containers()[EThread];  // Get containing holding threads
   1.393 +	threads.Wait();  // Obtain the container mutex so the list does get changed under us
   1.394 +	DThread* thread = Kern::ThreadFromId(aId);
   1.395 +	threads.Signal();  // Release the container mutex
   1.396 +	NKern::ThreadLeaveCS();  // End of critical section
   1.397 +	return thread;
   1.398 +	}
   1.399 +
   1.400 +TInt DSchedhookTest::Request(TInt aFunction, TAny* a1, TAny* a2)
   1.401 +/**
   1.402 + Handle requests from the test program
   1.403 +*/
   1.404 +	{
   1.405 +	TInt r=KErrNone;
   1.406 +	switch (aFunction)
   1.407 +		{
   1.408 +		case RSchedhookTest::EInstall:
   1.409 +			r=InsertSchedulerHooks();
   1.410 +			break;
   1.411 +
   1.412 +		case RSchedhookTest::EUninstall:
   1.413 +			RemoveSchedulerHooks();
   1.414 +			return KErrNone;
   1.415 +
   1.416 +		case RSchedhookTest::EInsertHooks:
   1.417 +			{
   1.418 +			NKern::Lock();
   1.419 +			NKern::InsertSchedulerHooks();
   1.420 +			TLinAddr start,end;
   1.421 +			NKern::SchedulerHooks(start,end);
   1.422 +			NKern::Unlock();
   1.423 +			Cache::IMB_Range(start,end-start);
   1.424 +			}
   1.425 +			return KErrNone;
   1.426 +
   1.427 +		case RSchedhookTest::ERemoveHooks:
   1.428 +			{
   1.429 +			NKern::Lock();
   1.430 +			NKern::SetRescheduleCallback(NULL);
   1.431 +			NKern::RemoveSchedulerHooks();
   1.432 +			TLinAddr start,end;
   1.433 +			NKern::SchedulerHooks(start,end);
   1.434 +			NKern::Unlock();
   1.435 +			Cache::IMB_Range(start,end-start);
   1.436 +			}
   1.437 +			return KErrNone;
   1.438 +
   1.439 +		case RSchedhookTest::EEnableCallback:
   1.440 +			NKern::Lock();
   1.441 +			NKern::SetRescheduleCallback(RescheduleCallbackFirst);
   1.442 +			NKern::Unlock();
   1.443 +			return KErrNone;
   1.444 +
   1.445 +		case RSchedhookTest::EDisableCallback:
   1.446 +			DisableRescheduleCallback();
   1.447 +			return KErrNone;
   1.448 +
   1.449 +		case RSchedhookTest::ESetTestThread:
   1.450 +			{
   1.451 +			NKern::ThreadEnterCS();  // Prevent us from dying or suspending whilst holding a DMutex
   1.452 +			DObjectCon& threads=*Kern::Containers()[EThread];  // Get containing holding threads
   1.453 +			threads.Wait();  // Obtain the container mutex so the list does get changed under us
   1.454 +			TestThread = &ThreadFromId((TUint)a1)->iNThread;
   1.455 +			threads.Signal();  // Release the container mutex
   1.456 +			NKern::ThreadLeaveCS();  // End of critical section
   1.457 +			TestCount = 0;
   1.458 +			}
   1.459 +			break;
   1.460 +
   1.461 +		case RSchedhookTest::EGetThreadContext:
   1.462 +			return TestGetThreadContext(ThreadFromId((TUint)a1),(TDes8*)a2);
   1.463 +
   1.464 +		case RSchedhookTest::EGetTestCount:
   1.465 +			return TestCount;
   1.466 +
   1.467 +		default:
   1.468 +			r=KErrNotSupported;
   1.469 +			break;
   1.470 +		}
   1.471 +	return r;
   1.472 +	}
   1.473 +