diff -r 000000000000 -r 08ec8eefde2f traceservices/commsdebugutility/SSVR/comsdbgsvr.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/traceservices/commsdebugutility/SSVR/comsdbgsvr.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,772 @@ +// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// Implements the Flogger server side +// +// + +/** + @file + @internalComponent +*/ + +#include "comsdbgsvr.h" +#include "comsdbgwriter.h" +#include "comsdbgstd.h" +#include "comsdbgmessages.h" + +_LIT8(KOOMErrorString, "#Logs may be lost out of memory!! Further OOM conditions may not be recorded.\r\n"); +_LIT8(KThreadDiedString, "#Something caused the secondary thread in comsdbgutil to die.\r\n"); +_LIT8(KIniFileProblem, "#There is a problem with the ini file\r\n"); +_LIT8(KIniFileUpdate, "#Ini file changes detected and noted.\r\n"); +_LIT(KFloggerSecondaryThreadName, "Comsdbg2"); + + + + + +// +// CFileLoggerServer class definition +// + + +CFileLoggerServer* CFileLoggerServer::NewL() + { + + CFileLoggerServer* r=new(ELeave) CFileLoggerServer(); + CleanupStack::PushL(r); + r->ConstructL(); + r->StartL(KFLoggerServerName); + CleanupStack::Pop(); + return r; + } + +CFileLoggerServer::CFileLoggerServer() + : CServer2(EPriorityNormal,ESharableSessions) + {} + +void CFileLoggerServer::ConstructL() +/** + * + * @note Constructs the secondary thread, passing through a pointer to the log queue. + * Constructs the file parser object and parses the file, attempting to copy the + * file from the ROM if it is not found on C drive. + * Constructs the time beat. + * Performs the first UpdateMedia to ensure a media is set. + */ + { + User::LeaveIfError(iCriticalSection.CreateLocal()); + User::LeaveIfError(iCompletionSemaphore.CreateLocal(0)); + User::LeaveIfError(iFs.Connect()); + +#if defined (__WINS__) + iDebugWriter = CDebugPortProtocol::NewL(); +#endif + + User::LeaveIfError(iLogMessageArray.Append(NULL)); + iPreAllocatedErrorMessage = CLogCommentMessage::NewL(KOOMErrorString); + iArrayHasSpaceForWrite = ETrue; + + TInt err(KErrNone); + TPtrC iniFile(_L("")); + + /* We first check whether KFloggerIniFile (commsdbg.ini) exists, + if it does we use that one, otherwise we use the old KFloggerIniOldFile + (comsdbg.ini) instead (or rather, we try), else copy the default ini file. */ + TFindFile ff(iFs); + + err = ff.FindByDir(KFloggerIniFile, KFloggerIniDir); + + if(err != KErrNone) + { + // KFloggerIniFile was not there, lets use KFloggerIniOldFile + err = ff.FindByDir(KFloggerIniOldFile, KFloggerIniDir); + } + + if (err == KErrNone) + { + // found the ini file to use, hold on to its name and location + iniFile.Set (ff.File ()); + } + else + { + // couldn't find an ini file with the new or old name, lets use ini file in the resource dir + err = ff.FindByDir(KFloggerIniFile, KFloggerIniRscDir); + + // Get the correct system drive letter in a buffer. + TChar sysDriveLetter = RFs::GetSystemDriveChar(); + TBuf<1> sysDriveLetterBuf; + sysDriveLetterBuf.Fill( sysDriveLetter, 1 ); + + // Get the full path to the default ini file including the system drive letter. + TBuf dfltIniFullPath( KFloggerDfltIniPartialPath ); + dfltIniFullPath.Insert( 0, sysDriveLetterBuf ); + + if(err == KErrNone) + { + //only ini file is the default one in z:\resource + CFileMan* fileMan = CFileMan::NewL(iFs); + err = fileMan->Copy(KFloggerIniInROM, dfltIniFullPath, CFileMan::ERecurse); + if (err==KErrNone) + { + err = fileMan->Attribs(dfltIniFullPath,0,KEntryAttReadOnly,TTime(0)); + iniFile.Set (dfltIniFullPath); + } + delete fileMan; + } + else + { + // Get the full path to the old ini file including the system drive letter. + TBuf dfltIniOldIniFullPath( KFloggerIniOldFilePartialPath ); + dfltIniOldIniFullPath.Insert( 0, sysDriveLetterBuf ); + + // watch ini file, even tho it isnt there + // also create another file watched to watch for the old file for backward compatibility + iniFile.Set(dfltIniFullPath); + iIniOldFileWatcher = CIniFileWatcher::NewL(iFs, *this, dfltIniOldIniFullPath); + } + } + + iIniFileParser = CIniFileParser::NewL(iFs); + if (err==KErrNone) //error getting the ini file or setting attributes + { + err=iIniFileParser->ParseIniFile(iniFile); + } + else + { + // no default ini file to copy either, so lets just pretend there is an empty file + err = KErrNone; + } + + // create and set in motion the second thread + // we can only create the second thread thread once we know whether we are running + // synchronous or not. If we still don't know, then we'll force it to false. + // When flogger first starts, the flush setting is not known because the ini file + // parsing may fail. However, once client's start calling this function, we must + // decide whether to flush or not since it is not easy to switch from + // non-flushing to flushing. Thus, instead, the first time this function + // is called, we make a decision + iIniFileParser->FinaliseFlushSetting(); + TBool currentFlushSetting; + iIniFileParser->FlushingOn(currentFlushSetting); + // the second thread needs to have been created before we need to place anything + // into the log queue, since the 2nd thread contains the semaphore we are using + // to count the # of mesgs in the queue. + iSecondaryThread = CSecondaryThread::NewL(*this,currentFlushSetting); + + // update the media regardless of whether the parsing worked + // since we need to get the default set if needs be. + UpdateMediaL(); + + if (err!=KErrNone) //Problem with ini file reading. Stick a message in the log... + { + CLogCommentMessage* message = CLogCommentMessage::NewL(KIniFileProblem); + err = AppendAndGiveOwnership(message); + if (err != KErrNone) + { + delete message; + User::Leave(err); + } + } + + iIniFileWatcher = CIniFileWatcher::NewL(iFs, *this, iniFile); + iTimeManager = CTimeManager::NewL(*this); + } + +void CFileLoggerServer::UpdateMediaL() + { + CMediaUpdateMessage* message = new(ELeave) CMediaUpdateMessage(*iIniFileParser); + CleanupStack::PushL(message); + if (AppendAndGiveOwnership(message)!=KErrNone) //Problem => Leave media as it was. + { + CleanupStack::Pop(message); + delete message; + return; + } + CleanupStack::Pop(message); + } + +CFileLoggerServer::~CFileLoggerServer() + { + delete iSecondaryThread; + delete iTimeManager; + delete iIniFileWatcher; + delete iIniOldFileWatcher; + delete iIniFileParser; + delete iPreAllocatedErrorMessage; + iFs.Close(); + iCriticalSection.Close(); + iCompletionSemaphore.Close(); + iLogMessageArray.ResetAndDestroy(); +#if defined (__WINS__) + delete iDebugWriter; +#endif + } + + +CSession2* CFileLoggerServer::NewSessionL(const TVersion &aVersion ,const RMessage2& /*aMessage*/) const +/** + * Create a new server session. Check that client is using current or older interface and make a new session. + * @note Called by kernel after RFileLogger::DoConnect(). + */ + { + + TVersion v(KFLogSrvMajorVersionNumber,KFLogSrvMinorVersionNumber,KFLogSrvBuildVersionNumber); + if (!User::QueryVersionSupported(v,aVersion)) + { + User::Leave(KErrNotSupported); + } + + return CFileLogSession::NewL(*(const_cast(this)), *iIniFileParser); + } + +void CFileLoggerServer::RePrepareForOOML() +/** + * Called to ensure there is space for the OOM error msg in log queue. + */ + { + if (!iPreAllocatedErrorMessage) + { + iPreAllocatedErrorMessage = CLogCommentMessage::NewL(KOOMErrorString); + } + //Must reserve 1 space in array for error handling or leave. + User::LeaveIfError(iLogMessageArray.Append(NULL)); + //We've got everything we need should we hit an OOM to guarentee we can get an error in the log. + iArrayHasSpaceForWrite=ETrue; + } + +TInt CFileLoggerServer::AppendAndGiveOwnership(CLogMessageBase* aMessage) +/** + * Append a log package to the queue end. + * @note Entry only allowed for primary (producer) thread. + * @note If win32 and debug port logging on, package is written immediately to the debug port as well. + * @note If no space has been allocated for the OOM error msg, then this is done first. + * @note If flushing (synchronous) operation then waits for completion. + @return KErrNoMemory + */ + { + iCriticalSection.Wait(); +#if defined (__WINS__) + if (iIniFileParser->Win32DebugEnabled()) + { + aMessage->Invoke(*iDebugWriter); + } +#endif + TInt err(KErrNone); + if (!iArrayHasSpaceForWrite) //We ran into low memmory a while ago, time to get set up again. + //iArrayHasSpaceForWrite==ETrue <==> 1 free slot in the array + //iArrayHasSpaceForWrite==EFalse <==> 0 free slots in the array + //Above MUST be true or we'll go horribly wrong!! + //RePrepareForOOML must be done in CS since we'll be mucking about with the array. + { + TRAP(err, RePrepareForOOML()); + if (err!=KErrNone) + { + iCriticalSection.Signal(); + return err; + } + } + //If we get to here then we must have 1 space in the array. First try to expand + //then if that works stick the message in the array, otherwise return error. + if ((err = iLogMessageArray.Append(NULL))==KErrNone) + { + const TInt arrayCount(iLogMessageArray.Count()); + iLogMessageArray[arrayCount-2] = aMessage; + iSecondaryThread->SignalRequestSemaphore(); + } + iCriticalSection.Signal(); + + if (err==KErrNone) + { + TBool flushSetting; + iIniFileParser->FlushingOn(flushSetting); + + if (flushSetting == EFlushOn) + { + iCompletionSemaphore.Wait(); // one for each signal of the request semaphore + } + } + return err; + } + +void CFileLoggerServer::PutOOMErrorInLog() + { + //Best attempt to put error in log, if there's no space then we can't + //We don't try to allocate space in the array. The reasons for this are left as + //an excercise to whomever is unfortunate enough to be trying to debug this. + //Basically, this is the only place the last slot of the array can ever be filled, + //so if iArrayHasSpaceForWrite==EFalse we're guaranteed to have got an error in the + //log already earlier on, and since + //in the AppendAndGiveOwnership method we fail if we cant realocate the error message + //we're guarenteed that the last line in the log is and error message already. + if (iArrayHasSpaceForWrite) + { + iCriticalSection.Wait(); + const TInt arrayCount(iLogMessageArray.Count()); + iLogMessageArray[arrayCount-1] = iPreAllocatedErrorMessage; + iPreAllocatedErrorMessage=NULL; + iArrayHasSpaceForWrite=EFalse; + iCriticalSection.Signal(); + iSecondaryThread->SignalRequestSemaphore(); + + TBool flushSetting; + iIniFileParser->FlushingOn(flushSetting); + + if (flushSetting == EFlushOn) + { + iCompletionSemaphore.Wait(); // one for each signal of the request semaphore + } + + } + + } + + +void CFileLoggerServer::GetFirstMessageAndTakeOwnership(CLogMessageBase*& aMessage) +/* + * Remove the message at the head of the log queue. + * @note Entry only allowed for secondary (consumer) thread. + * @note Blocks if no messages in the log queue. + */ + + { + // Wait on the request semaphore, since we are using that to communicate the number of + // outstanding log items in the queue. + User::WaitForAnyRequest(); + iCriticalSection.Wait(); + aMessage = iLogMessageArray[0]; + iLogMessageArray.Remove(0); + iCriticalSection.Signal(); + } + + + +void CFileLoggerServer::SignalCompletionSemaphore() +// signal the completion semaphore. Called by the slave/consumer thread when it is +// done with the current message. Since the slave thread doesn't know if flushing +// is on, it will call this regardless of whether we actually need to signal the +// semphore or not. + { + + TBool flushSetting; + iIniFileParser->FlushingOn(flushSetting); + + if (flushSetting == EFlushOn) + { + iCompletionSemaphore.Signal(); + } + } + + + +TInt CFileLoggerServer::RunError(TInt aError) +/** + * Leave has occured in CFileLogSession::ServiceL. + * Usually this is because the appending of the message to the queue has failed + * due to the queue being filled. + */ + { + PutOOMErrorInLog(); + return CServer2::RunError(aError); + } + + +void CFileLoggerServer::IniFileChanged(TDesC &aIniFile) +/** +Called by the file watcher when the ini file changes. +Any OOM problems are ignored +*/ + { + CLogCommentMessage* message = NULL; + TRAPD(err, message = CLogCommentMessage::NewL(KIniFileUpdate)) + if ((err == KErrNone) && (message)) + { + err = AppendAndGiveOwnership(message); + if (err != KErrNone) + { + delete message; + } + } + err = iIniFileParser->ParseIniFile (aIniFile); + + // update media regardless of success, since we may want to + // set the default media. update media will only leave if memory full. + // in which case we cant output an error msg anyway. + TRAPD(err2, UpdateMediaL()); + + if (err!=KErrNone) //problem parsing ini file, leave settings as they are and carry on. + { + CLogCommentMessage* message = NULL; + TRAP(err, message = CLogCommentMessage::NewL(KIniFileProblem)) + if ((err == KErrNone) && (message)) + { + err = AppendAndGiveOwnership(message); + if (err != KErrNone) + { + delete message; + } + } + return; + } + else if (err2 != KErrNone) + { + // memory full, but cant even output msg to say this + } + + + //Ignore error. Above can only fail due to OOM, so carry on regardless. + CSession2* p=NULL; + iSessionIter.SetToFirst(); + while ((p=iSessionIter++)!=NULL) + { + static_cast(p)->IniFileChanged(); + } + } + +#ifdef _DEBUG +void CFileLoggerServer::__DbgKillTimeManager() + { + delete iTimeManager; + iTimeManager=NULL; + } +#endif //_DEBUG +// +// CFileLogSession class definition +// + +CFileLogSession* CFileLogSession::NewL(MLogArrayAccess& aArrayAccess, const MIniFlushModeAndLogValidQuery& aLogValidQuery) +/** + * Construct new server end of session. + * @note Only called from CFileLoggerServer::NewSessionL() + */ + { + + CFileLogSession* self = new(ELeave) CFileLogSession(aArrayAccess, aLogValidQuery); + return self; + } + +CFileLogSession::CFileLogSession(MLogArrayAccess& aArrayAccess, const MIniFlushModeAndLogValidQuery& aLogValidQuery) + : iArrayAccess(aArrayAccess), iFlushModeLogValidQuery(aLogValidQuery), iLogValid(KLoggingOnOffDefault) + {} + + +CFileLogSession::~CFileLogSession() + { + } + +void CFileLogSession::ServiceL(const RMessage2& aMessage) +/** + * Processes message from client-side (RFileLogger) + * @note Most messages result in logs being added to the queue. If + * synchronous logging is on, this function will wait until the queue is then emptied. + */ + { + RThread clientThread; + TBool flushOn; + iFlushModeLogValidQuery.FlushingOn(flushOn); + + // All of the common & performance-critical requests need the client thread id. Note that RFileLogger + // sessions are shareable; the thread now logging may be other than that which created the session + User::LeaveIfError(aMessage.Client(clientThread)); + iThreadId = clientThread.Id(); + + + + switch(aMessage.Function()) + { + case EStaticWriteToLog: + { + aMessage.ReadL(0, iSubsystem); + aMessage.ReadL(1, iComponent); + if (iFlushModeLogValidQuery.LogValid(iSubsystem, iComponent)) + { + HBufC8* stringOnHeap = HBufC8::NewLC(aMessage.Int3()); + TPtr8 ptrToString(stringOnHeap->Des()); + aMessage.ReadL(2, ptrToString); + if (!flushOn) + { + //We're done with the client now, so we can complete its request. + aMessage.Complete(KErrNone); + } + CLogStringMessage* logMessage = new(ELeave) CLogStringMessage(stringOnHeap, iSubsystem, iComponent, iThreadId); + CleanupStack::Pop(stringOnHeap); + TInt err = iArrayAccess.AppendAndGiveOwnership(logMessage); + if(err != KErrNone) + { + delete logMessage; + User::Leave(err); + } + if (flushOn) + { + aMessage.Complete(KErrNone); + } + } + else + { + //We've done with the client now, so we can complete it's request. + aMessage.Complete(KErrNone); + } + break; + } + case EClearLog: + { + CheckClientHasSetTagsL(aMessage); + CClearLogMessage* clearLogMessage = new(ELeave) CClearLogMessage(clientThread.FullName()); + TInt err = iArrayAccess.AppendAndGiveOwnership(clearLogMessage); + if(err != KErrNone) + { + delete clearLogMessage; + User::Leave(err); + } + aMessage.Complete(KErrNone); + break; + } + case ESetLogTag: + { + aMessage.ReadL(0, iSubsystem); + aMessage.ReadL(1, iComponent); + iSetLogMessage = aMessage; + SetLoggingOnOffInClient(); + aMessage.Complete(KErrNone); + break; + } + case EWriteToLog: + { + CheckClientHasSetTagsL(aMessage); + HBufC8* stringOnHeap = HBufC8::NewLC(aMessage.Int1()); + TPtr8 ptrToString(stringOnHeap->Des()); + aMessage.ReadL(0, ptrToString); + if (!flushOn) + { + //We're done with the client now, so we can complete its request. + aMessage.Complete(KErrNone); + } + CLogStringMessage* logMessage = new(ELeave) CLogStringMessage(stringOnHeap, iSubsystem, iComponent, iThreadId); + CleanupStack::Pop(stringOnHeap);//Above call takes ownership of stringOnHeap + TInt err = iArrayAccess.AppendAndGiveOwnership(logMessage); + if(err != KErrNone) + { + delete logMessage; + User::Leave(err); + } + if (flushOn) + { + aMessage.Complete(KErrNone); + } + + break; + } + case EWriteBinary: + { + CheckClientHasSetTagsL(aMessage); + HBufC8* stringOnHeap = HBufC8::NewLC(aMessage.Int1()); + TPtr8 ptrToString(stringOnHeap->Des()); + aMessage.ReadL(0, ptrToString); + CLogBinaryString* logMessage = new(ELeave) CLogBinaryString(stringOnHeap, iSubsystem, iComponent); + CleanupStack::Pop(stringOnHeap);//Above call takes ownership of stringOnHeap + if (!flushOn) + { + //We're done with the client now, so we can complete its request. + aMessage.Complete(KErrNone); + } + + TInt err = iArrayAccess.AppendAndGiveOwnership(logMessage); + if(err != KErrNone) + { + delete logMessage; + User::Leave(err); + } + if (flushOn) + { + aMessage.Complete(KErrNone); + } + break; + } +#ifdef _DEBUG //These methods are only accessible in debug. + case EShutDownServer: + { + CActiveScheduler::Stop(); + aMessage.Complete(KErrNone); + break; + } + case ESetHeapFailure: + { + //we need to stop the timer otherwise server will + //keep allocing when we're doing heap failure test which makes the test fail. + const_cast(static_cast(Server()))->__DbgKillTimeManager(); + __UHEAP_FAILNEXT(aMessage.Int0()); + aMessage.Complete(KErrNone); + break; + } +#endif //_DEBUG + default: + { + aMessage.Panic(KFloggerServerPanic, EBadMessageFunction); + } + } + clientThread.Close(); + } + +void CFileLogSession::CheckClientHasSetTagsL(const RMessage2& aMessage) +/** + * Ensure for a connection that the client has set the tags, otherwise panic. + * @param aMessage the current client message in progress + * @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. + */ + + { + if (iSetLogMessage.IsNull()) + { + aMessage.Panic(KFloggerPanic, ESetLogTagsNotCalled); //Client ain't called set log tags. + User::Leave(KErrGeneral); + } + + } + +void CFileLogSession::SetLoggingOnOffInClient() + { + const TBool currentLoggingOnOffState = iLogValid; + iLogValid = iFlushModeLogValidQuery.LogValid(iSubsystem, iComponent); + if (currentLoggingOnOffState!=iLogValid) //Logging on/off has changed. Set state in client + { + // ignore the error returned + (void)iSetLogMessage.Write(2, TPckgBuf(iLogValid)); + } + } + +void CFileLogSession::IniFileChanged() + //We only need to update clients that have called SetLogTags + //We don't update clients that are using the static API client side. + { + if (!iSetLogMessage.IsNull()) + { + SetLoggingOnOffInClient(); + } + } + +///////////////////////////////////////////////////////////////// + +CSecondaryThread* CSecondaryThread::NewL(MLogArrayAccess& aArrayAccess, TBool aFlushOn) + { + CSecondaryThread* self = new(ELeave) CSecondaryThread(aArrayAccess,aFlushOn); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CSecondaryThread::CSecondaryThread(MLogArrayAccess& aArrayAccess, TBool aFlushOn) +: CActive(EPriorityHigh), iArrayAccess(aArrayAccess), iFlushingOn(aFlushOn) + {} + +void CSecondaryThread::RunL() +/** + * Runs when second thread dies, and simply restarts it again. + * @note On death a thread-death message is placed in the queue so that + * a user is aware this happened. + * @note StartSecondaryThread issues a request to be informed of thread death, + * so starts active object again. + */ + { + StartSecondaryThreadL(ETrue); + } + +void CSecondaryThread::DoCancel() +/** + * Appends a special shutdown message into the log array. When this reaches + * the head and is run, it shuts down the second thread. + * @note Logs onto second thread and waits for it to finish. + */ + + { + iSecondaryThread.LogonCancel(iStatus); + if (iArrayAccess.AppendAndGiveOwnership(iShutDownMessage)!=KErrNone) + { //Nothing else we can do here. We'll have to just kill the other thread. + iSecondaryThread.Kill(KErrGeneral); + } + else + { + iShutDownMessage = NULL; + TRequestStatus status(KRequestPending); + iSecondaryThread.Logon(status); + User::WaitForRequest(status); + } + } + +void CSecondaryThread::StartSecondaryThreadL(TBool aRestarting) +/** + * Start the second/consumer/slave thread and issue a request to be told when it dies. + */ + { + TRequestStatus stat; + + User::LeaveIfError(iSecondaryThread.Create(KFloggerSecondaryThreadName,CLogManager::ThreadEntryPoint,KDefaultStackSize,NULL,&iArrayAccess)); + + iSecondaryThread.Rendezvous(stat); + + if (iFlushingOn) + { + iSecondaryThread.SetPriority(EPriorityAbsoluteHigh); //was EPriorityMuchMore + } + else + { + iSecondaryThread.SetPriority(EPriorityAbsoluteForeground); // was EPriorityMuchLess + } + + iSecondaryThread.Resume(); + + User::WaitForRequest(stat); + + iSecondaryThread.Logon(iStatus); + + if (aRestarting) + { + CLogCommentMessage* errorMessage = CLogCommentMessage::NewL(KThreadDiedString); + TInt err = iArrayAccess.AppendAndGiveOwnership(errorMessage); + if(err != KErrNone) + { + delete errorMessage; + User::Leave(err); + } + } + + SetActive(); + + } + +void CSecondaryThread::ConstructL() +/** + * Contructs and kicks off the secondary thread, while also issuing a request to be informed + * if the thread dies, which will run the active object + */ + { + CActiveScheduler::Add(this); + //Preallocate a shutdown message + iShutDownMessage = new(ELeave) CShutDownMessage; + + StartSecondaryThreadL(EFalse); + } + +TInt CSecondaryThread::RunError(TInt /*aError*/) + { + CActiveScheduler::Stop(); //What the hell happened! Shut the server down + return KErrNone; + } + +CSecondaryThread::~CSecondaryThread() + { + Cancel(); + delete iShutDownMessage; + iSecondaryThread.Close(); + } +