|
1 // CloggerServer.cpp |
|
2 // |
|
3 // Copyright (c) 2006 - 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 "CloggerServer.h" |
|
14 #include "Clogger.h" |
|
15 #include "cliserv.h" |
|
16 #include "common.h" |
|
17 #include <HAL.h> |
|
18 #include <centralrepository.h> |
|
19 #include <s32mem.h> |
|
20 #include "SessionWriter.h" |
|
21 #include <fshell/common.mmh> |
|
22 |
|
23 #define ClientPanic(aMsg) PanicClient(aMsg, __LINE__) |
|
24 static const TUint32 KAllEnabled = 0xFFFFFFFFu; |
|
25 |
|
26 enum TFlags { |
|
27 EAllowCurrentBufferToBeRead, |
|
28 EInMiddleOfLog, // so don't call Log again, you'll stamp all over the buffers! This is a debug check to guard against my carelessness, ie calling something from within Log() that tries to call Log() again. Log() is not reenterant! |
|
29 ELogBufferOverflow, // Can't log when the overflow occurs, so remember it until the log is in an ok state to record the fact |
|
30 EWrittenToLog, // This indicates we have got far enough through construction to have written stuff to the buffers. This is an explicit flag to guard against future changes to the startup ordering |
|
31 ECenrepFailed, // Can't log when cenrep is accessed, so remember for when we can log it |
|
32 ENeedToCopyCompressedLog, // Indicates we need to call CopyLogToExternalMediaL when the background compress of the log completes |
|
33 EForceBreakpoint, // Forces a __DEBUGGER call at the start of DoServiceL(). Useful when debugging tclog |
|
34 EUsingTempBuf, // Another check to make sure I don't do something stupid with my buffers |
|
35 EMaxFlags |
|
36 }; |
|
37 |
|
38 __ASSERT_COMPILE(EMaxFlags <= 32); |
|
39 |
|
40 #define ACQUIRE_FLAG_LOCK_OR_FAIL(aFlag, failureOperation) \ |
|
41 ASSERT(!iFlags.IsSet(aFlag)); /* Debug behaviour */ \ |
|
42 if (iFlags.IsSet(aFlag)) { failureOperation; } /* Non debug behaviour - fail gracefully */ \ |
|
43 iFlags.Set(aFlag); |
|
44 #define ACQUIRE_FLAG_LOCK(aFlag) ACQUIRE_FLAG_LOCK_OR_FAIL(aFlag, return) |
|
45 #define RELEASE_FLAG_LOCK(aFlag) iFlags.Clear(aFlag) |
|
46 |
|
47 enum TInternalLoggingMasks { |
|
48 ELogTagChanges = 1, |
|
49 ELogNewTags = 2, |
|
50 ELogDisks = 4, |
|
51 ELogNewRdebuggers = 8, |
|
52 }; |
|
53 |
|
54 enum TRDebugPrintMasks { |
|
55 ELogRDebugPrint = 1, |
|
56 ELogKernPrintf = 2, |
|
57 ELogPlatSecDiagnostics = 4, |
|
58 }; |
|
59 |
|
60 _LIT8(KCloggerTag, "Clogger"); |
|
61 _LIT8(KOldRDebugTag, "RDebug::Print"); |
|
62 _LIT8(KRDebugTag, "RDebug"); |
|
63 _LIT8(KKernPrintfTag, "Kern::Printf"); // Not used any more - keep the definition because we explicitly ignore it when loading tags from cenrep |
|
64 _LIT8(KOldFallbackTag, "**Everything else**"); |
|
65 _LIT8(KFallbackTag, "DefaultForNewTags"); |
|
66 _LIT(KLogDir, "\\logs\\"); // Don't include the C: because that would upset moap. Rely on RFs to default to the system drive |
|
67 _LIT(KLogFile, "clogger.txt"); |
|
68 |
|
69 // Don't reorder these without also changing order they are constructed in CCloggerServer::ConstructL |
|
70 enum TWriters { |
|
71 EFile = 0, |
|
72 ERDebug, |
|
73 EMessageQueue, |
|
74 ESessionWriter, |
|
75 // Add new writers here! |
|
76 EMaxWriters |
|
77 }; |
|
78 |
|
79 enum TCenRep { |
|
80 EGlobalOptions = 0, |
|
81 EBufferSize, |
|
82 ENumBuffers, |
|
83 ENumRotates, |
|
84 ERotateBehaviour, |
|
85 EEnabledTags, // This is obsolete because we now use the partial key functionality of cenrep to store enabled tags. And that was necessary because cenrep limits binary values to 2048 bytes |
|
86 ERepositoryVersion, |
|
87 }; |
|
88 |
|
89 // Cenrep constants |
|
90 const TUint32 KEverythingMask = 0xFF000000; |
|
91 const TUint32 KEverythingPartialKey = 0x80000000; |
|
92 const TUint32 KEnabledMask = 0xFF000001; |
|
93 const TUint32 KTagEnabledPartialKey = 0x80000000; |
|
94 enum TCenrepVersion |
|
95 { |
|
96 ECenrepOriginalFormat = 0, |
|
97 ECenrepThreadNamesArePrettified = 1, |
|
98 }; |
|
99 |
|
100 const TInt KMinWriteSize = 512; // 512 bytes is a good minimum size for efficient writes to disk |
|
101 const TInt KSyncBufferIdx = -99; // Magic number given by the server to writers to mean 'the sync buffer' |
|
102 const TInt KCompressFileNameBufferIdx = -100; // Magic number given by the server to the compress sync writer, its meaning is internal to CLogCompressor and CCloggerServer |
|
103 |
|
104 /* |
|
105 Clogger AOs: |
|
106 |
|
107 EPriorityHigh + 1: |
|
108 CBtIncomingPrompt Starter - This shouldn't get starved because of writers |
|
109 |
|
110 EPriorityHigh: |
|
111 CLogWriter - emptying buffers |
|
112 CSyncWriterWrapper - emptying buffers |
|
113 CLogsDirWatcher - will never be running at same time as CLogWriter |
|
114 CSessionWriterServer - emptying buffers |
|
115 |
|
116 EPriorityStandard + 1: |
|
117 CDebugRouterClient - reads input from RDdebug::Print redirector |
|
118 |
|
119 EPriorityStandard: |
|
120 CCloggerServer |
|
121 CServerCallbackDispatcher - unused |
|
122 |
|
123 EPriorityLow: |
|
124 iIdleWriteTimer - only go to next buffer when nothing better to do |
|
125 */ |
|
126 |
|
127 |
|
128 class CLogsDirWatcher : public CActive |
|
129 { |
|
130 public: |
|
131 CLogsDirWatcher(RFs& aFs, CCloggerServer& aServer) |
|
132 : CActive(EPriorityHigh), iFs(aFs), iServer(aServer) |
|
133 , iDirToWatch(KLogDir().Left(KLogDir().Length()-1)) // Strip trailing backslash |
|
134 { |
|
135 |
|
136 CActiveScheduler::Add(this); |
|
137 //__DEBUGGER(); |
|
138 iFs.NotifyChange(ENotifyAll, iStatus, iDirToWatch); |
|
139 SetActive(); |
|
140 } |
|
141 |
|
142 void DoCancel() |
|
143 { |
|
144 iFs.NotifyChangeCancel(); |
|
145 } |
|
146 |
|
147 ~CLogsDirWatcher() |
|
148 { |
|
149 Cancel(); |
|
150 } |
|
151 |
|
152 void RunL() |
|
153 { |
|
154 //__DEBUGGER(); |
|
155 if (iStatus != KErrNone) return; // Give up |
|
156 TEntry entry; |
|
157 if (iFs.Entry(KLogDir, entry) == KErrNone) |
|
158 { |
|
159 iServer.LogsDirHasBeenCreated(); |
|
160 } |
|
161 else |
|
162 { |
|
163 // Keep listening |
|
164 iFs.NotifyChange(ENotifyAll, iStatus, iDirToWatch); |
|
165 SetActive(); |
|
166 } |
|
167 } |
|
168 |
|
169 private: |
|
170 RFs& iFs; |
|
171 CCloggerServer& iServer; |
|
172 TPtrC iDirToWatch; |
|
173 }; |
|
174 |
|
175 CSession2* CCloggerServer::NewSessionL(const TVersion& /*aVersion*/, const RMessage2& /*aMessage*/) const |
|
176 { |
|
177 return new(ELeave) CCloggerSession(); |
|
178 } |
|
179 |
|
180 CCloggerServer::CCloggerServer() |
|
181 : CSensibleServer() |
|
182 { |
|
183 } |
|
184 |
|
185 TInt CCloggerServer::TransientServerShutdownTime() const |
|
186 { |
|
187 return 0; // Never shutdown |
|
188 } |
|
189 |
|
190 CCloggerServer::~CCloggerServer() |
|
191 { |
|
192 //__DEBUGGER(); |
|
193 if (iFlags.IsSet(EWrittenToLog)) FlushBuffers(); |
|
194 CloseBuffers(); |
|
195 iBufs.Close(); |
|
196 iChunkForBufs.Close(); |
|
197 |
|
198 for (TInt i = 0; i < iWriters.Count(); i++) |
|
199 { |
|
200 iWriters[i]->CloseWriter(); |
|
201 } |
|
202 iWriters.Close(); |
|
203 delete iSessionWriterServer; |
|
204 |
|
205 if (iCompressor) |
|
206 { |
|
207 iCompressor->Cancel(); // This will wait for the compress to complete. Safest thing to do |
|
208 delete iCompressor; |
|
209 } |
|
210 |
|
211 delete iLogsDirWatcher; |
|
212 iLogFile.Close(); |
|
213 iFs.Close(); |
|
214 //iFileBeingRotated.Close(); |
|
215 |
|
216 TPtrHashMapIter<TDesC8, TTagData> tagIter(iTags); |
|
217 TTagData* tag; |
|
218 while ((tag = const_cast<TTagData*>(tagIter.NextValue())) != NULL) |
|
219 { |
|
220 tagIter.RemoveCurrent(); |
|
221 delete tag->iTagName; |
|
222 delete tag; |
|
223 } |
|
224 iTags.Close(); |
|
225 iTempBuf.Close(); |
|
226 iSessionTempBuf.Close(); |
|
227 iRDebugTags.Close(); |
|
228 |
|
229 delete iIdleWriteTimer; |
|
230 ASSERT(!iFlushBufferWait.IsStarted()); |
|
231 delete iKernDebugRouter; // When should this be deleted? |
|
232 } |
|
233 |
|
234 void CCloggerServer::ConstructL() |
|
235 { |
|
236 CSensibleServer::ConstructL(); |
|
237 GetSettingsL(); |
|
238 |
|
239 iWriters.ReserveL(EMaxWriters); |
|
240 // Note: important to get the order of writers in the array to be the same as is defined in enum TWriters |
|
241 CLogWriter* filelogger = new(ELeave) CLogWriter(*this, iLogFile); |
|
242 filelogger->SetEnabled(ETrue); |
|
243 iWriters.Append(filelogger); |
|
244 |
|
245 CRDebugWriter* rdebuglogger = new(ELeave) CRDebugWriter(*this); |
|
246 CleanupStack::PushL(rdebuglogger); |
|
247 CSyncWriterWrapper* rdebugWrapper = CSyncWriterWrapper::NewL(*this, *rdebuglogger, iWriters.Count()); |
|
248 CleanupStack::Pop(rdebuglogger); |
|
249 rdebugWrapper->SetEnabled(EFalse); |
|
250 iWriters.Append(rdebugWrapper); |
|
251 |
|
252 CMessageQueueWriter* msgq = CMessageQueueWriter::NewL(); |
|
253 CleanupStack::PushL(msgq); |
|
254 CSyncWriterWrapper* msgqWrapper = CSyncWriterWrapper::NewL(*this, *msgq, iWriters.Count()); |
|
255 CleanupStack::Pop(msgq); |
|
256 rdebugWrapper->SetEnabled(EFalse); |
|
257 iWriters.Append(msgqWrapper); |
|
258 |
|
259 CSessionWriter* sessionWriter = new(ELeave) CSessionWriter(*this); |
|
260 sessionWriter->SetEnabled(EFalse); |
|
261 iWriters.Append(sessionWriter); |
|
262 |
|
263 User::LeaveIfError(iFs.Connect()); |
|
264 User::LeaveIfError(iFs.SetSessionPath(KLogDir)); |
|
265 |
|
266 TInt err = OpenLogFile(); |
|
267 if (err == KErrPathNotFound) |
|
268 { |
|
269 err = KErrNone; |
|
270 iLogsDirWatcher = new(ELeave) CLogsDirWatcher(iFs, *this); |
|
271 } |
|
272 User::LeaveIfError(err); |
|
273 /*TInt err = iLogFile.Open(iFs, KLogFile, EFileWrite|EFileShareAny|EFileStream); |
|
274 if (err == KErrNotFound) err = iLogFile.Create(iFs, KLogFile, EFileWrite|EFileShareAny|EFileStream); |
|
275 if (err == KErrPathNotFound) |
|
276 { |
|
277 iWriters[EFile]->SetEnabled(EFalse); |
|
278 err = KErrNone; |
|
279 iLogsDirWatcher = new(ELeave) CLogsDirWatcher(iFs, *this); |
|
280 } |
|
281 else |
|
282 { |
|
283 User::LeaveIfError(err); |
|
284 TInt seekTo = -1; |
|
285 User::LeaveIfError(iLogFile.Seek(ESeekEnd, seekTo)); |
|
286 } |
|
287 */ |
|
288 iIdleWriteTimer = CPeriodic::NewL(CActive::EPriorityLow); |
|
289 |
|
290 // Important that we start the kern debug router early, before the first call to UpdateBufferSizeL because we now rely on the debug router to allocate our chunk for us |
|
291 TRAPD(debugRouterErr, iKernDebugRouter = CDebugRouterClient::NewL(*this)); // We check 'debugRouterErr' further down |
|
292 const TInt KMaxChunkSize = 512*1024; // We will say 512 KB for max chunk size |
|
293 err = CreateKernChunkForClient(NULL, KMaxChunkSize, 0, iChunkForBufs); |
|
294 if (err == KErrNotSupported) |
|
295 { |
|
296 // Then debug router not found - so just create a chunk ourselves |
|
297 _LIT(KName, "CloggerBufferChunk"); |
|
298 err = iChunkForBufs.CreateGlobal(KName, 0, KMaxChunkSize); |
|
299 } |
|
300 User::LeaveIfError(err); |
|
301 |
|
302 UpdateBufferSizeL(iBufferSize, iNumBuffers); |
|
303 iTempBuf.CreateL(512); |
|
304 iSessionTempBuf.CreateL(512); |
|
305 |
|
306 // The mechanism to compress log files in the background is a special case of a CSyncWriterWrapper |
|
307 CLogCompressor* compressor = CLogCompressor::NewLC(*this); |
|
308 iCompressor = CSyncWriterWrapper::NewL(*this, *compressor, -1); |
|
309 CleanupStack::Pop(compressor); |
|
310 iCompressor->SetEnabled(ETrue); // Otherwise CSyncWriterWrapper will ignore our requests to compress things! |
|
311 |
|
312 iTags.ReserveL(32); |
|
313 // Construct some tags that always exist |
|
314 iCloggerTag = iTags.Find(KCloggerTag); |
|
315 if (!iCloggerTag) |
|
316 { |
|
317 HBufC8* tag = KCloggerTag().AllocLC(); |
|
318 iCloggerTag = reinterpret_cast<TTagData*>(NewSessionForTagL(tag)); |
|
319 iCloggerTag->SetShouldDisplay(); |
|
320 iCloggerTag->iEnabled = ELogNewRdebuggers; // Defaults to off, except for logging new rdebuggers - because you generally want to be able to see the thread name |
|
321 CleanupStack::Pop(tag); |
|
322 } |
|
323 iRDebugTag = iTags.Find(KRDebugTag); |
|
324 if (!iRDebugTag) |
|
325 { |
|
326 HBufC8* tag = KRDebugTag().AllocLC(); |
|
327 iRDebugTag = reinterpret_cast<TTagData*>(NewSessionForTagL(tag)); |
|
328 // Should not be shown unless used, so don't SetShouldDisplay |
|
329 CleanupStack::Pop(tag); |
|
330 } |
|
331 iFallbackTag = iTags.Find(KFallbackTag); |
|
332 if (!iFallbackTag) |
|
333 { |
|
334 HBufC8* tag = KFallbackTag().AllocLC(); |
|
335 iFallbackTag = reinterpret_cast<TTagData*>(NewSessionForTagL(tag)); |
|
336 iFallbackTag->SetShouldDisplay(); |
|
337 CleanupStack::Pop(tag); |
|
338 } |
|
339 |
|
340 TUint32 tickCount = User::NTickCount(); |
|
341 iTimeAtStartup.UniversalTime(); |
|
342 TInt tickPeriod; |
|
343 //User::LeaveIfError(HAL::Get(HAL::EFastCounterFrequency, iTickFreq)); |
|
344 User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, tickPeriod)); |
|
345 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 |
|
346 |
|
347 iStartupTickInMicroseconds = ((TInt64)tickCount * 1000000) / (TInt64)iTickFreq; // Just making damn sure we're using 64bit math |
|
348 |
|
349 // Rotate uses the tick stuff so must come after it |
|
350 if (iRotateBehaviour & RClogger::EAutoRotateAtStartup) |
|
351 { |
|
352 Rotate(); |
|
353 } |
|
354 |
|
355 UpdatedGlobalOptionsL(iOptions & RClogger::EBufferLog); // Passing in the current value of EBufferLog means we don't trigger another UpdateBufferSizeL, which is good because we've explicitly called that earlier on in this function |
|
356 |
|
357 iFlags.Set(EWrittenToLog); |
|
358 |
|
359 _LIT8(KCrLf, "\r\n"); |
|
360 _LIT8(KStarted, "Server started (log format v4)"); |
|
361 // Format v1 used incorrect time stamps, v2 fixed that, v3 indicates that the buffer info is published via P&S |
|
362 // v4 scraps P&S and uses the kern debug router to map the buffers into the crashlog |
|
363 |
|
364 LogLine(KCrLf); |
|
365 Log(NULL, KStarted, User::NTickCount()); |
|
366 if (iFlags.IsSet(ECenrepFailed)) |
|
367 { |
|
368 _LIT8(KCenrep, "Couldn't access the cenrep repository."); |
|
369 LogError(KCenrep); |
|
370 } |
|
371 if (debugRouterErr) |
|
372 { |
|
373 _LIT8(KDebugRouterFailed, "Failed to load the debug router LDD, err=%i."); |
|
374 LogError(KDebugRouterFailed, debugRouterErr); |
|
375 } |
|
376 |
|
377 _LIT_SECURITY_POLICY_PASS(KRead); |
|
378 _LIT_SECURITY_POLICY_S0(KWrite, KCloggerUid.iUid); |
|
379 |
|
380 RProperty::Define(KCloggerUid, ESequenceNumber, RProperty::EInt, KRead, KWrite); |
|
381 RProperty::Set(KCloggerUid, ESequenceNumber, iEnabledStatesSequenceNumber); |
|
382 } |
|
383 |
|
384 void CCloggerServer::GetSettingsL() |
|
385 { |
|
386 TRAPD(err, DoGetSettingsL()); |
|
387 // Don't allow messed-up cenrep config to prevent us from launching |
|
388 |
|
389 if (err) |
|
390 { |
|
391 iBufferSize = 4096; |
|
392 iNumBuffers = 2; |
|
393 iNumRotates = 4; |
|
394 iFlags.Set(ECenrepFailed); |
|
395 } |
|
396 } |
|
397 |
|
398 void CCloggerServer::DoGetSettingsL() |
|
399 { |
|
400 CRepository* cenrep = CRepository::NewLC(KCloggerUid); |
|
401 User::LeaveIfError(cenrep->Get(EGlobalOptions, (TInt&)iOptions)); |
|
402 User::LeaveIfError(cenrep->Get(EBufferSize, iBufferSize)); |
|
403 User::LeaveIfError(cenrep->Get(ENumBuffers, iNumBuffers)); |
|
404 User::LeaveIfError(cenrep->Get(ENumRotates, iNumRotates)); |
|
405 User::LeaveIfError(cenrep->Get(ERotateBehaviour, (TInt&)iRotateBehaviour)); |
|
406 TInt version = 0; |
|
407 TBool needToPrettifyThreadNames = EFalse; |
|
408 TInt err = cenrep->Get(ERepositoryVersion, version); |
|
409 if (err || version < ECenrepThreadNamesArePrettified) |
|
410 { |
|
411 needToPrettifyThreadNames = ETrue; |
|
412 } |
|
413 |
|
414 RArray<TUint32> foundKeys; |
|
415 CleanupClosePushL(foundKeys); |
|
416 err = cenrep->FindL(KTagEnabledPartialKey, KEnabledMask, foundKeys); |
|
417 if (err == KErrNone) |
|
418 { |
|
419 TInt n = foundKeys.Count(); |
|
420 TBuf8<1> dummyBuf; |
|
421 for (TInt i = 0; i < n; i++) |
|
422 { |
|
423 TUint32 enabled; |
|
424 User::LeaveIfError(cenrep->Get(foundKeys[i], (TInt&)enabled)); |
|
425 TInt len; |
|
426 HBufC8* tagName = NULL; |
|
427 TInt err = cenrep->Get(foundKeys[i]+1, dummyBuf, len); |
|
428 if (err == KErrNone) |
|
429 { |
|
430 tagName = dummyBuf.AllocLC(); |
|
431 } |
|
432 else if (err == KErrOverflow) |
|
433 { |
|
434 tagName = HBufC8::NewLC(len); |
|
435 } |
|
436 else |
|
437 { |
|
438 User::LeaveIfError(err); |
|
439 } |
|
440 TPtr8 ptr = tagName->Des(); |
|
441 User::LeaveIfError(cenrep->Get(foundKeys[i]+1, ptr, len)); |
|
442 if (needToPrettifyThreadNames) |
|
443 { |
|
444 ThreadPrettyName(ptr); |
|
445 } |
|
446 |
|
447 if (*tagName == KKernPrintfTag) |
|
448 { |
|
449 // We don't use this any more |
|
450 CleanupStack::PopAndDestroy(tagName); |
|
451 continue; |
|
452 } |
|
453 else if (*tagName == KOldFallbackTag) |
|
454 { |
|
455 // In case the old definition is kicking round in a cenrep file |
|
456 *tagName = KFallbackTag; |
|
457 } |
|
458 else if (*tagName == KOldRDebugTag) |
|
459 { |
|
460 // We've renamed this too |
|
461 *tagName = KRDebugTag; |
|
462 } |
|
463 |
|
464 TTagData* tagData = iTags.Find(*tagName); |
|
465 if (tagData) |
|
466 { |
|
467 // tagName is not needed |
|
468 CleanupStack::PopAndDestroy(tagName); |
|
469 } |
|
470 else |
|
471 { |
|
472 tagData = new(ELeave)TTagData(tagName); |
|
473 if (*tagName == KRDebugTag) |
|
474 { |
|
475 // We don't want this tag to show up unless we've actually used it, since it's only used for last-ditch OOM logging (this is to minimise confusion about which tags are used for what) |
|
476 } |
|
477 else |
|
478 { |
|
479 tagData->SetShouldDisplay(); // If the tag comes from cenrep then we should show it |
|
480 } |
|
481 CleanupStack::PushL(tagData); |
|
482 iTags.InsertL(tagName, tagData); |
|
483 CleanupStack::Pop(tagData); |
|
484 CleanupStack::Pop(tagName); |
|
485 } |
|
486 tagData->iEnabled = enabled; |
|
487 } |
|
488 } |
|
489 CleanupStack::PopAndDestroy(&foundKeys); |
|
490 |
|
491 /* |
|
492 TInt descSize = 0; |
|
493 TBuf8<1> dummyBuf; // Don't bother trying to succeed without knowing the buffer size |
|
494 TInt err = cenrep->Get(EEnabledTags, dummyBuf, descSize); |
|
495 if (err == KErrOverflow) |
|
496 { |
|
497 // If err is not found, we don't care (repository key hasn't been defined) so do nothing |
|
498 RBuf8 buf; |
|
499 CleanupClosePushL(buf); |
|
500 buf.CreateL(descSize); |
|
501 err = cenrep->Get(EEnabledTags, buf, descSize); |
|
502 User::LeaveIfError(err); // Shouldn't happen |
|
503 |
|
504 CBufFlat* cbuf = CBufFlat::NewL(buf.Length()); |
|
505 CleanupStack::PushL(cbuf); |
|
506 cbuf->InsertL(0, buf); |
|
507 RBufReadStream stream(*cbuf); |
|
508 CleanupClosePushL(stream); |
|
509 |
|
510 TInt count = stream.ReadInt32L(); |
|
511 while (count--) |
|
512 { |
|
513 TUint32 enabled = stream.ReadUint32L(); |
|
514 HBufC8* tagName = HBufC8::NewL(stream, 2000); |
|
515 TTagData* tagData = iTags.Find(*tagName); |
|
516 if (tagData) |
|
517 { |
|
518 delete tagName; // Not needed |
|
519 } |
|
520 else |
|
521 { |
|
522 tagData = new(ELeave)TTagData(); |
|
523 tagData->iTagName = tagName; |
|
524 tagData->iRefCount = 0; |
|
525 CleanupStack::PushL(tagData); |
|
526 iTags.InsertL(tagName, tagData); // TODO hmm if this leaves, tagName will be leaked |
|
527 CleanupStack::Pop(tagData); |
|
528 } |
|
529 tagData->iEnabled = enabled; |
|
530 } |
|
531 CleanupStack::PopAndDestroy(3, &buf); // stream, cbuf, buf |
|
532 } |
|
533 */ |
|
534 CleanupStack::PopAndDestroy(cenrep); |
|
535 } |
|
536 |
|
537 void CCloggerServer::PersistSettingsL() |
|
538 { |
|
539 CRepository* cenrep = CRepository::NewLC(KCloggerUid); |
|
540 User::LeaveIfError(cenrep->Set(EGlobalOptions, (TInt&)iOptions)); |
|
541 User::LeaveIfError(cenrep->Set(EBufferSize, iBufferSize)); |
|
542 User::LeaveIfError(cenrep->Set(ENumBuffers, iNumBuffers)); |
|
543 User::LeaveIfError(cenrep->Set(ENumRotates, iNumRotates)); |
|
544 User::LeaveIfError(cenrep->Set(ERotateBehaviour, (TInt&)iRotateBehaviour)); |
|
545 User::LeaveIfError(cenrep->Set(ERepositoryVersion, ECenrepThreadNamesArePrettified)); |
|
546 |
|
547 User::LeaveIfError(cenrep->StartTransaction(CRepository::EReadWriteTransaction)); |
|
548 cenrep->CleanupCancelTransactionPushL(); |
|
549 TUint32 dontCare; |
|
550 TInt err = cenrep->Delete(KEverythingPartialKey, KEverythingMask, dontCare); |
|
551 if (err && err != KErrNotFound) User::Leave(err); |
|
552 TUint32 key = KTagEnabledPartialKey; |
|
553 TPtrHashMapIter<TDesC8, TTagData> tagIter(iTags); |
|
554 const TTagData* tag; |
|
555 while ((tag = tagIter.NextValue()) != NULL) |
|
556 { |
|
557 User::LeaveIfError(cenrep->Set(key, (TInt&)tag->iEnabled)); |
|
558 key++; |
|
559 User::LeaveIfError(cenrep->Set(key, *tag->iTagName)); |
|
560 key++; |
|
561 } |
|
562 CleanupStack::Pop(); // Transaction |
|
563 User::LeaveIfError(cenrep->CommitTransaction(dontCare)); |
|
564 CleanupStack::PopAndDestroy(cenrep); |
|
565 } |
|
566 |
|
567 void CCloggerServer::ResetSettingsL() |
|
568 { |
|
569 CRepository* cenrep = CRepository::NewLC(KCloggerUid); |
|
570 cenrep->Reset(); |
|
571 CleanupStack::PopAndDestroy(cenrep); |
|
572 GetSettingsL(); |
|
573 } |
|
574 |
|
575 |
|
576 void CCloggerServer::UpdateBufferSizeL(TInt aSize, TInt aNum) |
|
577 { |
|
578 if (aSize < KMinWriteSize) aSize = KMinWriteSize; // Anything smaller is just pointless |
|
579 if (aNum < 2) aNum = 2; // Likewise |
|
580 |
|
581 FlushBuffers(); // Ensure everything is empty before we start |
|
582 CloseBuffers(); |
|
583 |
|
584 iBufferSize = aSize; |
|
585 iNumBuffers = aNum; |
|
586 if (!(iOptions & RClogger::EBufferLog)) |
|
587 { |
|
588 return; |
|
589 } |
|
590 TUint oldOptions = iOptions; |
|
591 iOptions &= ~RClogger::EBufferLog; // Clear this, and set it back to oldOptions at the end, so that if we leave our flags accurately describe the state of our buffers |
|
592 |
|
593 User::LeaveIfError(AdjustBufferChunk(iBufferSize * iNumBuffers)); |
|
594 TUint8* ptr = iChunkForBufs.Base(); |
|
595 |
|
596 while (aNum--) |
|
597 { |
|
598 TBufEntry* entry = new(ELeave) TBufEntry(); |
|
599 CleanupStack::PushL(entry); |
|
600 entry->iBuf.Set(ptr, 0, iBufferSize); |
|
601 entry->iArrayIdx = iBufs.Count(); |
|
602 iBufs.AppendL(entry); |
|
603 CleanupStack::Pop(entry); |
|
604 ptr += iBufferSize; |
|
605 if (iBufs.Count() > 1) iBufs[iBufs.Count()-2]->iNext = entry; |
|
606 } |
|
607 |
|
608 iOptions = oldOptions; |
|
609 iBufs[iBufs.Count()-1]->iNext = iBufs[0]; |
|
610 iCurrent = iBufs[0]; |
|
611 UpdatedBuffers(); |
|
612 |
|
613 if (iLogFile.SubSessionHandle()) |
|
614 { |
|
615 ReCalculateFileAlignment(); |
|
616 // Need to update iSubtractSizeOfNextBuffer in case there's been some unbuffered logging since the file opened |
|
617 // (Or indeed if this is the first time UpdateBuffers has been called, immediately after the file was opened) |
|
618 } |
|
619 } |
|
620 |
|
621 void CCloggerServer::ReCalculateFileAlignment() |
|
622 { |
|
623 TInt seekTo = 0; |
|
624 /*TInt err =*/ iLogFile.Seek(ESeekCurrent, seekTo); |
|
625 // Assuming iLogFile is valid, ESeekCurrent can never return an error (the documentation is not clear but the implementation is) |
|
626 iSubtractSizeOfNextBuffer = KMinWriteSize - (seekTo % KMinWriteSize); |
|
627 } |
|
628 |
|
629 TInt CCloggerServer::OpenLogFile() |
|
630 { |
|
631 TInt err = iLogFile.Open(iFs, KLogFile, EFileWrite|EFileShareAny|EFileStream); |
|
632 if (err == KErrNotFound) err = iLogFile.Create(iFs, KLogFile, EFileWrite|EFileShareAny|EFileStream); |
|
633 if (err == KErrNone) |
|
634 { |
|
635 TInt seekTo = -1; |
|
636 /*err =*/ iLogFile.Seek(ESeekEnd, seekTo); |
|
637 iWriters[EFile]->SetEnabled(ETrue); |
|
638 ReCalculateFileAlignment(); |
|
639 } |
|
640 else |
|
641 { |
|
642 iWriters[EFile]->SetEnabled(EFalse); |
|
643 } |
|
644 |
|
645 return err; |
|
646 } |
|
647 |
|
648 // Session |
|
649 |
|
650 inline CCloggerServer& CCloggerSession::Server() |
|
651 { |
|
652 return *static_cast<CCloggerServer*>(const_cast<CServerBase*>(CSessionBase::Server())); |
|
653 } |
|
654 |
|
655 CCloggerSession::CCloggerSession() |
|
656 : CSensibleSession() |
|
657 { |
|
658 } |
|
659 |
|
660 |
|
661 CCloggerSession::~CCloggerSession() |
|
662 { |
|
663 if (iPerformanceLoggingChunk.Handle()) |
|
664 { |
|
665 TUint8* base = iPerformanceLoggingChunk.Base(); |
|
666 TInt size = *(TInt*)base; |
|
667 TPtrC8 ptr(base+4, size); |
|
668 //__DEBUGGER(); |
|
669 Server().LogHighPerformanceBuffer(ptr); |
|
670 iPerformanceLoggingChunk.Close(); |
|
671 } |
|
672 iGetTagStatesContext.Close(); |
|
673 Server().DropSession(this); |
|
674 } |
|
675 |
|
676 TBool CCloggerSession::DoServiceL(const RMessage& aMessage) |
|
677 { |
|
678 //CleanupPanicPushL(); //DEBUG |
|
679 |
|
680 if (Server().ForceBreakpointRequested()) |
|
681 { |
|
682 __DEBUGGER(); |
|
683 } |
|
684 |
|
685 TBool handled = ETrue; |
|
686 TInt res = KErrNone; |
|
687 |
|
688 TInt func = aMessage.Function(); |
|
689 if (!iContext && func != ESetTag && func != ESetTag8) |
|
690 { |
|
691 ClientPanic(aMessage); // SetTag should have been called |
|
692 return ETrue; |
|
693 } |
|
694 |
|
695 switch (func) |
|
696 { |
|
697 case ESetTag: |
|
698 case ESetTag8: |
|
699 { |
|
700 TInt tagLen = aMessage.GetDesLengthL(0); |
|
701 TBool twoLevel = aMessage.Ptr1() != NULL; |
|
702 if (tagLen && twoLevel) |
|
703 { |
|
704 // 2-level tag name, as used by flogger/CDU |
|
705 tagLen = tagLen + 1 + aMessage.GetDesLengthL(1); // plus 1 for the slash in "tag/subtag" |
|
706 } |
|
707 if (tagLen > 256) |
|
708 { |
|
709 // It makes it easier to guarantee correctness if we limit tag lengths. Since thread fullnames are also 256, this is a good number |
|
710 User::Leave(KErrTooBig); |
|
711 } |
|
712 |
|
713 HBufC8* newTag = NULL; |
|
714 if (tagLen) |
|
715 { |
|
716 newTag = HBufC8::NewMaxLC(tagLen); |
|
717 if (func == ESetTag) |
|
718 { |
|
719 HBufC* tag = HBufC::NewMaxLC(tagLen); |
|
720 TPtr ptr = tag->Des(); |
|
721 aMessage.ReadL(0, ptr); |
|
722 if (twoLevel) |
|
723 { |
|
724 ptr.Append('/'); |
|
725 TInt len = ptr.Length(); |
|
726 TPtr16 endBit((TUint16*)ptr.Ptr()+len, 0, ptr.MaxLength() - len); |
|
727 aMessage.ReadL(1, endBit); |
|
728 ptr.SetLength(len + endBit.Length()); |
|
729 } |
|
730 newTag->Des().Copy(ptr); |
|
731 CleanupStack::PopAndDestroy(tag); |
|
732 } |
|
733 else |
|
734 { |
|
735 TPtr8 ptr = newTag->Des(); |
|
736 aMessage.ReadL(0, ptr); |
|
737 if (twoLevel) |
|
738 { |
|
739 // Use a different separator for the 8-bit variant (as that distinguishes commsdebug from flogger) |
|
740 ptr.Append(':'); |
|
741 TInt len = ptr.Length(); |
|
742 TPtr8 endBit((TUint8*)ptr.Ptr()+len, 0, ptr.MaxLength() - len); |
|
743 aMessage.ReadL(1, endBit); |
|
744 ptr.SetLength(len + endBit.Length()); |
|
745 } |
|
746 } |
|
747 if (*newTag == KCloggerTag || *newTag == KFallbackTag) |
|
748 { |
|
749 // Not allowed for any session to pretend to be us |
|
750 CleanupStack::PopAndDestroy(newTag); |
|
751 newTag = NULL; |
|
752 tagLen = 0; |
|
753 // Drop through to using thread name in this case |
|
754 } |
|
755 } |
|
756 |
|
757 if (!tagLen) |
|
758 { |
|
759 // Default to using thread name for the tag |
|
760 RThread client; |
|
761 aMessage.ClientL(client); |
|
762 CleanupClosePushL(client); |
|
763 TFullName threadName = client.FullName(); |
|
764 CleanupStack::PopAndDestroy(&client); |
|
765 newTag = HBufC8::NewLC(threadName.Length()); |
|
766 TPtr8 tagPtr = newTag->Des(); |
|
767 tagPtr.Copy(threadName); |
|
768 CCloggerServer::ThreadPrettyName(tagPtr); |
|
769 } |
|
770 |
|
771 if (iContext) |
|
772 { |
|
773 Server().ReplaceTagL(iContext, newTag); |
|
774 CleanupStack::Pop(newTag); |
|
775 } |
|
776 else |
|
777 { |
|
778 iContext = Server().NewSessionForTagL(newTag); |
|
779 CleanupStack::Pop(newTag); |
|
780 } |
|
781 |
|
782 TInt sequenceNumber; |
|
783 TUint32 enabled = Server().TagEnabled(iContext, &sequenceNumber); |
|
784 TPckg<TUint32> enablePkg(enabled); |
|
785 aMessage.WriteL(2, enablePkg); |
|
786 res = sequenceNumber; |
|
787 break; |
|
788 } |
|
789 case ELog8: |
|
790 { |
|
791 TUint32 enabledMask = aMessage.Int2(); |
|
792 TUint32 tagEnabled = Server().TagEnabled(iContext); |
|
793 if (!(enabledMask & tagEnabled)) |
|
794 { |
|
795 Server().RegisterDisabledLog(iContext); // SetShouldDisplay usually gets called when you call log, but if it's disabled, then we don't get that far... |
|
796 // Don't bother reading the log data |
|
797 break; |
|
798 } |
|
799 |
|
800 TInt stringLen = aMessage.GetDesLengthL(0); |
|
801 RBuf8& buf = Server().SessionTempBuf(); |
|
802 if (stringLen > buf.MaxLength()) |
|
803 { |
|
804 buf.ReAllocL(stringLen); |
|
805 } |
|
806 aMessage.ReadL(0, buf); |
|
807 TInt tickCount = aMessage.Int1(); |
|
808 Server().Log(iContext, buf, tickCount); |
|
809 break; |
|
810 } |
|
811 case EUpdateEnabledMask: |
|
812 { |
|
813 TUint32 e = Server().TagEnabled(iContext); |
|
814 TPckg<TUint32> enabledBuf(e); |
|
815 aMessage.WriteL(0, enabledBuf); |
|
816 break; |
|
817 } |
|
818 case EHexDump: |
|
819 case EHexDump16: |
|
820 { |
|
821 TUint32 enabledMask = aMessage.Int3(); |
|
822 if (!(enabledMask & Server().TagEnabled(iContext))) |
|
823 { |
|
824 // Don't bother reading the log data |
|
825 break; |
|
826 } |
|
827 |
|
828 RBuf8& header = Server().SessionTempBuf(); |
|
829 TInt dataLen = aMessage.GetDesLengthL(1); |
|
830 |
|
831 if (func == EHexDump) |
|
832 { |
|
833 aMessage.ReadL(0, header); |
|
834 } |
|
835 else |
|
836 { |
|
837 TPtr16 widePtr((TUint16*)header.Ptr(), header.MaxLength() / 2); |
|
838 aMessage.ReadL(0, widePtr); |
|
839 header.SetLength(widePtr.Size()); |
|
840 header.Collapse(); |
|
841 } |
|
842 |
|
843 // Should we use a preallocated buffer here? For ease of coding, don't |
|
844 RBuf8 data; |
|
845 CleanupClosePushL(data); |
|
846 data.CreateL(dataLen); |
|
847 aMessage.ReadL(1, data); |
|
848 |
|
849 TInt tickCount = aMessage.Int2(); |
|
850 Server().HexDumpL(iContext, header, data, tickCount); |
|
851 CleanupStack::PopAndDestroy(&data); |
|
852 |
|
853 break; |
|
854 } |
|
855 case EGetTagStates1: |
|
856 { |
|
857 iGetTagStatesContext.Close(); |
|
858 TServerCallback cb; |
|
859 HBufC8* context; |
|
860 TCallbackWriter writer(cb, &context); |
|
861 Server().WriteCallbackForGetTagStatesL(writer); |
|
862 |
|
863 if (context) iGetTagStatesContext.Assign(context); |
|
864 |
|
865 TPckg<TServerCallback> pkg(cb); |
|
866 aMessage.WriteL(0, pkg); |
|
867 break; |
|
868 } |
|
869 case EGetTagStates2: |
|
870 { |
|
871 aMessage.WriteL(0, iGetTagStatesContext); |
|
872 iGetTagStatesContext.Close(); |
|
873 break; |
|
874 } |
|
875 case ECreatePerformanceLoggingChunk: |
|
876 { |
|
877 if (iPerformanceLoggingChunk.Handle()) |
|
878 { |
|
879 return KErrAlreadyExists; |
|
880 } |
|
881 RThread client; |
|
882 aMessage.ClientL(client); |
|
883 TInt size = aMessage.Int0(); |
|
884 res = Server().CreateKernChunkForClient(&client, size, size, iPerformanceLoggingChunk); |
|
885 client.Close(); |
|
886 break; |
|
887 } |
|
888 case ERegisterPerformanceLoggingChunk: |
|
889 { |
|
890 TInt err = iPerformanceLoggingChunk.Open(aMessage, 0, EFalse); |
|
891 User::LeaveIfError(err); |
|
892 break; |
|
893 } |
|
894 default: |
|
895 handled = EFalse; |
|
896 break; |
|
897 } |
|
898 //CleanupStack::Pop(); //DEBUG |
|
899 if (handled) |
|
900 { |
|
901 aMessage.Complete(res); |
|
902 } |
|
903 else |
|
904 { |
|
905 // Then just pass the message on to the server |
|
906 return Server().DoServiceL(aMessage); |
|
907 |
|
908 //DEBUG to catch unbalanced PushLs |
|
909 //TBool r = 0; |
|
910 //TRAPD(err, r = Server().DoServiceL(aMessage)); |
|
911 //User::LeaveIfError(err); |
|
912 //return r; |
|
913 //END DEBUG |
|
914 } |
|
915 return handled; |
|
916 } |
|
917 |
|
918 |
|
919 TBool CCloggerServer::DoServiceL(const RMessage& aMessage) |
|
920 { |
|
921 TBool handled = ETrue; |
|
922 TInt res = KErrNone; |
|
923 |
|
924 switch (aMessage.Function()) |
|
925 { |
|
926 case EDebugAlignLogFile: |
|
927 case EDebugShutdownServer: |
|
928 case EDebugForceBreakpointInServiceL: |
|
929 { |
|
930 _LIT_SECURITY_POLICY_S0(KTClogPolicy, FSHELL_UID_TCLOG); |
|
931 User::LeaveIfError(KTClogPolicy().CheckPolicy(aMessage, "Debug API called from not within tclog.exe")); |
|
932 } |
|
933 break; |
|
934 default: |
|
935 break; |
|
936 } |
|
937 |
|
938 switch (aMessage.Function()) |
|
939 { |
|
940 case ESetGlobalOptions: |
|
941 { |
|
942 TUint oldOptions = iOptions; |
|
943 iOptions = aMessage.Int0(); |
|
944 |
|
945 UpdatedGlobalOptionsL(oldOptions); |
|
946 break; |
|
947 } |
|
948 case EGetGlobalOptions: |
|
949 { |
|
950 res = iOptions; |
|
951 break; |
|
952 } |
|
953 case ESetEnabled: |
|
954 { |
|
955 |
|
956 ACQUIRE_FLAG_LOCK_OR_FAIL(EUsingTempBuf, break); |
|
957 iTempBuf.Zero(); |
|
958 TPtr16 ptr((TUint16*)iTempBuf.Ptr(), iTempBuf.MaxLength()/2); |
|
959 TInt err = aMessage.Read(0, ptr); |
|
960 if (err) |
|
961 { |
|
962 RELEASE_FLAG_LOCK(EUsingTempBuf); |
|
963 User::Leave(err); |
|
964 } |
|
965 TPtrC8 tagname8 = ptr.Collapse(); |
|
966 TTagData* tag = iTags.Find(tagname8); |
|
967 if (tag == NULL) |
|
968 { |
|
969 // Implicitly create the tag |
|
970 HBufC8* tagBuf = tagname8.AllocLC(); |
|
971 tag = reinterpret_cast<TTagData*>(NewSessionForTagL(tagBuf)); |
|
972 tag->SetShouldDisplay(); // The user wants to see it if they've just created it |
|
973 CleanupStack::Pop(tagBuf); |
|
974 } |
|
975 RELEASE_FLAG_LOCK(EUsingTempBuf); |
|
976 |
|
977 tag->iEnabled = aMessage.Int1(); |
|
978 _LIT8(KTagChanged, "Client %S changed log mask of tag [%S] to 0x%08x"); |
|
979 TPtrC8 safeTagName = tag->iTagName->Left(128); // LogNote doesn't check lengths |
|
980 RThread client; |
|
981 aMessage.ClientL(client); |
|
982 TFullName clientName = client.FullName(); |
|
983 TPtrC8 safeClientName = clientName.Collapse().Left(128); |
|
984 client.Close(); |
|
985 LogNote(ELogTagChanges, KTagChanged, &safeClientName, &safeTagName, tag->iEnabled); |
|
986 |
|
987 // Send update message to all sessions that have this tag and have enabled |
|
988 // EDontLogIfTagIsDisabled |
|
989 NotifySessionsOfChangedTag(tag); |
|
990 |
|
991 iEnabledStatesSequenceNumber++; |
|
992 RProperty::Set(KCloggerUid, ESequenceNumber, iEnabledStatesSequenceNumber); |
|
993 break; |
|
994 } |
|
995 case EIsEnabled: |
|
996 { |
|
997 ACQUIRE_FLAG_LOCK_OR_FAIL(EUsingTempBuf, break); |
|
998 iTempBuf.Zero(); |
|
999 TPckgBuf<TUint32> logMask; |
|
1000 TPtr16 ptr((TUint16*)iTempBuf.Ptr(), iTempBuf.MaxLength()/2); |
|
1001 TInt err = aMessage.Read(0, ptr); |
|
1002 if (err) |
|
1003 { |
|
1004 RELEASE_FLAG_LOCK(EUsingTempBuf); |
|
1005 User::Leave(err); |
|
1006 } |
|
1007 TTagData* tag = iTags.Find(ptr.Collapse()); |
|
1008 RELEASE_FLAG_LOCK(EUsingTempBuf); |
|
1009 if (tag) |
|
1010 { |
|
1011 logMask = tag->iEnabled; |
|
1012 } |
|
1013 else |
|
1014 { |
|
1015 logMask = iFallbackTag->iEnabled; // If/when it does start logging, this is the enabled mask it will get |
|
1016 } |
|
1017 aMessage.WriteL(1, logMask); |
|
1018 break; |
|
1019 } |
|
1020 case EGetRamBufferSize: |
|
1021 { |
|
1022 TBuf8<8> buf; |
|
1023 buf.SetLength(8); |
|
1024 TInt* ptr = (TInt*)buf.Ptr(); |
|
1025 ptr[0] = iBufferSize; |
|
1026 ptr[1] = iNumBuffers; |
|
1027 aMessage.WriteL(0, buf); |
|
1028 break; |
|
1029 } |
|
1030 case ESetRamBufferSize: |
|
1031 { |
|
1032 UpdateBufferSizeL(aMessage.Int0(), aMessage.Int1()); |
|
1033 break; |
|
1034 } |
|
1035 case ERotate: |
|
1036 { |
|
1037 Rotate(&aMessage); |
|
1038 return ETrue; |
|
1039 } |
|
1040 case EGetRotateBehaviour: |
|
1041 { |
|
1042 TBuf8<8> buf; |
|
1043 buf.SetLength(8); |
|
1044 TInt* ptr = (TInt*)buf.Ptr(); |
|
1045 ptr[0] = iRotateBehaviour; |
|
1046 ptr[1] = iNumRotates; |
|
1047 aMessage.WriteL(0, buf); |
|
1048 break; |
|
1049 } |
|
1050 case ESetRotateBehaviour: |
|
1051 { |
|
1052 iNumRotates = aMessage.Int0(); |
|
1053 iRotateBehaviour = aMessage.Int1(); |
|
1054 break; |
|
1055 } |
|
1056 case EPersistSettings: |
|
1057 { |
|
1058 PersistSettingsL(); |
|
1059 break; |
|
1060 } |
|
1061 case EResetSettings: |
|
1062 { |
|
1063 ResetSettingsL(); |
|
1064 break; |
|
1065 } |
|
1066 case EDebugAlignLogFile: |
|
1067 { |
|
1068 FlushBuffers(); |
|
1069 iLogFile.Write(TPtrC8((const TUint8*)MINTAGSTART)); |
|
1070 TInt pos = 0; |
|
1071 User::LeaveIfError(iLogFile.Seek(ESeekCurrent, pos)); |
|
1072 pos = 4096 - (pos % 4096) - aMessage.Int0(); |
|
1073 if (pos < 2) pos += 4096; // use 2 rather than zero so that we always have room in buf for "\r\n" |
|
1074 RBuf8 buf; |
|
1075 buf.CreateMaxL(pos); |
|
1076 buf.Fill('.'); |
|
1077 buf[pos-2] = '\r'; |
|
1078 buf[pos-1] = '\n'; |
|
1079 iLogFile.Write(buf); |
|
1080 iLogFile.Flush(); |
|
1081 buf.Close(); |
|
1082 break; |
|
1083 } |
|
1084 case EDebugShutdownServer: |
|
1085 { |
|
1086 CloseBuffers(); // The tests assume we don't flush buffers when we receive a shutdown. Because it tests more things we do a tidy shutdown that would normally involve flushing the buffers. |
|
1087 CActiveScheduler::Stop(); |
|
1088 return ETrue; // To skip the message complete |
|
1089 } |
|
1090 case ESetTagStates: |
|
1091 { |
|
1092 RBuf8 buf; |
|
1093 CleanupClosePushL(buf); |
|
1094 buf.CreateL(aMessage.GetDesLengthL(0)); |
|
1095 aMessage.ReadL(0, buf); |
|
1096 |
|
1097 // Dear Points of View, Why Oh Why does RBufReadStream's constructor take a CBufBase and not a TDesC8? |
|
1098 // Answer: Because no-one's really thought about this API for 15 years. |
|
1099 // |
|
1100 // There is one copy for IPC, one to construct the CBufFlat, and one to read an HBufC from the stream |
|
1101 // Something like TCallbackWriter extended slightly could do it in one copy (the IPC) |
|
1102 |
|
1103 CBufFlat* cbuf = CBufFlat::NewL(buf.Length()); |
|
1104 CleanupStack::PushL(cbuf); |
|
1105 cbuf->InsertL(0, buf); |
|
1106 RBufReadStream stream(*cbuf); |
|
1107 CleanupClosePushL(stream); |
|
1108 TInt count = stream.ReadInt32L(); |
|
1109 RBuf8 enabled; |
|
1110 CleanupClosePushL(enabled); |
|
1111 enabled.Assign(HBufC8::NewL(stream, KMaxTInt)); |
|
1112 |
|
1113 iEnabledStatesSequenceNumber++; |
|
1114 RProperty::Set(KCloggerUid, ESequenceNumber, iEnabledStatesSequenceNumber); |
|
1115 |
|
1116 _LIT8(KTagChanged, "Client %S updating tags"); |
|
1117 RThread client; |
|
1118 aMessage.ClientL(client); |
|
1119 TFullName clientName = client.FullName(); |
|
1120 TPtrC8 safeClientName = clientName.Collapse().Left(128); |
|
1121 client.Close(); |
|
1122 LogNote(ELogTagChanges, KTagChanged, &safeClientName); |
|
1123 |
|
1124 for (TInt i = 0; i < count; i++) |
|
1125 { |
|
1126 RBuf tagName; |
|
1127 CleanupClosePushL(tagName); |
|
1128 tagName.Assign(HBufC::NewL(stream, KMaxTInt)); |
|
1129 TUint32 enabledMask = *((TUint32*)&enabled[i * 4]); |
|
1130 TTagData* tag = iTags.Find(tagName.Collapse()); |
|
1131 if (tag) |
|
1132 { |
|
1133 tag->iEnabled = enabledMask; |
|
1134 NotifySessionsOfChangedTag(tag); |
|
1135 } |
|
1136 else |
|
1137 { |
|
1138 // If the client asked us to configure a non-existant tag, we'll just ignore it. The other option |
|
1139 // would be to set it up on the assumption that tag will be used later. But we've specified in the API |
|
1140 // what we'll do. |
|
1141 } |
|
1142 CleanupStack::PopAndDestroy(&tagName); |
|
1143 } |
|
1144 CleanupStack::PopAndDestroy(4, &buf); // enabled, stream, cbuf, buf |
|
1145 break; |
|
1146 } |
|
1147 case EDebugForceBreakpointInServiceL: |
|
1148 { |
|
1149 iFlags.Set(EForceBreakpoint); |
|
1150 break; |
|
1151 } |
|
1152 case EStartSessionWriterServer: |
|
1153 { |
|
1154 if (iSessionWriterServer) |
|
1155 { |
|
1156 res = KErrAlreadyExists; |
|
1157 } |
|
1158 else |
|
1159 { |
|
1160 iSessionWriterServer = new(ELeave) CSessionWriterServer(*this); |
|
1161 iSessionWriterServer->ConstructL(); |
|
1162 } |
|
1163 break; |
|
1164 } |
|
1165 default: |
|
1166 handled = EFalse; |
|
1167 break; |
|
1168 } |
|
1169 if (handled) |
|
1170 { |
|
1171 aMessage.Complete(res); |
|
1172 } |
|
1173 return handled; |
|
1174 } |
|
1175 |
|
1176 TAny* CCloggerServer::NewSessionForTag(HBufC8* aTag) |
|
1177 { |
|
1178 // TODO reverse these so that we don't need a TRAP |
|
1179 TAny* res = NULL; |
|
1180 TRAP_IGNORE(res = NewSessionForTagL(aTag)); |
|
1181 return res; |
|
1182 } |
|
1183 |
|
1184 TAny* CCloggerServer::NewSessionForTagL(HBufC8* aTag) |
|
1185 { |
|
1186 TTagData* tagData = iTags.Find(*aTag); |
|
1187 if (!tagData) |
|
1188 { |
|
1189 tagData = new(ELeave) TTagData(aTag); |
|
1190 tagData->iRefCount = 1; |
|
1191 if (iFallbackTag) |
|
1192 { |
|
1193 // If the fallback tag has been initialised (Which it always will be except when we are setting up default tags during startup |
|
1194 tagData->iEnabled = iFallbackTag->iEnabled; |
|
1195 } |
|
1196 else |
|
1197 { |
|
1198 tagData->iEnabled = KAllEnabled; |
|
1199 } |
|
1200 // Don't call SetShouldDisplay |
|
1201 CleanupStack::PushL(tagData); |
|
1202 iTags.InsertL(aTag, tagData); |
|
1203 CleanupStack::Pop(tagData); |
|
1204 |
|
1205 TPtrC8 safeTag = aTag->Left(200); |
|
1206 _LIT8(KNewTag, "New tag created: [%S]"); |
|
1207 LogNote(ELogNewTags, KNewTag, &safeTag); |
|
1208 } |
|
1209 else |
|
1210 { |
|
1211 delete aTag; |
|
1212 tagData->iRefCount++; |
|
1213 } |
|
1214 return tagData; |
|
1215 } |
|
1216 |
|
1217 void CCloggerServer::ReplaceTagL(TAny*& aCurrentContext, HBufC8* aNewTag) |
|
1218 { |
|
1219 //TTagData* tag = iTags.Find(aCurrentTag); |
|
1220 TTagData* tag = reinterpret_cast<TTagData*>(aCurrentContext); |
|
1221 if (!tag || tag->iRefCount != 1 || tag->ShouldDisplay()) |
|
1222 { |
|
1223 // Can't imagine how tag could be non-null. |
|
1224 // Don't allow tag name to updated if more than one session has been opened for it, or if anything's been written to the log |
|
1225 |
|
1226 // The commsdebug API actually allows this, so we have to cater for it by instead doing a NewSessionForTag |
|
1227 aCurrentContext = NewSessionForTagL(aNewTag); |
|
1228 } |
|
1229 else if (iTags.Find(*aNewTag) != NULL) |
|
1230 { |
|
1231 // We already have a session for the new tag name, so again NewSessionForTagL is the right thing to do |
|
1232 aCurrentContext = NewSessionForTagL(aNewTag); |
|
1233 } |
|
1234 else |
|
1235 { |
|
1236 // This is a bit more complex than you'd expect because tag->iTagName must not be deleted until tag has been removed from the hashtable (since it is used as the key) |
|
1237 |
|
1238 iTags.InsertL(aNewTag, tag); // On error, don't change anything, particularly what tag->iTagName points to |
|
1239 // If that succeeded, update the tag's iTagName, remove the old tag name from the hashtable and finally delete it |
|
1240 HBufC8* oldTagName = tag->iTagName; |
|
1241 tag->iTagName = aNewTag; // Takes ownership |
|
1242 iTags.Remove(oldTagName); |
|
1243 |
|
1244 _LIT8(KNewTag, "Tag %S renamed to %S"); |
|
1245 TPtrC8 safeOld = oldTagName->Left(128); |
|
1246 TPtrC8 safeNew = aNewTag->Left(128); |
|
1247 LogNote(ELogNewTags, KNewTag, &safeOld, &safeNew); |
|
1248 |
|
1249 delete oldTagName; |
|
1250 } |
|
1251 } |
|
1252 |
|
1253 void CCloggerServer::DropSession(CCloggerSession* /*aSession*/) |
|
1254 { |
|
1255 //TODO decrement ref count and remove tag if reaches zero? |
|
1256 } |
|
1257 |
|
1258 void CCloggerServer::LogHighPerformanceBuffer(const TDesC8& aBuf) |
|
1259 { |
|
1260 // To avoid having to copy the data again (it could be 2MB) we use the sync write mechanism |
|
1261 TUint oldOptions = iOptions; |
|
1262 if (iOptions & RClogger::EBufferLog) |
|
1263 { |
|
1264 iOptions &= ~RClogger::EBufferLog; |
|
1265 FlushBuffers(); |
|
1266 } |
|
1267 _LIT8(KHighPerf, "Dumping high-performance logging chunk (one tick=%00.3fus)"); |
|
1268 LogError(KHighPerf, 1000000.0/iTickFreq); |
|
1269 LogLine(aBuf); |
|
1270 // Should really call UpdatedGlobalOptions, or at least UpdateBufferSizeL. Meh. |
|
1271 iOptions = oldOptions; |
|
1272 ReCalculateFileAlignment(); |
|
1273 } |
|
1274 |
|
1275 void CCloggerServer::LogLine(const TDesC8& aLine) |
|
1276 { |
|
1277 if (iOptions & RClogger::EBufferLog) |
|
1278 { |
|
1279 ResetIdleWriteTimer(); |
|
1280 TPtrC8 lineFrag(aLine); |
|
1281 while (lineFrag.Length()) |
|
1282 { |
|
1283 TDes8& buf = iCurrent->iBuf; |
|
1284 TInt bufMaxLength = buf.MaxLength(); |
|
1285 if (iSubtractSizeOfNextBuffer > buf.MaxLength()) |
|
1286 { |
|
1287 // Then we have a buffer < 512B. Can't happen because that's the min buffer size |
|
1288 } |
|
1289 bufMaxLength -= iSubtractSizeOfNextBuffer; |
|
1290 |
|
1291 TInt spaceFree = bufMaxLength - buf.Length(); |
|
1292 ASSERT(spaceFree >= 0); // If it's negative then something's gone very wrong, we have less space in the buffer than the size of iSubtractSizeOfNextBuffer, implying the buffer was allowed to get too full |
|
1293 buf.Append(lineFrag.Left(spaceFree)); |
|
1294 if (buf.Length() == bufMaxLength) |
|
1295 { |
|
1296 TRAPD(err, GotoNextBufL()); //TODO make this not need a trap? |
|
1297 iSubtractSizeOfNextBuffer = 0; // If iSubtractSizeOfNextBuffer was non-zero, then by rotating buffers right now, we become all square |
|
1298 if (err == KErrNoMemory) |
|
1299 { |
|
1300 // We needed to allocate a new buffer but couldn't, so block until we have space |
|
1301 FlushBuffers(); // strictly we only need to wait for one buffer to be free, not all of them |
|
1302 _LIT8(KNoMem, MINTAGSTART "Ran out of memory trying to expand buffers\r\n"); // we shouldn't be using iTempBuf here, so we can't format the timestamp! |
|
1303 LogLine(KNoMem); |
|
1304 } |
|
1305 else if (err) |
|
1306 { |
|
1307 ASSERT(EFalse); // Nothing else should be causing an error in that function |
|
1308 } |
|
1309 } |
|
1310 lineFrag.Set(lineFrag.Mid(Min(spaceFree, lineFrag.Length()))); |
|
1311 // If the line was longer than the amount of freespace in the current buffer, we'll loop here to write the rest |
|
1312 } |
|
1313 } |
|
1314 else |
|
1315 { |
|
1316 iSyncWriteBuffer = &aLine; |
|
1317 TUint anythingEnabled = GetEnabledWriters(); // If no writers are enabled, no need to do anything (and moreover we'd hang if we tried it) |
|
1318 if (anythingEnabled) |
|
1319 { |
|
1320 TellAllWritersToWriteBuf(KSyncBufferIdx); |
|
1321 BlockRequestsFrom(this, iKernDebugRouter); |
|
1322 iFlushBufferWait.Start(); |
|
1323 // The logic to do the stop is in CompletedWritingBuf |
|
1324 ASSERT(iSyncWriteBufferBusy == 0); |
|
1325 StopBlocking(); |
|
1326 } |
|
1327 iSyncWriteBuffer = NULL; |
|
1328 } |
|
1329 } |
|
1330 |
|
1331 void CCloggerServer::Log(TAny* aContext, const TDesC8& aLine, TUint32 aTickCount) |
|
1332 { |
|
1333 ACQUIRE_FLAG_LOCK(EInMiddleOfLog); |
|
1334 ACQUIRE_FLAG_LOCK(EUsingTempBuf); |
|
1335 |
|
1336 TTagData* tag = reinterpret_cast<TTagData*>(aContext); |
|
1337 if (tag) |
|
1338 { |
|
1339 tag->SetShouldDisplay(); |
|
1340 } |
|
1341 TBuf8<32> rdebugId; |
|
1342 const TDesC8* t = &KCloggerTag; |
|
1343 if (tag) |
|
1344 { |
|
1345 if (tag->iThreadIdForRDebugger) |
|
1346 { |
|
1347 // For RDebug::Prints we use [tid] for the tag name |
|
1348 rdebugId.Num(tag->iThreadIdForRDebugger); |
|
1349 t = &rdebugId; |
|
1350 } |
|
1351 else |
|
1352 { |
|
1353 t = tag->iTagName; |
|
1354 } |
|
1355 } |
|
1356 const TDesC8& aTag = *t; |
|
1357 |
|
1358 TDateTime dt = TickCountToTime(aTickCount).DateTime(); |
|
1359 _LIT8(KFormat, "%i-%02i-%02i %02i:%02i:%02i.%03i: [%S] %S\r\n"); |
|
1360 // Have to add 1 to Month and Day, as these are zero-based |
|
1361 #define SF_ARGLIST dt.Year(), dt.Month()+1, dt.Day()+1, dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond()/1000, &aTag, &aLine |
|
1362 |
|
1363 #define SF_FORMAT KFormat |
|
1364 #define SF_BUF iTempBuf |
|
1365 #define SF_ACTION(buf) LogLine(buf) |
|
1366 #include "SensibleFormat.h" |
|
1367 |
|
1368 RELEASE_FLAG_LOCK(EInMiddleOfLog); |
|
1369 RELEASE_FLAG_LOCK(EUsingTempBuf); |
|
1370 |
|
1371 if (iFlags.IsSet(ELogBufferOverflow)) |
|
1372 { |
|
1373 iFlags.Clear(ELogBufferOverflow); |
|
1374 _LIT8(KCreated, "Had to create a new buffer because a writer wasn't finished (iBufferBusy=0x%x)"); |
|
1375 |
|
1376 // Is OK to use the temp buf now that the rest of the Log() function has finished |
|
1377 // Since we know our string is < 512 chars (and that's the minimum for iTempBuf) we can dispense with the usual paranoid formatting constructs above |
|
1378 LogError(KCreated, iCurrent->iNext->iBufferBusy); |
|
1379 } |
|
1380 } |
|
1381 |
|
1382 void CCloggerServer::CloseBuffers() |
|
1383 { |
|
1384 UpdatedBuffers(ETrue); |
|
1385 for (TInt i = iBufs.Count()-1; i >= 0; i--) |
|
1386 { |
|
1387 TBufEntry* buf = iBufs[i]; |
|
1388 //buf->iBuf.Close(); |
|
1389 iBufs.Remove(i); |
|
1390 delete buf; |
|
1391 } |
|
1392 AdjustBufferChunk(0); |
|
1393 iCurrent = NULL; |
|
1394 } |
|
1395 |
|
1396 void CCloggerServer::FlushBuffers() |
|
1397 { |
|
1398 iIdleWriteTimer->Cancel(); |
|
1399 // In the if statement below, check not only that we are buffering (iCurrent) but also that we have some writers to wait for (anythingEnabled) |
|
1400 TUint anythingEnabled = GetEnabledWriters(); // If no writers are enabled, no need to do anything (and moreover we'd hang if we tried it) |
|
1401 if (iCurrent && anythingEnabled) |
|
1402 { |
|
1403 iFlags.Set(EAllowCurrentBufferToBeRead); |
|
1404 // All enabled writers should already be writing out any filled buffers we have |
|
1405 // In case some have already finished writing all buffers, explicitly ask them to write out the current buffer as well |
|
1406 TellAllWritersToWriteBuf(iCurrent->iArrayIdx); |
|
1407 BlockRequestsFrom(this, iKernDebugRouter); |
|
1408 iFlushBufferWait.Start(); |
|
1409 // The logic to do the stop is in CompletedWritingBuf |
|
1410 StopBlocking(); |
|
1411 iFlags.Clear(EAllowCurrentBufferToBeRead); |
|
1412 |
|
1413 ASSERT(iCurrent->iBufferBusy == 0); // Otherwise something is still outstanding... |
|
1414 } |
|
1415 } |
|
1416 |
|
1417 void CCloggerServer::GotoNextBufL() |
|
1418 { |
|
1419 iSubtractSizeOfNextBuffer = KMinWriteSize - (iCurrent->iBuf.Size() % KMinWriteSize); |
|
1420 if (iSubtractSizeOfNextBuffer == KMinWriteSize) iSubtractSizeOfNextBuffer = 0; |
|
1421 |
|
1422 // Tell writers to get cracking if they aren't already |
|
1423 TellAllWritersToWriteBuf(iCurrent->iArrayIdx); |
|
1424 |
|
1425 // Now update current buf |
|
1426 TBufEntry* next = iCurrent->iNext; |
|
1427 if (next->iBufferBusy) |
|
1428 { |
|
1429 // Buffer is still busy - so need to allocate a new one |
|
1430 TBufEntry* entry = new(ELeave) TBufEntry(); |
|
1431 CleanupStack::PushL(entry); |
|
1432 TInt size = iChunkForBufs.Size(); |
|
1433 User::LeaveIfError(AdjustBufferChunk(size + iBufferSize)); |
|
1434 entry->iBuf.Set(iChunkForBufs.Base() + size, 0, iBufferSize); |
|
1435 entry->iArrayIdx = iBufs.Count(); |
|
1436 iBufs.AppendL(entry); |
|
1437 CleanupStack::Pop(entry); |
|
1438 // Insert buffer into the circular buffer |
|
1439 entry->iNext = next; |
|
1440 iCurrent->iNext = entry; |
|
1441 iCurrent = entry; |
|
1442 iFlags.Set(ELogBufferOverflow); |
|
1443 } |
|
1444 else |
|
1445 { |
|
1446 next->iBuf.Zero(); // If there are no writers enabled, next might not actually be empty |
|
1447 iCurrent = next; |
|
1448 } |
|
1449 } |
|
1450 |
|
1451 const TDesC8& CCloggerServer::GetBuf(TInt aIdx) |
|
1452 { |
|
1453 if (aIdx == KSyncBufferIdx) return *iSyncWriteBuffer; |
|
1454 if (aIdx == KCompressFileNameBufferIdx) return KNullDesC8; // The compressor sync writer doesn't use this parameter |
|
1455 |
|
1456 TBufEntry* entry = iBufs[aIdx]; |
|
1457 ASSERT(entry->iBufferBusy); // If the buffer isn't marked as busy, noone should be accessing it |
|
1458 return entry->iBuf; |
|
1459 } |
|
1460 |
|
1461 void CCloggerServer::CompletedWritingBuf(MWriter* aWriter, TInt aIdx) |
|
1462 { |
|
1463 if (aIdx == KSyncBufferIdx) |
|
1464 { |
|
1465 // Magic number for sync writes |
|
1466 TInt writerId = iWriters.Find(aWriter); |
|
1467 ASSERT(writerId != KErrNotFound); |
|
1468 iSyncWriteBufferBusy &= ~(1 << writerId); |
|
1469 if (iSyncWriteBufferBusy == 0) |
|
1470 { |
|
1471 iFlushBufferWait.AsyncStop(); |
|
1472 } |
|
1473 return; |
|
1474 } |
|
1475 else if (aIdx == KCompressFileNameBufferIdx) |
|
1476 { |
|
1477 // Magic number for background compress completed |
|
1478 TInt err = KErrNone; |
|
1479 if (iFlags.IsSet(ENeedToCopyCompressedLog) && iFileBeingRotated.Length()) |
|
1480 { |
|
1481 iFlags.Clear(ENeedToCopyCompressedLog); |
|
1482 TRAP(err, CopyLogToExternalMediaL(iFileBeingRotated)); |
|
1483 } |
|
1484 if (!iRotationMessage.IsNull()) |
|
1485 { |
|
1486 // Tell the client the rotate has finished |
|
1487 if (!err) |
|
1488 { |
|
1489 err = iRotationMessage.Write(0, iFileBeingRotated); |
|
1490 if (err == KErrBadDescriptor) err = KErrNone; // Probably means they called Rotate() with no arguments and so didn't care about the file name |
|
1491 } |
|
1492 iRotationMessage.Complete(err); |
|
1493 } |
|
1494 iFileBeingRotated.SetLength(0); |
|
1495 return; |
|
1496 } |
|
1497 |
|
1498 TBufEntry* entry = iBufs[aIdx]; |
|
1499 |
|
1500 TInt writerId = iWriters.Find(aWriter); |
|
1501 ASSERT(writerId != KErrNotFound); |
|
1502 |
|
1503 // Don't leave this in, it will cause an infinite loop if rdebug redirection is enabled! |
|
1504 //RDebug::Printf("Writer %i finished writing buffer %i", writerId, aIdx); |
|
1505 |
|
1506 // This writer is done with this buffer |
|
1507 entry->iBufferBusy &= ~(1 << writerId); |
|
1508 |
|
1509 TInt bufSize = entry->iBuf.Size(); |
|
1510 if (entry->iBufferBusy == 0) |
|
1511 { |
|
1512 // This buffer is now available again |
|
1513 entry->iBuf.Zero(); |
|
1514 } |
|
1515 |
|
1516 // Decide whether the next buffer in the chain also needs writing by this writer |
|
1517 |
|
1518 TBufEntry* next = entry->iNext; |
|
1519 if ((next->iBuf.Size() == next->iBuf.MaxSize() && entry != iCurrent) // The check for entry != iCurrent is to ensure we don't read past the end of the chain (ie it's technically possible for current to be full and next to be full but part of the tail of the chain) |
|
1520 || (next == iCurrent && iFlushBufferWait.IsStarted())) |
|
1521 { |
|
1522 // Next entry needs writing too |
|
1523 TellWriterToWriteBuf(writerId, next->iArrayIdx); |
|
1524 } |
|
1525 else |
|
1526 { |
|
1527 // No more buffers need writing |
|
1528 if (entry == iCurrent && entry->iBufferBusy == 0 && iFlushBufferWait.IsStarted()) |
|
1529 { |
|
1530 // All writers have now completed so stop the scheduler to return to FlushBuffers() |
|
1531 iFlushBufferWait.AsyncStop(); |
|
1532 |
|
1533 // Additionally we need to update iSubtractSizeOfNextBuffer since we don't do a GotoNextBuf when doing FlushBuffers |
|
1534 iSubtractSizeOfNextBuffer = KMinWriteSize - (bufSize % KMinWriteSize); |
|
1535 } |
|
1536 } |
|
1537 } |
|
1538 |
|
1539 TUint32 CCloggerServer::TagEnabled(const TAny* aContext, TInt* aSequenceNumber) |
|
1540 { |
|
1541 TUint32 res = KAllEnabled; |
|
1542 const TTagData* data = reinterpret_cast<const TTagData*>(aContext); |
|
1543 if (data) |
|
1544 { |
|
1545 res = data->iEnabled; |
|
1546 } |
|
1547 if (aSequenceNumber) |
|
1548 { |
|
1549 *aSequenceNumber = iEnabledStatesSequenceNumber; |
|
1550 } |
|
1551 return res; |
|
1552 } |
|
1553 |
|
1554 void CCloggerServer::WriteCallbackForGetTagStatesL(TCallbackWriter& aWriter) |
|
1555 { |
|
1556 RBuf8 enabled; |
|
1557 enabled.CreateL(iTags.Count() * 4); |
|
1558 CleanupClosePushL(enabled); |
|
1559 |
|
1560 TPtrHashMapIter<TDesC8, TTagData> tagIter(iTags); |
|
1561 const TTagData* tag; |
|
1562 while ((tag = tagIter.NextValue()) != NULL) |
|
1563 { |
|
1564 if (tag->ShouldDisplay()) |
|
1565 { |
|
1566 // Don't include tags that have never logged anything - they are probably only here for getting the tag states |
|
1567 const TPckgC<TUint32> tagEnabled(tag->iEnabled); |
|
1568 enabled.Append(tagEnabled); |
|
1569 } |
|
1570 } |
|
1571 aWriter.AddL(enabled); |
|
1572 CleanupStack::PopAndDestroy(&enabled); |
|
1573 |
|
1574 tagIter.Reset(); |
|
1575 while ((tag = tagIter.NextValue()) != NULL) |
|
1576 { |
|
1577 if (tag->ShouldDisplay()) |
|
1578 { |
|
1579 const TDesC8* tagName = tag->iTagName; |
|
1580 // Don't include tags that have never logged anything - they are probably only here for getting the tag states |
|
1581 HBufC* tag16 = HBufC::NewLC(tagName->Length()); |
|
1582 tag16->Des().Copy(*tagName); |
|
1583 aWriter.AddL(*tag16); |
|
1584 CleanupStack::PopAndDestroy(tag16); |
|
1585 } |
|
1586 } |
|
1587 } |
|
1588 |
|
1589 void CCloggerServer::NotifySessionsOfChangedTag(CCloggerServer::TTagData* tag) |
|
1590 { |
|
1591 iSessionIter.SetToFirst(); |
|
1592 CCloggerSession* sess; |
|
1593 TServerCallback callback(ETagEnabledChanged); |
|
1594 TCallbackWriter writer(callback, NULL); |
|
1595 writer.AddL((TUint)tag->iEnabled); |
|
1596 |
|
1597 while ((sess = (CCloggerSession*)iSessionIter++) != NULL) |
|
1598 { |
|
1599 if (sess->iContext == tag) |
|
1600 { |
|
1601 sess->DispatchCallback(callback); |
|
1602 } |
|
1603 } |
|
1604 } |
|
1605 void CCloggerServer::ResetIdleWriteTimer() |
|
1606 { |
|
1607 ASSERT(iOptions & RClogger::EBufferLog); // The timer shouldn't get set when buffering is disabled |
|
1608 const TInt KIdleTime = 500000; // 0.5 seconds |
|
1609 iIdleWriteTimer->Cancel(); |
|
1610 TCallBack cb(StaticIdleTimerExpired, this); |
|
1611 iIdleWriteTimer->Start(KIdleTime, KIdleTime, cb); |
|
1612 } |
|
1613 |
|
1614 TInt CCloggerServer::StaticIdleTimerExpired(TAny* aThis) |
|
1615 { |
|
1616 static_cast<CCloggerServer*>(aThis)->IdleTimerExpired(); |
|
1617 return 0; |
|
1618 } |
|
1619 |
|
1620 void CCloggerServer::IdleTimerExpired() |
|
1621 { |
|
1622 ASSERT(iOptions & RClogger::EBufferLog); // The timer shouldn't be firing when buffering is disabled |
|
1623 iIdleWriteTimer->Cancel(); // Don't be periodic, this'll just waste CPU |
|
1624 |
|
1625 // Go to the next buf, that will take care of everything |
|
1626 TRAPD(err, GotoNextBufL()); |
|
1627 if (err == KErrNoMemory) |
|
1628 { |
|
1629 // We needed to allocate a new buffer but couldn't, so block until we have space |
|
1630 FlushBuffers(); //TODO we only need to wait for one buffer to be free, not all of them |
|
1631 _LIT8(KNoMem, "Ran out of memory trying to expand buffers during idle write"); |
|
1632 Log(NULL, KNoMem, User::NTickCount()); |
|
1633 } |
|
1634 else if (err) |
|
1635 { |
|
1636 ASSERT(EFalse); // Nothing else should be causing an error in that function |
|
1637 } |
|
1638 if (iFlags.IsSet(ELogBufferOverflow)) |
|
1639 { |
|
1640 // Important to check this flag whenever GotoNextBufL is called! Otherwise you risk it not getting picked up |
|
1641 // until the next call to Log(), by which time buffering might have been disabled meaning iCurrent is no |
|
1642 // longer valid |
|
1643 iFlags.Clear(ELogBufferOverflow); |
|
1644 _LIT8(KCreated, "Had to create a new buffer during idle write because a writer wasn't finished (iBufferBusy=0x%x)"); |
|
1645 |
|
1646 LogError(KCreated, iCurrent->iNext->iBufferBusy); |
|
1647 } |
|
1648 |
|
1649 } |
|
1650 |
|
1651 TUint CCloggerServer::GetEnabledWriters() const |
|
1652 { |
|
1653 TUint res = 0; |
|
1654 for (TInt i = 0; i < iWriters.Count(); i++) |
|
1655 { |
|
1656 if (iWriters[i]->IsEnabled()) res |= 1 << i; |
|
1657 } |
|
1658 return res; |
|
1659 } |
|
1660 |
|
1661 void CCloggerServer::Rotate(const RMessage* aMsg) |
|
1662 { |
|
1663 // This function uses a number of different error variables (rotateErr, continuationErr, listingErr) because not all errors are fatal or should be handled in the same way |
|
1664 |
|
1665 if (iCompressor->IsActive() && (iRotateBehaviour & RClogger::ECompressRotatedLogs)) |
|
1666 { |
|
1667 if (aMsg) aMsg->Complete(KErrNotReady); |
|
1668 return; |
|
1669 } |
|
1670 |
|
1671 ASSERT(iRotationMessage.IsNull()); |
|
1672 |
|
1673 _LIT(KFormat, "%c:\\logs\\clogger_%i%02i%02i_%02i-%02i-%02i.%03i.txt"); |
|
1674 TDateTime dt = TickCountToTime(User::NTickCount()).DateTime(); |
|
1675 TDes& newName = iFileBeingRotated; |
|
1676 iFs.SessionPath(newName); |
|
1677 TUint driveLetter = newName[0]; |
|
1678 newName.Format(KFormat, driveLetter, dt.Year(), dt.Month()+1, dt.Day()+1, dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond()/1000); |
|
1679 |
|
1680 FlushBuffers(); // overkill again but easier to implement |
|
1681 iLogFile.Close(); |
|
1682 // NOTE: Be sure not to call LogNote or LogError between this point and the call to OpenLogFile below! |
|
1683 RFile logFile; |
|
1684 TInt rotateErr = logFile.Open(iFs, KLogFile, EFileShareExclusive|EFileWrite); // Need to reopen the file for exclusive access so I can rename it |
|
1685 if (rotateErr == KErrNone) |
|
1686 { |
|
1687 rotateErr = logFile.Rename(newName); |
|
1688 } |
|
1689 logFile.Close(); |
|
1690 |
|
1691 if (rotateErr == KErrNotFound) |
|
1692 { |
|
1693 rotateErr = KErrNone; |
|
1694 // No worries |
|
1695 } |
|
1696 else if (rotateErr) |
|
1697 { |
|
1698 // We couldn't rotate the log file. Just log the fact later on |
|
1699 } |
|
1700 |
|
1701 TInt continuationErr = OpenLogFile(); |
|
1702 if (continuationErr) |
|
1703 { |
|
1704 // We're in trouble now. Log to any remaining writers |
|
1705 _LIT8(KRotateErr, "Error trying to reopen log file (%i)"); |
|
1706 LogError(KRotateErr, continuationErr); |
|
1707 } |
|
1708 |
|
1709 if (rotateErr) |
|
1710 { |
|
1711 // Then we failed to rotate (although we may have succeeded in reopening the file for logging) |
|
1712 _LIT8(KRotateErr, "Failed to rotate log file (%i)"); |
|
1713 LogError(KRotateErr, rotateErr); |
|
1714 } |
|
1715 |
|
1716 // Delete all log files more than iNumRotates |
|
1717 _LIT(KLogFileFormat, "clogger_*"); |
|
1718 CDir* list = NULL; |
|
1719 TInt listingErr = iFs.GetDir(KLogFileFormat, KEntryAttNormal, ESortByName, list); |
|
1720 if (!listingErr && list->Count() > iNumRotates) |
|
1721 { |
|
1722 for (TInt i = 0; i < list->Count() - iNumRotates; i++) |
|
1723 { |
|
1724 // Don't delete the log we've just created if we need to rotate it or if iNumRotates is anything other than zero |
|
1725 // (On devices where the time is consistantly wrong it is possible to get in this situation) |
|
1726 TBool justCreated = (*list)[i].iName.CompareC(newName) == 0; |
|
1727 if (justCreated && ((iRotateBehaviour & RClogger::ECopyRotatedToExternalMedia) || iNumRotates > 0)) |
|
1728 { |
|
1729 continue; |
|
1730 } |
|
1731 iFs.Delete((*list)[i].iName); // Ignore any errors because there's nothing we need to do |
|
1732 } |
|
1733 } |
|
1734 delete list; |
|
1735 |
|
1736 TBool compress = EFalse; |
|
1737 #ifdef FSHELL_EZLIB_SUPPORT |
|
1738 compress = (rotateErr == KErrNone && (iRotateBehaviour & RClogger::ECompressRotatedLogs)); |
|
1739 #endif |
|
1740 if (compress) |
|
1741 { |
|
1742 if (iRotateBehaviour & RClogger::ECopyRotatedToExternalMedia) |
|
1743 { |
|
1744 iFlags.Set(ENeedToCopyCompressedLog); |
|
1745 } |
|
1746 iCompressor->WriteBuf(KCompressFileNameBufferIdx); |
|
1747 } |
|
1748 |
|
1749 if (rotateErr == KErrNone && !compress && (iRotateBehaviour & RClogger::ECopyRotatedToExternalMedia)) |
|
1750 { |
|
1751 TRAP(rotateErr, CopyLogToExternalMediaL(newName)); |
|
1752 } |
|
1753 TInt aResult = rotateErr ? rotateErr : continuationErr; |
|
1754 |
|
1755 TBool completeNow = !compress; |
|
1756 if (aMsg) |
|
1757 { |
|
1758 if (completeNow) |
|
1759 { |
|
1760 if (aResult == KErrNone) |
|
1761 { |
|
1762 aResult = aMsg->Write(0, iFileBeingRotated); |
|
1763 if (aResult == KErrBadDescriptor) aResult = KErrNone; // Probably means they called Rotate() with no arguments and so didn't care about the file name |
|
1764 } |
|
1765 aMsg->Complete(aResult); |
|
1766 iFileBeingRotated.SetLength(0); // Finished with this buffer for now |
|
1767 } |
|
1768 else |
|
1769 { |
|
1770 iRotationMessage = *aMsg; |
|
1771 } |
|
1772 } |
|
1773 } |
|
1774 |
|
1775 void CCloggerServer::CopyLogToExternalMediaL(TDes& aFile) |
|
1776 { |
|
1777 TInt err = KErrNone; |
|
1778 TInt removableDrive = KErrNotFound; |
|
1779 TInt hdAttributeDrive = KErrNotFound; |
|
1780 // Figure out the first external media device |
|
1781 for (TInt drive = EDriveA; drive <= EDriveZ; drive++) |
|
1782 { |
|
1783 TVolumeInfo info; |
|
1784 err = iFs.Volume(info, drive); |
|
1785 if (err == KErrNone) |
|
1786 { |
|
1787 LogNote(ELogDisks, _L8("Found drive %c iDrivaAtt=0x%x iType=0x%x"), 'A' + drive, info.iDrive.iDriveAtt, info.iDrive.iType); |
|
1788 } |
|
1789 if (err == KErrNone && (info.iDrive.iDriveAtt & KDriveAttRemovable)) |
|
1790 { |
|
1791 removableDrive = drive; |
|
1792 } |
|
1793 else if (err == KErrNone && info.iDrive.iType == EMediaHardDisk) |
|
1794 { |
|
1795 // Added the check for hard disk without removable so we can use _epoc_drive_d in the emulator |
|
1796 hdAttributeDrive = drive; |
|
1797 break; |
|
1798 } |
|
1799 } |
|
1800 TInt theDrive = hdAttributeDrive; |
|
1801 if (removableDrive != KErrNotFound) theDrive = removableDrive; // Prefer something with the removable attribute, if we have both |
|
1802 |
|
1803 if (theDrive != KErrNotFound) |
|
1804 { |
|
1805 CFileMan* fm = CFileMan::NewL(iFs); |
|
1806 CleanupStack::PushL(fm); |
|
1807 // aName is of the format C:\logs\clogger_xyz |
|
1808 // extName needs to be D:\logs\clogger_xyz |
|
1809 HBufC* nm = aFile.AllocLC(); |
|
1810 RBuf extName; |
|
1811 extName.Assign(nm); |
|
1812 TChar ffs; |
|
1813 RFs::DriveToChar(theDrive, ffs); |
|
1814 extName[0] = ffs; |
|
1815 // Actually copy the file |
|
1816 User::LeaveIfError(fm->Copy(aFile, extName, CFileMan::ERecurse)); |
|
1817 aFile = extName; // If all went well update the file name to reflect where we copied to |
|
1818 CleanupStack::PopAndDestroy(2, fm); // extName/nm, fms |
|
1819 } |
|
1820 else |
|
1821 { |
|
1822 LogNote(ELogDisks, _L8("Couldn't find any suitable volumes to copy log file on to")); |
|
1823 } |
|
1824 } |
|
1825 |
|
1826 void CCloggerServer::LogError(TRefByValue<const TDesC8> aFmt, ...) |
|
1827 { |
|
1828 VA_LIST args; |
|
1829 VA_START(args, aFmt); |
|
1830 DoLogErrorOrNote(RClogger::EAllEnabled, aFmt, args); |
|
1831 VA_END(args); |
|
1832 } |
|
1833 |
|
1834 void CCloggerServer::LogNote(TUint32 aLogMask, TRefByValue<const TDesC8> aFmt, ...) |
|
1835 { |
|
1836 VA_LIST args; |
|
1837 VA_START(args, aFmt); |
|
1838 DoLogErrorOrNote(aLogMask, aFmt, args); |
|
1839 VA_END(args); |
|
1840 } |
|
1841 |
|
1842 void CCloggerServer::DoLogErrorOrNote(TUint32 aLogMask, const TDesC8 &aFmt, VA_LIST args) |
|
1843 { |
|
1844 if (!iFlags.IsSet(EWrittenToLog)) |
|
1845 { |
|
1846 return; // Not ready to log yet - this can happen in the case that logging is attempted during ConstructL |
|
1847 } |
|
1848 |
|
1849 if (!(aLogMask & iCloggerTag->iEnabled) && aLogMask != RClogger::EAllEnabled) |
|
1850 { |
|
1851 // Make sure we always log errors, which are signified by aLogMask being EAllEnabled |
|
1852 return; |
|
1853 } |
|
1854 |
|
1855 // Whole string must be less than iTempBuf.MaxLength (ie 512) and will cause a panic if it isn't. So use with caution! |
|
1856 // ie don't pass in user-generated strings that might be massive. |
|
1857 |
|
1858 _LIT8(KFmt, "%i-%02i-%02i %02i:%02i:%02i.%03i: [Clogger] "); |
|
1859 _LIT8(KEnd, "\r\n"); |
|
1860 |
|
1861 ACQUIRE_FLAG_LOCK(EUsingTempBuf); |
|
1862 |
|
1863 iTempBuf.Zero(); |
|
1864 //iTempBuf.Append(KFmt); |
|
1865 |
|
1866 TDateTime dt = TickCountToTime(User::NTickCount()).DateTime(); |
|
1867 iTempBuf.Format(KFmt, dt.Year(), dt.Month()+1, dt.Day()+1, dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond()/1000); |
|
1868 iTempBuf.AppendFormatList(aFmt, args); |
|
1869 iTempBuf.Append(KEnd); |
|
1870 LogLine(iTempBuf); |
|
1871 |
|
1872 RELEASE_FLAG_LOCK(EUsingTempBuf); |
|
1873 } |
|
1874 |
|
1875 void CCloggerServer::LogsDirHasBeenCreated() |
|
1876 { |
|
1877 //__DEBUGGER(); |
|
1878 OpenLogFile(); // Ignore error |
|
1879 } |
|
1880 |
|
1881 TDes& CCloggerServer::GetFilenameToRotate() |
|
1882 { |
|
1883 return iFileBeingRotated; |
|
1884 } |
|
1885 |
|
1886 void CCloggerServer::HexDumpL(TAny* aContext, const TDesC8& aHeader, const TDesC8& aData, TUint32 aTickCount) |
|
1887 { |
|
1888 const TInt KLineSize = 16; |
|
1889 const TInt KExtra = 7 + 1; // The number of chars of padding that go into the output line. |
|
1890 // Header0000 : 00 01 02 03 .... |
|
1891 // So line len is headerLen + 4 + 3 + (3 * lineSize) + 1 + lineSize |
|
1892 _LIT8(KFormat, "%04X : "); |
|
1893 RBuf8 lineBuf; |
|
1894 CleanupClosePushL(lineBuf); |
|
1895 lineBuf.CreateL(aHeader.Length() + 4 * KLineSize + KExtra); |
|
1896 |
|
1897 TPtrC8 lineFrag(aData); |
|
1898 TInt offset = 0; |
|
1899 while (lineFrag.Length()) |
|
1900 { |
|
1901 lineBuf.Zero(); |
|
1902 TInt lineSize = Min(KLineSize, lineFrag.Length()); |
|
1903 // Start with header (or empty space if continuation) |
|
1904 if (offset == 0) |
|
1905 { |
|
1906 lineBuf.Append(aHeader); |
|
1907 } |
|
1908 else |
|
1909 { |
|
1910 lineBuf.AppendFill(' ', aHeader.Length()); |
|
1911 } |
|
1912 |
|
1913 // Add offset |
|
1914 lineBuf.AppendFormat(KFormat, offset); |
|
1915 offset += lineSize; |
|
1916 |
|
1917 // Add hex representation |
|
1918 for (TInt i = 0; i < lineSize; i++) |
|
1919 { |
|
1920 TBuf8<2> charBuf; |
|
1921 charBuf.NumFixedWidthUC(lineFrag[i], EHex, 2); |
|
1922 lineBuf.Append(charBuf); |
|
1923 lineBuf.Append(' '); |
|
1924 } |
|
1925 // Add filler if lineSize < KLineSize |
|
1926 lineBuf.AppendFill(' ', (KLineSize - lineSize) * 3); |
|
1927 |
|
1928 // Add ascii representation |
|
1929 lineBuf.Append(' '); |
|
1930 for (TInt i = 0; i < lineSize; i++) |
|
1931 { |
|
1932 TChar c = lineFrag[i]; |
|
1933 lineBuf.Append(c.IsPrint() ? c : TChar('.')); |
|
1934 } |
|
1935 lineFrag.Set(lineFrag.Mid(lineSize)); |
|
1936 Log(aContext, lineBuf, aTickCount); |
|
1937 } |
|
1938 CleanupStack::PopAndDestroy(&lineBuf); |
|
1939 } |
|
1940 |
|
1941 void CCloggerServer::SetKernelLoggingL(TBool aEnable) |
|
1942 { |
|
1943 if (!iKernDebugRouter) |
|
1944 { |
|
1945 // Either means we're being called very early during server ConstructL, or the debug router LDD is missing |
|
1946 return; |
|
1947 } |
|
1948 if (aEnable) |
|
1949 { |
|
1950 iKernDebugRouter->OpenChunkL(); // This will do nothing if already open |
|
1951 } |
|
1952 |
|
1953 if (aEnable) |
|
1954 { |
|
1955 TBool consume = ETrue; //iOptions & RClogger::EMirrorToRDebugPrint; |
|
1956 // ^ We always consume, it makes things easier. |
|
1957 iKernDebugRouter->StartRouting(consume); |
|
1958 } |
|
1959 else |
|
1960 { |
|
1961 iKernDebugRouter->StopRouting(); |
|
1962 } |
|
1963 } |
|
1964 |
|
1965 const TInt KThreadTagNamBufLen = 256; |
|
1966 |
|
1967 void CCloggerServer::LogKernMessage(TUint8 aWhere, TUint32 aTickCount, TUint aThreadId, const TDesC8& aMsg) |
|
1968 { |
|
1969 TUint enableMask = aWhere == 'K' ? ELogKernPrintf : aWhere == 'P' ? ELogPlatSecDiagnostics : ELogRDebugPrint; |
|
1970 TBool kern = aWhere != 'U'; |
|
1971 TTagData* tag = iRDebugTag; // This is used only as a last-ditch fallback if we've run out memory |
|
1972 |
|
1973 if (aThreadId != 0) |
|
1974 { |
|
1975 // Look for a tag in the iRDebugTags |
|
1976 TTagData** tagDataPtr = iRDebugTags.Find(aThreadId); |
|
1977 if (tagDataPtr) tag = *tagDataPtr; |
|
1978 else |
|
1979 { |
|
1980 // New tag (or rather, look in iTags keyed by thread name) |
|
1981 RThread thread; |
|
1982 TInt err = KErrNone; |
|
1983 HBufC8* tagBuf = HBufC8::New(KThreadTagNamBufLen*2); |
|
1984 if (!tagBuf) err = KErrNoMemory; |
|
1985 |
|
1986 if (err) |
|
1987 { |
|
1988 // Fall back to just RDebug::Print or Kern::Printf as appropriate |
|
1989 _LIT8(KUnknown, "Unknown thread %u has started rdebugging"); |
|
1990 _LIT8(KUnknownKern, "Unknown kernel thread %u has started rdebugging"); |
|
1991 LogNote(ELogNewRdebuggers, kern ? KUnknownKern() : KUnknown() , aThreadId); |
|
1992 iRDebugTags.Insert(aThreadId, tag); // So that we don't get the above message every time this thread debugs |
|
1993 } |
|
1994 else |
|
1995 { |
|
1996 err = thread.Open(aThreadId); |
|
1997 TPtr wPtr((TUint16*)tagBuf->Ptr(), KThreadTagNamBufLen); |
|
1998 if (err) |
|
1999 { |
|
2000 // Couldn't open thread - use Thread-xxx as tag |
|
2001 _LIT(KThreadFormat, "Thread-%u"); |
|
2002 wPtr.Format(KThreadFormat, aThreadId); |
|
2003 } |
|
2004 else |
|
2005 { |
|
2006 //thread.FullName(wPtr); // This didn't exist on 9.1... |
|
2007 TFullName name = thread.FullName(); |
|
2008 wPtr = name; |
|
2009 } |
|
2010 thread.Close(); |
|
2011 TInt len = wPtr.Collapse().Length(); |
|
2012 TPtr8 narrowPtr = tagBuf->Des(); |
|
2013 narrowPtr.SetLength(len); |
|
2014 ThreadPrettyName(narrowPtr); |
|
2015 TPtrC8 safeName = tagBuf->Left(150); |
|
2016 _LIT8(KNewTag, "Thread [%u] (%S) has started rdebugging"); |
|
2017 LogNote(ELogNewRdebuggers, KNewTag, aThreadId, &safeName); |
|
2018 TAny* theTag = NewSessionForTag(tagBuf); |
|
2019 if (theTag) |
|
2020 { |
|
2021 tag = reinterpret_cast<TTagData*>(theTag); |
|
2022 tag->iThreadIdForRDebugger = aThreadId; |
|
2023 iRDebugTags.Insert(aThreadId, tag); |
|
2024 } |
|
2025 else |
|
2026 { |
|
2027 delete tagBuf; |
|
2028 } |
|
2029 } |
|
2030 } |
|
2031 // If an error occurred then we'll just log using the RDebug::Print tag |
|
2032 } |
|
2033 |
|
2034 if (tag->iEnabled & enableMask) |
|
2035 { |
|
2036 Log(tag, aMsg, aTickCount); |
|
2037 } |
|
2038 else |
|
2039 { |
|
2040 RegisterDisabledLog(tag); |
|
2041 } |
|
2042 } |
|
2043 |
|
2044 void CCloggerServer::UpdatedGlobalOptionsL(TUint aOldOptions) |
|
2045 { |
|
2046 if ((aOldOptions & RClogger::EBufferLog) != (iOptions & RClogger::EBufferLog)) |
|
2047 { |
|
2048 UpdateBufferSizeL(iBufferSize, iNumBuffers); |
|
2049 } |
|
2050 |
|
2051 iWriters[ERDebug]->SetEnabled(iOptions & RClogger::EMirrorToRDebugPrint); |
|
2052 iWriters[EMessageQueue]->SetEnabled(iOptions & RClogger::EMirrorToMessageQueue); |
|
2053 iWriters[EFile]->SetEnabled(!(iOptions & RClogger::EDisableFileWriter)); |
|
2054 |
|
2055 TBool redirectRDebugBeingChanged = (aOldOptions & RClogger::ERedirectRDebugPrintToClogger) != (iOptions & RClogger::ERedirectRDebugPrintToClogger); |
|
2056 TBool mirrorRDebugChanged = (aOldOptions & RClogger::EMirrorToRDebugPrint) != (iOptions & RClogger::EMirrorToRDebugPrint); |
|
2057 |
|
2058 if (redirectRDebugBeingChanged || mirrorRDebugChanged) |
|
2059 { |
|
2060 SetKernelLoggingL(iOptions & RClogger::ERedirectRDebugPrintToClogger); |
|
2061 } |
|
2062 } |
|
2063 |
|
2064 void CCloggerServer::TellAllWritersToWriteBuf(TInt aBuf) |
|
2065 { |
|
2066 for (TInt i = 0; i < iWriters.Count(); i++) |
|
2067 { |
|
2068 TellWriterToWriteBuf(i, aBuf); |
|
2069 } |
|
2070 } |
|
2071 |
|
2072 void CCloggerServer::TellWriterToWriteBuf(TInt aWriterId, TInt aBuf) |
|
2073 { |
|
2074 MWriter* writer = iWriters[aWriterId]; |
|
2075 if (writer->IsEnabled() && !writer->IsBusyWriting()) |
|
2076 { |
|
2077 if (aBuf == KSyncBufferIdx) |
|
2078 { |
|
2079 iSyncWriteBufferBusy |= (1<<aWriterId); |
|
2080 } |
|
2081 else |
|
2082 { |
|
2083 TBufEntry* entry = iBufs[aBuf]; |
|
2084 entry->iBufferBusy |= (1<<aWriterId); |
|
2085 } |
|
2086 writer->WriteBuf(aBuf); |
|
2087 } |
|
2088 } |
|
2089 |
|
2090 void CCloggerServer::RegisterDisabledLog(TAny* aContext) |
|
2091 { |
|
2092 reinterpret_cast<TTagData*>(aContext)->SetShouldDisplay(); |
|
2093 } |
|
2094 |
|
2095 void CCloggerServer::UpdatedBuffers(TBool aAboutToCloseBuffers) |
|
2096 { |
|
2097 if (!iKernDebugRouter) |
|
2098 { |
|
2099 // No point doing anything |
|
2100 return; |
|
2101 } |
|
2102 |
|
2103 // If there are more than 8 buffers then you're on your own! |
|
2104 const TInt KMaxBuffersInParams = 8; // Note - this should probably be defined to be the same as KMaxCrashDumpAreas |
|
2105 |
|
2106 TUint32 base = (TUint32)iChunkForBufs.Base(); |
|
2107 |
|
2108 SCloggerCrashDumpArea bufAreas[KMaxBuffersInParams]; |
|
2109 TInt numBuffers = 0; |
|
2110 if (!aAboutToCloseBuffers) |
|
2111 { |
|
2112 for (TInt i = 0; numBuffers < KMaxBuffersInParams && i < iBufs.Count(); i++) |
|
2113 { |
|
2114 TBufEntry* buf = iBufs[i]; |
|
2115 bufAreas[numBuffers].iChunkHandle = iChunkForBufs.Handle(); |
|
2116 bufAreas[numBuffers].iOffset = (TUint32)buf->iBuf.Ptr() - base; |
|
2117 bufAreas[numBuffers].iSize = buf->iBuf.MaxSize(); |
|
2118 _LIT8(KBufName, "Clogger Buffer %i"); |
|
2119 bufAreas[numBuffers].iName.Format(KBufName, i); |
|
2120 numBuffers++; |
|
2121 } |
|
2122 } |
|
2123 // Now check sessions for performance logging chunks |
|
2124 iSessionIter.SetToFirst(); |
|
2125 CCloggerSession* sess; |
|
2126 while ((sess = (CCloggerSession*)iSessionIter++) != NULL) |
|
2127 { |
|
2128 TInt chunkHandle = sess->iPerformanceLoggingChunk.Handle(); |
|
2129 if (chunkHandle && numBuffers < KMaxBuffersInParams) |
|
2130 { |
|
2131 bufAreas[numBuffers].iChunkHandle = chunkHandle; |
|
2132 bufAreas[numBuffers].iOffset = 0; // Performance logging uses the whole chunk |
|
2133 bufAreas[numBuffers].iSize = sess->iPerformanceLoggingChunk.Size(); |
|
2134 _LIT8(KBufName, "Clogger MemLog "); |
|
2135 bufAreas[numBuffers].iName = KBufName; |
|
2136 TPtrC8 tagName = reinterpret_cast<TTagData*>(sess->iContext)->iTagName->Left(bufAreas[numBuffers].iName.MaxLength() - bufAreas[numBuffers].iName.Length()); |
|
2137 bufAreas[numBuffers].iName.Append(tagName); |
|
2138 numBuffers++; |
|
2139 } |
|
2140 } |
|
2141 |
|
2142 TPtrC8 ptr((const TUint8*)&bufAreas[0], numBuffers * sizeof(SCloggerCrashDumpArea)); |
|
2143 iKernDebugRouter->RegisterCrashDumpAreas(ptr); |
|
2144 } |
|
2145 |
|
2146 TInt CCloggerServer::CreateKernChunkForClient(RThread* aClient, TInt aMaxSize, TInt aCommittedSize, RChunk& aOurChunk) |
|
2147 { |
|
2148 if (!iKernDebugRouter) |
|
2149 { |
|
2150 return KErrNotSupported; |
|
2151 } |
|
2152 |
|
2153 return iKernDebugRouter->CreateKernChunkForClient(aClient, aMaxSize, aCommittedSize, aOurChunk); |
|
2154 } |
|
2155 |
|
2156 TInt CCloggerServer::AdjustBufferChunk(TInt aNewSize) |
|
2157 { |
|
2158 // This is needed because you can't adjust a chunk created on kernel-side from user side. Grr. |
|
2159 TInt err = KErrNotSupported; |
|
2160 if (iKernDebugRouter) |
|
2161 { |
|
2162 err = iKernDebugRouter->AdjustChunk(iChunkForBufs, aNewSize); |
|
2163 } |
|
2164 else if (iChunkForBufs.Handle()) // Might not have been created yet |
|
2165 { |
|
2166 // If the chunk wasn't created via the device driver, it's fine to adjust it directly |
|
2167 err = iChunkForBufs.Adjust(aNewSize); |
|
2168 } |
|
2169 return err; |
|
2170 } |
|
2171 |
|
2172 CSessionWriter* CCloggerServer::RegisterSessionWithSessionWriter(CSessionWriterSession* aSession) |
|
2173 { |
|
2174 CSessionWriter* sessionWriter = static_cast<CSessionWriter*>(iWriters[ESessionWriter]); |
|
2175 if (!aSession) |
|
2176 { |
|
2177 // Session closing |
|
2178 sessionWriter->SetEnabled(EFalse); |
|
2179 sessionWriter->iSession = NULL; |
|
2180 return NULL; |
|
2181 } |
|
2182 else if (sessionWriter->iSession) |
|
2183 { |
|
2184 // Already got one |
|
2185 return NULL; |
|
2186 } |
|
2187 else |
|
2188 { |
|
2189 sessionWriter->iSession = aSession; |
|
2190 return sessionWriter; |
|
2191 } |
|
2192 } |
|
2193 |
|
2194 RChunk& CCloggerServer::GetBufChunk() |
|
2195 { |
|
2196 return iChunkForBufs; |
|
2197 } |
|
2198 |
|
2199 void CCloggerServer::ThreadPrettyName(TDes8& aName) |
|
2200 { |
|
2201 // Algorithm is: |
|
2202 // Given a aName xxx(.exe)?\[uid\]nonce::(!)?yyy |
|
2203 // if yyy == xxx (case insensitive compare) then just display yyy (with exclamation mark if present) |
|
2204 // else display xxx::yyy |
|
2205 |
|
2206 // Find the :: that splits thread and process name |
|
2207 _LIT8(KDoubleColon, "::"); |
|
2208 TInt colon = aName.Find(KDoubleColon); |
|
2209 if (colon == KErrNotFound) |
|
2210 { |
|
2211 // It's not a thread name |
|
2212 return; |
|
2213 } |
|
2214 |
|
2215 // Remove .exe if present immediately before [uid] |
|
2216 TInt brak = aName.Left(colon).LocateReverse('['); |
|
2217 _LIT8(KExe, ".exe"); |
|
2218 if (brak >= 5 && aName.Mid(brak-4, 4).CompareF(KExe) == 0) |
|
2219 { |
|
2220 aName.Delete(brak-4, 4); |
|
2221 brak -= 4; |
|
2222 colon -= 4; |
|
2223 } |
|
2224 |
|
2225 TPtrC8 procaName = brak >=0 ? aName.Left(brak) : aName.Left(colon); |
|
2226 TPtrC8 threadaName = aName.Mid(colon+2); |
|
2227 TPtrC8 realThreadaName = threadaName.Length() && threadaName[0] == '!' ? threadaName.Mid(1) : threadaName; |
|
2228 if (procaName.CompareF(realThreadaName) == 0) |
|
2229 { |
|
2230 aName.Delete(0, colon+2); |
|
2231 } |
|
2232 else if (brak >= 0) |
|
2233 { |
|
2234 aName.Delete(brak, colon-brak); |
|
2235 } |
|
2236 } |
|
2237 |
|
2238 TBool CCloggerServer::ForceBreakpointRequested() const |
|
2239 { |
|
2240 return iFlags.IsSet(EForceBreakpoint); |
|
2241 } |
|
2242 |
|
2243 inline TTime CCloggerServer::TickCountToTime(TUint32 aTickCount) const |
|
2244 { |
|
2245 return TTime(iTimeAtStartup.Int64() + (((TInt64)aTickCount*1000000) / (TInt64)iTickFreq) - iStartupTickInMicroseconds); |
|
2246 } |