|         |      1 // showdebug.cpp | 
|         |      2 //  | 
|         |      3 // Copyright (c) 2010 Accenture. All rights reserved. | 
|         |      4 // This component and the accompanying materials are made available | 
|         |      5 // under the terms of the "Eclipse Public License v1.0" | 
|         |      6 // which accompanies this distribution, and is available | 
|         |      7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". | 
|         |      8 //  | 
|         |      9 // Initial Contributors: | 
|         |     10 // Accenture - Initial contribution | 
|         |     11 // | 
|         |     12  | 
|         |     13 #include <fshell/ioutils.h> | 
|         |     14 #include <fshell/common.mmh> | 
|         |     15 #include <fshell/debugrouter.h> | 
|         |     16 #include <HAL.h> | 
|         |     17  | 
|         |     18 using namespace IoUtils; | 
|         |     19  | 
|         |     20 class CCmdShowDebug : public CCommandBase, public MCommandExtensionsV2 | 
|         |     21 	{ | 
|         |     22 public: | 
|         |     23 	static CCommandBase* NewLC(); | 
|         |     24 	~CCmdShowDebug(); | 
|         |     25 private: | 
|         |     26 	CCmdShowDebug(); | 
|         |     27 	void Log(TUint8 aWhere, TUint32 aTickCount, TUint aThreadId, const TDesC8& aMsg); | 
|         |     28 	inline TTime TickCountToTime(TUint32 aTickCount) const; | 
|         |     29  | 
|         |     30 private: // From CCommandBase. | 
|         |     31 	virtual const TDesC& Name() const; | 
|         |     32 	virtual void DoRunL(); | 
|         |     33 	virtual void ArgumentsL(RCommandArgumentList& aArguments); | 
|         |     34 	virtual void OptionsL(RCommandOptionList& aOptions); | 
|         |     35 	virtual void DoCancel(); | 
|         |     36 	virtual void RunL(); | 
|         |     37  | 
|         |     38 private: // From MCommandExtensionsV2 | 
|         |     39 	virtual void CtrlCPressed(); | 
|         |     40  | 
|         |     41  | 
|         |     42 	class CLogonCompleter : public CActive | 
|         |     43 		{ | 
|         |     44 	public: | 
|         |     45 		CLogonCompleter(CCmdShowDebug* aCommand) : CActive(CActive::EPriorityStandard), iCommand(aCommand) | 
|         |     46 			{ | 
|         |     47 			CActiveScheduler::Add(this); | 
|         |     48 			iCommand->iProcess.Process().Logon(iStatus); | 
|         |     49 			SetActive(); | 
|         |     50 			} | 
|         |     51 		void RunL() { iCommand->Complete(iStatus.Int()); } | 
|         |     52 		void DoCancel() { iCommand->iProcess.Process().LogonCancel(iStatus); } | 
|         |     53 		~CLogonCompleter() { Cancel(); } | 
|         |     54  | 
|         |     55 	private: | 
|         |     56 		CCmdShowDebug* iCommand; | 
|         |     57 		}; | 
|         |     58  | 
|         |     59 private: | 
|         |     60 	RCloggerDebugRouter iRouter; | 
|         |     61 	RChunk iChunk; | 
|         |     62 	TBuf8<2048> iTempBuf; | 
|         |     63 	TBuf<2048> iTempWideBuf; | 
|         |     64 	RChildProcess iProcess; | 
|         |     65 	CLogonCompleter* iCompleter; | 
|         |     66 	TInt64 iStartupTickInMicroseconds; | 
|         |     67 	TTime iTimeAtStartup; | 
|         |     68 	TInt iTickFreq; | 
|         |     69  | 
|         |     70 	HBufC* iProcessName; | 
|         |     71 	HBufC* iArgs; | 
|         |     72 	RArray<TBool> iVerbose; | 
|         |     73 	TBool iFilter; | 
|         |     74 	}; | 
|         |     75  | 
|         |     76 EXE_BOILER_PLATE(CCmdShowDebug) | 
|         |     77  | 
|         |     78 CCommandBase* CCmdShowDebug::NewLC() | 
|         |     79 	{ | 
|         |     80 	CCmdShowDebug* self = new(ELeave) CCmdShowDebug(); | 
|         |     81 	CleanupStack::PushL(self); | 
|         |     82 	self->BaseConstructL(); | 
|         |     83 	return self; | 
|         |     84 	} | 
|         |     85  | 
|         |     86 CCmdShowDebug::~CCmdShowDebug() | 
|         |     87 	{ | 
|         |     88 	Cancel(); | 
|         |     89 	if (iRouter.Handle()) | 
|         |     90 		{ | 
|         |     91 		iRouter.EnableDebugRouting(RCloggerDebugRouter::EDisable); | 
|         |     92 		} | 
|         |     93 	iRouter.Close(); | 
|         |     94 	iChunk.Close(); | 
|         |     95 	delete iCompleter; | 
|         |     96 	if (iProcess.Process().Handle() && iProcess.Process().ExitType() == EExitPending) | 
|         |     97 		{ | 
|         |     98 		iProcess.Process().Kill(KErrAbort); | 
|         |     99 		} | 
|         |    100 	iProcess.Close(); | 
|         |    101 	delete iProcessName; | 
|         |    102 	delete iArgs; | 
|         |    103 	} | 
|         |    104  | 
|         |    105 CCmdShowDebug::CCmdShowDebug() | 
|         |    106 	: CCommandBase(EManualComplete | ECaptureCtrlC) | 
|         |    107 	{ | 
|         |    108 	SetExtension(this); | 
|         |    109 	} | 
|         |    110  | 
|         |    111 const TDesC& CCmdShowDebug::Name() const | 
|         |    112 	{ | 
|         |    113 	_LIT(KName, "showdebug");	 | 
|         |    114 	return KName; | 
|         |    115 	} | 
|         |    116  | 
|         |    117 void CCmdShowDebug::ArgumentsL(RCommandArgumentList& aArguments) | 
|         |    118 	{ | 
|         |    119 	aArguments.AppendStringL(iProcessName, _L("process")); | 
|         |    120 	aArguments.AppendStringL(iArgs, _L("arguments")); | 
|         |    121 	} | 
|         |    122  | 
|         |    123 void CCmdShowDebug::OptionsL(RCommandOptionList& aOptions) | 
|         |    124 	{ | 
|         |    125 	aOptions.AppendBoolL(iVerbose, _L("verbose")); | 
|         |    126 	aOptions.AppendBoolL(iFilter, _L("filter")); | 
|         |    127 	} | 
|         |    128  | 
|         |    129 void CCmdShowDebug::DoRunL() | 
|         |    130 	{ | 
|         |    131 	TInt err = RCloggerDebugRouter::LoadDriver(); | 
|         |    132 	if (err != KErrAlreadyExists) LeaveIfErr(err, _L("Couldn't load clogger debug router")); | 
|         |    133 	LeaveIfErr(iRouter.Open(), _L("Couldn't open debug router")); | 
|         |    134 	LeaveIfErr(iRouter.OpenChunk(iChunk), _L("Couldn't open debug router shared chunk")); | 
|         |    135 	LeaveIfErr(iRouter.EnableDebugRouting(RCloggerDebugRouter::EEnableRouting), _L("Couldn't enable routing")); | 
|         |    136  | 
|         |    137 	iRouter.ReceiveData(iStatus); | 
|         |    138 	SetActive(); | 
|         |    139  | 
|         |    140 	if (iProcessName) | 
|         |    141 		{ | 
|         |    142 		TRAPL(iProcess.CreateL(*iProcessName, iArgs ? *iArgs : KNullDesC(), IoSession(), Stdin(), Stdout(), Stderr(), Env()), _L("Failed to execute %S"), iProcessName); | 
|         |    143 		iCompleter = new(ELeave) CLogonCompleter(this); | 
|         |    144 		SetErrorReported(ETrue); // So that if iProcess completes with an error it doesn't cause a strange printout when we complete with its error code | 
|         |    145 		iProcess.Process().Resume(); | 
|         |    146 		} | 
|         |    147  | 
|         |    148 	if (iVerbose.Count()) | 
|         |    149 		{ | 
|         |    150 		// Need to do some maths to figure out how to translate tick counts to time | 
|         |    151 		TUint32 tickCount = User::NTickCount(); | 
|         |    152 		iTimeAtStartup.UniversalTime(); | 
|         |    153 		TInt tickPeriod; | 
|         |    154 		User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, tickPeriod)); | 
|         |    155 		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 | 
|         |    156  | 
|         |    157 		iStartupTickInMicroseconds = ((TInt64)tickCount * 1000000) / (TInt64)iTickFreq; // Just making damn sure we're using 64bit math | 
|         |    158 		} | 
|         |    159 	} | 
|         |    160  | 
|         |    161 void CCmdShowDebug::DoCancel() | 
|         |    162 	{ | 
|         |    163 	iRouter.CancelReceive(); | 
|         |    164 	} | 
|         |    165  | 
|         |    166 TPtrC8 Read(TDes8& aTempBuf, TPtrC8& aData, TInt aLength, TPtrC8& aOverflowData) | 
|         |    167 	{ | 
|         |    168 	if (aLength <= aData.Length()) | 
|         |    169 		{ | 
|         |    170 		// Can read it from this buffer | 
|         |    171 		TPtrC8 res(aData.Left(aLength)); | 
|         |    172 		aData.Set(aData.Mid(aLength)); | 
|         |    173 		return res; | 
|         |    174 		} | 
|         |    175 	else /*if (aLength > aData.Length())*/ | 
|         |    176 		{ | 
|         |    177 		// Descriptor spans wrap point, so need to copy into temp buf | 
|         |    178 		aTempBuf.Copy(aData.Left(aTempBuf.MaxLength())); // If anyone's crazy enough to write a platsec diagnostic string longer than 2k, it gets truncated | 
|         |    179 		TInt overflowLen = aLength - aData.Length(); | 
|         |    180 		aData.Set(aOverflowData); // Wrap aData | 
|         |    181 		aOverflowData.Set(TPtrC8()); | 
|         |    182 		if (overflowLen > aData.Length()) | 
|         |    183 			{ | 
|         |    184 			ASSERT(EFalse); // Shouldn't happen | 
|         |    185 			// in urel, return everything we've got | 
|         |    186 			return aData; | 
|         |    187 			} | 
|         |    188 		aTempBuf.Append(aData.Left(overflowLen)); | 
|         |    189 		aData.Set(aData.Mid(overflowLen)); | 
|         |    190 		return TPtrC8(aTempBuf); | 
|         |    191 		} | 
|         |    192 	} | 
|         |    193  | 
|         |    194 void CCmdShowDebug::RunL() | 
|         |    195 	{ | 
|         |    196 	TUint chunkSize = iChunk.Size(); | 
|         |    197 	const TUint KDataStartOffset = sizeof(SDebugChunkHeader); | 
|         |    198 	SDebugChunkHeader* chunkHeader = (SDebugChunkHeader*)iChunk.Base(); | 
|         |    199 	TUint start = chunkHeader->iStartOffset; | 
|         |    200 	TUint end = chunkHeader->iEndOffset; | 
|         |    201 	TUint overflows = chunkHeader->iOverflows; | 
|         |    202  | 
|         |    203 	TBool wrap = (start > end); | 
|         |    204 	TUint endLen = wrap ? chunkSize - start : end - start; | 
|         |    205 	TUint startLen = wrap ? end - KDataStartOffset : 0; | 
|         |    206  | 
|         |    207 	TPtrC8 endData(iChunk.Base() + start, endLen); | 
|         |    208 	TPtrC8 startData; | 
|         |    209 	if (wrap) startData.Set(iChunk.Base() + KDataStartOffset, startLen); | 
|         |    210 	TPtrC8 data(endData); | 
|         |    211  | 
|         |    212 	while (data.Length()) | 
|         |    213 		{ | 
|         |    214 		TPtrC8 header = Read(iTempBuf, data, sizeof(SCloggerTraceInfo), startData); | 
|         |    215 		if (header.Length() < (TInt)sizeof(SCloggerTraceInfo)) | 
|         |    216 			{ | 
|         |    217 			ASSERT(EFalse); // for udeb | 
|         |    218 			break; // Something's broken | 
|         |    219 			} | 
|         |    220 		SCloggerTraceInfo info; | 
|         |    221 		Mem::Copy(&info, header.Ptr(), sizeof(SCloggerTraceInfo)); | 
|         |    222 		ASSERT(info.iTraceType == 'K' || info.iTraceType == 'U' || info.iTraceType == 'P'); | 
|         |    223 		TPtrC8 msg = Read(iTempBuf, data, info.iLength, startData); | 
|         |    224 		Log(info.iTraceType, info.iTickCount, info.iThreadId, msg); | 
|         |    225 		} | 
|         |    226 	if (overflows) | 
|         |    227 		{ | 
|         |    228 		_LIT(KErr, "RDebug::Print buffer overflowed, %u calls not logged"); | 
|         |    229 		PrintWarning(KErr, overflows); | 
|         |    230 		} | 
|         |    231  | 
|         |    232 	iRouter.ReceiveData(iStatus); | 
|         |    233 	SetActive(); | 
|         |    234 	} | 
|         |    235  | 
|         |    236 void CCmdShowDebug::Log(TUint8 /*aWhere*/, TUint32 aTickCount, TUint aThreadId, const TDesC8& aMsg) | 
|         |    237 	{ | 
|         |    238 	RThread thread; thread.SetHandle(0); | 
|         |    239 	if (iVerbose.Count() > 1 || iFilter) | 
|         |    240 		{ | 
|         |    241 		// Need to open the thread in those cases | 
|         |    242 		thread.Open(aThreadId); | 
|         |    243 		} | 
|         |    244  | 
|         |    245 	if (iFilter && thread.Handle()) | 
|         |    246 		{ | 
|         |    247 		RProcess proc; | 
|         |    248 		TInt err = thread.Process(proc); | 
|         |    249 		if (!err) | 
|         |    250 			{ | 
|         |    251 			if (proc.Id() != iProcess.Process().Id()) | 
|         |    252 				{ | 
|         |    253 				// Trace definitely doesn't belong to our process, skip it | 
|         |    254 				proc.Close(); | 
|         |    255 				thread.Close(); | 
|         |    256 				return; | 
|         |    257 				} | 
|         |    258 			} | 
|         |    259 		} | 
|         |    260 	 | 
|         |    261 	if (iVerbose.Count()) | 
|         |    262 		{ | 
|         |    263 		TDateTime dt = TickCountToTime(aTickCount).DateTime(); | 
|         |    264 		_LIT(KFormat, "%i-%02i-%02i %02i:%02i:%02i.%03i: "); | 
|         |    265 		// Have to add 1 to Month and Day, as these are zero-based | 
|         |    266 		iTempWideBuf.Format(KFormat, dt.Year(), dt.Month()+1, dt.Day()+1, dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond()/1000); | 
|         |    267 		if (iVerbose.Count() > 1 && thread.Handle()) | 
|         |    268 			{ | 
|         |    269 			TFullName name = thread.FullName(); | 
|         |    270 			iTempWideBuf.AppendFormat(_L("%S "), &name); | 
|         |    271 			} | 
|         |    272 		else | 
|         |    273 			{ | 
|         |    274 			// Just use thread id | 
|         |    275 			iTempWideBuf.AppendFormat(_L("[%d] "), aThreadId); | 
|         |    276 			} | 
|         |    277 		Write(iTempWideBuf); | 
|         |    278 		} | 
|         |    279  | 
|         |    280 	thread.Close(); | 
|         |    281 	 | 
|         |    282 	iTempWideBuf.Copy(aMsg); | 
|         |    283 	Write(iTempWideBuf); | 
|         |    284 	Write(_L("\r\n")); | 
|         |    285 	} | 
|         |    286  | 
|         |    287 void CCmdShowDebug::CtrlCPressed() | 
|         |    288 	{ | 
|         |    289 	// TODO clean up iProcess | 
|         |    290  | 
|         |    291 	Printf(_L("CTRL-C received, exiting.\r\n")); | 
|         |    292 	Complete(); | 
|         |    293 	} | 
|         |    294  | 
|         |    295 inline TTime CCmdShowDebug::TickCountToTime(TUint32 aTickCount) const | 
|         |    296 	{ | 
|         |    297 	return TTime(iTimeAtStartup.Int64() + (((TInt64)aTickCount*1000000) / (TInt64)iTickFreq) - iStartupTickInMicroseconds); | 
|         |    298 	} |