|
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 } |