--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/benchmark/bm_ldd.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,704 @@
+// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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 "k32bm.h"
+
+const TUint8 KMutexOrder = 0xf0;
+
+class DBMLDevice : public DLogicalDevice
+ {
+public:
+ DBMLDevice();
+ virtual TInt Install();
+ virtual void GetCaps(TDes8& aDes) const;
+ virtual TInt Create(DLogicalChannelBase*& aChannel);
+ };
+
+class DBMLChannel : public DLogicalChannelBase, public MBMIsr, public MBMInterruptLatencyIsr
+ {
+public:
+ DBMLChannel();
+ ~DBMLChannel();
+
+ virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
+ virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
+
+ DBMPChannel* PChannel() { return (DBMPChannel*) iPdd; }
+
+private:
+ static const TInt KBMDfcQThreadPriority;
+ static const TInt KBMKernelThreadPriority;
+
+ static void Dfc(TAny*);
+
+ virtual void Isr(TBMTicks now);
+
+ TInt (DBMLChannel::*iRequestInterrupt)(); // Measurement specific RBMChannel::RequestInterrupt() implmentation
+ TInt RequestInterrupt(); // Default iRequestInterrupt() implementation
+
+ TBMTicks (DBMLChannel::*iResult)(); // Measurement specific RBMChannel::Result() implmentation
+ TBMTicks Result(); // Default iResult() implementation
+
+ TInt Start(RBMChannel::TMode);
+
+ TInt StartInterruptLatency();
+ virtual void InterruptLatencyIsr(TBMTicks latency);
+
+ TInt StartKernelPreemptionLatency();
+ static TInt KernelPreemptionLatencyThreadEntry(TAny* ptr);
+ void KernelPreemptionLatencyThread();
+
+ TInt StartUserPreemptionLatency();
+ TBMTicks UserPreemptionLatencyResult(); // iResult() implementation
+
+ TInt StartNTimerJitter();
+ TInt RequestNTimerJitterInterrupt(); // iRequestInterrupt() implementation
+ static void NTimerJitterCallBack(TAny*);
+
+ TInt StartTimerStampOverhead();
+ TInt RequestTimerStampOverhead(); // iRequestInterrupt() implementation
+
+ TInt SetAbsPrioirty(TInt aThreadHandle, TInt aNewPrio, TInt* aOldPrio);
+
+ DMutex* iLock; // Shall be acquired by anyone who access the object's writable state.
+
+ TBool iStarted; // ETrue when a particular sequence of measurements has been started
+ TBool iPendingInterruptRequest; // ETrue when an interrupt has been requested
+
+ TDynamicDfcQue* iDfcQ;
+ TDfc iDfc;
+
+ DThread* iKernelThread; // the kernel thread created by some benchmarks
+ DThread* iUserThread; // the user-side thread
+ DThread* iInterruptThread; // the thread signaled by DFC; if non-NULL either iKernelThread or iUserThread
+
+ NTimer iNTimer; // the timer used in "NTimer jitter" benchmark
+ TBMTicks iOneNTimerTick; // number of high-resolution timer ticks in one NKern tick.
+ TInt iNTimerShotCount; // used in "NTimer jitter" to distinguish between the first and the second shots
+
+ TBMTicks iTime;
+ TBMTicks iTimerPeriod; // period of high-resolution timer in ticks
+
+ NFastSemaphore* iKernelThreadExitSemaphore;
+
+ void Lock()
+ {
+ NKern::ThreadEnterCS();
+ Kern::MutexWait(*iLock);
+ }
+ void Unlock()
+ {
+ Kern::MutexSignal(*iLock);
+ NKern::ThreadLeaveCS();
+ }
+
+ TBMTicks Delta(TBMTicks aT0, TBMTicks aT1)
+ {
+ return (aT0 <= aT1) ? (aT1 - aT0) : iTimerPeriod - (aT0 - aT1);
+ }
+ };
+
+_LIT(KBMLChannelLit, "BMLChannel");
+
+const TInt DBMLChannel::KBMDfcQThreadPriority = KBMLDDHighPriority;
+const TInt DBMLChannel::KBMKernelThreadPriority = KBMLDDMidPriority;
+
+
+
+DECLARE_STANDARD_LDD()
+//
+// Create a new device
+//
+ {
+ __ASSERT_CRITICAL;
+ return new DBMLDevice;
+ }
+
+DBMLDevice::DBMLDevice()
+//
+// Constructor
+//
+ {
+ //iUnitsMask=0;
+ iVersion = TVersion(1,0,1);
+ iParseMask = KDeviceAllowPhysicalDevice;
+ }
+
+TInt DBMLDevice::Install()
+//
+// Install the device driver.
+//
+ {
+ TInt r = SetName(&KBMLdName);
+ return r;
+ }
+
+void DBMLDevice::GetCaps(TDes8&) const
+//
+// Return the Comm capabilities.
+//
+ {
+ }
+
+TInt DBMLDevice::Create(DLogicalChannelBase*& aChannel)
+//
+// Create a channel on the device.
+//
+ {
+ __ASSERT_CRITICAL;
+ aChannel = new DBMLChannel;
+ return aChannel ? KErrNone : KErrNoMemory;
+ }
+
+DBMLChannel::DBMLChannel() :
+ iDfc(0, this, 0, 0),
+ iNTimer(NULL, this)
+ {
+ // iDfcQueue = NULL;
+ // iStarted = EFalse;
+ // iPendingInterruptRequest = EFalse;
+ // iKernelThread = NULL;
+ }
+
+TInt DBMLChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /* aInfo*/ , const TVersion& aVer)
+//
+// Create the channel from the passed info.
+//
+ {
+ __ASSERT_CRITICAL;
+ if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
+ return KErrNotSupported;
+ TInt r = Kern::MutexCreate(iLock, KBMLChannelLit, KMutexOrder);
+ if (r != KErrNone)
+ {
+ return r;
+ }
+ iTimerPeriod = PChannel()->TimerPeriod();
+ // Calculate the number of high-resolution timer ticks in one NKern tick
+ // deviding the number of high-resolution timer ticks in one second by the
+ // number of NKern ticks in one second.
+ //
+ iOneNTimerTick = PChannel()->TimerNsToTicks(BMSecondsToNs(1))/NKern::TimerTicks(1000);
+ return KErrNone;
+ }
+
+DBMLChannel::~DBMLChannel()
+// Called on a channel close. Note that if the PDD channel create failed
+// then the DoCreate() call will not have been made so don't assume anything
+// about non-ctor initialisation of members.
+ {
+ if (iLock)
+ iLock->Close(0);
+
+ if (iPendingInterruptRequest)
+ {
+ PChannel()->CancelInterrupt();
+ iDfc.Cancel();
+ }
+
+ if (iDfcQ)
+ {
+ iDfcQ->Destroy();
+ }
+
+ if (iKernelThread)
+ {
+ NFastSemaphore exitSemaphore;
+ exitSemaphore.iOwningThread = NKern::CurrentThread();
+ iKernelThreadExitSemaphore = &exitSemaphore;
+ NKern::ThreadRequestSignal(&iKernelThread->iNThread);
+ NKern::FSWait(&exitSemaphore);
+ }
+ }
+
+void DBMLChannel::Dfc(TAny* ptr)
+ {
+ DBMLChannel* lCh = (DBMLChannel*) ptr;
+ BM_ASSERT(lCh->iPendingInterruptRequest);
+ BM_ASSERT(lCh->iInterruptThread);
+ NKern::ThreadRequestSignal(&lCh->iInterruptThread->iNThread);
+ lCh->iPendingInterruptRequest = EFalse;
+ }
+
+//
+// Default DBMLChannel::iRequestInterrupt implementation
+//
+TInt DBMLChannel::RequestInterrupt()
+ {
+ if (!iStarted)
+ {
+ return KErrNotReady;
+ }
+ if (iPendingInterruptRequest)
+ {
+ return KErrInUse;
+ }
+ iPendingInterruptRequest = ETrue;
+ PChannel()->RequestInterrupt();
+ return KErrNone;
+ }
+
+//
+// Default DBMLChannel::iResult implementation
+//
+TBMTicks DBMLChannel::Result()
+ {
+ return iTime;
+ }
+
+void DBMLChannel::Isr(TBMTicks aNow)
+ {
+ //
+ // Store the ISR entry time and queue a DFC.
+ //
+ iTime = aNow;
+ iDfc.Add();
+ }
+
+//
+// "INTERRUPT LATENCY"
+//
+// SCENARIO:
+//
+// A user thread requests an interrupt (RBMChannel::RequestInterrupt()) and waits at User::WaitForAnyRequest()
+// (RBMChannel::Result()).
+// When the interrupt occurs DBMLChannel::InterruptLatencyIsr() stores the interrupt latency
+// provided by LDD, in DBMLChannel::iTime and queues a DFC (DBMLChannel::iDfc, DBMLChannel::Dfc())
+// which in its turn signals the user thread.
+//
+
+TInt DBMLChannel::StartInterruptLatency()
+ {
+ if (iStarted)
+ {
+ return KErrInUse;
+ }
+ TInt r = PChannel()->BindInterrupt((MBMInterruptLatencyIsr*) this);
+ if (r != KErrNone)
+ {
+ return r;
+ }
+ // Use the default iRequestInterrupt() implmentation
+ iRequestInterrupt = &DBMLChannel::RequestInterrupt;
+ // Use the default iResult() implmentation
+ iResult = &DBMLChannel::Result;
+ iInterruptThread = &Kern::CurrentThread();
+ iStarted = ETrue;
+ return KErrNone;
+ }
+
+void DBMLChannel::InterruptLatencyIsr(TBMTicks aLatency)
+ {
+ iTime = aLatency;
+ iDfc.Add();
+ }
+
+//
+// "KERNEL THREAD PREEMPTION LATENCY"
+//
+// SCENARIO:
+//
+// A new kernel thread is created at the beginning of a sequence of measurements
+// (DBMLChannel::StartKernelPreemptionLatency()). The kernel thread waits at Kern::WaitForAnyRequest()
+// (DBMLChannel::KernelPreemptionLatencyThread()).
+// The user thread requests an interrupt (RBMChannel::RequestInterrupt()) and waits at User::WaitForAnyRequest()
+// (RBMChannel::Result()).
+// When the interrupt occurs DBMLChannel::Isr() stores the ISR entry time, provided by LDD
+// in DBMLChannel::iTime and queues a DFC (DBMLChannel::iDfc, DBMLChannel::Dfc()) which, in its turn,
+// signals the kernel thread.
+// The kernel thread, when awaken, calculates the latency as the difference between the ISR entry time
+// and the current time and finally signals the user thread.
+//
+
+TInt DBMLChannel::StartKernelPreemptionLatency()
+ {
+ if (iStarted)
+ {
+ return KErrInUse;
+ }
+ TInt r = PChannel()->BindInterrupt((MBMIsr*) this);
+ if (r != KErrNone)
+ {
+ return r;
+ }
+ {
+ SThreadCreateInfo info;
+ info.iType = EThreadSupervisor;
+ info.iFunction = DBMLChannel::KernelPreemptionLatencyThreadEntry;
+ info.iPtr = this;
+ info.iSupervisorStack = NULL;
+ info.iSupervisorStackSize = 0;
+ info.iInitialThreadPriority = KBMKernelThreadPriority;
+ info.iName.Set(KBMLChannelLit);
+ info.iTotalSize = sizeof(info);
+ r = Kern::ThreadCreate(info);
+ if (r != KErrNone)
+ {
+ return r;
+ }
+ iKernelThread = (DThread*) info.iHandle;
+ }
+
+ iUserThread = &Kern::CurrentThread();
+ // Use the default iRequestInterrupt() implmentation
+ iRequestInterrupt = &DBMLChannel::RequestInterrupt;
+ // Use the default iResult() implmentation
+ iResult = &DBMLChannel::Result;
+ iInterruptThread = iKernelThread;
+ iStarted = ETrue;
+
+ Kern::ThreadResume(*iKernelThread);
+
+ return KErrNone;
+ }
+
+TInt DBMLChannel::KernelPreemptionLatencyThreadEntry(TAny* ptr)
+ {
+ DBMLChannel* lCh = (DBMLChannel*) ptr;
+ lCh->KernelPreemptionLatencyThread();
+ BM_ASSERT(0);
+ return 0;
+ }
+
+void DBMLChannel::KernelPreemptionLatencyThread()
+ {
+ for(;;)
+ {
+ NKern::WaitForAnyRequest();
+
+ if(iKernelThreadExitSemaphore)
+ break;
+
+ TBMTicks now = PChannel()->TimerStamp();
+ iTime = Delta(iTime, now);
+ BM_ASSERT(iUserThread);
+ NKern::ThreadRequestSignal(&iUserThread->iNThread);
+ }
+
+ NKern::FSSignal(iKernelThreadExitSemaphore);
+ Kern::Exit(0);
+ }
+
+
+//
+// "USER THREAD PREEMPTION LATENCY"
+//
+// SCENARIO:
+//
+// A user thread requests an interrupt (RBMChannel::RequestInterrupt()) and waits at User::WaitForAnyRequest()
+// (RBMChannel::Result()).
+// When the interrupt occurs DBMLChannel::Isr() stores the ISR entry time provided by LDD,
+// in DBMLChannel::iTime and queues a DFC (DBMLChannel::iDfc, DBMLChannel::Dfc()) which in its turn
+// signals the user thread.
+// The user thread, when awaken, immediately re-enters in the LDD, and calculates the latency as
+// the difference between the ISR entry time and the current time.
+//
+
+TInt DBMLChannel::StartUserPreemptionLatency()
+ {
+ if (iStarted)
+ {
+ return KErrInUse;
+ }
+ TInt r = PChannel()->BindInterrupt((MBMIsr*) this);
+ if (r != KErrNone)
+ {
+ return r;
+ }
+ // Default iRequestInterrupt() implmentation
+ iRequestInterrupt = &DBMLChannel::RequestInterrupt;
+ iResult = &DBMLChannel::UserPreemptionLatencyResult;
+ iInterruptThread = &Kern::CurrentThread();
+ iStarted = ETrue;
+ return KErrNone;
+ }
+
+TBMTicks DBMLChannel::UserPreemptionLatencyResult()
+ {
+ TBMTicks now = PChannel()->TimerStamp();
+ return Delta(iTime, now);
+ }
+
+//
+// "NTimer PERIOD JITTER"
+//
+// SCENARIO:
+//
+// One measuremnt is done by two consecutive NTimer callbacks.
+// The first callback stores the current time and the second one calculate the actual period as
+// the difference between its own current time and the time stored by the first callback.
+// The difference between this actual period and the theoretical period is considered as the jitter.
+//
+
+TInt DBMLChannel::StartNTimerJitter()
+ {
+ if (iStarted)
+ {
+ return KErrInUse;
+ }
+ new (&iNTimer) NTimer(&NTimerJitterCallBack, this);
+ iRequestInterrupt = &DBMLChannel::RequestNTimerJitterInterrupt;
+ // Use the default iResult() implmentation
+ iResult = &DBMLChannel::Result;
+ iInterruptThread = &Kern::CurrentThread();
+ iStarted = ETrue;
+ return KErrNone;
+ }
+
+TInt DBMLChannel::RequestNTimerJitterInterrupt()
+ {
+ if (!iStarted)
+ {
+ return KErrNotReady;
+ }
+ if (iPendingInterruptRequest)
+ {
+ return KErrInUse;
+ }
+ iPendingInterruptRequest = ETrue;
+ iNTimerShotCount = 0;
+ iNTimer.OneShot(1);
+ return KErrNone;
+ }
+
+
+void DBMLChannel::NTimerJitterCallBack(TAny* ptr)
+ {
+ DBMLChannel* lCh = (DBMLChannel*) ptr;
+ TBMTicks now = lCh->PChannel()->TimerStamp();
+ if (lCh->iNTimerShotCount++ == 0)
+ {
+ //
+ // This is the first callback: store the time and request another one.
+ //
+ lCh->iTime = now;
+ lCh->iNTimer.Again(1);
+ }
+ else
+ {
+ //
+ // This is the second callback: measure the jitter and schedule a DFC
+ // which in its turn will signal the user thread.
+ //
+ lCh->iTime = lCh->Delta(lCh->iTime, now);
+ lCh->iDfc.Add();
+ }
+ }
+
+//
+// "TIMER OVERHEAD"
+//
+// SCENARIO:
+// To measure the overhead of the high-precision timer read operation we get
+// two consecutive timestamps through DBMPChannel::TimerStamp() interface.
+// The difference beween this two values is considered as the measured overhead.
+//
+
+TInt DBMLChannel::StartTimerStampOverhead()
+ {
+ if (iStarted)
+ {
+ return KErrInUse;
+ }
+ iRequestInterrupt = &DBMLChannel::RequestTimerStampOverhead;
+ // Use the default iResult() implmentation
+ iResult = &DBMLChannel::Result;
+ iInterruptThread = &Kern::CurrentThread();
+ iStarted = ETrue;
+ return KErrNone;
+ }
+
+TInt DBMLChannel::RequestTimerStampOverhead()
+ {
+ TBMTicks t1 = PChannel()->TimerStamp();
+ TBMTicks t2 = PChannel()->TimerStamp();
+ iTime = Delta(t1, t2);
+ NKern::ThreadRequestSignal(&iInterruptThread->iNThread);
+ return KErrNone;
+ }
+//
+// END OF "GETTING TIMER OVERHEAD"
+//
+
+//
+// The implmentation of RBMDriver::SetAbsPrioirty() call.
+//
+TInt DBMLChannel::SetAbsPrioirty(TInt aThreadHandle, TInt aNewPrio, TInt* aOldPrio)
+ {
+ NKern::LockSystem();
+ //
+ // Under the system lock find the DThread object and increment its ref-count (i.e Open())
+ //
+ DThread* thr = (DThread*) Kern::ObjectFromHandle(&Kern::CurrentThread(), aThreadHandle, EThread);
+ TInt r;
+ if (!thr)
+ {
+ r = EBadHandle;
+ }
+ else
+ {
+ r = thr->Open();
+ }
+ //
+ // Now it's safe to release the system lock and to work with the object.
+ //
+ NKern::ThreadEnterCS();
+ NKern::UnlockSystem();
+ if (r != KErrNone)
+ {
+ NKern::ThreadLeaveCS();
+ return r;
+ }
+ *aOldPrio = thr->iDefaultPriority;
+ Kern::SetThreadPriority(aNewPrio, thr);
+ //
+ // Work is done - close the object.
+ //
+ thr->Close(NULL);
+ NKern::ThreadLeaveCS();
+ return KErrNone;
+ }
+
+_LIT(KBmDfcQName, "BmDfcQ");
+
+//
+// Starts a new sequence of measurements.
+//
+// Only one sequence can be started for any particular DBMLChannel object during its life.
+// If more than one sequence is required a new DBMLChannel object must be created.
+//
+TInt DBMLChannel::Start(RBMChannel::TMode aMode)
+ {
+ TInt r;
+ if (iDfcQ == NULL)
+ {
+ r = Kern::DynamicDfcQCreate(iDfcQ, KBMDfcQThreadPriority, KBmDfcQName);
+ if (r != KErrNone)
+ return r;
+
+ iDfc.SetDfcQ(iDfcQ);
+ iDfc.SetFunction(Dfc);
+ }
+
+ switch (aMode)
+ {
+ case RBMChannel::EInterruptLatency:
+ r = StartInterruptLatency();
+ break;
+ case RBMChannel::EKernelPreemptionLatency:
+ r = StartKernelPreemptionLatency();
+ break;
+ case RBMChannel::EUserPreemptionLatency:
+ r = StartUserPreemptionLatency();
+ break;
+ case RBMChannel::ENTimerJitter:
+ r = StartNTimerJitter();
+ break;
+ case RBMChannel::ETimerStampOverhead:
+ r = StartTimerStampOverhead();
+ break;
+ default:
+ r = KErrNotSupported;
+ break;
+ }
+
+ return r;
+ }
+
+//
+// Client requests.
+//
+TInt DBMLChannel::Request(TInt aFunction, TAny* a1, TAny* a2)
+ {
+ TInt r = KErrNone;
+ switch (aFunction)
+ {
+ case RBMChannel::EStart:
+ {
+ RBMChannel::TMode mode = (RBMChannel::TMode) (TInt) a1;
+ Lock();
+ r = Start(mode);
+ Unlock();
+ break;
+ }
+ case RBMChannel::ERequestInterrupt:
+ {
+ Lock();
+ r = (this->*iRequestInterrupt)();
+ Unlock();
+ break;
+ }
+ case RBMChannel::EResult:
+ {
+ //
+ // We don't acquire the lock because:
+ // (1) iResult() typically reads iTime which was written BEFORE to signal the current thread
+ // and therefore BEFORE the current thread comes here.
+ // (2) we really want if possible (i.e. correct!) to avoid the lock acquisition because it can
+ // increase the measurement overhead in the case when we are in a measured path (e.g. user
+ // preemption latency benchmark).
+ //
+ TBMTicks ticks = (this->*iResult)();
+ umemput(a1, &ticks, sizeof(ticks));
+ break;
+ }
+ //
+ // All below requests do not access writable DBMChannel state and therefore do not require the lock
+ //
+ case RBMChannel::ETimerStamp:
+ {
+ TBMTicks ticks = PChannel()->TimerStamp();
+ umemput(a1, &ticks, sizeof(ticks));
+ break;
+ }
+ case RBMChannel::ETimerPeriod:
+ {
+ TBMTicks ticks = iTimerPeriod;
+ umemput(a1, &ticks, sizeof(ticks));
+ break;
+ }
+ case RBMChannel::ETimerTicksToNs:
+ {
+ TBMTicks ticks;
+ umemget(&ticks, a1, sizeof(ticks));
+ TBMNs ns = PChannel()->TimerTicksToNs(ticks);
+ umemput(a2, &ns, sizeof(ns));
+ break;
+ }
+ case RBMChannel::ETimerNsToTicks:
+ {
+ TBMNs ns;
+ umemget(&ns, a1, sizeof(ns));
+ TBMTicks ticks = PChannel()->TimerNsToTicks(ns);
+ umemput(a2, &ticks, sizeof(ticks));
+ break;
+ }
+ case RBMChannel::ESetAbsPriority:
+ {
+ TInt newPrio;
+ TInt oldPrio;
+ umemget(&newPrio, a2, sizeof(newPrio));
+ r = SetAbsPrioirty((TInt) a1, newPrio, &oldPrio);
+ umemput(a2, &oldPrio, sizeof(oldPrio));
+ break;
+ }
+ default:
+ r = KErrNotSupported;
+ break;
+ }
+ return r;
+ }
+
+