os/kernelhwsrv/kerneltest/e32test/debug/d_debugapi.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of the License "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
// e32test\debug\d_debugapi.cpp
sl@0
    15
// LDD-based debug agent. It uses debugAPI provided by kernel extension 
sl@0
    16
// kdebug.dll (ARMv5) or kdebugv6 (ARMv6) to access and display various
sl@0
    17
// kernel objects. It uses debug port as output. See t_DebugAPI.cpp
sl@0
    18
// 
sl@0
    19
//
sl@0
    20
sl@0
    21
#include <kernel/kern_priv.h>
sl@0
    22
#include "d_debugapi.h"
sl@0
    23
sl@0
    24
_LIT(KClientPanicCat, "D_DEBUGAPI");
sl@0
    25
#define KMaxNameSize 20
sl@0
    26
sl@0
    27
TInt DDebugAPIChecker::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
sl@0
    28
	{
sl@0
    29
	//This is the entry point for all debuggers. Super page contains the address of DebuggerInfo instance.
sl@0
    30
	iDebugInfo = Kern::SuperPage().iDebuggerInfo;
sl@0
    31
sl@0
    32
	if (!iDebugInfo)
sl@0
    33
		{
sl@0
    34
		Kern::Printf("Error:Debugger is not installed");
sl@0
    35
		return KErrNotReady;
sl@0
    36
		}
sl@0
    37
	return GetOffsets(); //Obtain the copy of offsets.
sl@0
    38
	}
sl@0
    39
sl@0
    40
/** 
sl@0
    41
Copies the offset tables from Debug API Kernel extension.
sl@0
    42
*/
sl@0
    43
TInt DDebugAPIChecker::GetOffsets()
sl@0
    44
	{
sl@0
    45
	//Get the memory-model-specific offset table
sl@0
    46
	switch (iDebugInfo->iMemoryModelType)
sl@0
    47
		{
sl@0
    48
	case EARMv5MMU:
sl@0
    49
		iMMUType = iDebugInfo->iMemoryModelType;
sl@0
    50
		if ((iVariantOffsetTable = new TMovingDebugOffsetTable)==NULL)
sl@0
    51
			return KErrNoMemory;
sl@0
    52
		memcpy(iVariantOffsetTable, iDebugInfo->iMemModelObjectOffsetTable, sizeof(TMovingDebugOffsetTable));
sl@0
    53
		break;
sl@0
    54
			
sl@0
    55
	case EARMv6MMU:
sl@0
    56
		iMMUType = iDebugInfo->iMemoryModelType;
sl@0
    57
		if ((iVariantOffsetTable = new TMultipleDebugOffsetTable)==NULL)
sl@0
    58
			return KErrNoMemory;
sl@0
    59
		memcpy(iVariantOffsetTable, iDebugInfo->iMemModelObjectOffsetTable, sizeof(TMultipleDebugOffsetTable));
sl@0
    60
		break;
sl@0
    61
sl@0
    62
	default:
sl@0
    63
		return KErrNotSupported;
sl@0
    64
		}
sl@0
    65
sl@0
    66
	//Get the main offset table
sl@0
    67
	if ((iOffsetTable = new TDebugOffsetTable)==NULL)
sl@0
    68
		{
sl@0
    69
		delete iVariantOffsetTable;
sl@0
    70
		return KErrNoMemory;
sl@0
    71
		}
sl@0
    72
	memcpy(iOffsetTable, iDebugInfo->iObjectOffsetTable, sizeof(TDebugOffsetTable));
sl@0
    73
sl@0
    74
	//Get the scheduler's address
sl@0
    75
	iScheduler = (TInt*)iDebugInfo->iScheduler;
sl@0
    76
	return KErrNone;
sl@0
    77
	}
sl@0
    78
sl@0
    79
DDebugAPIChecker::~DDebugAPIChecker()
sl@0
    80
	{
sl@0
    81
	delete iVariantOffsetTable;
sl@0
    82
	delete iOffsetTable;
sl@0
    83
	}
sl@0
    84
sl@0
    85
