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 +