sl@0: // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32test\debug\d_debugapi.cpp sl@0: // LDD-based debug agent. It uses debugAPI provided by kernel extension sl@0: // kdebug.dll (ARMv5) or kdebugv6 (ARMv6) to access and display various sl@0: // kernel objects. It uses debug port as output. See t_DebugAPI.cpp sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include "d_debugapi.h" sl@0: sl@0: _LIT(KClientPanicCat, "D_DEBUGAPI"); sl@0: #define KMaxNameSize 20 sl@0: sl@0: TInt DDebugAPIChecker::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) sl@0: { sl@0: //This is the entry point for all debuggers. Super page contains the address of DebuggerInfo instance. sl@0: iDebugInfo = Kern::SuperPage().iDebuggerInfo; sl@0: sl@0: if (!iDebugInfo) sl@0: { sl@0: Kern::Printf("Error:Debugger is not installed"); sl@0: return KErrNotReady; sl@0: } sl@0: return GetOffsets(); //Obtain the copy of offsets. sl@0: } sl@0: sl@0: /** sl@0: Copies the offset tables from Debug API Kernel extension. sl@0: */ sl@0: TInt DDebugAPIChecker::GetOffsets() sl@0: { sl@0: //Get the memory-model-specific offset table sl@0: switch (iDebugInfo->iMemoryModelType) sl@0: { sl@0: case EARMv5MMU: sl@0: iMMUType = iDebugInfo->iMemoryModelType; sl@0: if ((iVariantOffsetTable = new TMovingDebugOffsetTable)==NULL) sl@0: return KErrNoMemory; sl@0: memcpy(iVariantOffsetTable, iDebugInfo->iMemModelObjectOffsetTable, sizeof(TMovingDebugOffsetTable)); sl@0: break; sl@0: sl@0: case EARMv6MMU: sl@0: iMMUType = iDebugInfo->iMemoryModelType; sl@0: if ((iVariantOffsetTable = new TMultipleDebugOffsetTable)==NULL) sl@0: return KErrNoMemory; sl@0: memcpy(iVariantOffsetTable, iDebugInfo->iMemModelObjectOffsetTable, sizeof(TMultipleDebugOffsetTable)); sl@0: break; sl@0: sl@0: default: sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: //Get the main offset table sl@0: if ((iOffsetTable = new TDebugOffsetTable)==NULL) sl@0: { sl@0: delete iVariantOffsetTable; sl@0: return KErrNoMemory; sl@0: } sl@0: memcpy(iOffsetTable, iDebugInfo->iObjectOffsetTable, sizeof(TDebugOffsetTable)); sl@0: sl@0: //Get the scheduler's address sl@0: iScheduler = (TInt*)iDebugInfo->iScheduler; sl@0: return KErrNone; sl@0: } sl@0: sl@0: DDebugAPIChecker::~DDebugAPIChecker() sl@0: { sl@0: delete iVariantOffsetTable; sl@0: delete iOffsetTable; sl@0: } sl@0: sl@0: /** sl@0: Transfer Symbian-like string into C style string. sl@0: The magic numbers come from descriptor implementation. sl@0: @param aSymbianName The address of the symbian-like string (TDesC8 type) sl@0: @param aCharName The address of the C style string sl@0: @returns aCharName sl@0: */ sl@0: TUint8* DDebugAPIChecker::ExtractName(TInt aSymbianName, TUint8* aCharName) sl@0: { sl@0: if(!aSymbianName) //zero length case sl@0: { sl@0: aCharName[0] = '*'; aCharName[1] = 0; sl@0: return aCharName; sl@0: } sl@0: TInt nameLen = Read((void*)aSymbianName, 0); //The type & length of the desc. is kept in the first word sl@0: sl@0: //We actually need only EBuf type of descriptor in this test. sl@0: if (nameLen >> 28 != 3) sl@0: { sl@0: aCharName[0] = '?'; sl@0: aCharName[1] = 0; sl@0: return aCharName; sl@0: } sl@0: sl@0: nameLen &= 0x0fffffff; sl@0: const TUint8* namePtr = (TUint8*)(aSymbianName+8); sl@0: sl@0: TInt charNameLen = nameLen<(KMaxNameSize-1) ? nameLen : KMaxNameSize-1; sl@0: memcpy(aCharName, namePtr, charNameLen); sl@0: aCharName[charNameLen] = 0; sl@0: return aCharName; sl@0: } sl@0: sl@0: /** sl@0: Prints the list of processes sl@0: */ sl@0: TInt DDebugAPIChecker::Process() sl@0: { sl@0: DObjectCon* processCon; sl@0: TUint8 charName[KMaxNameSize]; sl@0: sl@0: //Fetch the address of the object container for processes sl@0: processCon = iDebugInfo->iContainers[EProcess]; sl@0: sl@0: //Pend on the container's mutex before accessing any data sl@0: NKern::ThreadEnterCS(); sl@0: processCon->Wait(); sl@0: sl@0: TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); sl@0: TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); sl@0: sl@0: Kern::Printf("PROCESS TABLE:"); sl@0: Kern::Printf("Id attribut codeSeg BccRunAd DatBssSC Name"); sl@0: for (TInt i = 0; i < containerCount; i++) sl@0: { sl@0: TInt* process = containerObjects[i]; sl@0: TInt processId = Read(process, iOffsetTable->iProcess_Id); sl@0: TInt processAttributes = Read(process, iOffsetTable->iProcess_Attributes); sl@0: TInt processCodeSeg = Read(process, iOffsetTable->iProcess_CodeSeg); sl@0: TInt processCBssRunAddress = Read(process, iOffsetTable->iProcess_DataBssRunAddress); sl@0: TInt processDataBssStackChunk = Read(process, iOffsetTable->iProcess_DataBssStackChunk); sl@0: TInt processName = Read(process, iOffsetTable->iProcess_Name); sl@0: sl@0: Kern::Printf("%02x %08x %08x %08x %08x %s", sl@0: processId, sl@0: processAttributes, sl@0: processCodeSeg, sl@0: processCBssRunAddress, sl@0: processDataBssStackChunk, sl@0: ExtractName(processName, charName)); sl@0: } sl@0: sl@0: //Release container's mutex sl@0: processCon->Signal(); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Prints the list of chunks sl@0: */ sl@0: TInt DDebugAPIChecker::Chunk() sl@0: { sl@0: TInt state = -1; sl@0: TInt homeBase = -1; sl@0: TInt* owningProcess = (TInt*)-1; sl@0: sl@0: DObjectCon* processCon; sl@0: TUint8 charName[KMaxNameSize]; sl@0: sl@0: //Fetch the address of the object container for processes sl@0: processCon = iDebugInfo->iContainers[EChunk]; sl@0: sl@0: //Pend on the container's mutex before accessing any data. sl@0: NKern::ThreadEnterCS(); sl@0: processCon->Wait(); sl@0: sl@0: TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); sl@0: TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); sl@0: sl@0: Kern::Printf("CHUNK TABLE:"); sl@0: Kern::Printf("size attribut type state HomeBase process"); sl@0: for (TInt i = 0; i < containerCount; i++) sl@0: { sl@0: TInt* chunk = containerObjects[i]; sl@0: TInt size = Read(chunk, iOffsetTable->iChunk_Size); sl@0: TInt attributes = Read(chunk, iOffsetTable->iChunk_Attributes); sl@0: TInt type = Read(chunk, iOffsetTable->iChunk_ChunkType); sl@0: sl@0: //This part is memory-model specific sl@0: switch (iDebugInfo->iMemoryModelType) sl@0: { sl@0: case EARMv5MMU: sl@0: { sl@0: TMovingDebugOffsetTable* variantOffsets = (TMovingDebugOffsetTable*)iVariantOffsetTable; sl@0: state = Read(chunk, iOffsetTable->iChunk_ChunkState);//armv5 specific sl@0: homeBase = Read(chunk, iOffsetTable->iChunk_HomeBase);//armv5 specific sl@0: owningProcess = (TInt*)Read(chunk, iOffsetTable->iChunk_OwningProcess);//armv5 sl@0: sl@0: //In moving MM, the specific offsets are provided in both tables. Check the values match. sl@0: if ( state != Read(chunk, variantOffsets->iChunk_ChunkState) sl@0: || homeBase != Read(chunk, variantOffsets->iChunk_HomeBase) sl@0: || owningProcess != (TInt*)Read(chunk, variantOffsets->iChunk_OwningProcess) ) sl@0: { sl@0: Kern::Printf("Error: Offsets in main & specific table do not match"); sl@0: return KErrGeneral; sl@0: } sl@0: } sl@0: break; sl@0: sl@0: case EARMv6MMU: sl@0: { sl@0: TMultipleDebugOffsetTable* variantOffsets = (TMultipleDebugOffsetTable*)iVariantOffsetTable; sl@0: owningProcess = (TInt*)Read(chunk, variantOffsets->iChunk_OwningProcess); sl@0: break; sl@0: } sl@0: default: sl@0: Kern::Printf("Error: Unsupported memory model"); sl@0: return KErrGeneral; sl@0: } sl@0: sl@0: TInt processName; sl@0: if(owningProcess) sl@0: processName = Read(owningProcess, iOffsetTable->iProcess_Name); sl@0: else sl@0: processName = 0; sl@0: sl@0: Kern::Printf("%08x %08x %08x %08x %08x %s", sl@0: size, sl@0: attributes, sl@0: type, sl@0: state, sl@0: homeBase, sl@0: ExtractName(processName, charName)); sl@0: } sl@0: sl@0: //Release container's mutex sl@0: processCon->Signal(); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: Prints the list of threads sl@0: */ sl@0: TInt DDebugAPIChecker::Thread() sl@0: { sl@0: sl@0: DObjectCon* processCon; sl@0: TUint8 threadCharName[KMaxNameSize]; sl@0: TUint8 processCharName[KMaxNameSize]; sl@0: sl@0: //Fetch the address of the object container for threads sl@0: processCon = iDebugInfo->iContainers[EThread]; sl@0: sl@0: //Pend on the container's mutex before accessing any data sl@0: NKern::ThreadEnterCS(); sl@0: processCon->Wait(); sl@0: sl@0: TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); sl@0: TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); sl@0: sl@0: Kern::Printf("THREAD TABLE:"); sl@0: Kern::Printf("Id Pri Typ SupStack+Size UsrStack+Size ContType SavedSP ThreadName Process"); sl@0: sl@0: for (TInt i = 0; i < containerCount; i++) sl@0: { sl@0: TInt* thread = containerObjects[i]; sl@0: TInt id = Read(thread, iOffsetTable->iThread_Id); sl@0: TInt supStack = Read(thread, iOffsetTable->iThread_SupervisorStack); sl@0: TInt supStackSize = Read(thread, iOffsetTable->iThread_SupervisorStackSize); sl@0: TInt userStackRunAddr = Read(thread, iOffsetTable->iThread_UserStackRunAddress); sl@0: TInt userStackSize = Read(thread, iOffsetTable->iThread_UserStackSize); sl@0: TInt userContextType = Read8(thread, iOffsetTable->iThread_UserContextType); sl@0: sl@0: TInt savedSP = Read(thread, iOffsetTable->iThread_SavedSupervisorSP); sl@0: TInt priority = Read8(thread, iOffsetTable->iThread_Priority); sl@0: TInt type = Read8(thread, iOffsetTable->iThread_ThreadType); sl@0: TInt name = Read(thread, iOffsetTable->iThread_Name); sl@0: TInt* owningProcess = (TInt*)Read(thread, iOffsetTable->iThread_OwningProcess); sl@0: sl@0: TInt processName = Read(owningProcess, iOffsetTable->iProcess_Name); sl@0: sl@0: Kern::Printf("%02x %3x %3x %08x %04x %08x %04x %08x %08x %14s %s", sl@0: id, sl@0: priority, sl@0: type, sl@0: supStack, sl@0: supStackSize, sl@0: userStackRunAddr, sl@0: userStackSize, sl@0: userContextType, sl@0: savedSP, sl@0: ExtractName(name, threadCharName), sl@0: ExtractName(processName, processCharName) sl@0: ); sl@0: } sl@0: sl@0: //Release container's mutex sl@0: processCon->Signal(); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: Reads memory location that belongs to the other process and compares the value with provided one. sl@0: The input argument contains the following data: sl@0: - ProcessId of the process that owns the address space in question sl@0: - Address of memory location to be read. sl@0: - The value at the location. sl@0: */ sl@0: TInt DDebugAPIChecker::IPAccess(TAny* a1) sl@0: { sl@0: TInt* process; sl@0: TInt otherProcess = 0; sl@0: TBool processFound = EFalse; sl@0: TBool currentProcessFound = EFalse; sl@0: DObjectCon* processCon; sl@0: sl@0: RDebugAPIChecker::IPAccessArgs args; sl@0: kumemget32 (&args, a1, sizeof(args)); sl@0: sl@0: //Find the addresses of the current nano-thread & SymbianOS-thread sl@0: TInt currentNThread = Read(iScheduler, iOffsetTable->iScheduler_CurrentThread); sl@0: TInt currentDThread = currentNThread - iOffsetTable->iThread_NThread; sl@0: sl@0: //Find the addresses of the current process sl@0: TInt currentProcess = Read((void*)currentDThread, iOffsetTable->iThread_OwningProcess); sl@0: sl@0: //Find process in the container with given processID sl@0: processCon = iDebugInfo->iContainers[EProcess]; sl@0: sl@0: //Pend on the container's mutex before accessing any data sl@0: NKern::ThreadEnterCS(); sl@0: processCon->Wait(); sl@0: sl@0: TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); sl@0: TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); sl@0: sl@0: for (TInt i = 0; i < containerCount; i++) sl@0: { sl@0: process = containerObjects[i]; sl@0: TInt processId = Read(process, iOffsetTable->iProcess_Id); sl@0: sl@0: if (currentProcess == (TInt)process) sl@0: currentProcessFound = ETrue; sl@0: sl@0: if (processId == (TInt)args.iProcessID) sl@0: { sl@0: otherProcess = (TInt)process; sl@0: processFound = ETrue; sl@0: } sl@0: } sl@0: sl@0: if(!(processFound && currentProcessFound)) sl@0: { sl@0: Kern::Printf("Could not find the-current-process or the-other-process in the process container"); sl@0: processCon->Signal(); sl@0: NKern::ThreadLeaveCS(); sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: //Release container's mutex sl@0: processCon->Signal(); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: switch (iMMUType) sl@0: { sl@0: case EARMv6MMU: sl@0: { sl@0: TMultipleDebugOffsetTable* variantOffsets = (TMultipleDebugOffsetTable*)iVariantOffsetTable; sl@0: iCurrentProcess_OsAsid = Read((void*)currentProcess, variantOffsets->iProcess_OsAsid); sl@0: iCurrentProcess_LocalPageDir = Read((void*)currentProcess, variantOffsets->iProcess_LocalPageDir); sl@0: iOtherProcess_OsAsid = Read((void*)otherProcess, variantOffsets->iProcess_OsAsid); sl@0: iOtherProcess_LocalPageDir = Read((void*)otherProcess, variantOffsets->iProcess_LocalPageDir); sl@0: iAddress = args.iAddress; sl@0: sl@0: TUint r = ReadFromOtherProcessArmv6(); sl@0: sl@0: //Chech if the value we just read matches the provided value. sl@0: if ( r != args.iValue) sl@0: { sl@0: Kern::Printf("Returned value does not match"); sl@0: return KErrGeneral; sl@0: } sl@0: break; sl@0: } sl@0: default: sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt DDebugAPIChecker::Request(TInt aFunction, TAny* a1, TAny* /*a2*/) sl@0: { sl@0: TInt r = KErrNone; sl@0: switch (aFunction) sl@0: { sl@0: case RDebugAPIChecker::ETProcess: sl@0: r = Process(); sl@0: break; sl@0: sl@0: case RDebugAPIChecker::ETChunk: sl@0: r = Chunk(); sl@0: break; sl@0: sl@0: case RDebugAPIChecker::ETThread: sl@0: r = Thread(); sl@0: break; sl@0: sl@0: case RDebugAPIChecker::ETIPAccess: sl@0: r = IPAccess(a1); sl@0: break; sl@0: sl@0: default: sl@0: Kern::PanicCurrentThread(KClientPanicCat, __LINE__); sl@0: break; sl@0: } sl@0: return r; sl@0: } sl@0: sl@0: ////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: class DTestFactory : public DLogicalDevice sl@0: { sl@0: public: sl@0: DTestFactory(); sl@0: // from DLogicalDevice sl@0: virtual TInt Install(); sl@0: virtual void GetCaps(TDes8& aDes) const; sl@0: virtual TInt Create(DLogicalChannelBase*& aChannel); sl@0: }; sl@0: sl@0: DTestFactory::DTestFactory() sl@0: { sl@0: iVersion = RDebugAPIChecker::Version(); sl@0: iParseMask = KDeviceAllowUnit; sl@0: iUnitsMask = 0x3; sl@0: } sl@0: sl@0: TInt DTestFactory::Create(DLogicalChannelBase*& aChannel) sl@0: { sl@0: aChannel = new DDebugAPIChecker; sl@0: return (aChannel ? KErrNone : KErrNoMemory); sl@0: } sl@0: sl@0: TInt DTestFactory::Install() sl@0: { sl@0: return SetName(&KTestLddName); sl@0: } sl@0: sl@0: void DTestFactory::GetCaps(TDes8& /*aDes*/) const sl@0: { sl@0: } sl@0: sl@0: ////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: DECLARE_STANDARD_LDD() sl@0: { sl@0: return new DTestFactory; sl@0: }