First public contribution.
1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32test\debug\d_schedhook.cpp
18 #include <kernel/kernel.h>
19 #include <kernel/kern_priv.h>
21 #include <kernel/cache.h>
23 #include "d_schedhook.h"
25 _LIT(KLddName,"D_SCHEDHOOK");
30 const TInt KMajorVersionNumber = 0;
31 const TInt KMinorVersionNumber = 1;
32 const TInt KBuildVersionNumber = 1;
36 class DSchedhookTestFactory : public DLogicalDevice
39 DSchedhookTestFactory();
40 virtual TInt Install();
41 virtual void GetCaps(TDes8& aDes) const;
42 virtual TInt Create(DLogicalChannelBase*& aChannel);
45 class DHwExcHandler : public DKernelEventHandler
48 DHwExcHandler() : DKernelEventHandler(HandleEvent, this) {}
50 static TUint HandleEvent(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis);
53 class DSchedhookTest : public DLogicalChannelBase
56 virtual ~DSchedhookTest();
58 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
59 virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
61 DHwExcHandler* iHwExcHandler;
64 DECLARE_STANDARD_LDD()
66 return new DSchedhookTestFactory;
70 // DSchedhookTestFactory
73 DSchedhookTestFactory::DSchedhookTestFactory()
75 iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
78 TInt DSchedhookTestFactory::Create(DLogicalChannelBase*& aChannel)
80 Create a new DSchedhookTest on this logical device
83 aChannel=new DSchedhookTest;
84 return aChannel ? KErrNone : KErrNoMemory;
87 TInt DSchedhookTestFactory::Install()
89 Install the LDD - overriding pure virtual
92 return SetName(&KLddName);
95 void DSchedhookTestFactory::GetCaps(TDes8& aDes) const
97 Get capabilities - overriding pure virtual
101 b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
102 Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
110 Handle exceptions on our test thread by suspending it
112 TUint DHwExcHandler::HandleEvent(TKernelEvent aEvent, TAny* a1, TAny* /*a2*/, TAny* /*aThis*/)
114 if (aEvent == EEventHwExc)
116 if(&Kern::CurrentThread().iNThread!=TestThread)
118 TArmExcInfo *pR=(TArmExcInfo*)a1;
120 Kern::ThreadSuspend(Kern::CurrentThread(),1);
121 return (TUint)EExcHandled;
123 return (TUint)ERunNext;
130 TInt DSchedhookTest::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
135 if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
136 return KErrNotSupported;
137 iHwExcHandler = new DHwExcHandler;
140 return iHwExcHandler->Add();
143 DSchedhookTest::~DSchedhookTest()
146 iHwExcHandler->Close();
152 // Scheduler Hook functions
155 NThread* CurrentThread=NULL;
157 void DisableRescheduleCallback()
159 Stops the Scheduler calling us on every context switch
162 // Prevent rescheduling whilst we disable the callback
165 NKern::SetRescheduleCallback(NULL);
166 // Invalidate CurrentThread
167 CurrentThread = NULL;
168 // Callback now disabled...
174 void RemoveSchedulerHooks()
176 Removes the patches added to the Scheduler code.
179 // Make sure callback is disabled (required before removing hooks)
180 DisableRescheduleCallback();
181 // Get range of memory used by hooks
184 NKern::SchedulerHooks(start,end);
186 // Free shadow pages which cover hooks
187 TUint32 pageSize=Kern::RoundToPageSize(1);
188 NKern::ThreadEnterCS();
189 for(TLinAddr a=start; a<end; a+=pageSize)
190 Epoc::FreeShadowPage(a); // Ignore errors because we're trying to clean up anyway
191 NKern::ThreadLeaveCS();
196 TInt InsertSchedulerHooks()
198 Enables use of the Scheduler callback by using shadow pages to patch the Scheduler code.
201 // Get range of memory used by hooks
202 TLinAddr start=0,end=0;
204 NKern::SchedulerHooks(start,end);
206 if (start==0 && end==0) return KErrNotSupported;
208 // Create shadow pages for hooks
209 TUint32 pageSize=Kern::RoundToPageSize(1);
210 for(TLinAddr a=start; a<end; a+=pageSize)
212 NKern::ThreadEnterCS();
213 TInt r=Epoc::AllocShadowPage(a);
214 NKern::ThreadLeaveCS();
215 if(r!=KErrNone && r!=KErrAlreadyExists)
217 RemoveSchedulerHooks();
223 NKern::InsertSchedulerHooks();
225 // Make I and D caches consistant for hook region
226 Cache::IMB_Range(start,end-start);
233 void RescheduleTestFunction()
235 Test function to be called on each thread reschedule
238 // Count rechedules to the test thread
239 if(CurrentThread==TestThread)
245 void RescheduleCallback(NThread* aNThread)
247 Main scheduler callback.
248 Called with the Kernel Lock (Preemption Lock) held.
252 // The 'CurrentThread' is now unscheduled and has become the 'previous thread'
253 // Set this thread 'UserContextType'...
254 CurrentThread->SetUserContextType();
256 // Make the newly scheduled thread the CurrentThread
257 CurrentThread = aNThread;
259 RescheduleTestFunction();
264 void RescheduleCallbackFirst(NThread* aNThread)
266 Scheduler callback used once for initialisation.
267 Called with the Kernel Lock (Preemption Lock) held.
270 // Initialise CurrentThread
271 CurrentThread = aNThread;
272 // Switch future callbacks to the main RescheduleCallback
273 NKern::SetRescheduleCallback(RescheduleCallback);
275 RescheduleTestFunction();
280 void EnableRescheduleCallback()
282 Sets the Scheduler to call us back on every thread reschedule
285 // Reset the User Context Type for all threads, because these values
286 // will be out-of-date. (It is our Rescheduler callback which set's them.
287 // and we're just about enable that.)
289 NKern::ThreadEnterCS(); // Prevent us from dying or suspending whilst holding a DMutex
290 DObjectCon& threads=*Kern::Containers()[EThread]; // Get containing holding threads
291 threads.Wait(); // Obtain the container mutex so the list does get changed under us
294 // For each thread...
295 TInt c=threads.Count();
296 for (TInt i=0; i<c; i++)
297 ((DThread*)threads[i])->iNThread.ResetUserContextType();
300 threads.Signal(); // Release the container mutex
301 NKern::ThreadLeaveCS(); // End of critical section
304 NKern::SetRescheduleCallback(RescheduleCallbackFirst);
309 TInt GetThreadUserContext(NThread* aThread,TArmRegSet& aContext)
311 Test function to get a threads User Context
314 // Get the User Context Type which our Reschedule hook set
315 TInt type = aThread->iSpare3; /*iUserContextType*/
317 // Get the table corresponding to the User Context Type
318 const TArmContextElement* c = NThread::UserContextTables()[type];
320 // Set the User Context by using the table we got
321 TUint32* sp = (TUint32*)aThread->iSavedSP;
322 TUint32* st = (TUint32*)((TUint32)aThread->iStackBase+(TUint32)aThread->iStackSize);
323 TArmReg* out = (TArmReg*)(&aContext);
324 TArmReg* end = (TArmReg*)(&aContext+1);
330 if(t==TArmContextElement::EOffsetFromSp)
332 else if(t==TArmContextElement::EOffsetFromStackTop)
343 void DumpContext(TArmRegSet& aContext,TInt aType)
345 Kern::Printf(" Context type %d",aType);
346 Kern::Printf(" r0 =%08x r1 =%08x r2 =%08x r3 =%08x",aContext.iR0,aContext.iR1,aContext.iR2,aContext.iR3);
347 Kern::Printf(" r4 =%08x r5 =%08x r6 =%08x r7 =%08x",aContext.iR4,aContext.iR5,aContext.iR6,aContext.iR7);
348 Kern::Printf(" r8 =%08x r9 =%08x r10=%08x r11=%08x",aContext.iR8,aContext.iR9,aContext.iR10,aContext.iR11);
349 Kern::Printf(" r12=%08x r13=%08x r14=%08x r15=%08x",aContext.iR12,aContext.iR13,aContext.iR14,aContext.iR15);
350 Kern::Printf(" cpsr=%08x dacr=%08x",aContext.iFlags, aContext.iDacr);
355 TInt TestGetThreadContext(DThread* aThread,TDes8* aContext)
360 memclr(&context1,sizeof(context1));
361 memclr(&context2,sizeof(context2));
364 NKern::ThreadGetUserContext(&aThread->iNThread,&context1,unused);
365 TInt r=GetThreadUserContext(&aThread->iNThread,context2);
367 DumpContext(context1,-1);
368 DumpContext(context2,r);
371 Kern::KUDesInfo(*aContext,len,maxLen);
372 if(maxLen>KMaxThreadContext)
373 maxLen = KMaxThreadContext;
374 TPtr8 ptr((TUint8*)&context1,maxLen,maxLen);
375 Kern::KUDesPut(*aContext,ptr);
377 for(TUint i=0; i<sizeof(context1); i++)
378 if(((TUint8*)&context1)[i]!=((TUint8*)&context2)[i])
384 DThread* ThreadFromId(TUint aId)
386 // This is risky because the thread could die on us an the DThread* become invalid.
387 // We are relying on this never happening in our test code.
388 NKern::ThreadEnterCS(); // Prevent us from dying or suspending whilst holding a DMutex
389 DObjectCon& threads=*Kern::Containers()[EThread]; // Get containing holding threads
390 threads.Wait(); // Obtain the container mutex so the list does get changed under us
391 DThread* thread = Kern::ThreadFromId(aId);
392 threads.Signal(); // Release the container mutex
393 NKern::ThreadLeaveCS(); // End of critical section
397 TInt DSchedhookTest::Request(TInt aFunction, TAny* a1, TAny* a2)
399 Handle requests from the test program
405 case RSchedhookTest::EInstall:
406 r=InsertSchedulerHooks();
409 case RSchedhookTest::EUninstall:
410 RemoveSchedulerHooks();
413 case RSchedhookTest::EInsertHooks:
416 NKern::InsertSchedulerHooks();
418 NKern::SchedulerHooks(start,end);
420 Cache::IMB_Range(start,end-start);
424 case RSchedhookTest::ERemoveHooks:
427 NKern::SetRescheduleCallback(NULL);
428 NKern::RemoveSchedulerHooks();
430 NKern::SchedulerHooks(start,end);
432 Cache::IMB_Range(start,end-start);
436 case RSchedhookTest::EEnableCallback:
438 NKern::SetRescheduleCallback(RescheduleCallbackFirst);
442 case RSchedhookTest::EDisableCallback:
443 DisableRescheduleCallback();
446 case RSchedhookTest::ESetTestThread:
448 NKern::ThreadEnterCS(); // Prevent us from dying or suspending whilst holding a DMutex
449 DObjectCon& threads=*Kern::Containers()[EThread]; // Get containing holding threads
450 threads.Wait(); // Obtain the container mutex so the list does get changed under us
451 TestThread = &ThreadFromId((TUint)a1)->iNThread;
452 threads.Signal(); // Release the container mutex
453 NKern::ThreadLeaveCS(); // End of critical section
458 case RSchedhookTest::EGetThreadContext:
459 return TestGetThreadContext(ThreadFromId((TUint)a1),(TDes8*)a2);
461 case RSchedhookTest::EGetTestCount: