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