Added showdebug command.
Also:
* Added an exported constructor to RChildProcess so the iProcess handle was set to zero on construction.
* Fixed bug in fshell_builddocs script that created HTML relative links with backslashes instead of forward slashes.
// showdebug.cpp
//
// Copyright (c) 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/ioutils.h>
#include <fshell/common.mmh>
#include <fshell/debugrouter.h>
#include <HAL.h>
using namespace IoUtils;
class CCmdShowDebug : public CCommandBase, public MCommandExtensionsV2
{
public:
static CCommandBase* NewLC();
~CCmdShowDebug();
private:
CCmdShowDebug();
void Log(TUint8 aWhere, TUint32 aTickCount, TUint aThreadId, const TDesC8& aMsg);
inline TTime TickCountToTime(TUint32 aTickCount) const;
private: // From CCommandBase.
virtual const TDesC& Name() const;
virtual void DoRunL();
virtual void ArgumentsL(RCommandArgumentList& aArguments);
virtual void OptionsL(RCommandOptionList& aOptions);
virtual void DoCancel();
virtual void RunL();
private: // From MCommandExtensionsV2
virtual void CtrlCPressed();
class CLogonCompleter : public CActive
{
public:
CLogonCompleter(CCmdShowDebug* aCommand) : CActive(CActive::EPriorityStandard), iCommand(aCommand)
{
CActiveScheduler::Add(this);
iCommand->iProcess.Process().Logon(iStatus);
SetActive();
}
void RunL() { iCommand->Complete(iStatus.Int()); }
void DoCancel() { iCommand->iProcess.Process().LogonCancel(iStatus); }
~CLogonCompleter() { Cancel(); }
private:
CCmdShowDebug* iCommand;
};
private:
RCloggerDebugRouter iRouter;
RChunk iChunk;
TBuf8<2048> iTempBuf;
TBuf<2048> iTempWideBuf;
RChildProcess iProcess;
CLogonCompleter* iCompleter;
TInt64 iStartupTickInMicroseconds;
TTime iTimeAtStartup;
TInt iTickFreq;
HBufC* iProcessName;
HBufC* iArgs;
RArray<TBool> iVerbose;
TBool iFilter;
};
EXE_BOILER_PLATE(CCmdShowDebug)
CCommandBase* CCmdShowDebug::NewLC()
{
CCmdShowDebug* self = new(ELeave) CCmdShowDebug();
CleanupStack::PushL(self);
self->BaseConstructL();
return self;
}
CCmdShowDebug::~CCmdShowDebug()
{
Cancel();
if (iRouter.Handle())
{
iRouter.EnableDebugRouting(RCloggerDebugRouter::EDisable);
}
iRouter.Close();
iChunk.Close();
delete iCompleter;
if (iProcess.Process().Handle() && iProcess.Process().ExitType() == EExitPending)
{
iProcess.Process().Kill(KErrAbort);
}
iProcess.Close();
delete iProcessName;
delete iArgs;
}
CCmdShowDebug::CCmdShowDebug()
: CCommandBase(EManualComplete | ECaptureCtrlC)
{
SetExtension(this);
}
const TDesC& CCmdShowDebug::Name() const
{
_LIT(KName, "showdebug");
return KName;
}
void CCmdShowDebug::ArgumentsL(RCommandArgumentList& aArguments)
{
aArguments.AppendStringL(iProcessName, _L("process"));
aArguments.AppendStringL(iArgs, _L("arguments"));
}
void CCmdShowDebug::OptionsL(RCommandOptionList& aOptions)
{
aOptions.AppendBoolL(iVerbose, _L("verbose"));
aOptions.AppendBoolL(iFilter, _L("filter"));
}
void CCmdShowDebug::DoRunL()
{
TInt err = RCloggerDebugRouter::LoadDriver();
if (err != KErrAlreadyExists) LeaveIfErr(err, _L("Couldn't load clogger debug router"));
LeaveIfErr(iRouter.Open(), _L("Couldn't open debug router"));
LeaveIfErr(iRouter.OpenChunk(iChunk), _L("Couldn't open debug router shared chunk"));
LeaveIfErr(iRouter.EnableDebugRouting(RCloggerDebugRouter::EEnableRouting), _L("Couldn't enable routing"));
iRouter.ReceiveData(iStatus);
SetActive();
if (iProcessName)
{
TRAPL(iProcess.CreateL(*iProcessName, iArgs ? *iArgs : KNullDesC(), IoSession(), Stdin(), Stdout(), Stderr(), Env()), _L("Failed to execute %S"), iProcessName);
iCompleter = new(ELeave) CLogonCompleter(this);
SetErrorReported(ETrue); // So that if iProcess completes with an error it doesn't cause a strange printout when we complete with its error code
iProcess.Process().Resume();
}
if (iVerbose.Count())
{
// Need to do some maths to figure out how to translate tick counts to time
TUint32 tickCount = User::NTickCount();
iTimeAtStartup.UniversalTime();
TInt tickPeriod;
User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, tickPeriod));
iTickFreq = 1000000 / tickPeriod; // We work in frequencies because they are the round numbers when using the fast counter, and at some point we might want to again
iStartupTickInMicroseconds = ((TInt64)tickCount * 1000000) / (TInt64)iTickFreq; // Just making damn sure we're using 64bit math
}
}
void CCmdShowDebug::DoCancel()
{
iRouter.CancelReceive();
}
TPtrC8 Read(TDes8& aTempBuf, TPtrC8& aData, TInt aLength, TPtrC8& aOverflowData)
{
if (aLength <= aData.Length())
{
// Can read it from this buffer
TPtrC8 res(aData.Left(aLength));
aData.Set(aData.Mid(aLength));
return res;
}
else /*if (aLength > aData.Length())*/
{
// Descriptor spans wrap point, so need to copy into temp buf
aTempBuf.Copy(aData.Left(aTempBuf.MaxLength())); // If anyone's crazy enough to write a platsec diagnostic string longer than 2k, it gets truncated
TInt overflowLen = aLength - aData.Length();
aData.Set(aOverflowData); // Wrap aData
aOverflowData.Set(TPtrC8());
if (overflowLen > aData.Length())
{
ASSERT(EFalse); // Shouldn't happen
// in urel, return everything we've got
return aData;
}
aTempBuf.Append(aData.Left(overflowLen));
aData.Set(aData.Mid(overflowLen));
return TPtrC8(aTempBuf);
}
}
void CCmdShowDebug::RunL()
{
TUint chunkSize = iChunk.Size();
const TUint KDataStartOffset = sizeof(SDebugChunkHeader);
SDebugChunkHeader* chunkHeader = (SDebugChunkHeader*)iChunk.Base();
TUint start = chunkHeader->iStartOffset;
TUint end = chunkHeader->iEndOffset;
TUint overflows = chunkHeader->iOverflows;
TBool wrap = (start > end);
TUint endLen = wrap ? chunkSize - start : end - start;
TUint startLen = wrap ? end - KDataStartOffset : 0;
TPtrC8 endData(iChunk.Base() + start, endLen);
TPtrC8 startData;
if (wrap) startData.Set(iChunk.Base() + KDataStartOffset, startLen);
TPtrC8 data(endData);
while (data.Length())
{
TPtrC8 header = Read(iTempBuf, data, sizeof(SCloggerTraceInfo), startData);
if (header.Length() < (TInt)sizeof(SCloggerTraceInfo))
{
ASSERT(EFalse); // for udeb
break; // Something's broken
}
SCloggerTraceInfo info;
Mem::Copy(&info, header.Ptr(), sizeof(SCloggerTraceInfo));
ASSERT(info.iTraceType == 'K' || info.iTraceType == 'U' || info.iTraceType == 'P');
TPtrC8 msg = Read(iTempBuf, data, info.iLength, startData);
Log(info.iTraceType, info.iTickCount, info.iThreadId, msg);
}
if (overflows)
{
_LIT(KErr, "RDebug::Print buffer overflowed, %u calls not logged");
PrintWarning(KErr, overflows);
}
iRouter.ReceiveData(iStatus);
SetActive();
}
void CCmdShowDebug::Log(TUint8 /*aWhere*/, TUint32 aTickCount, TUint aThreadId, const TDesC8& aMsg)
{
RThread thread; thread.SetHandle(0);
if (iVerbose.Count() > 1 || iFilter)
{
// Need to open the thread in those cases
thread.Open(aThreadId);
}
if (iFilter && thread.Handle())
{
RProcess proc;
TInt err = thread.Process(proc);
if (!err)
{
if (proc.Id() != iProcess.Process().Id())
{
// Trace definitely doesn't belong to our process, skip it
proc.Close();
thread.Close();
return;
}
}
}
if (iVerbose.Count())
{
TDateTime dt = TickCountToTime(aTickCount).DateTime();
_LIT(KFormat, "%i-%02i-%02i %02i:%02i:%02i.%03i: ");
// Have to add 1 to Month and Day, as these are zero-based
iTempWideBuf.Format(KFormat, dt.Year(), dt.Month()+1, dt.Day()+1, dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond()/1000);
if (iVerbose.Count() > 1 && thread.Handle())
{
TFullName name = thread.FullName();
iTempWideBuf.AppendFormat(_L("%S "), &name);
}
else
{
// Just use thread id
iTempWideBuf.AppendFormat(_L("[%d] "), aThreadId);
}
Write(iTempWideBuf);
}
thread.Close();
iTempWideBuf.Copy(aMsg);
Write(iTempWideBuf);
Write(_L("\r\n"));
}
void CCmdShowDebug::CtrlCPressed()
{
// TODO clean up iProcess
Printf(_L("CTRL-C received, exiting.\r\n"));
Complete();
}
inline TTime CCmdShowDebug::TickCountToTime(TUint32 aTickCount) const
{
return TTime(iTimeAtStartup.Int64() + (((TInt64)aTickCount*1000000) / (TInt64)iTickFreq) - iStartupTickInMicroseconds);
}