Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase.
Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.
// ps.cpp
//
// Copyright (c) 2005 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include <fshell/common.mmh>
#include <fshell/ltkutils.h>
#include <fshell/heaputils.h>
#include "ps.h"
_LIT(KDefaultMatch, "*");
CCommandBase* CCmdPs::NewLC()
{
CCmdPs* self = new(ELeave) CCmdPs();
CleanupStack::PushL(self);
self->BaseConstructL();
return self;
}
CCmdPs::~CCmdPs()
{
delete iMatch;
delete iFormatter;
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
iMemoryAccess.Close();
#endif
}
CCmdPs::CCmdPs()
{
}
const TDesC& CCmdPs::Name() const
{
_LIT(KName, "ps");
return KName;
}
void CCmdPs::DoRunL()
{
if (iMatch == NULL)
{
iMatch = KDefaultMatch().AllocL();
}
iFormatter = CTextFormatter::NewL(Stdout());
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
TInt err = RMemoryAccess::LoadDriver();
if ((err == KErrNone) || (err == KErrAlreadyExists))
{
err = iMemoryAccess.Open();
}
if (err)
{
PrintWarning(_L("Unable to load memory access device driver: %d"), err);
}
#endif
#if defined(__WINS__) && !defined(EKA2)
TFindThread finder(*iMatch);
RThread process;
#else
TFindProcess finder(*iMatch);
RProcess process;
#endif
if (iProcessId)
{
User::LeaveIfError(process.Open(iProcessId));
CleanupClosePushL(process);
iProcessName = process.Name();
PrintInfoL(process);
CleanupStack::PopAndDestroy(&process);
}
else
{
while (finder.Next(iProcessName) == KErrNone)
{
TInt err = process.Open(finder);
if (err)
{
PrintWarning(_L("Unable to open handle to process %S: %d"), &iProcessName, err);
continue;
}
if ((iExcludeDead && (process.ExitType() != EExitPending)) || (iOnlyDead && (process.ExitType() == EExitPending)))
{
process.Close();
continue;
}
CleanupClosePushL(process);
PrintInfoL(process);
CleanupStack::PopAndDestroy(&process);
}
}
Write(iFormatter->Descriptor());
Complete();
}
void CCmdPs::ArgumentsL(RCommandArgumentList& aArguments)
{
_LIT(KArg1, "process_id");
aArguments.AppendUintL(iProcessId, KArg1);
}
void CCmdPs::OptionsL(RCommandOptionList& aOptions)
{
_LIT(KOptVerbose, "verbose");
aOptions.AppendBoolL(iVerbose, KOptVerbose);
_LIT(KOptMatch, "match");
aOptions.AppendStringL(iMatch, KOptMatch);
_LIT(KOptHuman, "human");
aOptions.AppendBoolL(iHuman, KOptHuman);
_LIT(KOptPriority, "priority");
aOptions.AppendBoolL(iPrintPriority, KOptPriority);
_LIT(KOptExclude, "exclude-dead");
aOptions.AppendBoolL(iExcludeDead, KOptExclude);
_LIT(KOptOnlyDead, "only-dead");
aOptions.AppendBoolL(iOnlyDead, KOptOnlyDead);
_LIT(KOptHandleCount, "handle-count");
aOptions.AppendBoolL(iHandleCount, KOptHandleCount);
#if defined(EKA2) || !defined(__WINS__)
_LIT(KOptThreads, "threads");
aOptions.AppendBoolL(iPrintThreads, KOptThreads);
_LIT(KOptStack, "stacks");
aOptions.AppendBoolL(iPrintStackInfo, KOptStack);
_LIT(KOptHeap, "heaps");
aOptions.AppendBoolL(iPrintHeapInfo, KOptHeap);
_LIT(KOptCpu, "cpu-time");
aOptions.AppendBoolL(iPrintCpuTime, KOptCpu);
_LIT(KOptChunk, "chunks");
aOptions.AppendBoolL(iPrintChunkInfo, KOptChunk);
_LIT(KOptFileName, "filename");
aOptions.AppendBoolL(iPrintFileName, KOptFileName);
_LIT(KOptMemoryInfo, "memory");
aOptions.AppendBoolL(iPrintMemoryInfo, KOptMemoryInfo);
#endif
#if !defined(EKA2) && !defined(__WINS__)
_LIT(KOptCommandLine, "command_line");
aOptions.AppendBoolL(iPrintCommandLine, KOptCommandLine);
#endif
#if !defined(EKA2)
_LIT(KOptFlags, "flags");
aOptions.AppendBoolL(iPrintFlags, KOptFlags);
#endif
_LIT(KOptAddresses, "addresses");
aOptions.AppendBoolL(iAddresses, KOptAddresses);
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
_LIT(KOptCodeSegs, "codesegs");
aOptions.AppendBoolL(iPrintCodesegs, KOptCodeSegs);
#endif
}
#if defined(__WINS__) && !defined(EKA2)
void CCmdPs::PrintInfoL(RThread& aProcess)
#else
void CCmdPs::PrintInfoL(RProcess& aProcess)
#endif
{
TInt processHandleCount = 0;
TBool dead = aProcess.ExitType() != EExitPending;
iFormatter->AppendFormatL(_L("%u "), TUint(aProcess.Id()));
if (dead) iFormatter->AppendL(_L("["));
if (iVerbose)
{
iFormatter->AppendL(iProcessName);
}
else
{
TFullName name = iProcessName;
LtkUtils::MakeProcessNameFriendly(name);
iFormatter->AppendL(name);
}
if (dead) iFormatter->AppendL(_L("]"));
iFormatter->AppendL(_L("\r\n"));
iProcessName.Append(_L("::*"));
if (iPrintThreads)
{
iFormatter->AppendL(_L("\tThreads:\r\n"));
TFindThread threadFinder(iProcessName);
RThread thread;
while (threadFinder.Next(iThreadName) == KErrNone)
{
TInt err = thread.Open(threadFinder);
if (err)
{
if ((err != KErrPermissionDenied) || iVerbose)
{
PrintWarning(_L("Unable to open handle to thread %S: %d"), &iThreadName, err);
}
continue;
}
if ((iExcludeDead && (thread.ExitType() != EExitPending)) || (iOnlyDead && (thread.ExitType() == EExitPending)))
{
thread.Close();
continue;
}
CleanupClosePushL(thread);
iThreadName = thread.Name();
#ifdef EKA2
iFormatter->AppendFormatL(_L("\t\t%Lu %S\r\n"), thread.Id().Id(), &iThreadName);
#else
iFormatter->AppendFormatL(_L("\t\t%u %S\r\n"), thread.Id(), &iThreadName);
#endif
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
if (iAddresses && iMemoryAccess.Handle())
{
TObjectKernelInfo objectInfo;
TPckg<TObjectKernelInfo> objectInfoPckg(objectInfo);
TInt err = iMemoryAccess.GetObjectInfoByHandle(EThread, RThread().Id(), thread.Handle(), objectInfoPckg);
if (err == KErrNone)
{
iFormatter->AppendFormatL(_L("\t\t\tAddress: 0x%08x\r\n"), objectInfo.iAddressOfKernelObject);
}
}
#endif // FSHELL_MEMORY_ACCESS_SUPPORT
if (iPrintPriority)
{
iFormatter->AppendFormatL(_L("\t\t\tPriority: %d\r\n"), thread.Priority());
}
PrintStackInfoL(thread, iThreadName);
PrintHeapInfoL(thread, iThreadName);
PrintCpuTimeL(thread, iThreadName);
if (iHandleCount)
{
TInt threadHandleCount;
thread.HandleCount(processHandleCount, threadHandleCount);
iFormatter->AppendFormatL(_L("\t\t\tHandle count: %d\r\n"), threadHandleCount);
}
CleanupStack::PopAndDestroy(&thread);
}
}
if (iPrintPriority)
{
iFormatter->AppendFormatL(_L("\tPriority: %d\r\n"), aProcess.Priority());
}
#if defined(EKA2) || !defined(__WINS__)
PrintChunkInfoL(iProcessName);
if (iPrintFileName)
{
TFileName fileName(aProcess.FileName());
iFormatter->AppendFormatL(_L("\tFile name: %S\r\n"), &fileName);
}
if (iPrintMemoryInfo)
{
TProcessMemoryInfo memoryInfo;
TInt err = aProcess.GetMemoryInfo(memoryInfo);
if (err)
{
PrintWarning(_L("Couldn't read memory information: %d"), err);
}
else
{
iFormatter->AppendFormatL(_L("\tCode base: 0x%08x\r\n"), memoryInfo.iCodeBase);
PrintSizeL(_L("\tCode size: "), memoryInfo.iCodeSize);
iFormatter->AppendFormatL(_L("\tConst data base: 0x%08x\r\n"), memoryInfo.iConstDataBase);
PrintSizeL(_L("\tConst data size: "), memoryInfo.iConstDataSize);
iFormatter->AppendFormatL(_L("\tInitialised data base: 0x%08x\r\n"), memoryInfo.iInitialisedDataBase);
PrintSizeL(_L("\tInitialised data size: "), memoryInfo.iInitialisedDataSize);
iFormatter->AppendFormatL(_L("\tUninitialised data base: 0x%08x\r\n"), memoryInfo.iUninitialisedDataBase);
PrintSizeL(_L("\tUninitialised data size: "), memoryInfo.iUninitialisedDataSize);
}
}
#endif
#if !defined(EKA2) && !defined(__WINS__)
if (iPrintCommandLine)
{
HBufC* cl = HBufC::NewL(aProcess.CommandLineLength());
TPtr clPtr(cl->Des());
aProcess.CommandLine(clPtr);
iFormatter->AppendFormatL(_L("\tCommand line: %S\r\n"), cl);
delete cl;
}
#endif
if (iPrintFlags)
{
#ifndef EKA2
iFormatter->AppendFormatL(_L("\tSystem: %d\r\n\tProtected: %d\r\n"), aProcess.System(), aProcess.Protected());
#endif
#if !defined(__WINS__) && !defined(EKA2)
iFormatter->AppendFormatL(_L("\tLoaded from RAM: %d\r\n"), aProcess.LoadedFromRam());
#endif
}
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
if (iAddresses && iMemoryAccess.Handle())
{
TObjectKernelInfo objectInfo;
TPckg<TObjectKernelInfo> objectInfoPckg(objectInfo);
TInt err = iMemoryAccess.GetObjectInfoByHandle(EProcess, RThread().Id(), aProcess.Handle(), objectInfoPckg);
if (err == KErrNone)
{
iFormatter->AppendFormatL(_L("\tAddress: 0x%08x\r\n"), objectInfo.iAddressOfKernelObject);
}
}
if (iPrintCodesegs && iMemoryAccess.Handle())
{
PrintCodeSegsL(aProcess);
}
#endif // FSHELL_MEMORY_ACCESS_SUPPORT
if (iHandleCount)
{
if (iPrintThreads)
{
iFormatter->AppendFormatL(_L("\tProcess handle count: %d\r\n"), processHandleCount);
}
else
{
// Summarise the total handle count for this process and all its threads.
TInt threadHandleCount = 0;
TFindThread threadFinder(iProcessName);
RThread thread;
while (threadFinder.Next(iThreadName) == KErrNone)
{
TInt err = thread.Open(threadFinder);
if (err)
{
continue;
}
TInt thc;
thread.HandleCount(processHandleCount, thc);
thread.Close();
threadHandleCount += thc;
}
iFormatter->AppendFormatL(_L("\tTotal handle count: %d\r\n"), processHandleCount + threadHandleCount);
}
}
}
void CCmdPs::PrintStackInfoL(RThread& aThread, const TDesC& aThreadName)
{
#ifdef EKA2
if (iPrintStackInfo)
{
TThreadStackInfo stackInfo;
TInt err = aThread.StackInfo(stackInfo);
if (err)
{
PrintWarning(_L("Unable to get stack info for thread %S: %d"), &aThreadName, err);
}
else
{
const TInt stackSize = stackInfo.iBase - stackInfo.iLimit;
iFormatter->AppendFormatL(_L("\t\t\tStack info:\r\n"));
PrintSizeL(_L("\t\t\t\tSize: "), stackSize);
iFormatter->AppendFormatL(_L("\t\t\t\tBase: 0x%08x\r\n"), stackInfo.iBase);
iFormatter->AppendFormatL(_L("\t\t\t\tLimit: 0x%08x\r\n"), stackInfo.iLimit);
iFormatter->AppendFormatL(_L("\t\t\t\tExpand limit: 0x%08x\r\n"), stackInfo.iExpandLimit);
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
if (iMemoryAccess.Handle())
{
const TInt KBufSize = 4096; // The largest amount RMemoryAccess allows us to copy in one go.
HBufC8* stackBuf = HBufC8::NewLC(KBufSize);
TPtr8 stackBufPtr(stackBuf->Des());
TThreadMemoryAccessParamsBuf accessParamsBuf;
TThreadMemoryAccessParams& accessParams = accessParamsBuf();
accessParams.iId = (TInt)aThread.Id().Id();
TInt numBytesRead = 0;
TInt numUnusedBytes = 0;
while (numBytesRead < stackSize)
{
accessParams.iAddr = (TUint8*)stackInfo.iLimit + numBytesRead;
accessParams.iSize = Min(KBufSize, stackSize - numBytesRead);
stackBufPtr.Zero();
err = iMemoryAccess.GetThreadMem(accessParamsBuf, stackBufPtr);
if (err)
{
PrintWarning(_L("Unable to read stack data for thread %S: %d"), &aThreadName, err);
break;
}
else
{
const TInt bufLength = stackBuf->Length();
for (TInt i = 0; i < bufLength; ++i)
{
if ((*stackBuf)[i] != 0x29)
{
break;
}
++numUnusedBytes;
}
numBytesRead += bufLength;
}
}
if (err == KErrNone)
{
const TInt numUsedBytes = stackSize - numUnusedBytes;
iFormatter->AppendFormatL(_L("\t\t\t\tHigh water mark: 0x%08x ("), stackInfo.iBase - numUsedBytes);
if (iHuman)
{
iFormatter->AppendHumanReadableSizeL(numUsedBytes, EUnaligned);
iFormatter->AppendFormatL(_L(")\r\n"));
}
else
{
iFormatter->AppendFormatL(_L("%d bytes)\r\n"), numUsedBytes);
}
}
CleanupStack::PopAndDestroy(stackBuf);
}
#endif // FSHELL_MEMORY_ACCESS_SUPPORT
}
}
#endif // EKA2
}
#if defined(EKA2) && defined(FSHELL_MEMORY_ACCESS_SUPPORT)
void CCmdPs::PrintHeapInfoL(RThread& aThread, const TDesC& aThreadName)
{
if (iPrintHeapInfo && iMemoryAccess.Handle())
{
LtkUtils::RProxyAllocatorHelper allocHelper;
CleanupClosePushL(allocHelper);
TInt err = allocHelper.Open(iMemoryAccess, TUint(aThread.Id()));
if (err)
{
PrintWarning(_L("Couldn't open allocator helper for thread %S: %d"), &aThreadName, err);
}
else
{
TInt committed = allocHelper.CommittedSize();
TInt alloced = allocHelper.AllocatedSize();
PrintSizeL(_L("\t\t\tHeap size: "), committed);
iFormatter->AppendFormatL(_L("\t\t\tAlloc count: %d\r\n"), allocHelper.AllocationCount());
PrintSizeL(_L("\t\t\tAlloc size: "), alloced);
}
CleanupStack::PopAndDestroy(&allocHelper);
}
}
#else
void CCmdPs::PrintHeapInfoL(RThread&, const TDesC&)
{
}
#endif
#ifdef EKA2
void CCmdPs::PrintCpuTimeL(RThread& aThread, const TDesC& aThreadName)
{
if (iPrintCpuTime)
{
TTimeIntervalMicroSeconds time;
TInt err = aThread.GetCpuTime(time);
if (err)
{
PrintWarning(_L("Unable to get CPU time for thread %S: %d"), &aThreadName, err);
}
else
{
iFormatter->AppendFormatL(_L("\t\t\tCPU time: %Lu\r\n"), time.Int64());
}
}
}
#else
void CCmdPs::PrintCpuTimeL(RThread&, const TDesC&)
{
}
#endif
void CCmdPs::PrintChunkInfoL(const TDesC& aProcessName)
{
if (iPrintChunkInfo)
{
iFormatter->AppendL(_L("\tChunks:\r\n"));
TFindChunk findChunk(aProcessName);
while (findChunk.Next(iChunkName) == KErrNone)
{
TPtrC shortChunkName(iChunkName.Mid(aProcessName.Length() - 1));
iFormatter->AppendFormatL(_L("\t\t%S\r\n"), &shortChunkName);
#ifdef EKA2
#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
if (iMemoryAccess.Handle())
{
TChunkKernelInfo chunkInfo;
TPckg<TChunkKernelInfo> chunkInfoPckg(chunkInfo);
TInt err = iMemoryAccess.GetObjectInfo(EChunk, iChunkName, chunkInfoPckg);
if (err)
{
PrintWarning(_L("Unable to get info for chunk %S: %d"), &iChunkName, err);
}
else
{
iFormatter->AppendFormatL(_L("\t\t\tAddress: 0x%08x\r\n"), chunkInfo.iAddressOfKernelObject);
PrintSizeL(_L("\t\t\tSize: "), chunkInfo.iSize);
PrintSizeL(_L("\t\t\tMax size: "), chunkInfo.iMaxSize);
iFormatter->AppendFormatL(_L("\t\t\tType: %d\r\n"), chunkInfo.iChunkType);
}
}
#endif // FSHELL_MEMORY_ACCESS_SUPPORT
#else // !EKA2
RChunk chunk;
TInt err = chunk.Open(findChunk);
if (err)
{
PrintWarning(_L("Unable to open chunk %S: %d"), &iChunkName, err);
}
else
{
CleanupClosePushL(chunk);
PrintSizeL(_L("\t\t\tSize: "), chunk.Size());
PrintSizeL(_L("\t\t\tMax size: "), chunk.MaxSize());
CleanupStack::PopAndDestroy(&chunk);
}
#endif // EKA2
}
}
}
void CCmdPs::PrintSizeL(const TDesC& aCaption, TInt aSize)
{
if (iHuman)
{
iFormatter->AppendL(aCaption);
iFormatter->AppendHumanReadableSizeL(aSize, EUnaligned);
_LIT(KNewLine, "\r\n");
iFormatter->AppendL(KNewLine);
}
else
{
_LIT(KFormat, "%S%d\r\n");
iFormatter->AppendFormatL(KFormat, &aCaption, aSize);
}
}
void ReleaseCodesegMutex(TAny* aMemAccess)
{
static_cast<RMemoryAccess*>(aMemAccess)->ReleaseCodeSegMutex();
}
void CCmdPs::PrintCodeSegsL(RProcess& aProcess)
{
TInt count = iMemoryAccess.AcquireCodeSegMutexAndFilterCodesegsForProcess(aProcess.Id());
LeaveIfErr(count, _L("Couldn't acquire codeseg mutex"));
CleanupStack::PushL(TCleanupItem(&ReleaseCodesegMutex, &iMemoryAccess));
iFormatter->AppendFormatL(_L("\t%d code segments:\r\n"), count);
TCodeSegKernelInfo codeSegInfo;
TPckg<TCodeSegKernelInfo> pkg(codeSegInfo);
while (iMemoryAccess.GetNextCodeSegInfo(pkg) == KErrNone)
{
// Reuse iChunkName, I'm sure it doesn't mind
iChunkName.Copy(codeSegInfo.iFileName);
iFormatter->AppendFormatL(_L("\t\t%S\r\n"), &iChunkName);
}
CleanupStack::PopAndDestroy(); // ReleaseCodesegMutex
}
#ifdef EXE_BUILD
EXE_BOILER_PLATE(CCmdPs)
#endif