/**
sl@0
    86
Transfer Symbian-like string into C style string.
sl@0
    87
The magic numbers come from descriptor implementation.
sl@0
    88
@param aSymbianName The address of the symbian-like string (TDesC8 type)
sl@0
    89
@param aCharName The address of the C style string
sl@0
    90
@returns aCharName
sl@0
    91
*/
sl@0
    92
TUint8* DDebugAPIChecker::ExtractName(TInt aSymbianName, TUint8* aCharName)
sl@0
    93
	{
sl@0
    94
	if(!aSymbianName) //zero length case
sl@0
    95
		{
sl@0
    96
		aCharName[0] = '*';	aCharName[1] = 0;
sl@0
    97
		return 	aCharName;
sl@0
    98
		}
sl@0
    99
	TInt nameLen =	Read((void*)aSymbianName, 0);	//The type & length of the desc. is kept in the first word
sl@0
   100
sl@0
   101
	//We actually need only EBuf type of descriptor in this test.
sl@0
   102
	if (nameLen >> 28 != 3)		
sl@0
   103
		{
sl@0
   104
		aCharName[0] = '?';
sl@0
   105
		aCharName[1] = 0;
sl@0
   106
		return 	aCharName;
sl@0
   107
		}
sl@0
   108
sl@0
   109
	nameLen &= 0x0fffffff;
sl@0
   110
	const TUint8* namePtr =	(TUint8*)(aSymbianName+8);
sl@0
   111
sl@0
   112
	TInt charNameLen = nameLen<(KMaxNameSize-1) ? nameLen : KMaxNameSize-1;
sl@0
   113
	memcpy(aCharName, namePtr, charNameLen);
sl@0
   114
	aCharName[charNameLen] = 0;
sl@0
   115
	return 	aCharName;
sl@0
   116
	}
sl@0
   117
sl@0
   118
/**
sl@0
   119
Prints the list of processes
sl@0
   120
*/
sl@0
   121
TInt DDebugAPIChecker::Process()
sl@0
   122
	{
sl@0
   123
	DObjectCon* processCon;
sl@0
   124
	TUint8 charName[KMaxNameSize];
sl@0
   125
sl@0
   126
	//Fetch the address of the object container for processes
sl@0
   127
	processCon = iDebugInfo->iContainers[EProcess];
sl@0
   128
sl@0
   129
	//Pend on the container's mutex before accessing any data
sl@0
   130
	NKern::ThreadEnterCS();
sl@0
   131
	processCon->Wait();
sl@0
   132
sl@0
   133
	TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count);
sl@0
   134
	TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects);
sl@0
   135
sl@0
   136
	Kern::Printf("PROCESS TABLE:");
sl@0
   137
	Kern::Printf("Id attribut codeSeg  BccRunAd DatBssSC Name");
sl@0
   138
	for (TInt i = 0; i < containerCount; i++)
sl@0
   139
		{
sl@0
   140
		TInt* process =					containerObjects[i];
sl@0
   141
		TInt processId =				Read(process, iOffsetTable->iProcess_Id);
sl@0
   142
		TInt processAttributes =		Read(process, iOffsetTable->iProcess_Attributes);
sl@0
   143
		TInt processCodeSeg =			Read(process, iOffsetTable->iProcess_CodeSeg);
sl@0
   144
		TInt processCBssRunAddress =	Read(process, iOffsetTable->iProcess_DataBssRunAddress);
sl@0
   145
		TInt processDataBssStackChunk = Read(process, iOffsetTable->iProcess_DataBssStackChunk);
sl@0
   146
		TInt processName =				Read(process, iOffsetTable->iProcess_Name);
sl@0
   147
sl@0
   148
		Kern::Printf("%02x %08x %08x %08x %08x %s", 
sl@0
   149
				processId, 
sl@0
   150
				processAttributes,
sl@0
   151
				processCodeSeg,
sl@0
   152
				processCBssRunAddress,
sl@0
   153
				processDataBssStackChunk,
sl@0
   154
				ExtractName(processName, charName));
sl@0
   155
		}
sl@0
   156
sl@0
   157
	//Release container's mutex
