--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/debug/d_eventtracker.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,916 @@
+// Copyright (c) 2003-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:
+// e32test\debug\d_eventtracker.cpp
+// LDD-based debug agent used to track kernel events. See
+// t_eventtracker.cpp
+//
+//
+
+#include <kernel/kern_priv.h>
+#include "reventtracker.h"
+#include "d_eventtracker.h"
+#include "nk_trace.h"
+
+#ifdef __MARM__
+#include <kernel/kdebug.h>
+#endif //__MARM__
+
+#ifdef _DEBUG
+static const char KPanicCat[] = "D_EVENTTRACKER";
+#endif // _DEBUG
+_LIT(KClientPanicCat, "D_EVENTTRACKER");
+
+DEventTracker* TheEventTracker;
+
+//////////////////////////////////////////////////////////////////////////////
+
+/** Data about objects being tracked.
+ All tracked objects are kept in a tracking list. The object address
+ is a key and so must be unique.
+ */
+
+TTrackedItem::TTrackedItem(const DBase* aObject)
+ : iObject(aObject), iAccountedFor(EFalse)
+ {
+ }
+
+
+/** Subclass for DObjects being tracked */
+
+TTrackedObject::TTrackedObject(DObject* aObject, TObjectType aType)
+ : TTrackedItem(aObject),
+ iType(aType)
+ {
+ aObject->FullName(iFullName);
+ }
+
+TBool TTrackedObject::CheckIntegrity(const TDesC& aName, TObjectType aType) const
+ {
+ TBool ok = EFalse;
+
+ if (aType == iType)
+ {
+ if (aType == EThread || aType == EProcess)
+ {
+ ok = (iFullName == aName);
+ }
+ else
+ {
+ ok = ETrue;
+ }
+ }
+
+ if (!ok)
+ {
+ Kern::Printf("EVENTTRACKER: container / tracking list mismatch (0x%08x)", iObject);
+ Kern::Printf("EVENTTRACKER: \tcontainer: %S (type %d)", &aName, aType);
+ Kern::Printf("EVENTTRACKER: \ttracking list: %S (type %d)", &iFullName, iType);
+ }
+
+ return ok;
+ }
+
+/** Subclass for DCodeSegs being tracked */
+
+TTrackedCodeSeg::TTrackedCodeSeg(const DCodeSeg* aCodeSeg)
+ : TTrackedItem(aCodeSeg),
+ iAccessCount(aCodeSeg ? aCodeSeg->iAccessCount : 0)
+ {
+ }
+
+TBool TTrackedCodeSeg::CheckIntegrity(TInt aAccessCount) const
+ {
+ const TBool ok = (aAccessCount == iAccessCount);
+
+ if (!ok)
+ {
+ Kern::Printf("EVENTTRACKER: code seg list / tracking list mismatch (0x%08x)", iObject);
+ Kern::Printf("EVENTTRACKER: \tcode seg list: %d", aAccessCount);
+ Kern::Printf("EVENTTRACKER: \ttracking list: %d", iAccessCount);
+ }
+
+ return ok;
+ }
+
+
+/** Event handler and container for all objects being tracked. */
+
+DEventTracker::DEventTracker()
+ : DKernelEventHandler(EventHandler, this)
+ {
+ __ASSERT_DEBUG(!TheEventTracker, Kern::Fault(KPanicCat, __LINE__));
+
+ TheEventTracker = this;
+ }
+
+
+//
+// If aUseHook is true, the event tracker hooks the stop-mode debugger
+// breakpoint in preference to adding itself to the kernel event handler
+// queue. In order to clean up on its destruction, it has to
+// reset the breakpoint by installing a dummy nop breakpoint
+// handler, which is cut-and-pasted from kdebug.dll in order to
+// avoid a dependency on kdebug.dll. In order to use the event
+// tracker using the stop-mode debugger breakpoint rather than
+// the kernel event handler queue, kdebug.dll must be present in
+// the ROM
+//
+TInt DEventTracker::Create(DLogicalDevice* aDevice, TBool aUseHook)
+ {
+ TInt err = aDevice->Open();
+
+ if (err)
+ {
+ return err;
+ }
+
+ iDevice = aDevice;
+
+ err = Kern::MutexCreate(iLock, _L("EventHandlerLock"), KMutexOrdNone);
+
+ if (!err)
+ {
+ if (aUseHook)
+ {
+ // Find debugger info, if any
+ DDebuggerInfo* const debugInfo = Kern::SuperPage().iDebuggerInfo;
+
+ // Test stop-mode breakpoint if available
+ if (debugInfo)
+ {
+#ifdef __MARM__
+ // Receive all events
+ for (TInt i = 0; i < ((EEventLimit + 31) >> 5); ++i)
+ {
+ debugInfo->iEventMask[i] = 0xffffffffu;
+ }
+
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Copying breakpoint (0x%x) into handler (0x%x), size %d", &BranchToEventHandler, debugInfo->iEventHandlerBreakpoint, BreakPointSize()));
+
+ // Set up breakpoint to call handler
+ memcpy((TAny*)debugInfo->iEventHandlerBreakpoint, (TAny*) &BranchToEventHandler, BreakPointSize());
+#else // !__MARM__
+ err = KErrNotFound;
+#endif // __MARM__
+ }
+ else
+ {
+ err = KErrNotFound;
+ }
+ }
+ else
+ {
+ err = Add();
+ }
+ }
+
+ return err;
+ }
+
+
+DEventTracker::~DEventTracker()
+ {
+#ifdef __MARM__
+ // Remove breakpoint, if any
+ DDebuggerInfo* const debugInfo = Kern::SuperPage().iDebuggerInfo;
+ if (debugInfo)
+ {
+ CopyDummyHandler(debugInfo->iEventHandlerBreakpoint);
+ }
+#endif //__MARM__
+
+ // clean-up tracking list
+ SDblQueLink* link = iItems.GetFirst();
+ while (link)
+ {
+ delete _LOFF(link, TTrackedItem, iLink);
+ link = iItems.GetFirst();
+ }
+
+ if (iLock)
+ {
+ iLock->Close(NULL);
+ }
+
+ if (iDevice)
+ {
+ iDevice->Close(NULL);
+ }
+
+ TheEventTracker = NULL;
+ }
+
+
+TInt DEventTracker::Start()
+ {
+ TInt err = AddExistingObjects();
+
+ if (!err)
+ {
+ iTracking = ETrue;
+ }
+
+ return err;
+ }
+
+
+TInt DEventTracker::Stop()
+ {
+ NKern::ThreadEnterCS();
+ Kern::MutexWait(*iLock);
+
+ iTracking = EFalse;
+
+ Kern::MutexSignal(*iLock);
+ NKern::ThreadLeaveCS();
+
+ DumpCounters();
+
+ return CheckIntegrity();
+ }
+
+
+TUint DEventTracker::EventHandler(TKernelEvent aType, TAny* a1, TAny* a2, TAny* aThis)
+ {
+ return ((DEventTracker*)aThis)->HandleEvent(aType, a1, a2);
+ }
+
+
+TUint DEventTracker::HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2)
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Handling event type 0x%x", aType));
+
+ Kern::MutexWait(*iLock);
+
+ if (iTracking)
+ {
+ ++iCounters[aType];
+
+ switch (aType)
+ {
+ case EEventAddProcess:
+ AddObject(EProcess, (DObject*)a1);
+ break;
+ case EEventUpdateProcess:
+ // could be renaming or chunk addition/deletion
+ UpdateObject(EProcess, (DObject*)a1, EFalse);
+ break;
+ case EEventRemoveProcess:
+ RemoveObject(EProcess, (DObject*)a1);
+ break;
+ case EEventAddThread:
+ AddObject(EThread, (DObject*)a1);
+ break;
+ case EEventUpdateThread:
+ UpdateObject(EThread, (DObject*)a1, ETrue);
+ break;
+ case EEventRemoveThread:
+ RemoveObject(EThread, (DObject*)a1);
+ break;
+ case EEventAddLibrary:
+ {
+ DLibrary* pL = (DLibrary*)a1;
+ if (pL->iMapCount == 1)
+ AddObject(ELibrary, pL);
+ }
+ break;
+ case EEventRemoveLibrary:
+ {
+ DLibrary* pL = (DLibrary*)a1;
+ if (pL->iMapCount == 0)
+ RemoveObject(ELibrary, pL);
+ }
+ break;
+ case EEventNewChunk:
+ AddObject(EChunk, (DObject*)a1);
+ break;
+ case EEventDeleteChunk:
+ RemoveObject(EChunk, (DObject*)a1);
+ break;
+ case EEventAddCodeSeg:
+ {
+ AddCodeSeg((DCodeSeg*)a1, (DProcess*)a2);
+ }
+ break;
+ case EEventRemoveCodeSeg:
+ {
+ RemoveCodeSeg((DCodeSeg*)a1, (DProcess*)a2);
+ }
+ break;
+ case EEventLoadedProcess:
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Process %O loaded", a1));
+ ProcessLoaded((DProcess*)a1);
+ }
+ break;
+ case EEventUnloadingProcess:
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Process %O unloaded", a1));
+ break;
+ default:
+ // no-op
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Handling default case"));
+ break;
+ }
+ }
+
+ Kern::MutexSignal(*iLock);
+
+ // Allow other handlers to see this event
+ return DKernelEventHandler::ERunNext;
+ }
+
+
+void DEventTracker::AddObject(TObjectType aType, DObject* aObject)
+ {
+ TTrackedObject* trackedObject = (TTrackedObject*)LookupItem(aObject);
+
+ if (trackedObject)
+ {
+ Kern::Printf("EVENTTRACKER: Found orphaned object %O in tracking list while adding new object", aObject);
+ ++iErrorCount;
+ return;
+ }
+
+ NKern::ThreadEnterCS();
+ trackedObject = new TTrackedObject(aObject, aType);
+ NKern::ThreadLeaveCS();
+
+ if (trackedObject)
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Adding %O (type %d) to tracking list", aObject, aType));
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: DBase ptr == 0x%x", trackedObject->iObject));
+ iItems.Add(&trackedObject->iLink);
+ }
+ else
+ {
+ iOOM = ETrue;
+ ++iErrorCount;
+ }
+ }
+
+
+void DEventTracker::RemoveObject(TObjectType aType, DObject* aObject)
+ {
+ TTrackedObject* const trackedObject = (TTrackedObject*)LookupItem(aObject);
+
+ if (trackedObject)
+ {
+ TFullName name;
+ aObject->FullName(name);
+ if (!trackedObject->CheckIntegrity(name, aType))
+ {
+ ++iErrorCount;
+ }
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Removing %S (type %d) from tracking list", &name, aType));
+ trackedObject->iLink.Deque();
+
+ NKern::ThreadEnterCS();
+ delete trackedObject;
+ NKern::ThreadLeaveCS();
+ }
+ else
+ {
+ Kern::Printf("EVENTTRACKER: %O (type %d) removed but not in tracking list", aObject, aType);
+ ++iErrorCount;
+ }
+ }
+
+
+void DEventTracker::UpdateObject(TObjectType aType, DObject* aObject, TBool aMustBeRenamed)
+ {
+ TTrackedObject* const trackedObject = (TTrackedObject*)LookupItem(aObject);
+
+ if (trackedObject)
+ {
+ TFullName newName;
+ aObject->FullName(newName);
+ if (newName != trackedObject->iFullName)
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Renaming %S --> %S (type %d)",
+ &trackedObject->iFullName, &newName));
+ trackedObject->iFullName = newName;
+ }
+ else if (aMustBeRenamed)
+ {
+ Kern::Printf("EVENTTRACKER: %O (type %d) renamed with same name", aObject, aType);
+ ++iErrorCount;
+ }
+ }
+ else
+ {
+ Kern::Printf("EVENTTRACKER: %O (type %d) updated but not in tracking list", aObject, aType);
+ Kern::Printf("EVENTTRACKER: DBase ptr == 0x%x", (DBase*)aObject);
+ ++iErrorCount;
+ }
+ }
+
+void DEventTracker::AddCodeSeg(DCodeSeg* aCodeSeg, DProcess* aProcess)
+ {
+ TTrackedCodeSeg* trackedCodeSeg = (TTrackedCodeSeg*)LookupItem(aCodeSeg);
+
+ if (trackedCodeSeg)
+ {
+ if (aProcess && (aProcess->iTempCodeSeg == aCodeSeg))
+ {
+ // This is the exe code seg for a loading process
+ // and hence the access count is currently
+ // incremented by one
+ ++trackedCodeSeg->iAccessCount;
+ }
+
+ if (trackedCodeSeg->iAccessCount != aCodeSeg->iAccessCount)
+ {
+ Kern::Printf(
+ "EVENTTRACKER: Access count for %C (%d) does not match the tracking list (%d)",
+ aCodeSeg,
+ aCodeSeg->iAccessCount,
+ trackedCodeSeg->iAccessCount
+ );
+ ++iErrorCount;
+ return;
+ }
+ }
+
+ if (!trackedCodeSeg)
+ {
+ NKern::ThreadEnterCS();
+ trackedCodeSeg = new TTrackedCodeSeg(aCodeSeg);
+ NKern::ThreadLeaveCS();
+
+ if (trackedCodeSeg)
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Adding %C to tracking list", aCodeSeg));
+ iItems.Add(&trackedCodeSeg->iLink);
+ }
+ }
+ else // trackedCodeSeg
+ {
+ if (aProcess)
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Updating access count for %C (%d), attaching to process %O", aCodeSeg, aCodeSeg->iAccessCount, aProcess));
+ }
+ else // !aProcess
+ {
+ Kern::Printf("EVENTTRACKER: Found orphaned code seg %C in tracking list while adding new code seg", aCodeSeg);
+ ++iErrorCount;
+ }
+ }
+
+ if (!trackedCodeSeg)
+ {
+ iOOM = ETrue;
+ ++iErrorCount;
+ }
+ }
+
+void DEventTracker::ProcessLoaded(DProcess* aProcess)
+ {
+ if (aProcess->iCodeSeg)
+ {
+ TTrackedCodeSeg* trackedCodeSeg = (TTrackedCodeSeg*)LookupItem(aProcess->iCodeSeg);
+
+ if (trackedCodeSeg)
+ {
+ // This is the exe code seg for a process that
+ // has completed loading and hence the access
+ // count has just been decremented by one
+ --trackedCodeSeg->iAccessCount;
+ }
+ }
+ }
+
+void DEventTracker::RemoveCodeSeg(DCodeSeg* aCodeSeg, DProcess* aProcess)
+ {
+ TTrackedCodeSeg* const trackedCodeSeg = (TTrackedCodeSeg*)LookupItem(aCodeSeg);
+
+ if (trackedCodeSeg)
+ {
+ if (!trackedCodeSeg->CheckIntegrity(aCodeSeg->iAccessCount))
+ {
+ ++iErrorCount;
+ }
+
+ if (aCodeSeg->iAccessCount == 1)
+ {
+ __KTRACE_OPT(KDEBUGGER, Kern::Printf("EVENTTRACKER: Removing %C from tracking list, process %O", aCodeSeg, aProcess));
+ trackedCodeSeg->iLink.Deque();
+
+ NKern::ThreadEnterCS();
+ delete trackedCodeSeg;
+ NKern::ThreadLeaveCS();
+ }
+ }
+ else
+ {
+ Kern::Printf("EVENTTRACKER: %C removed but not in tracking list. Removing from process %O", aCodeSeg, aProcess);
+ ++iErrorCount;
+ }
+ }
+
+
+/** Add all objects from relevant containers into the tracking list. */
+
+TInt DEventTracker::AddExistingObjects()
+ {
+ // Tracking can be started only after all containers read to avoid
+ // race conditions.
+ __ASSERT_DEBUG(!iTracking, Kern::Fault(KPanicCat, __LINE__));
+
+ TInt err = KErrNone;
+ Kern::Printf("Adding processes");
+ err = AddObjectsFromContainer(EProcess);
+ if (err)
+ {
+ return err;
+ }
+ Kern::Printf("Adding threads");
+ err = AddObjectsFromContainer(EThread);
+ if (err)
+ {
+ return err;
+ }
+ Kern::Printf("Adding libraries");
+ err = AddObjectsFromContainer(ELibrary);
+ if (err)
+ {
+ return err;
+ }
+ Kern::Printf("Adding chunks");
+ err = AddObjectsFromContainer(EChunk);
+ if (err)
+ {
+ return err;
+ }
+ Kern::Printf("Adding LDDs");
+ err = AddObjectsFromContainer(ELogicalDevice);
+ if (err)
+ {
+ return err;
+ }
+ Kern::Printf("Adding PDDs");
+ err = AddObjectsFromContainer(EPhysicalDevice);
+ if (err)
+ {
+ return err;
+ }
+ Kern::Printf("Adding code segs");
+ return AddCodeSegsFromList();
+ }
+
+/** Add all objects from specified container into tracking list. */
+
+TInt DEventTracker::AddObjectsFromContainer(TObjectType aType)
+ {
+ DObjectCon* const container = Kern::Containers()[aType];
+
+ NKern::ThreadEnterCS();
+ container->Wait();
+
+ const TInt count = container->Count();
+ TInt err = KErrNone;
+
+ for (TInt i = 0; (i < count && err == KErrNone); ++i)
+ {
+ DObject* const object = (*container)[i];
+ if (object->Open() == KErrNone)
+ {
+ AddObject(aType, object);
+ if (iOOM)
+ {
+ err = KErrNoMemory;
+ }
+ object->Close(NULL);
+ }
+ }
+
+ container->Signal();
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+TInt DEventTracker::AddCodeSegsFromList()
+ {
+ Kern::AccessCode();
+
+ const SDblQueLink* const anchor = &Kern::CodeSegList()->iA;
+ for (SDblQueLink* link = Kern::CodeSegList()->First(); link != anchor; link = link->iNext)
+ {
+ DCodeSeg* const codeSeg = _LOFF(link, DCodeSeg, iLink);
+ AddCodeSeg(codeSeg, NULL);
+ }
+
+ Kern::EndAccessCode();
+
+ return KErrNone;
+ }
+
+
+/** Check that tracking list matches existing objects.
+ @return number of discrepancies found
+ */
+
+TInt DEventTracker::CheckIntegrity()
+ {
+ // Tracking must be stopped to avoid race conditions.
+ __ASSERT_DEBUG(!iTracking, Kern::Fault(KPanicCat, __LINE__));
+
+ if (iOOM)
+ {
+ Kern::Printf("EVENTTRACKER: OOM during tracking");
+ }
+
+ CheckContainerIntegrity(EProcess);
+ CheckContainerIntegrity(EThread);
+ CheckContainerIntegrity(ELibrary);
+ CheckContainerIntegrity(EChunk);
+ CheckContainerIntegrity(ELogicalDevice);
+ CheckContainerIntegrity(EPhysicalDevice);
+ CheckCodeSegListIntegrity();
+
+ CheckAllAccountedFor();
+
+ if (iErrorCount)
+ {
+ Kern::Printf("EVENTTRACKER: %d error(s) found", iErrorCount);
+ return KErrGeneral;
+ }
+
+ return KErrNone;
+ }
+
+/** Check all objects in specified container are in tracking list. */
+
+void DEventTracker::CheckContainerIntegrity(TObjectType aType)
+ {
+ DObjectCon* const container = Kern::Containers()[aType];
+
+ NKern::ThreadEnterCS();
+ container->Wait();
+
+ const TInt count = container->Count();
+
+ for (TInt i = 0; i < count; ++i)
+ {
+ DObject* const object = (*container)[i];
+ if (object->Open() == KErrNone)
+ {
+ TFullName name;
+ object->FullName(name);
+
+ TTrackedObject* const trackedObject = (TTrackedObject*)LookupItem(object);
+
+ if (trackedObject)
+ {
+ trackedObject->iAccountedFor = ETrue;
+ if (!trackedObject->CheckIntegrity(name, aType))
+ {
+ ++iErrorCount;
+ }
+ }
+ else
+ {
+ Kern::Printf("EVENTTRACKER: %S (type %d) is in container but not in tracking list", &name, aType);
+ ++iErrorCount;
+ }
+
+ object->Close(NULL);
+ }
+ }
+
+ container->Signal();
+ NKern::ThreadLeaveCS();
+ }
+
+void DEventTracker::CheckCodeSegListIntegrity()
+ {
+ Kern::AccessCode();
+
+ const SDblQueLink* const anchor = &Kern::CodeSegList()->iA;
+ for (SDblQueLink* link = Kern::CodeSegList()->First(); link != anchor; link = link->iNext)
+ {
+ DCodeSeg* const codeSeg = _LOFF(link, DCodeSeg, iLink);
+ TTrackedCodeSeg* const trackedCodeSeg = (TTrackedCodeSeg*)LookupItem(codeSeg);
+
+ if (trackedCodeSeg)
+ {
+ trackedCodeSeg->iAccountedFor = ETrue;
+ if (!trackedCodeSeg->CheckIntegrity(codeSeg->iAccessCount))
+ {
+ ++iErrorCount;
+ }
+ }
+ else
+ {
+ Kern::Printf("EVENTTRACKER: %C is in global list but not in tracking list", codeSeg);
+ ++iErrorCount;
+ }
+ }
+
+ Kern::EndAccessCode();
+ }
+
+
+/** Check that all objects in tracking list have been accounted for. */
+void DEventTracker::CheckAllAccountedFor()
+ {
+ const SDblQueLink* link = iItems.GetFirst();
+ while (link)
+ {
+ TTrackedItem* const item = _LOFF(link, TTrackedItem, iLink);
+ if (!item->iAccountedFor)
+ {
+ Kern::Printf(
+ "EVENTTRACKER: 0x%x is in tracking list but not in container / list",
+ &item->iObject
+ );
+ ++iErrorCount;
+ }
+ link = iItems.GetFirst();
+ }
+ }
+
+/** Look for specified object in the tracking list.
+ @pre iLock held
+ @post iLock held
+ */
+TTrackedItem* DEventTracker::LookupItem(DBase* aItem) const
+ {
+ const SDblQueLink* const anchor = &iItems.iA;
+
+ for (SDblQueLink* link = iItems.First(); link != anchor; link = link->iNext)
+ {
+ TTrackedItem* const item = _LOFF(link, TTrackedItem, iLink);
+
+ if (item->iObject == aItem)
+ {
+ return item;
+ }
+ }
+
+ return NULL;
+ }
+
+
+void DEventTracker::DumpCounters() const
+ {
+ static const char* const KEventName[] =
+ {
+ "SwExc ",
+ "HwExc ",
+ "AddProcess ",
+ "UpdateProcess ",
+ "RemoveProcess ",
+ "AddThread ",
+ "StartThread ",
+ "UpdateThread ",
+ "KillThread ",
+ "RemoveThread ",
+ "NewChunk ",
+ "UpdateChunk ",
+ "DeleteChunk ",
+ "AddLibrary ",
+ "RemoveLibrary ",
+ "LoadLdd ",
+ "UnloadLdd ",
+ "LoadPdd ",
+ "UnloadPdd ",
+ "UserTrace ",
+ "AddCodeSeg ",
+ "RemoveCodeSeg ",
+ "LoadedProcess ",
+ "UnloadingProcess"
+ };
+
+ Kern::Printf("EVENT USAGE STATISTICS:");
+
+ for (TInt i = 0; i < EEventLimit; ++i)
+ {
+ Kern::Printf("\t%s\t\t %d times", KEventName[i], iCounters[i]);
+ }
+ }
+
+#ifdef __MARM__
+void DEventTracker::CopyDummyHandler(TLinAddr aLinAddr)
+ {
+ const TUint handlerSize = DummyHandlerSize();
+
+ // Copy the breakpoint-able handler into RAM by copying from the one (possibly) in ROM
+ memcpy((TAny*)aLinAddr, (TAny*) &DummyHandler, handlerSize);
+ __KTRACE_OPT(KBOOT, Kern::Printf("Breakpoint-able handler copied from 0x%x to (va) 0x%x, size %d", &DummyHandler, aLinAddr, handlerSize));
+ }
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DTestChannel : public DLogicalChannelBase
+ {
+public:
+ virtual ~DTestChannel();
+protected:
+ // from DLogicalChannelBase
+ virtual TInt DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer);
+ virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
+private:
+ DEventTracker* iHandler;
+ };
+
+
+// called in thread critical section
+TInt DTestChannel::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
+ {
+ if((TUint)aUnit>=2)
+ return KErrNotSupported;
+
+ TBool useHook = aUnit;
+
+ iHandler = new DEventTracker;
+
+ if (!iHandler)
+ {
+ return KErrNoMemory;
+ }
+
+ return iHandler->Create(iDevice, useHook);
+ }
+
+// called in thread critical section
+DTestChannel::~DTestChannel()
+ {
+ if (iHandler)
+ {
+ iHandler->Close();
+ }
+ }
+
+
+TInt DTestChannel::Request(TInt aFunction, TAny* /*a1*/, TAny* /*a2*/)
+ {
+ TInt r = KErrNone;
+ switch (aFunction)
+ {
+ case REventTracker::EStart:
+ iHandler->Start();
+ break;
+ case REventTracker::EStop:
+ iHandler->Stop();
+ break;
+ default:
+ Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
+ break;
+ }
+ return r;
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DTestFactory : public DLogicalDevice
+ {
+public:
+ DTestFactory();
+ // from DLogicalDevice
+ virtual TInt Install();
+ virtual void GetCaps(TDes8& aDes) const;
+ virtual TInt Create(DLogicalChannelBase*& aChannel);
+ };
+
+DTestFactory::DTestFactory()
+ {
+ iVersion = REventTracker::Version();
+ iParseMask = KDeviceAllowUnit;
+ iUnitsMask = 0x3;
+ }
+
+TInt DTestFactory::Create(DLogicalChannelBase*& aChannel)
+ {
+ aChannel = new DTestChannel;
+ return (aChannel ? KErrNone : KErrNoMemory);
+ }
+
+TInt DTestFactory::Install()
+ {
+ return SetName(&KTestLddName);
+ }
+
+void DTestFactory::GetCaps(TDes8& /*aDes*/) const
+ {
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+DECLARE_STANDARD_LDD()
+ {
+ return new DTestFactory;
+ }