|
1 // MiscServer.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 <fshell/common.mmh> |
|
15 #include <fshell/ltkutils.h> |
|
16 |
|
17 #ifdef FSHELL_EZLIB_SUPPORT |
|
18 #include <EZCompressor.h> |
|
19 //#include <EZDecompressor.h> |
|
20 #include <EZlib.h> |
|
21 #include <EZGzip.h> |
|
22 #endif |
|
23 |
|
24 _LIT8(KDiskFull, "\r\n" MINTAGSTART "Log truncated due to disk full\r\n"); |
|
25 |
|
26 void CLogWriter::WriteBuf(TInt aBuf) |
|
27 { |
|
28 ASSERT(!IsActive() && iEnabled); |
|
29 { |
|
30 iBuf = aBuf; |
|
31 iFile.Write(iServer.GetBuf(aBuf), iStatus); |
|
32 SetActive(); |
|
33 } |
|
34 } |
|
35 |
|
36 TBool CLogWriter::IsBusyWriting() |
|
37 { |
|
38 return IsActive(); |
|
39 } |
|
40 |
|
41 void CLogWriter::RunL() |
|
42 { |
|
43 if (iStatus == KErrDiskFull) |
|
44 { |
|
45 //TODO Make this occur before the disk is actually completely empty? |
|
46 TInt filePos = 0; |
|
47 TInt err = iFile.Seek(ESeekCurrent, filePos); |
|
48 if (err == KErrNone && filePos > KDiskFull().Length()) // ensure the file has enough room in it for our message |
|
49 { |
|
50 // Rewind the log file and overwrite the tail of it with a warning to say the disk is full |
|
51 filePos = filePos - KDiskFull().Length(); |
|
52 err = iFile.Seek(ESeekStart, filePos); |
|
53 if (err == KErrNone) |
|
54 { |
|
55 iFile.Write(KDiskFull, iStatus); |
|
56 SetActive(); |
|
57 return; |
|
58 } |
|
59 } |
|
60 } |
|
61 iServer.CompletedWritingBuf(this, iBuf); |
|
62 } |
|
63 |
|
64 void CLogWriter::DoCancel() |
|
65 { |
|
66 // There's no way to cancel a file write (or any semantic sense to do so) so by doing nothing we get the SyncWaitForCompletion behaviour we wanted |
|
67 } |
|
68 |
|
69 CLogWriter::~CLogWriter() |
|
70 { |
|
71 // Don't cancel, CloggerServer is supposed to guarantee we won't be deleted when still writing |
|
72 //Cancel(); |
|
73 } |
|
74 |
|
75 void CLogWriter::CloseWriter() |
|
76 { |
|
77 delete this; |
|
78 } |
|
79 |
|
80 void CLogWriter::SetEnabled(TBool aEnabled) |
|
81 { |
|
82 if (!iFile.SubSessionHandle()) |
|
83 { |
|
84 // If the log file is not open, we can't enable ourselves even if the client asks us to |
|
85 aEnabled = EFalse; |
|
86 } |
|
87 iEnabled = aEnabled; |
|
88 } |
|
89 |
|
90 CRDebugWriter::CRDebugWriter(CCloggerServer& aServer) |
|
91 : iServer(aServer) |
|
92 {} |
|
93 |
|
94 void CRDebugWriter::WriteBufL(const TDesC8& aBuf) |
|
95 { |
|
96 TInt maxLen = 256; |
|
97 |
|
98 // RDebug::Print only accepts 256 chars on the kernel side, so split the write up |
|
99 TInt pos = 0; |
|
100 while (pos < aBuf.Length()) |
|
101 { |
|
102 TPtrC8 frag = aBuf.Mid(pos, Min(maxLen, aBuf.Length() - pos)); |
|
103 //Exec::DebugPrint((TAny*)&frag, 1); // This is quicker than calling RDebugPrint - cuts down on a memory copy |
|
104 // ^ Except that you can't do it from non-euser code... |
|
105 LtkUtils::RawPrint(frag); |
|
106 pos += frag.Length(); |
|
107 } |
|
108 } |
|
109 |
|
110 void CRDebugWriter::CloseWriter() |
|
111 { |
|
112 delete this; |
|
113 } |
|
114 |
|
115 /////////////// |
|
116 |
|
117 TInt CSyncWriterWrapper::ThreadFunction(TAny* aSelf) |
|
118 { |
|
119 CTrapCleanup* cleanup=CTrapCleanup::New(); |
|
120 if (!cleanup) return KErrNoMemory; |
|
121 |
|
122 CSyncWriterWrapper* self = static_cast<CSyncWriterWrapper*>(aSelf); |
|
123 for (;;) |
|
124 { |
|
125 User::WaitForRequest(self->iThreadStatus); |
|
126 //User::After(1); //DEBUG TO TEST SLOW WRITERS |
|
127 if (self->iThreadStatus != KErrNone) |
|
128 { |
|
129 break; |
|
130 } |
|
131 |
|
132 const TDesC8& buf = self->iServer.GetBuf(self->iBuf); |
|
133 |
|
134 TRAPD(err, self->iWriter.WriteBufL(buf)); |
|
135 |
|
136 self->iThreadStatus = KRequestPending; |
|
137 TRequestStatus* stat = &self->iStatus; |
|
138 self->iMainThread.RequestComplete(stat, err); |
|
139 } |
|
140 |
|
141 delete cleanup; |
|
142 return self->iThreadStatus.Int(); |
|
143 } |
|
144 |
|
145 void CSyncWriterWrapper::WriteBuf(TInt aBuf) |
|
146 { |
|
147 ASSERT(!IsActive() && iEnabled); |
|
148 { |
|
149 iBuf = aBuf; |
|
150 iStatus = KRequestPending; |
|
151 SetActive(); |
|
152 TRequestStatus* stat = &iThreadStatus; |
|
153 iWorkerThread.RequestComplete(stat, KErrNone); |
|
154 } |
|
155 } |
|
156 |
|
157 TBool CSyncWriterWrapper::IsBusyWriting() |
|
158 { |
|
159 return IsActive(); |
|
160 } |
|
161 |
|
162 void CSyncWriterWrapper::RunL() |
|
163 { |
|
164 iServer.CompletedWritingBuf(this, iBuf); |
|
165 } |
|
166 |
|
167 void CSyncWriterWrapper::DoCancel() |
|
168 { |
|
169 // There's no way to cancel a sync writer |
|
170 } |
|
171 |
|
172 CSyncWriterWrapper::~CSyncWriterWrapper() |
|
173 { |
|
174 // Don't cancel, CloggerServer is supposed to guarantee we won't be deleted when still writing |
|
175 //Cancel(); |
|
176 if (iWorkerThread.Handle()) |
|
177 { |
|
178 TRequestStatus* stat = &iThreadStatus; |
|
179 iWorkerThread.RequestComplete(stat, KErrCancel); |
|
180 TRequestStatus rv; |
|
181 iWorkerThread.Rendezvous(rv); |
|
182 User::WaitForRequest(rv); |
|
183 iWorkerThread.Close(); |
|
184 } |
|
185 iMainThread.Close(); |
|
186 if (iOwnWriter) |
|
187 { |
|
188 iWriter.CloseWriter(); |
|
189 } |
|
190 } |
|
191 |
|
192 void CSyncWriterWrapper::CloseWriter() |
|
193 { |
|
194 delete this; |
|
195 } |
|
196 |
|
197 void CSyncWriterWrapper::SetEnabled(TBool aEnabled) |
|
198 { |
|
199 iEnabled = aEnabled; |
|
200 } |
|
201 |
|
202 CSyncWriterWrapper::CSyncWriterWrapper(CCloggerServer& aServer, MSyncWriter& aWriter) |
|
203 : CActive(EPriorityHigh), // It's more important to keep writing than to handle clientserver calls |
|
204 iServer(aServer), iWriter(aWriter), iBuf(-1), iEnabled(EFalse) |
|
205 { |
|
206 CActiveScheduler::Add(this); |
|
207 } |
|
208 |
|
209 CSyncWriterWrapper* CSyncWriterWrapper::NewL(CCloggerServer& aServer, MSyncWriter& aWriter, TInt aWriterId) |
|
210 { |
|
211 CSyncWriterWrapper* self = new(ELeave) CSyncWriterWrapper(aServer, aWriter); |
|
212 CleanupStack::PushL(self); |
|
213 self->ConstructL(aWriterId); |
|
214 CleanupStack::Pop(self); |
|
215 self->iOwnWriter = ETrue; |
|
216 return self; |
|
217 } |
|
218 |
|
219 void CSyncWriterWrapper::ConstructL(TInt aWriterId) |
|
220 { |
|
221 User::LeaveIfError(iMainThread.Open(RThread().Id())); |
|
222 _LIT(KName, "SyncWriter%i"); |
|
223 TBuf<16> name; |
|
224 name.Format(KName, aWriterId); |
|
225 User::LeaveIfError(iWorkerThread.Create(name, &ThreadFunction, 2048, NULL, this)); |
|
226 if (aWriterId >= 0) |
|
227 { |
|
228 iWorkerThread.SetPriority(EPriorityMore); // To ensure buffers aren't being filled without us having a chance to empty them |
|
229 } |
|
230 else |
|
231 { |
|
232 iWorkerThread.SetPriority(EPriorityMuchLess); // We use the sync writer wrapper around the background compress of rotated log files, and that operation should be low priority |
|
233 } |
|
234 iWorkerThread.Resume(); |
|
235 } |
|
236 |
|
237 ////////// |
|
238 |
|
239 CLogCompressor* CLogCompressor::NewLC(CCloggerServer& aServer) |
|
240 { |
|
241 CLogCompressor* self = new(ELeave) CLogCompressor(aServer); |
|
242 CleanupStack::PushL(self); |
|
243 User::LeaveIfError(self->iFs.Connect()); |
|
244 self->iFs.ShareAuto(); |
|
245 return self; |
|
246 } |
|
247 |
|
248 CLogCompressor::CLogCompressor(CCloggerServer& aServer) |
|
249 : iServer(aServer) |
|
250 { |
|
251 } |
|
252 |
|
253 CLogCompressor::~CLogCompressor() |
|
254 { |
|
255 } |
|
256 |
|
257 void CLogCompressor::WriteBufL(const TDesC8& /*aBuf*/) |
|
258 { |
|
259 TDes& filename = iServer.GetFilenameToRotate(); |
|
260 |
|
261 HBufC* compressedFile = HBufC::NewLC(filename.Length()+3); |
|
262 _LIT(KFmt, "%S.gz"); |
|
263 compressedFile->Des().Format(KFmt, &filename); |
|
264 |
|
265 RFile input; |
|
266 |
|
267 //_LIT(KInfo,"Compressing file %S to %S\n"); |
|
268 //console->Printf(KInfo,&inputFile,compressedFile); |
|
269 |
|
270 User::LeaveIfError(input.Open(iFs, filename, EFileStream | EFileRead | EFileShareAny)); |
|
271 CleanupClosePushL(input); |
|
272 |
|
273 TRAPD(err, DoGzipL(input, *compressedFile)); |
|
274 |
|
275 if (err) |
|
276 { |
|
277 // If the compress failed, delete any fragment (don't care if this fails) |
|
278 iFs.Delete(*compressedFile); |
|
279 } |
|
280 |
|
281 //_LIT(KHoorah,"Hoorah"); |
|
282 //console->Printf(KHoorah); |
|
283 |
|
284 CleanupStack::PopAndDestroy(&input); |
|
285 if (err == KErrNone) |
|
286 { |
|
287 // Don't delete the original unless the compress succeeded! |
|
288 err = iFs.Delete(filename); |
|
289 } |
|
290 if (err == KErrNone) |
|
291 { |
|
292 // We've sucessfully compressed our file, so update filename to reflect that |
|
293 filename = *compressedFile; |
|
294 } |
|
295 // Don't do anything if the delete fails - the file(s) will be cleaned up when the max number of rotated logs is exceeded |
|
296 CleanupStack::PopAndDestroy(compressedFile); |
|
297 } |
|
298 |
|
299 void CLogCompressor::CloseWriter() |
|
300 { |
|
301 delete this; |
|
302 } |
|
303 |
|
304 void CLogCompressor::DoGzipL(RFile& aInput, const TDesC& aOutput) |
|
305 { |
|
306 #ifdef FSHELL_EZLIB_SUPPORT |
|
307 const TInt bufferSize = 0x8000; // 32 KB |
|
308 CEZFileToGZip* com = CEZFileToGZip::NewLC(iFs, aOutput, aInput, bufferSize); |
|
309 while (com->DeflateL()){/*do nothing*/} |
|
310 CleanupStack::PopAndDestroy(com); |
|
311 #else |
|
312 User::Leave(KErrNotSupported); |
|
313 #endif |
|
314 } |
|
315 |
|
316 //// |
|
317 |
|
318 CMessageQueueWriter* CMessageQueueWriter::NewL() |
|
319 { |
|
320 CMessageQueueWriter* self = new(ELeave) CMessageQueueWriter; |
|
321 TInt err = self->iQ.CreateGlobal(_L("Clogger.LogMessageQueue"), 4); |
|
322 |
|
323 if (err) |
|
324 { |
|
325 DISOWN(self); |
|
326 User::Leave(err); |
|
327 } |
|
328 return self; |
|
329 } |
|
330 |
|
331 void CMessageQueueWriter::WriteBufL(const TDesC8& aBuf) |
|
332 { |
|
333 TBuf8<128> buf; |
|
334 TPtrC8 lineFrag(aBuf); |
|
335 while (lineFrag.Length()) |
|
336 { |
|
337 TInt spaceFree = buf.MaxLength() - buf.Length(); |
|
338 buf.Append(lineFrag.Left(spaceFree)); |
|
339 TInt err = iQ.Send(buf); |
|
340 if (err == KErrOverflow) |
|
341 { |
|
342 // Drop a frame |
|
343 TBuf8<128> buf2; |
|
344 iQ.Receive(buf2); |
|
345 // And resend |
|
346 err = iQ.Send(buf); |
|
347 } |
|
348 |
|
349 lineFrag.Set(lineFrag.Mid(Min(spaceFree, lineFrag.Length()))); |
|
350 buf.Zero(); |
|
351 } |
|
352 } |
|
353 |
|
354 void CMessageQueueWriter::CloseWriter() |
|
355 { |
|
356 delete this; |
|
357 } |
|
358 |
|
359 CMessageQueueWriter::CMessageQueueWriter() |
|
360 { |
|
361 } |
|
362 |
|
363 CMessageQueueWriter::~CMessageQueueWriter() |
|
364 { |
|
365 iQ.Close(); |
|
366 } |
|
367 |
|
368 CDebugRouterClient* CDebugRouterClient::NewL(CCloggerServer& aServer) |
|
369 { |
|
370 CDebugRouterClient* self = new(ELeave) CDebugRouterClient(aServer); |
|
371 CleanupStack::PushL(self); |
|
372 self->ConstructL(); |
|
373 CleanupStack::Pop(self); |
|
374 return self; |
|
375 } |
|
376 |
|
377 void CDebugRouterClient::ConstructL() |
|
378 { |
|
379 TInt err = RCloggerDebugRouter::LoadDriver(); |
|
380 if (err != KErrAlreadyExists) |
|
381 { |
|
382 User::LeaveIfError(err); |
|
383 } |
|
384 User::LeaveIfError(iDebugRouter.Open()); |
|
385 } |
|
386 |
|
387 void CDebugRouterClient::OpenChunkL() |
|
388 { |
|
389 if (!iSharedChunk.Handle()) |
|
390 { |
|
391 User::LeaveIfError(iDebugRouter.OpenChunk(iSharedChunk)); |
|
392 iTempBuf.CreateL(2048); |
|
393 } |
|
394 } |
|
395 |
|
396 CDebugRouterClient::CDebugRouterClient(CCloggerServer& aServer) |
|
397 : CActive(CActive::EPriorityStandard + 1), iServer(aServer) // Priority just higher than the server object - it's slightly more important we keep the device driver buffer serviced than we handle normal logging requests |
|
398 { |
|
399 CActiveScheduler::Add(this); |
|
400 } |
|
401 |
|
402 CDebugRouterClient::~CDebugRouterClient() |
|
403 { |
|
404 Cancel(); |
|
405 if (iDebugRouter.Handle()) |
|
406 { |
|
407 iDebugRouter.EnableDebugRouting(RCloggerDebugRouter::EDisable); // I don't really know whether to call this before or after the cancel |
|
408 iDebugRouter.Close(); |
|
409 } |
|
410 RCloggerDebugRouter::CloseDriver(); |
|
411 iSharedChunk.Close(); |
|
412 iTempBuf.Close(); |
|
413 } |
|
414 |
|
415 TPtrC8 Read(TDes8& aTempBuf, TPtrC8& aData, TInt aLength, TPtrC8& aOverflowData) |
|
416 { |
|
417 if (aLength <= aData.Length()) |
|
418 { |
|
419 // Can read it from this buffer |
|
420 TPtrC8 res(aData.Left(aLength)); |
|
421 aData.Set(aData.Mid(aLength)); |
|
422 return res; |
|
423 } |
|
424 else /*if (aLength > aData.Length())*/ |
|
425 { |
|
426 // Descriptor spans wrap point, so need to copy into temp buf |
|
427 aTempBuf.Copy(aData.Left(aTempBuf.MaxLength())); // If anyone's crazy enough to write a platsec diagnostic string longer than 2k, it gets truncated |
|
428 TInt overflowLen = aLength - aData.Length(); |
|
429 aData.Set(aOverflowData); // Wrap aData |
|
430 aOverflowData.Set(TPtrC8()); |
|
431 if (overflowLen > aData.Length()) |
|
432 { |
|
433 ASSERT(EFalse); // Shouldn't happen |
|
434 // in urel, return everything we've got |
|
435 return aData; |
|
436 } |
|
437 aTempBuf.Append(aData.Left(overflowLen)); |
|
438 aData.Set(aData.Mid(overflowLen)); |
|
439 return TPtrC8(aTempBuf); |
|
440 } |
|
441 } |
|
442 |
|
443 void CDebugRouterClient::RunL() |
|
444 { |
|
445 TUint chunkSize = iSharedChunk.Size(); |
|
446 const TUint KDataStartOffset = sizeof(SDebugChunkHeader); |
|
447 SDebugChunkHeader* chunkHeader = (SDebugChunkHeader*)iSharedChunk.Base(); |
|
448 TUint start = chunkHeader->iStartOffset; |
|
449 TUint end = chunkHeader->iEndOffset; |
|
450 TUint overflows = chunkHeader->iOverflows; |
|
451 |
|
452 TBool wrap = (start > end); |
|
453 TUint endLen = wrap ? chunkSize - start : end - start; |
|
454 TUint startLen = wrap ? end - KDataStartOffset : 0; |
|
455 |
|
456 TPtrC8 endData(iSharedChunk.Base() + start, endLen); |
|
457 TPtrC8 startData; |
|
458 if (wrap) startData.Set(iSharedChunk.Base() + KDataStartOffset, startLen); |
|
459 TPtrC8 data(endData); |
|
460 |
|
461 while (data.Length()) |
|
462 { |
|
463 TPtrC8 header = Read(iTempBuf, data, sizeof(SCloggerTraceInfo), startData); |
|
464 if (header.Length() < (TInt)sizeof(SCloggerTraceInfo)) |
|
465 { |
|
466 ASSERT(EFalse); // for udeb |
|
467 break; // Something's broken |
|
468 } |
|
469 SCloggerTraceInfo info; |
|
470 Mem::Copy(&info, header.Ptr(), sizeof(SCloggerTraceInfo)); |
|
471 ASSERT(info.iTraceType == 'K' || info.iTraceType == 'U' || info.iTraceType == 'P'); |
|
472 TPtrC8 msg = Read(iTempBuf, data, info.iLength, startData); |
|
473 iServer.LogKernMessage(info.iTraceType, info.iTickCount, info.iThreadId, msg); |
|
474 } |
|
475 if (overflows) |
|
476 { |
|
477 _LIT8(KErr, "RDebug::Print buffer overflowed, %u calls not logged"); |
|
478 iServer.LogError(KErr, overflows); |
|
479 } |
|
480 // Zero the memory so it's easier to read in the crashlog |
|
481 memclr(iSharedChunk.Base() + start, endLen); |
|
482 if (startLen) memclr(iSharedChunk.Base() + KDataStartOffset, startLen); |
|
483 |
|
484 StartRouting(-1); // Magic number to indicate no need to call EnableDebugRouting again |
|
485 } |
|
486 |
|
487 void CDebugRouterClient::DoCancel() |
|
488 { |
|
489 iDebugRouter.CancelReceive(); |
|
490 //iDebugRouter.EnableDebugRouting(EFalse); |
|
491 // We only call Cancel() outside of our destructor when doing a FlushBuffers, in that case we'd like to make use of the device drivers buffering if possible, and not tell it to stop completely |
|
492 } |
|
493 |
|
494 void CDebugRouterClient::StartRouting(TBool aConsumeLogs) |
|
495 { |
|
496 if (aConsumeLogs != -1) |
|
497 { |
|
498 RCloggerDebugRouter::TEnableOption enable = aConsumeLogs ? RCloggerDebugRouter::EEnableRoutingAndConsume : RCloggerDebugRouter::EEnableRouting; |
|
499 iDebugRouter.EnableDebugRouting(enable); |
|
500 } |
|
501 if (!IsActive()) |
|
502 { |
|
503 iDebugRouter.ReceiveData(iStatus); |
|
504 SetActive(); |
|
505 } |
|
506 } |
|
507 |
|
508 void CDebugRouterClient::StopRouting() |
|
509 { |
|
510 Cancel(); |
|
511 iDebugRouter.EnableDebugRouting(RCloggerDebugRouter::EDisable); |
|
512 } |
|
513 |
|
514 TInt CDebugRouterClient::RegisterCrashDumpAreas(const TDesC8& aCrashDumpAreas) |
|
515 { |
|
516 return iDebugRouter.RegisterCrashDumpAreas(aCrashDumpAreas); |
|
517 } |
|
518 |
|
519 TInt CDebugRouterClient::CreateKernChunkForClient(RThread* aClient, TInt aMaxSize, TInt aCommittedSize, RChunk& aOurChunk) |
|
520 { |
|
521 SCreateChunkParams params; |
|
522 params.iHandleOfOtherThread = aClient ? aClient->Handle() : 0; |
|
523 params.iMaxSize = aMaxSize; |
|
524 params.iCommittedSize = aCommittedSize; |
|
525 params.iChunkHandle = 0; // Not strictly necessary to set this |
|
526 params.iOtherThreadChunkHandle = 0; // Not strictly necessary to set this |
|
527 |
|
528 TInt err = iDebugRouter.CreateChunk(params); |
|
529 if (err == KErrNone) |
|
530 { |
|
531 aOurChunk.SetHandle(params.iChunkHandle); |
|
532 return aClient ? params.iOtherThreadChunkHandle : KErrNone; |
|
533 } |
|
534 return err; |
|
535 // Return either an error, or the handle for the other thread, or KErrNone if there was no error and we didn't specify another thread |
|
536 } |
|
537 |
|
538 TInt CDebugRouterClient::AdjustChunk(RChunk& aChunk, TInt aNewSize) |
|
539 { |
|
540 return iDebugRouter.AdjustChunk(aChunk, aNewSize); |
|
541 } |