sl@0
   158
	processCon->Signal();
sl@0
   159
	NKern::ThreadLeaveCS();
sl@0
   160
sl@0
   161
	return KErrNone;
sl@0
   162
	}
sl@0
   163
sl@0
   164
sl@0
   165
/**
sl@0
   166
Prints the list of chunks
sl@0
   167
*/
sl@0
   168
TInt DDebugAPIChecker::Chunk()
sl@0
   169
	{
sl@0
   170
	TInt state = -1;
sl@0
   171
	TInt homeBase = -1;
sl@0
   172
	TInt* owningProcess = (TInt*)-1;
sl@0
   173
sl@0
   174
	DObjectCon* processCon;
sl@0
   175
	TUint8 charName[KMaxNameSize];
sl@0
   176
sl@0
   177
	//Fetch the address of the object container for processes
sl@0
   178
	processCon = iDebugInfo->iContainers[EChunk];
sl@0
   179
sl@0
   180
	//Pend on the container's mutex before accessing any data.
sl@0
   181
	NKern::ThreadEnterCS();
sl@0
   182
	processCon->Wait();
sl@0
   183
sl@0
   184
	TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count);
sl@0
   185
	TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects);
sl@0
   186
sl@0
   187
	Kern::Printf("CHUNK TABLE:");
sl@0
   188
	Kern::Printf("size     attribut type     state    HomeBase process");
sl@0
   189
	for (TInt i = 0; i < containerCount; i++)
sl@0
   190
		{
sl@0
   191
		TInt* chunk =			containerObjects[i];
sl@0
   192
		TInt size =				Read(chunk, iOffsetTable->iChunk_Size);
sl@0
   193
		TInt attributes =		Read(chunk, iOffsetTable->iChunk_Attributes);
sl@0
   194
		TInt type =				Read(chunk, iOffsetTable->iChunk_ChunkType);
sl@0
   195
		
sl@0
   196
		//This part is memory-model specific
sl@0
   197
		switch (iDebugInfo->iMemoryModelType)
sl@0
   198
		{
sl@0
   199
		case EARMv5MMU:
sl@0
   200
			{
sl@0
   201
			TMovingDebugOffsetTable* variantOffsets = (TMovingDebugOffsetTable*)iVariantOffsetTable;
sl@0
   202
			state =			Read(chunk, iOffsetTable->iChunk_ChunkState);//armv5 specific
sl@0
   203
			homeBase =		Read(chunk, iOffsetTable->iChunk_HomeBase);//armv5 specific
sl@0
   204
			owningProcess =	(TInt*)Read(chunk, iOffsetTable->iChunk_OwningProcess);//armv5
sl@0
   205
			
sl@0
   206
			//In moving MM, the specific offsets are provided in both tables. Check the values match.
sl@0
   207
			if (   state         != Read(chunk, variantOffsets->iChunk_ChunkState) 
sl@0
   208
				|| homeBase      !=	Read(chunk, variantOffsets->iChunk_HomeBase)
sl@0
   209
				|| owningProcess != (TInt*)Read(chunk, variantOffsets->iChunk_OwningProcess) )
sl@0
   210
				{
sl@0
   211
				Kern::Printf("Error: Offsets in main & specific table do not match");
sl@0
   212
				return KErrGeneral;
sl@0
   213
				}
sl@0
   214
			}
sl@0
   215
			break;
sl@0
   216
sl@0
   217
		case EARMv6MMU:
sl@0
   218
			{
sl@0
   219
			TMultipleDebugOffsetTable* variantOffsets = (TMultipleDebugOffsetTable*)iVariantOffsetTable;
sl@0
   220
			owningProcess =	(TInt*)Read(chunk, variantOffsets->iChunk_OwningProcess);
sl@0
   221
			break;
sl@0
   222
			}
sl@0
   223
		default:
sl@0
   224
			Kern::Printf("Error: Unsupported memory model");
sl@0
   225
			return KErrGeneral;
sl@0
   226
		}
sl@0
   227
sl@0
   228
		TInt processName;
sl@0
   229
		if(owningProcess)
sl@0
   230
			processName = Read(owningProcess, iOffsetTable->iProcess_Name);
sl@0
   231
		else
sl@0
   232
			processName = 0;
sl@0
   233
sl@0
   234
		Kern::Printf("%08x %08x %08x %08x %08x %s", 
sl@0
   235
				size, 
sl@0
   236
				attributes,
sl@0
   237
				type,
sl@0
   238
				state,
sl@0
   239
				homeBase,
sl@0
   240
				ExtractName(processName, charName));
sl@0
   241
		}
