libraries/clogger/src/MiscServer.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     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 	}