--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/clogger/src/MiscServer.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,541 @@
+// MiscServer.cpp
+//
+// Copyright (c) 2006 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+#include "CloggerServer.h"
+#include <fshell/common.mmh>
+#include <fshell/ltkutils.h>
+
+#ifdef FSHELL_EZLIB_SUPPORT
+#include <EZCompressor.h>
+//#include <EZDecompressor.h>
+#include <EZlib.h>
+#include <EZGzip.h>
+#endif
+
+_LIT8(KDiskFull, "\r\n" MINTAGSTART "Log truncated due to disk full\r\n");
+
+void CLogWriter::WriteBuf(TInt aBuf)
+ {
+ ASSERT(!IsActive() && iEnabled);
+ {
+ iBuf = aBuf;
+ iFile.Write(iServer.GetBuf(aBuf), iStatus);
+ SetActive();
+ }
+ }
+
+TBool CLogWriter::IsBusyWriting()
+ {
+ return IsActive();
+ }
+
+void CLogWriter::RunL()
+ {
+ if (iStatus == KErrDiskFull)
+ {
+ //TODO Make this occur before the disk is actually completely empty?
+ TInt filePos = 0;
+ TInt err = iFile.Seek(ESeekCurrent, filePos);
+ if (err == KErrNone && filePos > KDiskFull().Length()) // ensure the file has enough room in it for our message
+ {
+ // Rewind the log file and overwrite the tail of it with a warning to say the disk is full
+ filePos = filePos - KDiskFull().Length();
+ err = iFile.Seek(ESeekStart, filePos);
+ if (err == KErrNone)
+ {
+ iFile.Write(KDiskFull, iStatus);
+ SetActive();
+ return;
+ }
+ }
+ }
+ iServer.CompletedWritingBuf(this, iBuf);
+ }
+
+void CLogWriter::DoCancel()
+ {
+ // 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
+ }
+
+CLogWriter::~CLogWriter()
+ {
+ // Don't cancel, CloggerServer is supposed to guarantee we won't be deleted when still writing
+ //Cancel();
+ }
+
+void CLogWriter::CloseWriter()
+ {
+ delete this;
+ }
+
+void CLogWriter::SetEnabled(TBool aEnabled)
+ {
+ if (!iFile.SubSessionHandle())
+ {
+ // If the log file is not open, we can't enable ourselves even if the client asks us to
+ aEnabled = EFalse;
+ }
+ iEnabled = aEnabled;
+ }
+
+CRDebugWriter::CRDebugWriter(CCloggerServer& aServer)
+ : iServer(aServer)
+ {}
+
+void CRDebugWriter::WriteBufL(const TDesC8& aBuf)
+ {
+ TInt maxLen = 256;
+
+ // RDebug::Print only accepts 256 chars on the kernel side, so split the write up
+ TInt pos = 0;
+ while (pos < aBuf.Length())
+ {
+ TPtrC8 frag = aBuf.Mid(pos, Min(maxLen, aBuf.Length() - pos));
+ //Exec::DebugPrint((TAny*)&frag, 1); // This is quicker than calling RDebugPrint - cuts down on a memory copy
+ // ^ Except that you can't do it from non-euser code...
+ LtkUtils::RawPrint(frag);
+ pos += frag.Length();
+ }
+ }
+
+void CRDebugWriter::CloseWriter()
+ {
+ delete this;
+ }
+
+///////////////
+
+TInt CSyncWriterWrapper::ThreadFunction(TAny* aSelf)
+ {
+ CTrapCleanup* cleanup=CTrapCleanup::New();
+ if (!cleanup) return KErrNoMemory;
+
+ CSyncWriterWrapper* self = static_cast<CSyncWriterWrapper*>(aSelf);
+ for (;;)
+ {
+ User::WaitForRequest(self->iThreadStatus);
+ //User::After(1); //DEBUG TO TEST SLOW WRITERS
+ if (self->iThreadStatus != KErrNone)
+ {
+ break;
+ }
+
+ const TDesC8& buf = self->iServer.GetBuf(self->iBuf);
+
+ TRAPD(err, self->iWriter.WriteBufL(buf));
+
+ self->iThreadStatus = KRequestPending;
+ TRequestStatus* stat = &self->iStatus;
+ self->iMainThread.RequestComplete(stat, err);
+ }
+
+ delete cleanup;
+ return self->iThreadStatus.Int();
+ }
+
+void CSyncWriterWrapper::WriteBuf(TInt aBuf)
+ {
+ ASSERT(!IsActive() && iEnabled);
+ {
+ iBuf = aBuf;
+ iStatus = KRequestPending;
+ SetActive();
+ TRequestStatus* stat = &iThreadStatus;
+ iWorkerThread.RequestComplete(stat, KErrNone);
+ }
+ }
+
+TBool CSyncWriterWrapper::IsBusyWriting()
+ {
+ return IsActive();
+ }
+
+void CSyncWriterWrapper::RunL()
+ {
+ iServer.CompletedWritingBuf(this, iBuf);
+ }
+
+void CSyncWriterWrapper::DoCancel()
+ {
+ // There's no way to cancel a sync writer
+ }
+
+CSyncWriterWrapper::~CSyncWriterWrapper()
+ {
+ // Don't cancel, CloggerServer is supposed to guarantee we won't be deleted when still writing
+ //Cancel();
+ if (iWorkerThread.Handle())
+ {
+ TRequestStatus* stat = &iThreadStatus;
+ iWorkerThread.RequestComplete(stat, KErrCancel);
+ TRequestStatus rv;
+ iWorkerThread.Rendezvous(rv);
+ User::WaitForRequest(rv);
+ iWorkerThread.Close();
+ }
+ iMainThread.Close();
+ if (iOwnWriter)
+ {
+ iWriter.CloseWriter();
+ }
+ }
+
+void CSyncWriterWrapper::CloseWriter()
+ {
+ delete this;
+ }
+
+void CSyncWriterWrapper::SetEnabled(TBool aEnabled)
+ {
+ iEnabled = aEnabled;
+ }
+
+CSyncWriterWrapper::CSyncWriterWrapper(CCloggerServer& aServer, MSyncWriter& aWriter)
+ : CActive(EPriorityHigh), // It's more important to keep writing than to handle clientserver calls
+ iServer(aServer), iWriter(aWriter), iBuf(-1), iEnabled(EFalse)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+CSyncWriterWrapper* CSyncWriterWrapper::NewL(CCloggerServer& aServer, MSyncWriter& aWriter, TInt aWriterId)
+ {
+ CSyncWriterWrapper* self = new(ELeave) CSyncWriterWrapper(aServer, aWriter);
+ CleanupStack::PushL(self);
+ self->ConstructL(aWriterId);
+ CleanupStack::Pop(self);
+ self->iOwnWriter = ETrue;
+ return self;
+ }
+
+void CSyncWriterWrapper::ConstructL(TInt aWriterId)
+ {
+ User::LeaveIfError(iMainThread.Open(RThread().Id()));
+ _LIT(KName, "SyncWriter%i");
+ TBuf<16> name;
+ name.Format(KName, aWriterId);
+ User::LeaveIfError(iWorkerThread.Create(name, &ThreadFunction, 2048, NULL, this));
+ if (aWriterId >= 0)
+ {
+ iWorkerThread.SetPriority(EPriorityMore); // To ensure buffers aren't being filled without us having a chance to empty them
+ }
+ else
+ {
+ iWorkerThread.SetPriority(EPriorityMuchLess); // We use the sync writer wrapper around the background compress of rotated log files, and that operation should be low priority
+ }
+ iWorkerThread.Resume();
+ }
+
+//////////
+
+CLogCompressor* CLogCompressor::NewLC(CCloggerServer& aServer)
+ {
+ CLogCompressor* self = new(ELeave) CLogCompressor(aServer);
+ CleanupStack::PushL(self);
+ User::LeaveIfError(self->iFs.Connect());
+ self->iFs.ShareAuto();
+ return self;
+ }
+
+CLogCompressor::CLogCompressor(CCloggerServer& aServer)
+ : iServer(aServer)
+ {
+ }
+
+CLogCompressor::~CLogCompressor()
+ {
+ }
+
+void CLogCompressor::WriteBufL(const TDesC8& /*aBuf*/)
+ {
+ TDes& filename = iServer.GetFilenameToRotate();
+
+ HBufC* compressedFile = HBufC::NewLC(filename.Length()+3);
+ _LIT(KFmt, "%S.gz");
+ compressedFile->Des().Format(KFmt, &filename);
+
+ RFile input;
+
+ //_LIT(KInfo,"Compressing file %S to %S\n");
+ //console->Printf(KInfo,&inputFile,compressedFile);
+
+ User::LeaveIfError(input.Open(iFs, filename, EFileStream | EFileRead | EFileShareAny));
+ CleanupClosePushL(input);
+
+ TRAPD(err, DoGzipL(input, *compressedFile));
+
+ if (err)
+ {
+ // If the compress failed, delete any fragment (don't care if this fails)
+ iFs.Delete(*compressedFile);
+ }
+
+ //_LIT(KHoorah,"Hoorah");
+ //console->Printf(KHoorah);
+
+ CleanupStack::PopAndDestroy(&input);
+ if (err == KErrNone)
+ {
+ // Don't delete the original unless the compress succeeded!
+ err = iFs.Delete(filename);
+ }
+ if (err == KErrNone)
+ {
+ // We've sucessfully compressed our file, so update filename to reflect that
+ filename = *compressedFile;
+ }
+ // Don't do anything if the delete fails - the file(s) will be cleaned up when the max number of rotated logs is exceeded
+ CleanupStack::PopAndDestroy(compressedFile);
+ }
+
+void CLogCompressor::CloseWriter()
+ {
+ delete this;
+ }
+
+void CLogCompressor::DoGzipL(RFile& aInput, const TDesC& aOutput)
+ {
+#ifdef FSHELL_EZLIB_SUPPORT
+ const TInt bufferSize = 0x8000; // 32 KB
+ CEZFileToGZip* com = CEZFileToGZip::NewLC(iFs, aOutput, aInput, bufferSize);
+ while (com->DeflateL()){/*do nothing*/}
+ CleanupStack::PopAndDestroy(com);
+#else
+ User::Leave(KErrNotSupported);
+#endif
+ }
+
+////
+
+CMessageQueueWriter* CMessageQueueWriter::NewL()
+ {
+ CMessageQueueWriter* self = new(ELeave) CMessageQueueWriter;
+ TInt err = self->iQ.CreateGlobal(_L("Clogger.LogMessageQueue"), 4);
+
+ if (err)
+ {
+ DISOWN(self);
+ User::Leave(err);
+ }
+ return self;
+ }
+
+void CMessageQueueWriter::WriteBufL(const TDesC8& aBuf)
+ {
+ TBuf8<128> buf;
+ TPtrC8 lineFrag(aBuf);
+ while (lineFrag.Length())
+ {
+ TInt spaceFree = buf.MaxLength() - buf.Length();
+ buf.Append(lineFrag.Left(spaceFree));
+ TInt err = iQ.Send(buf);
+ if (err == KErrOverflow)
+ {
+ // Drop a frame
+ TBuf8<128> buf2;
+ iQ.Receive(buf2);
+ // And resend
+ err = iQ.Send(buf);
+ }
+
+ lineFrag.Set(lineFrag.Mid(Min(spaceFree, lineFrag.Length())));
+ buf.Zero();
+ }
+ }
+
+void CMessageQueueWriter::CloseWriter()
+ {
+ delete this;
+ }
+
+CMessageQueueWriter::CMessageQueueWriter()
+ {
+ }
+
+CMessageQueueWriter::~CMessageQueueWriter()
+ {
+ iQ.Close();
+ }
+
+CDebugRouterClient* CDebugRouterClient::NewL(CCloggerServer& aServer)
+ {
+ CDebugRouterClient* self = new(ELeave) CDebugRouterClient(aServer);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+void CDebugRouterClient::ConstructL()
+ {
+ TInt err = RCloggerDebugRouter::LoadDriver();
+ if (err != KErrAlreadyExists)
+ {
+ User::LeaveIfError(err);
+ }
+ User::LeaveIfError(iDebugRouter.Open());
+ }
+
+void CDebugRouterClient::OpenChunkL()
+ {
+ if (!iSharedChunk.Handle())
+ {
+ User::LeaveIfError(iDebugRouter.OpenChunk(iSharedChunk));
+ iTempBuf.CreateL(2048);
+ }
+ }
+
+CDebugRouterClient::CDebugRouterClient(CCloggerServer& aServer)
+: 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
+ {
+ CActiveScheduler::Add(this);
+ }
+
+CDebugRouterClient::~CDebugRouterClient()
+ {
+ Cancel();
+ if (iDebugRouter.Handle())
+ {
+ iDebugRouter.EnableDebugRouting(RCloggerDebugRouter::EDisable); // I don't really know whether to call this before or after the cancel
+ iDebugRouter.Close();
+ }
+ RCloggerDebugRouter::CloseDriver();
+ iSharedChunk.Close();
+ iTempBuf.Close();
+ }
+
+TPtrC8 Read(TDes8& aTempBuf, TPtrC8& aData, TInt aLength, TPtrC8& aOverflowData)
+ {
+ if (aLength <= aData.Length())
+ {
+ // Can read it from this buffer
+ TPtrC8 res(aData.Left(aLength));
+ aData.Set(aData.Mid(aLength));
+ return res;
+ }
+ else /*if (aLength > aData.Length())*/
+ {
+ // Descriptor spans wrap point, so need to copy into temp buf
+ aTempBuf.Copy(aData.Left(aTempBuf.MaxLength())); // If anyone's crazy enough to write a platsec diagnostic string longer than 2k, it gets truncated
+ TInt overflowLen = aLength - aData.Length();
+ aData.Set(aOverflowData); // Wrap aData
+ aOverflowData.Set(TPtrC8());
+ if (overflowLen > aData.Length())
+ {
+ ASSERT(EFalse); // Shouldn't happen
+ // in urel, return everything we've got
+ return aData;
+ }
+ aTempBuf.Append(aData.Left(overflowLen));
+ aData.Set(aData.Mid(overflowLen));
+ return TPtrC8(aTempBuf);
+ }
+ }
+
+void CDebugRouterClient::RunL()
+ {
+ TUint chunkSize = iSharedChunk.Size();
+ const TUint KDataStartOffset = sizeof(SDebugChunkHeader);
+ SDebugChunkHeader* chunkHeader = (SDebugChunkHeader*)iSharedChunk.Base();
+ TUint start = chunkHeader->iStartOffset;
+ TUint end = chunkHeader->iEndOffset;
+ TUint overflows = chunkHeader->iOverflows;
+
+ TBool wrap = (start > end);
+ TUint endLen = wrap ? chunkSize - start : end - start;
+ TUint startLen = wrap ? end - KDataStartOffset : 0;
+
+ TPtrC8 endData(iSharedChunk.Base() + start, endLen);
+ TPtrC8 startData;
+ if (wrap) startData.Set(iSharedChunk.Base() + KDataStartOffset, startLen);
+ TPtrC8 data(endData);
+
+ while (data.Length())
+ {
+ TPtrC8 header = Read(iTempBuf, data, sizeof(SCloggerTraceInfo), startData);
+ if (header.Length() < (TInt)sizeof(SCloggerTraceInfo))
+ {
+ ASSERT(EFalse); // for udeb
+ break; // Something's broken
+ }
+ SCloggerTraceInfo info;
+ Mem::Copy(&info, header.Ptr(), sizeof(SCloggerTraceInfo));
+ ASSERT(info.iTraceType == 'K' || info.iTraceType == 'U' || info.iTraceType == 'P');
+ TPtrC8 msg = Read(iTempBuf, data, info.iLength, startData);
+ iServer.LogKernMessage(info.iTraceType, info.iTickCount, info.iThreadId, msg);
+ }
+ if (overflows)
+ {
+ _LIT8(KErr, "RDebug::Print buffer overflowed, %u calls not logged");
+ iServer.LogError(KErr, overflows);
+ }
+ // Zero the memory so it's easier to read in the crashlog
+ memclr(iSharedChunk.Base() + start, endLen);
+ if (startLen) memclr(iSharedChunk.Base() + KDataStartOffset, startLen);
+
+ StartRouting(-1); // Magic number to indicate no need to call EnableDebugRouting again
+ }
+
+void CDebugRouterClient::DoCancel()
+ {
+ iDebugRouter.CancelReceive();
+ //iDebugRouter.EnableDebugRouting(EFalse);
+ // 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
+ }
+
+void CDebugRouterClient::StartRouting(TBool aConsumeLogs)
+ {
+ if (aConsumeLogs != -1)
+ {
+ RCloggerDebugRouter::TEnableOption enable = aConsumeLogs ? RCloggerDebugRouter::EEnableRoutingAndConsume : RCloggerDebugRouter::EEnableRouting;
+ iDebugRouter.EnableDebugRouting(enable);
+ }
+ if (!IsActive())
+ {
+ iDebugRouter.ReceiveData(iStatus);
+ SetActive();
+ }
+ }
+
+void CDebugRouterClient::StopRouting()
+ {
+ Cancel();
+ iDebugRouter.EnableDebugRouting(RCloggerDebugRouter::EDisable);
+ }
+
+TInt CDebugRouterClient::RegisterCrashDumpAreas(const TDesC8& aCrashDumpAreas)
+ {
+ return iDebugRouter.RegisterCrashDumpAreas(aCrashDumpAreas);
+ }
+
+TInt CDebugRouterClient::CreateKernChunkForClient(RThread* aClient, TInt aMaxSize, TInt aCommittedSize, RChunk& aOurChunk)
+ {
+ SCreateChunkParams params;
+ params.iHandleOfOtherThread = aClient ? aClient->Handle() : 0;
+ params.iMaxSize = aMaxSize;
+ params.iCommittedSize = aCommittedSize;
+ params.iChunkHandle = 0; // Not strictly necessary to set this
+ params.iOtherThreadChunkHandle = 0; // Not strictly necessary to set this
+
+ TInt err = iDebugRouter.CreateChunk(params);
+ if (err == KErrNone)
+ {
+ aOurChunk.SetHandle(params.iChunkHandle);
+ return aClient ? params.iOtherThreadChunkHandle : KErrNone;
+ }
+ return err;
+ // 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
+ }
+
+TInt CDebugRouterClient::AdjustChunk(RChunk& aChunk, TInt aNewSize)
+ {
+ return iDebugRouter.AdjustChunk(aChunk, aNewSize);
+ }