sl@0
   242
sl@0
   243
	//Release container's mutex
sl@0
   244
	processCon->Signal();
sl@0
   245
	NKern::ThreadLeaveCS();
sl@0
   246
sl@0
   247
	return KErrNone;
sl@0
   248
	}
sl@0
   249
sl@0
   250
/**
sl@0
   251
Prints the list of threads
sl@0
   252
*/
sl@0
   253
TInt DDebugAPIChecker::Thread()
sl@0
   254
	{
sl@0
   255
sl@0
   256
	DObjectCon* processCon;
sl@0
   257
	TUint8 threadCharName[KMaxNameSize];
sl@0
   258
	TUint8 processCharName[KMaxNameSize];
sl@0
   259
sl@0
   260
	//Fetch the address of the object container for threads
sl@0
   261
	processCon = iDebugInfo->iContainers[EThread];
sl@0
   262
sl@0
   263
	//Pend on the container's mutex before accessing any data
sl@0
   264
	NKern::ThreadEnterCS();
sl@0
   265
	processCon->Wait();
sl@0
   266
sl@0
   267
	TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count);
sl@0
   268
	TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects);
sl@0
   269
sl@0
   270
	Kern::Printf("THREAD TABLE:");
sl@0
   271
	Kern::Printf("Id Pri Typ SupStack+Size UsrStack+Size ContType SavedSP   ThreadName    Process");
sl@0
   272
sl@0
   273
	for (TInt i = 0; i < containerCount; i++)
sl@0
   274
		{
sl@0
   275
		TInt* thread =			containerObjects[i];
sl@0
   276
		TInt id =				Read(thread, iOffsetTable->iThread_Id);
sl@0
   277
		TInt supStack =			Read(thread, iOffsetTable->iThread_SupervisorStack);
sl@0
   278
		TInt supStackSize =		Read(thread, iOffsetTable->iThread_SupervisorStackSize);
sl@0
   279
		TInt userStackRunAddr =	Read(thread, iOffsetTable->iThread_UserStackRunAddress);
sl@0
   280
		TInt userStackSize =	Read(thread, iOffsetTable->iThread_UserStackSize);
sl@0
   281
		TInt userContextType =	Read8(thread, iOffsetTable->iThread_UserContextType);
sl@0
   282
sl@0
   283
		TInt savedSP	=		Read(thread, iOffsetTable->iThread_SavedSupervisorSP);
sl@0
   284
		TInt priority =			Read8(thread, iOffsetTable->iThread_Priority);
sl@0
   285
		TInt type =				Read8(thread, iOffsetTable->iThread_ThreadType);
sl@0
   286
		TInt name =				Read(thread, iOffsetTable->iThread_Name);
sl@0
   287
		TInt* owningProcess =	(TInt*)Read(thread, iOffsetTable->iThread_OwningProcess);
sl@0
   288
sl@0
   289
		TInt processName =		Read(owningProcess, iOffsetTable->iProcess_Name);
sl@0
   290
sl@0
   291
		Kern::Printf("%02x %3x %3x %08x %04x %08x %04x %08x %08x %14s %s", 
sl@0
   292
				id,
sl@0
   293
				priority, 
sl@0
   294
				type,
sl@0
   295
				supStack,
sl@0
   296
				supStackSize,
sl@0
   297
				userStackRunAddr,
sl@0
   298
				userStackSize,
sl@0
   299
				userContextType,
sl@0
   300
				savedSP,
sl@0
   301
				ExtractName(name, threadCharName),
sl@0
   302
				ExtractName(processName, processCharName)
sl@0
   303
				);
sl@0
   304
		}
