First public contribution.
1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // Implements the Flogger server side
23 #include "comsdbgsvr.h"
24 #include "comsdbgwriter.h"
25 #include "comsdbgstd.h"
26 #include "comsdbgmessages.h"
28 _LIT8(KOOMErrorString, "#Logs may be lost out of memory!! Further OOM conditions may not be recorded.\r\n");
29 _LIT8(KThreadDiedString, "#Something caused the secondary thread in comsdbgutil to die.\r\n");
30 _LIT8(KIniFileProblem, "#There is a problem with the ini file\r\n");
31 _LIT8(KIniFileUpdate, "#Ini file changes detected and noted.\r\n");
32 _LIT(KFloggerSecondaryThreadName, "Comsdbg2");
39 // CFileLoggerServer class definition
43 CFileLoggerServer* CFileLoggerServer::NewL()
46 CFileLoggerServer* r=new(ELeave) CFileLoggerServer();
47 CleanupStack::PushL(r);
49 r->StartL(KFLoggerServerName);
54 CFileLoggerServer::CFileLoggerServer()
55 : CServer2(EPriorityNormal,ESharableSessions)
58 void CFileLoggerServer::ConstructL()
61 * @note Constructs the secondary thread, passing through a pointer to the log queue.
62 * Constructs the file parser object and parses the file, attempting to copy the
63 * file from the ROM if it is not found on C drive.
64 * Constructs the time beat.
65 * Performs the first UpdateMedia to ensure a media is set.
68 User::LeaveIfError(iCriticalSection.CreateLocal());
69 User::LeaveIfError(iCompletionSemaphore.CreateLocal(0));
70 User::LeaveIfError(iFs.Connect());
72 #if defined (__WINS__)
73 iDebugWriter = CDebugPortProtocol::NewL();
76 User::LeaveIfError(iLogMessageArray.Append(NULL));
77 iPreAllocatedErrorMessage = CLogCommentMessage::NewL(KOOMErrorString);
78 iArrayHasSpaceForWrite = ETrue;
81 TPtrC iniFile(_L(""));
83 /* We first check whether KFloggerIniFile (commsdbg.ini) exists,
84 if it does we use that one, otherwise we use the old KFloggerIniOldFile
85 (comsdbg.ini) instead (or rather, we try), else copy the default ini file. */
88 err = ff.FindByDir(KFloggerIniFile, KFloggerIniDir);
92 // KFloggerIniFile was not there, lets use KFloggerIniOldFile
93 err = ff.FindByDir(KFloggerIniOldFile, KFloggerIniDir);
98 // found the ini file to use, hold on to its name and location
99 iniFile.Set (ff.File ());
103 // couldn't find an ini file with the new or old name, lets use ini file in the resource dir
104 err = ff.FindByDir(KFloggerIniFile, KFloggerIniRscDir);
106 // Get the correct system drive letter in a buffer.
107 TChar sysDriveLetter = RFs::GetSystemDriveChar();
108 TBuf<1> sysDriveLetterBuf;
109 sysDriveLetterBuf.Fill( sysDriveLetter, 1 );
111 // Get the full path to the default ini file including the system drive letter.
112 TBuf<KMaxFileName> dfltIniFullPath( KFloggerDfltIniPartialPath );
113 dfltIniFullPath.Insert( 0, sysDriveLetterBuf );
117 //only ini file is the default one in z:\resource
118 CFileMan* fileMan = CFileMan::NewL(iFs);
119 err = fileMan->Copy(KFloggerIniInROM, dfltIniFullPath, CFileMan::ERecurse);
122 err = fileMan->Attribs(dfltIniFullPath,0,KEntryAttReadOnly,TTime(0));
123 iniFile.Set (dfltIniFullPath);
129 // Get the full path to the old ini file including the system drive letter.
130 TBuf<KMaxFileName> dfltIniOldIniFullPath( KFloggerIniOldFilePartialPath );
131 dfltIniOldIniFullPath.Insert( 0, sysDriveLetterBuf );
133 // watch ini file, even tho it isnt there
134 // also create another file watched to watch for the old file for backward compatibility
135 iniFile.Set(dfltIniFullPath);
136 iIniOldFileWatcher = CIniFileWatcher::NewL(iFs, *this, dfltIniOldIniFullPath);
140 iIniFileParser = CIniFileParser::NewL(iFs);
141 if (err==KErrNone) //error getting the ini file or setting attributes
143 err=iIniFileParser->ParseIniFile(iniFile);
147 // no default ini file to copy either, so lets just pretend there is an empty file
151 // create and set in motion the second thread
152 // we can only create the second thread thread once we know whether we are running
153 // synchronous or not. If we still don't know, then we'll force it to false.
154 // When flogger first starts, the flush setting is not known because the ini file
155 // parsing may fail. However, once client's start calling this function, we must
156 // decide whether to flush or not since it is not easy to switch from
157 // non-flushing to flushing. Thus, instead, the first time this function
158 // is called, we make a decision
159 iIniFileParser->FinaliseFlushSetting();
160 TBool currentFlushSetting;
161 iIniFileParser->FlushingOn(currentFlushSetting);
162 // the second thread needs to have been created before we need to place anything
163 // into the log queue, since the 2nd thread contains the semaphore we are using
164 // to count the # of mesgs in the queue.
165 iSecondaryThread = CSecondaryThread::NewL(*this,currentFlushSetting);
167 // update the media regardless of whether the parsing worked
168 // since we need to get the default set if needs be.
171 if (err!=KErrNone) //Problem with ini file reading. Stick a message in the log...
173 CLogCommentMessage* message = CLogCommentMessage::NewL(KIniFileProblem);
174 err = AppendAndGiveOwnership(message);
182 iIniFileWatcher = CIniFileWatcher::NewL(iFs, *this, iniFile);
183 iTimeManager = CTimeManager::NewL(*this);
186 void CFileLoggerServer::UpdateMediaL()
188 CMediaUpdateMessage* message = new(ELeave) CMediaUpdateMessage(*iIniFileParser);
189 CleanupStack::PushL(message);
190 if (AppendAndGiveOwnership(message)!=KErrNone) //Problem => Leave media as it was.
192 CleanupStack::Pop(message);
196 CleanupStack::Pop(message);
199 CFileLoggerServer::~CFileLoggerServer()
201 delete iSecondaryThread;
203 delete iIniFileWatcher;
204 delete iIniOldFileWatcher;
205 delete iIniFileParser;
206 delete iPreAllocatedErrorMessage;
208 iCriticalSection.Close();
209 iCompletionSemaphore.Close();
210 iLogMessageArray.ResetAndDestroy();
211 #if defined (__WINS__)
217 CSession2* CFileLoggerServer::NewSessionL(const TVersion &aVersion ,const RMessage2& /*aMessage*/) const
219 * Create a new server session. Check that client is using current or older interface and make a new session.
220 * @note Called by kernel after RFileLogger::DoConnect().
224 TVersion v(KFLogSrvMajorVersionNumber,KFLogSrvMinorVersionNumber,KFLogSrvBuildVersionNumber);
225 if (!User::QueryVersionSupported(v,aVersion))
227 User::Leave(KErrNotSupported);
230 return CFileLogSession::NewL(*(const_cast<CFileLoggerServer*>(this)), *iIniFileParser);
233 void CFileLoggerServer::RePrepareForOOML()
235 * Called to ensure there is space for the OOM error msg in log queue.
238 if (!iPreAllocatedErrorMessage)
240 iPreAllocatedErrorMessage = CLogCommentMessage::NewL(KOOMErrorString);
242 //Must reserve 1 space in array for error handling or leave.
243 User::LeaveIfError(iLogMessageArray.Append(NULL));
244 //We've got everything we need should we hit an OOM to guarentee we can get an error in the log.
245 iArrayHasSpaceForWrite=ETrue;
248 TInt CFileLoggerServer::AppendAndGiveOwnership(CLogMessageBase* aMessage)
250 * Append a log package to the queue end.
251 * @note Entry only allowed for primary (producer) thread.
252 * @note If win32 and debug port logging on, package is written immediately to the debug port as well.
253 * @note If no space has been allocated for the OOM error msg, then this is done first.
254 * @note If flushing (synchronous) operation then waits for completion.
258 iCriticalSection.Wait();
259 #if defined (__WINS__)
260 if (iIniFileParser->Win32DebugEnabled())
262 aMessage->Invoke(*iDebugWriter);
266 if (!iArrayHasSpaceForWrite) //We ran into low memmory a while ago, time to get set up again.
267 //iArrayHasSpaceForWrite==ETrue <==> 1 free slot in the array
268 //iArrayHasSpaceForWrite==EFalse <==> 0 free slots in the array
269 //Above MUST be true or we'll go horribly wrong!!
270 //RePrepareForOOML must be done in CS since we'll be mucking about with the array.
272 TRAP(err, RePrepareForOOML());
275 iCriticalSection.Signal();
279 //If we get to here then we must have 1 space in the array. First try to expand
280 //then if that works stick the message in the array, otherwise return error.
281 if ((err = iLogMessageArray.Append(NULL))==KErrNone)
283 const TInt arrayCount(iLogMessageArray.Count());
284 iLogMessageArray[arrayCount-2] = aMessage;
285 iSecondaryThread->SignalRequestSemaphore();
287 iCriticalSection.Signal();
292 iIniFileParser->FlushingOn(flushSetting);
294 if (flushSetting == EFlushOn)
296 iCompletionSemaphore.Wait(); // one for each signal of the request semaphore
302 void CFileLoggerServer::PutOOMErrorInLog()
304 //Best attempt to put error in log, if there's no space then we can't
305 //We don't try to allocate space in the array. The reasons for this are left as
306 //an excercise to whomever is unfortunate enough to be trying to debug this.
307 //Basically, this is the only place the last slot of the array can ever be filled,
308 //so if iArrayHasSpaceForWrite==EFalse we're guaranteed to have got an error in the
309 //log already earlier on, and since
310 //in the AppendAndGiveOwnership method we fail if we cant realocate the error message
311 //we're guarenteed that the last line in the log is and error message already.
312 if (iArrayHasSpaceForWrite)
314 iCriticalSection.Wait();
315 const TInt arrayCount(iLogMessageArray.Count());
316 iLogMessageArray[arrayCount-1] = iPreAllocatedErrorMessage;
317 iPreAllocatedErrorMessage=NULL;
318 iArrayHasSpaceForWrite=EFalse;
319 iCriticalSection.Signal();
320 iSecondaryThread->SignalRequestSemaphore();
323 iIniFileParser->FlushingOn(flushSetting);
325 if (flushSetting == EFlushOn)
327 iCompletionSemaphore.Wait(); // one for each signal of the request semaphore
335 void CFileLoggerServer::GetFirstMessageAndTakeOwnership(CLogMessageBase*& aMessage)
337 * Remove the message at the head of the log queue.
338 * @note Entry only allowed for secondary (consumer) thread.
339 * @note Blocks if no messages in the log queue.
343 // Wait on the request semaphore, since we are using that to communicate the number of
344 // outstanding log items in the queue.
345 User::WaitForAnyRequest();
346 iCriticalSection.Wait();
347 aMessage = iLogMessageArray[0];
348 iLogMessageArray.Remove(0);
349 iCriticalSection.Signal();
354 void CFileLoggerServer::SignalCompletionSemaphore()
355 // signal the completion semaphore. Called by the slave/consumer thread when it is
356 // done with the current message. Since the slave thread doesn't know if flushing
357 // is on, it will call this regardless of whether we actually need to signal the
362 iIniFileParser->FlushingOn(flushSetting);
364 if (flushSetting == EFlushOn)
366 iCompletionSemaphore.Signal();
372 TInt CFileLoggerServer::RunError(TInt aError)
374 * Leave has occured in CFileLogSession::ServiceL.
375 * Usually this is because the appending of the message to the queue has failed
376 * due to the queue being filled.
380 return CServer2::RunError(aError);
384 void CFileLoggerServer::IniFileChanged(TDesC &aIniFile)
386 Called by the file watcher when the ini file changes.
387 Any OOM problems are ignored
390 CLogCommentMessage* message = NULL;
391 TRAPD(err, message = CLogCommentMessage::NewL(KIniFileUpdate))
392 if ((err == KErrNone) && (message))
394 err = AppendAndGiveOwnership(message);
400 err = iIniFileParser->ParseIniFile (aIniFile);
402 // update media regardless of success, since we may want to
403 // set the default media. update media will only leave if memory full.
404 // in which case we cant output an error msg anyway.
405 TRAPD(err2, UpdateMediaL());
407 if (err!=KErrNone) //problem parsing ini file, leave settings as they are and carry on.
409 CLogCommentMessage* message = NULL;
410 TRAP(err, message = CLogCommentMessage::NewL(KIniFileProblem))
411 if ((err == KErrNone) && (message))
413 err = AppendAndGiveOwnership(message);
421 else if (err2 != KErrNone)
423 // memory full, but cant even output msg to say this
427 //Ignore error. Above can only fail due to OOM, so carry on regardless.
429 iSessionIter.SetToFirst();
430 while ((p=iSessionIter++)!=NULL)
432 static_cast<CFileLogSession*>(p)->IniFileChanged();
437 void CFileLoggerServer::__DbgKillTimeManager()
444 // CFileLogSession class definition
447 CFileLogSession* CFileLogSession::NewL(MLogArrayAccess& aArrayAccess, const MIniFlushModeAndLogValidQuery& aLogValidQuery)
449 * Construct new server end of session.
450 * @note Only called from CFileLoggerServer::NewSessionL()
454 CFileLogSession* self = new(ELeave) CFileLogSession(aArrayAccess, aLogValidQuery);
458 CFileLogSession::CFileLogSession(MLogArrayAccess& aArrayAccess, const MIniFlushModeAndLogValidQuery& aLogValidQuery)
459 : iArrayAccess(aArrayAccess), iFlushModeLogValidQuery(aLogValidQuery), iLogValid(KLoggingOnOffDefault)
463 CFileLogSession::~CFileLogSession()
467 void CFileLogSession::ServiceL(const RMessage2& aMessage)
469 * Processes message from client-side (RFileLogger)
470 * @note Most messages result in logs being added to the queue. If
471 * synchronous logging is on, this function will wait until the queue is then emptied.
474 RThread clientThread;
476 iFlushModeLogValidQuery.FlushingOn(flushOn);
478 // All of the common & performance-critical requests need the client thread id. Note that RFileLogger
479 // sessions are shareable; the thread now logging may be other than that which created the session
480 User::LeaveIfError(aMessage.Client(clientThread));
481 iThreadId = clientThread.Id();
485 switch(aMessage.Function())
487 case EStaticWriteToLog:
489 aMessage.ReadL(0, iSubsystem);
490 aMessage.ReadL(1, iComponent);
491 if (iFlushModeLogValidQuery.LogValid(iSubsystem, iComponent))
493 HBufC8* stringOnHeap = HBufC8::NewLC(aMessage.Int3());
494 TPtr8 ptrToString(stringOnHeap->Des());
495 aMessage.ReadL(2, ptrToString);
498 //We're done with the client now, so we can complete its request.
499 aMessage.Complete(KErrNone);
501 CLogStringMessage* logMessage = new(ELeave) CLogStringMessage(stringOnHeap, iSubsystem, iComponent, iThreadId);
502 CleanupStack::Pop(stringOnHeap);
503 TInt err = iArrayAccess.AppendAndGiveOwnership(logMessage);
511 aMessage.Complete(KErrNone);
516 //We've done with the client now, so we can complete it's request.
517 aMessage.Complete(KErrNone);
523 CheckClientHasSetTagsL(aMessage);
524 CClearLogMessage* clearLogMessage = new(ELeave) CClearLogMessage(clientThread.FullName());
525 TInt err = iArrayAccess.AppendAndGiveOwnership(clearLogMessage);
528 delete clearLogMessage;
531 aMessage.Complete(KErrNone);
536 aMessage.ReadL(0, iSubsystem);
537 aMessage.ReadL(1, iComponent);
538 iSetLogMessage = aMessage;
539 SetLoggingOnOffInClient();
540 aMessage.Complete(KErrNone);
545 CheckClientHasSetTagsL(aMessage);
546 HBufC8* stringOnHeap = HBufC8::NewLC(aMessage.Int1());
547 TPtr8 ptrToString(stringOnHeap->Des());
548 aMessage.ReadL(0, ptrToString);
551 //We're done with the client now, so we can complete its request.
552 aMessage.Complete(KErrNone);
554 CLogStringMessage* logMessage = new(ELeave) CLogStringMessage(stringOnHeap, iSubsystem, iComponent, iThreadId);
555 CleanupStack::Pop(stringOnHeap);//Above call takes ownership of stringOnHeap
556 TInt err = iArrayAccess.AppendAndGiveOwnership(logMessage);
564 aMessage.Complete(KErrNone);
571 CheckClientHasSetTagsL(aMessage);
572 HBufC8* stringOnHeap = HBufC8::NewLC(aMessage.Int1());
573 TPtr8 ptrToString(stringOnHeap->Des());
574 aMessage.ReadL(0, ptrToString);
575 CLogBinaryString* logMessage = new(ELeave) CLogBinaryString(stringOnHeap, iSubsystem, iComponent);
576 CleanupStack::Pop(stringOnHeap);//Above call takes ownership of stringOnHeap
579 //We're done with the client now, so we can complete its request.
580 aMessage.Complete(KErrNone);
583 TInt err = iArrayAccess.AppendAndGiveOwnership(logMessage);
591 aMessage.Complete(KErrNone);
595 #ifdef _DEBUG //These methods are only accessible in debug.
596 case EShutDownServer:
598 CActiveScheduler::Stop();
599 aMessage.Complete(KErrNone);
602 case ESetHeapFailure:
604 //we need to stop the timer otherwise server will
605 //keep allocing when we're doing heap failure test which makes the test fail.
606 const_cast<CFileLoggerServer*>(static_cast<const CFileLoggerServer*>(Server()))->__DbgKillTimeManager();
607 __UHEAP_FAILNEXT(aMessage.Int0());
608 aMessage.Complete(KErrNone);
614 aMessage.Panic(KFloggerServerPanic, EBadMessageFunction);
617 clientThread.Close();
620 void CFileLogSession::CheckClientHasSetTagsL(const RMessage2& aMessage)
622 * Ensure for a connection that the client has set the tags, otherwise panic.
623 * @param aMessage the current client message in progress
624 * @note Tags are kept server side so that we don't need to store the tags in the client and pass them through with each request.
628 if (iSetLogMessage.IsNull())
630 aMessage.Panic(KFloggerPanic, ESetLogTagsNotCalled); //Client ain't called set log tags.
631 User::Leave(KErrGeneral);
636 void CFileLogSession::SetLoggingOnOffInClient()
638 const TBool currentLoggingOnOffState = iLogValid;
639 iLogValid = iFlushModeLogValidQuery.LogValid(iSubsystem, iComponent);
640 if (currentLoggingOnOffState!=iLogValid) //Logging on/off has changed. Set state in client
642 // ignore the error returned
643 (void)iSetLogMessage.Write(2, TPckgBuf<TBool>(iLogValid));
647 void CFileLogSession::IniFileChanged()
648 //We only need to update clients that have called SetLogTags
649 //We don't update clients that are using the static API client side.
651 if (!iSetLogMessage.IsNull())
653 SetLoggingOnOffInClient();
657 /////////////////////////////////////////////////////////////////
659 CSecondaryThread* CSecondaryThread::NewL(MLogArrayAccess& aArrayAccess, TBool aFlushOn)
661 CSecondaryThread* self = new(ELeave) CSecondaryThread(aArrayAccess,aFlushOn);
662 CleanupStack::PushL(self);
664 CleanupStack::Pop(self);
668 CSecondaryThread::CSecondaryThread(MLogArrayAccess& aArrayAccess, TBool aFlushOn)
669 : CActive(EPriorityHigh), iArrayAccess(aArrayAccess), iFlushingOn(aFlushOn)
672 void CSecondaryThread::RunL()
674 * Runs when second thread dies, and simply restarts it again.
675 * @note On death a thread-death message is placed in the queue so that
676 * a user is aware this happened.
677 * @note StartSecondaryThread issues a request to be informed of thread death,
678 * so starts active object again.
681 StartSecondaryThreadL(ETrue);
684 void CSecondaryThread::DoCancel()
686 * Appends a special shutdown message into the log array. When this reaches
687 * the head and is run, it shuts down the second thread.
688 * @note Logs onto second thread and waits for it to finish.
692 iSecondaryThread.LogonCancel(iStatus);
693 if (iArrayAccess.AppendAndGiveOwnership(iShutDownMessage)!=KErrNone)
694 { //Nothing else we can do here. We'll have to just kill the other thread.
695 iSecondaryThread.Kill(KErrGeneral);
699 iShutDownMessage = NULL;
700 TRequestStatus status(KRequestPending);
701 iSecondaryThread.Logon(status);
702 User::WaitForRequest(status);
706 void CSecondaryThread::StartSecondaryThreadL(TBool aRestarting)
708 * Start the second/consumer/slave thread and issue a request to be told when it dies.
713 User::LeaveIfError(iSecondaryThread.Create(KFloggerSecondaryThreadName,CLogManager::ThreadEntryPoint,KDefaultStackSize,NULL,&iArrayAccess));
715 iSecondaryThread.Rendezvous(stat);
719 iSecondaryThread.SetPriority(EPriorityAbsoluteHigh); //was EPriorityMuchMore
723 iSecondaryThread.SetPriority(EPriorityAbsoluteForeground); // was EPriorityMuchLess
726 iSecondaryThread.Resume();
728 User::WaitForRequest(stat);
730 iSecondaryThread.Logon(iStatus);
734 CLogCommentMessage* errorMessage = CLogCommentMessage::NewL(KThreadDiedString);
735 TInt err = iArrayAccess.AppendAndGiveOwnership(errorMessage);
747 void CSecondaryThread::ConstructL()
749 * Contructs and kicks off the secondary thread, while also issuing a request to be informed
750 * if the thread dies, which will run the active object
753 CActiveScheduler::Add(this);
754 //Preallocate a shutdown message
755 iShutDownMessage = new(ELeave) CShutDownMessage;
757 StartSecondaryThreadL(EFalse);
760 TInt CSecondaryThread::RunError(TInt /*aError*/)
762 CActiveScheduler::Stop(); //What the hell happened! Shut the server down
766 CSecondaryThread::~CSecondaryThread()
769 delete iShutDownMessage;
770 iSecondaryThread.Close();