sl@0
   305
sl@0
   306
	//Release container's mutex
sl@0
   307
	processCon->Signal();
sl@0
   308
	NKern::ThreadLeaveCS();
sl@0
   309
sl@0
   310
	return KErrNone;
sl@0
   311
	}
sl@0
   312
sl@0
   313
/**
sl@0
   314
Reads memory location that belongs to the other process and compares the value with provided one.
sl@0
   315
The input argument contains the following data:
sl@0
   316
	- ProcessId of the process that owns the address space in question
sl@0
   317
	- Address of memory location to be read.
sl@0
   318
	- The value at the location.
sl@0
   319
	*/
sl@0
   320
TInt DDebugAPIChecker::IPAccess(TAny* a1)
sl@0
   321
	{
sl@0
   322
	TInt* process;
sl@0
   323
	TInt otherProcess = 0;
sl@0
   324
	TBool processFound = EFalse;
sl@0
   325
	TBool currentProcessFound = EFalse;
sl@0
   326
	DObjectCon* processCon;
sl@0
   327
sl@0
   328
	RDebugAPIChecker::IPAccessArgs args;
sl@0
   329
	kumemget32 (&args, a1, sizeof(args));
sl@0
   330
sl@0
   331
	//Find the addresses of the current nano-thread & SymbianOS-thread
sl@0
   332
	TInt currentNThread = Read(iScheduler, iOffsetTable->iScheduler_CurrentThread);
sl@0
   333
	TInt currentDThread = currentNThread - iOffsetTable->iThread_NThread;
sl@0
   334
	
sl@0
   335
	//Find the addresses of the current process
sl@0
   336
	TInt currentProcess = Read((void*)currentDThread, iOffsetTable->iThread_OwningProcess);
sl@0
   337
sl@0
   338
	//Find process in the container with given processID
sl@0
   339
	processCon = iDebugInfo->iContainers[EProcess];
sl@0
   340
sl@0
   341
	//Pend on the container's mutex before accessing any data
sl@0
   342
	NKern::ThreadEnterCS();
sl@0
   343
	processCon->Wait();
sl@0
   344
sl@0
   345
	TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count);
sl@0
   346
	TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects);
sl@0
   347
sl@0
   348
	for (TInt i = 0; i < containerCount; i++)
sl@0
   349
		{
sl@0
   350
		process = containerObjects[i];
sl@0
   351
		TInt processId = Read(process, iOffsetTable->iProcess_Id);
sl@0
   352
sl@0
   353
		if (currentProcess == (TInt)process)
sl@0
   354
			currentProcessFound = ETrue;
sl@0
   355
sl@0
   356
		if (processId == (TInt)args.iProcessID)
sl@0
   357
			{
sl@0
   358
			otherProcess = (TInt)process;
sl@0
   359
			processFound = ETrue;
sl@0
   360
			}
sl@0
   361
		}
sl@0
   362
sl@0
   363
	if(!(processFound &&  currentProcessFound))
sl@0
   364
		{
sl@0
   365
		Kern::Printf("Could not find the-current-process or the-other-process in the process container");
sl@0
   366
		processCon->Signal();
sl@0
   367
		NKern::ThreadLeaveCS();
sl@0
   368
		return KErrNotFound;
sl@0
   369
		}
sl@0
   370
sl@0
   371
	//Release container's mutex
sl@0
   372
	processCon->Signal();
sl@0
   373
	NKern::ThreadLeaveCS();
sl@0
   374
sl@0
   375
	switch (iMMUType)
sl@0
   376
		{
sl@0
   377
	case EARMv6MMU:
sl@0
   378
		{
sl@0
   379
		TMultipleDebugOffsetTable* variantOffsets = (TMultipleDebugOffsetTable*)iVariantOffsetTable;
sl@0
   380
		iCurrentProcess_OsAsid =		Read((void*)currentProcess, variantOffsets->iProcess_OsAsid);
sl@0
   381
		iCurrentProcess_LocalPageDir =	Read((void*)currentProcess, variantOffsets->iProcess_LocalPageDir);
sl@0
   382
		iOtherProcess_OsAsid =			Read((void*)otherProcess, variantOffsets->iProcess_OsAsid);
sl@0
   383
		iOtherProcess_LocalPageDir =	Read((void*)otherProcess, variantOffsets->iProcess_LocalPageDir);
sl@0
   384
		iAddress =						args.iAddress;
sl@0
   385
sl@0
   386
		TUint r = ReadFromOtherProcessArmv6();
sl@0
   387
		
sl@0
   388
		//Chech if the value we just read matches the provided value.
sl@0
   389
		if ( r != args.iValue)
sl@0
   390
			{
sl@0
   391
			Kern::Printf("Returned value does not match");
sl@0
   392
			return KErrGeneral;
sl@0
   393
			}
sl@0
   394
		break;	
sl@0
   395
		}
sl@0
   396
	default:
sl@0
   397
		return KErrNotSupported;
sl@0
   398
		}	
sl@0
   399
sl@0
   400
  return KErrNone;
sl@0
   401
	}
sl@0
   402
sl@0
   403
TInt DDebugAPIChecker::Request(TInt aFunction, TAny* a1, TAny* /*a2*/)
sl@0
   404
	{
sl@0
   405
	TInt r = KErrNone;
sl@0
   406
	switch (aFunction)
sl@0
   407
		{
sl@0
   408
	case RDebugAPIChecker::ETProcess:
sl@0
   409
		r = Process();
sl@0
   410
		break;
sl@0
   411
sl@0
   412
	case RDebugAPIChecker::ETChunk:
sl@0
   413
		r = Chunk();
sl@0
   414
		break;
sl@0
   415
sl@0
   416
	case RDebugAPIChecker::ETThread:
sl@0
   417
		r = Thread();
sl@0
   418
		break;
sl@0
   419
sl@0
   420
	case RDebugAPIChecker::ETIPAccess:
sl@0
   421
		r = IPAccess(a1);
sl@0
   422
		break;
sl@0
   423
sl@0
   424
	default:
sl@0
   425
		Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
sl@0
   426
		break;
sl@0
   427
		}
sl@0
   428
	return r;
sl@0
   429
	}
sl@0
   430
sl@0
   431
//////////////////////////////////////////////////////////////////////////////
sl@0
   432
sl@0
   433
class DTestFactory : public DLogicalDevice
sl@0
   434
	{
sl@0
   435
public:
sl@0
   436
	DTestFactory();
sl@0
   437
	// from DLogicalDevice
sl@0
   438
	virtual TInt Install();
sl@0
   439
	virtual void GetCaps(TDes8& aDes) const;
sl@0
   440
	virtual TInt Create(DLogicalChannelBase*& aChannel);
sl@0
   441
	};
sl@0
   442
sl@0
   443
DTestFactory::DTestFactory()
sl@0
   444
    {
sl@0
   445
    iVersion = RDebugAPIChecker::Version();
sl@0
   446
    iParseMask = KDeviceAllowUnit;
sl@0
   447
    iUnitsMask = 0x3;
sl@0
   448
    }
sl@0
   449
sl@0
   450
TInt DTestFactory::Create(DLogicalChannelBase*& aChannel)
sl@0
   451
    {
sl@0
   452
	aChannel = new DDebugAPIChecker;
sl@0
   453
	return (aChannel ? KErrNone : KErrNoMemory);
sl@0
   454
    }
sl@0
   455
sl@0
   456
TInt DTestFactory::Install()
sl@0
   457
    {
sl@0
   458
    return SetName(&KTestLddName);
sl@0
   459
    }
sl@0
   460
sl@0
   461
void DTestFactory::GetCaps(TDes8& /*aDes*/) const
sl@0
   462
    {
sl@0
   463
    }
sl@0
   464
sl@0
   465
//////////////////////////////////////////////////////////////////////////////
sl@0
   466
sl@0
   467
DECLARE_STANDARD_LDD()
sl@0
   468
	{
sl@0
   469
    return new DTestFactory;
sl@0
   470
	}