/*
* Copyright (c) 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:
*
*/
#include <e32cons.h>
#include <e32base.h>
#include <f32file.h>
#include <c32comm.h>
#include <s32file.h>
#include <pathinfo.h>
#include <s32mem.h>
#include <bautils.h>
#include <sysutil.h>
#include <piprofiler/ProfilerConfig.h>
#include "ProfilerEngine.h"
#include <piprofiler/ProfilerTraces.h>
// properties
const TUid KEngineStatusPropertyCat={0x2001E5AD};
enum TEnginePropertyKeys
{
EProfilerEngineStatus = 8,
EProfilerErrorStatus
};
static _LIT_SECURITY_POLICY_PASS( KAllowAllPolicy );
// CONSTANTS
const TInt KStreamBufferSize = 32768;
const TInt KSavedLineCount = 64;
const TInt KFileNameBufSize = 128;
// Use this UID if plugin belongs to DebOutWriterPlugin:
const TUid KDebOutWriterPluginUid = { 0x2001E5BA };
// Use this UID if plugin belongs to MmcOutWriterPlugin:
const TUid KDiskWriterPluginUid = { 0x2001E5BB };
// LITERALS
_LIT8(KGenericTraceOutput, "output_type");
_LIT8(KGenericTraceFilePrefix, "file_prefix");
_LIT8(KGenericTraceFileSaveDrive, "save_file_location");
_LIT8(KGenericTimedProfilingPeriod, "profiling_period");
_LIT8(KEquals, "=");
_LIT8(KNewLineSeparator, "\n");
_LIT8(KProfilerVersionTag, "version");
_LIT8(KEndMark, "[end]");
_LIT8(KOutputToDebugOutput, "debug_output");
/**
*
* The controller class used to provide the Profiler functions.
* This runs as a server in the engine thread
*
*/
class CPServer : public CServer2, public MProfilerController
{
public:
static MProfilerController* NewL(TInt aPriority, MProfilerEngine& aEngine);
private:
CPServer(TInt aPriority, MProfilerEngine& aEngine);
void Release();
CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
public:
static TInt iClientCount;
};
TInt CPServer::iClientCount = 0;
// The session class used by the server controller
class CPSession : public CSession2
{
private:
inline const CPServer& Server() const;
void ServiceL(const RMessage2& aMessage);
};
/*
*
* CProfiler class implementation
*
*/
// --------------------------------------------------------------------------------------------
CProfiler* CProfiler::NewLC(const TDesC& aSettingsFile, TBool aBootMode)
{
CProfiler* self = new(ELeave) CProfiler(aSettingsFile ,aBootMode);
CleanupStack::PushL(self);
self->ConstructL();
LOGTEXT(_L("CProfiler::NewLC - done"));
return self;
}
// --------------------------------------------------------------------------------------------
CProfiler::CProfiler(const TDesC& aSettingsFile, TBool aBootMode) :
iSettingsFileLocation(aSettingsFile),
iBootMode(aBootMode)
{
// define property for Profiler Engine status, UI may read it for control purposes
if ( RProperty::Define(KEngineStatusPropertyCat,
EProfilerEngineStatus,
RProperty::EInt,
KAllowAllPolicy,
KAllowAllPolicy,
0) != KErrAlreadyExists )
{
LOGTEXT(_L("CProfiler::CProfiler - status property already defined"));
}
if ( RProperty::Define(KEngineStatusPropertyCat,
EProfilerErrorStatus,
RProperty::EInt,
KAllowAllPolicy,
KAllowAllPolicy,
0) != KErrAlreadyExists )
{
LOGTEXT(_L("CProfiler::CProfiler - status property already defined"));
}
// attach to own property
iEngineStatus.Attach(KEngineStatusPropertyCat, EProfilerEngineStatus);
// set status idle
iEngineStatus.Set(KEngineStatusPropertyCat, EProfilerEngineStatus, RProfiler::EIdle);
// attach to own property
iUpdateStatus.Attach(KEngineStatusPropertyCat, EProfilerErrorStatus);
// set status idle
iUpdateStatus.Set(KEngineStatusPropertyCat, EProfilerErrorStatus, EFalse);
}
// --------------------------------------------------------------------------------------------
CProfiler::~CProfiler()
{
LOGTEXT(_L("CProfiler::~CProfiler - Enter"));
// delete error checker
if(iErrorChecker)
{
iErrorChecker->Cancel();
delete iErrorChecker;
iErrorChecker = NULL;
}
// delete settings array
if(iDefaultSamplerAttributesArray)
{
iDefaultSamplerAttributesArray->Reset();
delete iDefaultSamplerAttributesArray;
iDefaultSamplerAttributesArray = NULL;
}
// delete settings file raw line array
if(iSavedLineArray)
{
iSavedLineArray->Reset();
delete iSavedLineArray;
iSavedLineArray = NULL;
}
// delete sampler controller, cleans up the sampler plugin instances
if(iSamplerHandler)
{
delete iSamplerHandler;
iSamplerHandler = NULL;
}
// delete writer controller, cleans up the writer plugin instances
if(iWriterHandler)
{
delete iWriterHandler;
iWriterHandler = NULL;
}
// delete user side sampler stream
if(iUserStream)
{
delete iUserStream;
iUserStream = NULL;
}
// close engine status property
iEngineStatus.Close();
TInt ret = RProperty::Delete(KEngineStatusPropertyCat, EProfilerEngineStatus);
if ( ret != KErrNone)
{
LOGSTRING2("CProfiler::~CProfiler - cannot close status property %d", ret);
}
// close engine update property
iUpdateStatus.Close();
ret = RProperty::Delete(KEngineStatusPropertyCat, EProfilerErrorStatus);
if (ret != KErrNone)
{
LOGSTRING2("CProfiler::~CProfiler - cannot close update property %d",ret);
}
// close server process
if (iServer)
{
LOGTEXT(_L("CProfiler::~CProfiler - Releasing server"));
iServer->Release();
}
if( iTimer )
{
iTimer->Cancel();
delete iTimer;
iTimer = 0;
}
LOGTEXT(_L("CProfiler::~CProfiler - Finished"));
}
// --------------------------------------------------------------------------------------------
void CProfiler::ConstructL()
{
LOGTEXT(_L("CProfiler::ConstructL - Enter"));
TInt err(0);
TLex lex;
if(iBootMode)
{
LOGSTRING("boot mode enabled");
}
else
{
LOGSTRING("normal mode enabled");
}
if ( iSettingsFileLocation.CompareF(KNullDesC) != 0 )
{
lex=(iSettingsFileLocation);
// parse the first command line argument, the command itself
lex.Mark();
lex.SkipCharacters();
if(lex.TokenLength() != 0)
{
// there is another item in the list
TPtrC filename = lex.MarkedToken();
LOGSTRING2("filename %S", &filename);
}
}
else
{
LOGSTRING("CProfiler::ConstructL - Enter - settingsfilelocation null");
}
// create new sampler stream instance
iUserStream = CProfilerSampleStream::NewL(KStreamBufferSize);
if(!iUserStream)
{
LOGTEXT(_L("Profiler engine cannot reserve memory"));
User::Leave(KErrCancel); // operation cancelled
}
// engine status checker
iErrorChecker = CProfilerErrorChecker::NewL();
iErrorChecker->SetObserver(this);
// create and initiate plug-in controller instances
iSamplerHandler = CSamplerController::NewL(*iUserStream, iBootMode);
LOGSTRING("CSamplerController started from ProfilerEngine");
iWriterHandler = CWriterController::NewL(*iUserStream, iBootMode);
LOGSTRING("CWriterController started from ProfilerEngine");
iWriterHandler->InitialiseWriterListL();
LOGSTRING("WriterList initialised started from ProfilerEngine");
// set engine as an observer to sampler controller to get the notification of plugin load has ended
iSamplerHandler->SetObserver(this);
// default settings from sampler plugins, maximum 20 sampler plugins
iDefaultSamplerAttributesArray = new(ELeave) CArrayFixFlat<TSamplerAttributes>(20);
// set profiler status to initializing
iState = RProfiler::EInitializing;
iEngineStatus.Set(RProfiler::EInitializing);
// set default general settings, will be overdriven if changed in settings file
iGeneralAttributes.iTraceOutput.Copy(KDefaultTraceOutput);
iGeneralAttributes.iTraceFilePrefix.Copy(KDefaultTraceFilePrefix);
iGeneralAttributes.iSaveFileDrive.Copy(KDefaultTraceFileSaveDrive);
iGeneralAttributes.iTimedSamplingPeriod = KDefaultTimedSamplingPeriod;
RThread me;
me.SetPriority(EPriorityRealTime);
err = KErrGeneral;
TInt count = 0;
while(err != KErrNone && count < 30)
{
err = User::RenameThread(KProfilerName);
if(err != KErrNone)
{
LOGSTRING2("CProfiler: error renaming the thread, err %d", err);
User::Leave(err);
}
else break;
}
if(err != KErrNone)
{
User::Leave(err);
}
// set settings file loading preferences
iSettingsFileLoaded = EFalse;
// change status property to idle since initialization successfull
iState = RProfiler::EIdle;
if( iEngineStatus.Set((TInt)RProfiler::EIdle) != KErrNone )
{
LOGTEXT(_L("CProfiler::ConstructL - engine status property change failed"));
}
if( iUpdateStatus.Set(EFalse) != KErrNone )
{
LOGTEXT(_L("CProfiler::ConstructL - engine status property change failed"));
}
// create a server instance for clients to communicate with
iServer = CPServer::NewL(10,*this);
// close the handle
me.Close();
iTimer = CProfilerTimer::NewL(CActive::EPriorityStandard, *this);
LOGTEXT(_L("CProfiler::ConstructL - Exit"));
}
CProfilerSampleStream* CProfiler::GetSamplerStream()
{
return iUserStream;
}
void CProfiler::HandleSamplerControllerReadyL()
{
// load settings
// check if settings file already loaded
if(!iSettingsFileLoaded)
{
// load default settings file
LoadSettingsL();
iSettingsFileLoaded = ETrue;
}
LOGSTRING("CProfiler::HandleSamplerControllerReadyL rendezvous");
// notify engine's launcher(UI or PIProfilerLauncher) to continue launch
RProcess::Rendezvous(KErrNone);
}
void CProfiler::NotifyRequesterForSettingsUpdate()
{
// set update status P&S property true => update needed on UI side
iUpdateStatus.Set(ETrue);
}
void CProfiler::HandleProfilerErrorChangeL(TInt aError)
{
LOGSTRING2("CProfiler::HandleProfilerErrorChangeL() - error received, %d", aError);
// check if profiler running
if(iState == RProfiler::ERunning)
{
// stop profiler if error occurred during the trace
iEngineStatus.Set(aError);
// stop samplers, NOTE! Writer plugins not stopped since
iSamplerHandler->StopSamplerPlugins();
// stop debug output plugin and write the rest of the trace data to output
if(iGeneralAttributes.iTraceOutput.CompareF(KOutputToDebugOutput) == 0)
{
// write the rest of trace data only if debug output selected
iWriterHandler->StopSelectedPlugin();
}
LOGSTRING2("CProfiler::HandleProfilerErrorChangeL - sampling stopped, going to state %d", RProfiler::EIdle);
}
}
// ----------------------------------------------------------------------------
// Gets a value from settings file for certain attribute.
// ----------------------------------------------------------------------------
void CProfiler::DoGetValueFromSettingsArray(CDesC8ArrayFlat* aLineArray, const TDesC8& aAttribute, TDes8& aValue)
{
LOGTEXT(_L("CProfiler::DoGetValueFromSettingsArray()"));
_LIT8(KSettingItemSeparator, "=");
// read a line of given array
for (TInt i=0; i<aLineArray->MdcaCount(); i++)
{
// check if this line has a separator
TInt sepPos = aLineArray->MdcaPoint(i).Find(KSettingItemSeparator);
if (sepPos > 0)
{
// check that the element matches
if (aLineArray->MdcaPoint(i).Left(sepPos).CompareF(aAttribute) == 0)
{
// get the value
aValue.Copy(aLineArray->MdcaPoint(i).Right(aLineArray->MdcaPoint(i).Length()-sepPos-1));
break;
}
}
}
}
void CProfiler::DoGetValueFromSettingsArray(CDesC8ArrayFlat* aLineArray, const TDesC8& aAttribute, TInt& aValue)
{
LOGTEXT(_L("CProfiler::DoGetValueFromSettingsFile() TLex"));
_LIT8(KSettingItemSeparator, "=");
// read a line of given array
for (TInt i=0; i<aLineArray->MdcaCount(); i++)
{
// check if this line has a separator
TInt sepPos = aLineArray->MdcaPoint(i).Find(KSettingItemSeparator);
if (sepPos > 0)
{
// check that the element matches
if (aLineArray->MdcaPoint(i).Left(sepPos).CompareF(aAttribute) == 0)
{
// get the value
TLex8 parser(aLineArray->MdcaPoint(i).Right(aLineArray->MdcaPoint(i).Length()-sepPos-1));
parser.Val(aValue);
break;
}
}
}
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::GetSamplerAttributesL(const RMessage2& aMessage)
{
TInt err(KErrNone);
TInt pos(0);
// get sampler count
TInt count(iDefaultSamplerAttributesArray->Count());
// write each of the default sampler plugin setting attributes over client-server session
for (TInt i(0); i<count; ++i)
{
TSamplerAttributes attr = iDefaultSamplerAttributesArray->At(i);
TPckgC<TSamplerAttributes> attrPckg(attr);
// write a TSamplerAttributes container at a time
aMessage.WriteL(0, attrPckg, pos);
pos += attrPckg.Length();
}
aMessage.Complete(err);
return err;
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::SetSamplerAttributesL(const RMessage2& aMessage)
{
TSamplerAttributes attr;
TPckg<TSamplerAttributes> inAttr(attr);
TInt err = aMessage.Read(0, inAttr, 0);
// apply the changes directly to a plugin
iSamplerHandler->SetSamplerSettingsL(attr.iUid, attr);
aMessage.Complete(err);
return err;
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::GetGeneralAttributesL(const RMessage2& aMessage)
{
TPckgBuf<TGeneralAttributes> generalSettings( iGeneralAttributes );
// write general attributes over client-server session
TInt err = aMessage.Write(0, generalSettings);
aMessage.Complete(err);
return err;
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::SetGeneralAttributesL(const RMessage2& aMessage)
{
// read the general settings from message
TGeneralAttributes attr;
TPckg<TGeneralAttributes> inPckg(attr);
TInt err = aMessage.Read(0, inPckg, 0);
// copy to the general attributes
iGeneralAttributes.iSaveFileDrive.Copy(attr.iSaveFileDrive);
iGeneralAttributes.iTraceFilePrefix.Copy(attr.iTraceFilePrefix);
iGeneralAttributes.iTraceOutput.Copy(attr.iTraceOutput);
iGeneralAttributes.iTimedSamplingPeriod = attr.iTimedSamplingPeriod;
aMessage.Complete(err);
return err;
}
TInt CProfiler::GetSamplerAttributeCountL(const RMessage2& aMessage)
{
// get the plugin array count and wrap it to TPckgBuf<>
TPckgBuf<TInt> attributeCount(iDefaultSamplerAttributesArray->Count());
// write general attributes over client-server session
TInt err = aMessage.Write(0, attributeCount);
aMessage.Complete(err);
return err;
}
TInt CProfiler::RefreshStatus(const RMessage2& aMessage)
{
TInt err(KErrNone);
// update profiler status for requester
iEngineStatus.Set(iState);
aMessage.Complete(err);
return err;
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::LoadSettingsL(/*const TDesC& configFile*/)
{
RFs fileServer;
RFile file;
TInt err(KErrNone);
// connect to file server
err = fileServer.Connect();
// check if file server can be connected
if (err != KErrNone)
{
// file server couldn't be connected
return KErrGeneral;
}
// check if settings file location length reasonable
if ( iSettingsFileLocation.CompareF(KNullDesC) == 0 )
{
// open the file with the default path and name
TBuf<256> pathAndName;
pathAndName.Append(PathInfo::PhoneMemoryRootPath());
pathAndName.Append(KProfilerSettingsFileName);
iSettingsFileLocation.Copy(pathAndName);
LOGTEXT(_L("CProfiler::LoadSettings - Opening settings file with name (with the default path)"));
LOGTEXT(pathAndName);
}
// open the file with the given path and name
err = file.Open(fileServer,iSettingsFileLocation,EFileRead);
// check if RFile::Open() returned error
if (err != KErrNone)
{
// file couldn't be opened
LOGTEXT(_L("CProfiler::LoadSettings - Failed to open settings, using default"));
// check if settings already loaded
if(iDefaultSamplerAttributesArray->Count() > 0)
{
// reset default settings array
iDefaultSamplerAttributesArray->Reset();
}
// load default settings, instead of settings file ones
iSamplerHandler->GetSamplerAttributesL(iDefaultSamplerAttributesArray);
fileServer.Close();
return KErrNone;
}
// initialize iSavedLineArray, initial settings file lines 64
if(iSavedLineArray)
{
iSavedLineArray->Reset();
}
else
{
iSavedLineArray = new (ELeave) CDesC8ArrayFlat(KSavedLineCount);
}
iSavedLinesCount = KSavedLineCount;
// get size of the file
TInt fileSize(0);
err = file.Size(fileSize);
// check if an error occurred reading the file size
if(err != KErrNone)
{
return KErrGeneral; // could not find the size
}
// sanity check for the file size
if (fileSize < 3 || fileSize > 20000)
{
fileSize = KSettingsFileSize;
return KErrNotSupported;
}
// read the contents of the file to buffer.
iSettingsBuffer.Zero();
file.Read(iSettingsBuffer, fileSize);
file.Close();
fileServer.Close();
LOGSTRING2("CProfiler::LoadSettings: read %d bytes",iSettingsBuffer.Length());
// append end mark "[end]"
iSettingsBuffer.Append(KEndMark);
// force an ending newline
iSettingsBuffer.Append('\n');
// next fill the saved settings array (CDesC8ArrayFlat) for further comparison with changes and default values
TBuf8<384> tmpBuf;
TInt lineCount(0);
TBool commentFound(EFalse);
for (TInt i(0); i<iSettingsBuffer.Length(); i++) // loop all chars
{
// if new line char found, create a new text line
if (iSettingsBuffer[i]=='\r' || iSettingsBuffer[i]=='\n')
{
// check if collected string has reasonable length
if (tmpBuf.Length() > 0)
{
// remove extra spaces
tmpBuf.TrimAll();
// check if the size of the array too small
if(lineCount >= iSavedLinesCount)
{
iSavedLineArray->ExpandL(20); // expand by 20 lines
iSavedLinesCount += 20;
}
iSavedLineArray->AppendL(tmpBuf);
tmpBuf.Copy(KNullDesC8);
lineCount++;
}
commentFound = EFalse;
}
// check if comment mark ';' is found on the line, skip the rest of the line
else if(iSettingsBuffer[i]==';')
{
commentFound = ETrue;
}
// otherwise append a char to the temp line buffer if it is a wanted ASCII char
else if (iSettingsBuffer[i]>=32 && iSettingsBuffer[i]<=127 && !commentFound)
{
tmpBuf.Append(iSettingsBuffer[i]);
}
}
// empty tmpBuf
tmpBuf.Copy(KNullDesC8);
// check settings file version
DoGetValueFromSettingsArray(iSavedLineArray, KProfilerVersionTag, tmpBuf);
TBuf8<32> version;
version.Copy(PROFILER_VERSION_SHORT);
// check if settings file version is
if(tmpBuf.CompareF(version) >= 0)
{
// update general attributes
UpdateSavedGeneralAttributes(iSavedLineArray);
// update settings to sampler plugins and save the attributes to default array
iSamplerHandler->UpdateSavedSamplerAttributesL(iSavedLineArray, iDefaultSamplerAttributesArray);
}
else
{
// check if settings already loaded
if(iDefaultSamplerAttributesArray)
{
// reset default settings array
iDefaultSamplerAttributesArray->Reset();
// get the default settings if settings file version too old
iSamplerHandler->GetSamplerAttributesL(iDefaultSamplerAttributesArray);
}
}
return err;
}
// --------------------------------------------------------------------------------------------
void CProfiler::UpdateSavedGeneralAttributes(CDesC8ArrayFlat* aSavedAttributes)
{
// get saved general settings
LOGSTRING("CProfiler::UpdateSavedGeneralAttributes");
DoGetValueFromSettingsArray(aSavedAttributes, KGenericTraceOutput, iGeneralAttributes.iTraceOutput);
DoGetValueFromSettingsArray(aSavedAttributes, KGenericTraceFilePrefix, iGeneralAttributes.iTraceFilePrefix);
DoGetValueFromSettingsArray(aSavedAttributes, KGenericTraceFileSaveDrive, iGeneralAttributes.iSaveFileDrive);
DoGetValueFromSettingsArray(aSavedAttributes, KGenericTimedProfilingPeriod, iGeneralAttributes.iTimedSamplingPeriod);
}
TBool CProfiler::CheckLocationSanity(RFs& fs, const TDesC8& aLocation)
{
TBool ret(EFalse);
TBool noDiskSpace(EFalse);
TBuf<32> drive;
CnvUtfConverter::ConvertToUnicodeFromUtf8(drive, aLocation);
TDriveUnit driveUnit = TDriveUnit(drive);
// check that the root folder is correct
if (drive.Length() > 2 && BaflUtils::CheckFolder(fs, drive.Left(3)) == KErrNone)
{
// test available disk space
TRAP_IGNORE((noDiskSpace = SysUtil::DiskSpaceBelowCriticalLevelL(&fs, 0, driveUnit)));
if(!noDiskSpace)
ret = ETrue;
}
return ret;
}
TInt CProfiler::HandleGeneralSettingsChange()
{
// local literals
_LIT8(KBackSlash, "\\");
_LIT8(KTraceFileExtension, ".dat");
TBuf8<KFileNameBufSize> fileNameBuf;
TBuf8<10> number;
TInt result(0);
TInt index(1);
TInt hashLocation(0);
TParse parse;
// check if plugin writer changed
if(iGeneralAttributes.iTraceOutput.CompareF(KOutputToDebugOutput) == 0)
{
iWriterHandler->SetPluginActive( KDebOutWriterPluginUid, EWriterPluginEnabled );
}
else
{
RFs fileServer;
RFile file;
// connect to the file server
result = fileServer.Connect();
if(result == KErrNone)
{
// disk writer plugin will be activated
iWriterHandler->SetPluginActive( KDiskWriterPluginUid, EWriterPluginEnabled );
// fix the trace data file location as well
iTotalPrefix.Zero();
iTotalPrefix.Append(iGeneralAttributes.iSaveFileDrive);
// check that trace file location sane
if(!CProfiler::CheckLocationSanity(fileServer, iTotalPrefix))
{
fileServer.Close();
return KErrPathNotFound;
}
// remove extra spaces
iTotalPrefix.TrimAll();
// check the directory contains a trailing backlash
if(iTotalPrefix.Right(1) != _L8("\\") &&
iTotalPrefix.Right(1) != _L8("/"))
{
// append backslash to end
iTotalPrefix.Append(KBackSlash);
}
// append trace file name prefix e.g. PIProfiler_#
iTotalPrefix.Append(iGeneralAttributes.iTraceFilePrefix);
// locate '#' mark for finding the next free trace file name, e.g. E:\data\PIProfiler_4.dat
hashLocation = iTotalPrefix.Locate('#');
if( hashLocation == KErrNotFound )
{
// append simply at the end of the trace file prefix, no need to inform user
iTotalPrefix.Append('#');
// get new hash mark location
hashLocation = iTotalPrefix.Locate('#');
}
// add the file extension
iTotalPrefix.Append(KTraceFileExtension);
// search for files with different indices
// until a free filename is found
while(result != KErrNotFound)
{
fileNameBuf.Zero();
// start with the original prefix
fileNameBuf.Append(iTotalPrefix);
// convert the number to a descriptor
number.Num(index);
// replace the hashmark with the real number
fileNameBuf.Replace(hashLocation,1,number);
// make a copy to the iFileNameStream descriptor
iFileNameStream.Zero();
CnvUtfConverter::ConvertToUnicodeFromUtf8(iFileNameStream, fileNameBuf);
LOGSTRING2("CProfiler::HandleGeneralSettingsChange() - trying to open files %S ",&iFileNameStream);
if((result = parse.Set(iFileNameStream, NULL, NULL)) != KErrNone)
{
// break loop if fails, problems in file name => change to log into debug output
break;
}
// create directory for trace files if not exists
result = fileServer.MkDirAll(parse.FullName());
// check that file server responded with KErrNone or KErrAlreadyExists
if( result != KErrNone && result != KErrAlreadyExists)
{
// if some other result, e.g. memory full => break
break;
}
// attempt opening the file
result = file.Open(fileServer,parse.FullName(),EFileShareReadersOnly);
if(result != KErrNotFound)
{
if( result != KErrNotReady &&
result != KErrServerBusy )
{
// close the file if it could be opened
LOGSTRING2("Found STREAM file with index %d",index);
index++;
}
else
{
// in boot measurements the file system might not be ready yet.
LOGSTRING2("Problem in opening STREAM file %d",index);
}
file.Close();
}
} // while
}
else
{
// return error code
return result;
}
TUint32 id(iWriterHandler->GetActiveWriter()->GetWriterType());
// check if a file name is one that does not exist and selected plugin is disk writer
if(result == KErrNotFound && id == KDiskWriterPluginUid.iUid)
{
// write right trace data file name to disk writer plugin
iWriterHandler->SetPluginSettings( KDiskWriterPluginUid, iFileNameStream );
}
else
{
// return error if could not create trace log file
return result;
}
// close file server
fileServer.Close();
} // if output == KOutputToDebugOutput
return KErrNone;
}
// --------------------------------------------------------------------------------------------
void CProfiler::HandleTimerExpiresL(TInt aError)
{
LOGSTRING2("CProfiler::HandleTimerExpiresL - Error: %d", aError);
this->ControlL(RProfiler::EStopSampling);
if( CPServer::iClientCount <= 0 )
{
LOGSTRING("CProfiler::HandleTimerExpiresL - No clients attached, shutting down server...");
this->ControlL(RProfiler::EExitProfiler);
}
}
// --------------------------------------------------------------------------------------------
void CProfiler::SaveSettingsL()
{
LOGTEXT(_L("CProfiler::SaveSettings()"));
// local literal
_LIT(KGeneralHeader, "[general]");
_LIT(KVersionHeader, "version");
_LIT8(KPIProfilerSettingsHeader, "; PI Profiler Settings File");
_LIT8(KGeneralSettingsHeader, "; general settings");
_LIT8(KOutputFileDescription,"; \"output_type=file_system\" writes *.dat file to external memory");
_LIT8(KOutputDebugDescription,"; \"output_type=debug_output\" writes *.dat file to debug port");
_LIT8(KOutputFilePrefixDescription,"; if writing to file, prefix of the *.dat file\r\n; first '#' in the prefix is replaced with an integer");
_LIT8(KOutputSaveDriveDescription,"; if writing to file, the location to store the *.dat file");
_LIT8(KTimedProfilingPeriod,"; period (in seconds) used when using timed profiling");
RFs fs;
RFile settingsFile;
TInt err(KErrNone);
TBuf8<384> line;
// connect to file server
err = fs.Connect();
if( err != KErrNone )
{
// failed to write settings to settings file
RDebug::Printf("FS connect failed");
return;
}
// create and set the private path
fs.CreatePrivatePath(EDriveC);
fs.SetSessionToPrivate(EDriveC);
// create the new settings file
err = settingsFile.Replace(fs, iSettingsFileLocation, EFileWrite);
if(err != KErrNone)
{
RDebug::Printf("Settingsfile replace failed");
fs.Close();
return;
}
// write the header
line.Copy(KPIProfilerSettingsHeader);
line.Append(KNewLineSeparator);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// write the header
line.Copy(KGeneralSettingsHeader);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// write all generic settings
line.Copy(KGeneralHeader);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// write version info
line.Copy(KVersionHeader);
line.Append(KEquals);
line.Append(PROFILER_VERSION_SHORT);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// output explanation
line.Copy(KOutputFileDescription);
line.Append(KNewLineSeparator);
line.Append(KOutputDebugDescription);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// write trace output
line.Copy(KGenericTraceOutput);
line.Append(KEquals);
line.Append(iGeneralAttributes.iTraceOutput);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// file prefix explanation
line.Copy(KOutputFilePrefixDescription);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// write trace file prefix
line.Copy(KGenericTraceFilePrefix);
line.Append(KEquals);
line.Append(iGeneralAttributes.iTraceFilePrefix);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// file prefix explanation
line.Copy(KOutputSaveDriveDescription);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// write trace file location
line.Copy(KGenericTraceFileSaveDrive);
line.Append(KEquals);
line.Append(iGeneralAttributes.iSaveFileDrive);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// timed profiling period explanation
line.Copy(KTimedProfilingPeriod);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// Write timed profiling period value
line.Copy(KGenericTimedProfilingPeriod);
line.Append(KEquals);
TBuf<16> tmpNum;
tmpNum.AppendNum(iGeneralAttributes.iTimedSamplingPeriod);
line.Append(tmpNum);
line.Append(KNewLineSeparator);
settingsFile.Write(line);
// reset the default attributes array
iDefaultSamplerAttributesArray->Reset();
// update the latest changes from plugins
iSamplerHandler->GetSamplerAttributesL(iDefaultSamplerAttributesArray);
// call CSamplerController to write all sampler settings
iSamplerHandler->ComposeAttributesToSettingsFileFormat(settingsFile, iDefaultSamplerAttributesArray);
settingsFile.Close();
// close file
fs.Close();
}
TInt CProfiler::CheckOldProfilerRunning()
{
TFindProcess procName;
procName.Find(_L("BappeaProf.exe*"));
TFullName aResult;
TInt err(KErrNone);
RProcess proc;
// now check if old Profiler is still running on
err = procName.Next(aResult);
// check if old profiler process found
if(err == KErrNone)
{
// other process found, i.e. right process to communicate with, in case started from eshell
err = proc.Open(procName);
if(err == KErrNone)
{
if(proc.ExitCategory().Length() > 0)
{
proc.Close();
// process already exited => create a new one
return KErrNotFound;
}
proc.Close();
}
// return error for error handling
return KErrAlreadyExists;
}
return err;
}
// --------------------------------------------------------------------------------------------
void CProfiler::HandleError(TInt aErr)
{
// write error to status property to inform requester
TInt err(iEngineStatus.Set(KEngineStatusPropertyCat, EProfilerEngineStatus, aErr));
if(err != KErrNone)
RDebug::Print(_L("CProfiler::HandleError() - error setting status: %d"), err);
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::ControlDataL(TInt aCommand,TAny* value1,TAny* /*value2*/)
{
LOGSTRING3("CProfiler::ControlData %d, 0x%x",aCommand,value1);
_LIT(KDebugOutput, "debug_output");
_LIT(KFileOutput, "file_system");
_LIT8(KOutputToDebugOutput, "debug_output");
TDes* desc;
TPtrC ptrDesc;
switch(aCommand)
{
// new controls
case RProfiler::EGetFileName:
{
LOGTEXT(_L("Profiler::EGetFileName - start"));
LOGSTRING2("Profiler::EGetFileName - total file name is: %S",(TDes*)value1);
desc = (TDes*)value1;
desc->Zero();
desc->Append(iFileNameStream);
LOGSTRING2("Profiler::EGetFileName - now total file name is: %S",(TDes*)value1);
return KErrNone;
}
case RProfiler::EGetActiveWriter:
{
LOGTEXT(_L("Profiler::EGetActiveWriter - start"));
desc = (TDes*)value1;
desc->Zero();
if(iGeneralAttributes.iTraceOutput.CompareF(KOutputToDebugOutput) == 0)
{
desc->Append(KDebugOutput);
}
else
{
desc->Append(KFileOutput);
}
return KErrNone;
}
}
return KErrNone;
}
// --------------------------------------------------------------------------------------------
TInt CProfiler::ControlL(TInt aCommand)
{
LOGSTRING2("CProfiler::Control - Controlling ProfilerEngine %d",aCommand);
TInt err(KErrNone);
switch (aCommand)
{
case RProfiler::EStartSampling:
case RProfiler::EStartTimedSampling:
{
// check first if old Profiler already running
err = CheckOldProfilerRunning();
if(err == KErrAlreadyExists)
{
// if exists do not start a profiling process since corrupts the collected trace data
HandleError(err);
err = KErrNone;
return err;
}
// save settings before launching the profiler
// reason: the profiling may have set to the background => need for get right settings
SaveSettingsL();
// set the general settings to writer plugins to reflect the latest changes
err = HandleGeneralSettingsChange();
if(err == KErrNone)
{
// reset the buffers before new profiling
iUserStream->ResetBuffers();
// give the CProfilerSampleStream a handle to current writer
iUserStream->SetWriter(*iWriterHandler->GetActiveWriter());
// set initially debug output writer active
err = iWriterHandler->StartSelectedPlugin();
// check that writer plugin started
if(err != KErrNone)
{
// if not started handle error
HandleError(err);
}
else
{
// start activated sampler plug-in, NOTE: plugins check if errors occur in startup for some reason
iSamplerHandler->StartSamplerPluginsL();
// set engine state P&S property to running, e.g. for PIProfiler UI to read
iState = RProfiler::ERunning;
// set the engine into running mode
iEngineStatus.Set(iState);
}
}
else
{
// handle error and notify requester
HandleError(err);
}
if( aCommand == RProfiler::EStartTimedSampling )
{
iTimer->After(iGeneralAttributes.iTimedSamplingPeriod);
LOGTEXT(_L("CProfiler::Control - Finished processing EStartTimedSampling!"));
}
else
{
LOGTEXT(_L("CProfiler::Control - Finished processing EStartSampling!"));
}
return err;
}
case RProfiler::EStopSampling:
LOGTEXT(_L("CProfiler::Control - Starting to stop sampling..."));
// stop sampler plugins
if(iState == RProfiler::ERunning)
{
iState = RProfiler::EStopping;
iEngineStatus.Set(RProfiler::EStopping);
iSamplerHandler->StopSamplerPlugins();
// finalize the filled buffer writing
iUserStream->Finalise();
// stop output plugin and write the rest of the trace data to output
LOGTEXT(_L("CProfiler::Control - stopping writer"));
iWriterHandler->StopSelectedPlugin();
// set engine state P&S property idle
iState = RProfiler::EIdle;
iEngineStatus.Set(RProfiler::EIdle);
LOGSTRING2("CProfiler::Control - sampling stopped, going to state %d", RProfiler::EIdle);
}
return KErrNone;
case RProfiler::EExitProfiler:
{
// save settings into settings file when exiting
SaveSettingsL();
if(iUserStream)
{
delete iUserStream;
iUserStream = NULL;
}
// set engine state P&S property idle
iState = RProfiler::EIdle;
iEngineStatus.Set(RProfiler::EIdle);
LOGTEXT(_L("Stopping Activer Scheduler"));
CActiveScheduler::Stop();
LOGTEXT(_L("Stopped Activer Scheduler"));
return KErrNone;
}
case RProfiler::EAttachClient:
{
// Increase client reference count
++CPServer::iClientCount;
LOGSTRING2("Increased client reference count to: %d", CPServer::iClientCount);
return KErrNone;
}
case RProfiler::ERemoveClient:
{
// Decrease client reference count
--CPServer::iClientCount;
LOGSTRING2("Decreasing client reference count to: %d", CPServer::iClientCount);
return KErrNone;
}
}
LOGTEXT(_L("CProfiler::Control - returning"));
return err;
}
// --------------------------------------------------------------------------------------------
void CProfiler::Finalise()
{
LOGTEXT(_L("CProfiler::Finalise - Finished processing EStopSampling!"));
}
// --------------------------------------------------------------------------------------------
RProfiler::TSamplerState CProfiler::State() const
{
return iState;
}
/*
*
* Class CPServer definition
*
*/
// --------------------------------------------------------------------------------------------
inline const CPServer& CPSession::Server() const
{
return *static_cast<const CPServer*>(CSession2::Server());
}
// --------------------------------------------------------------------------------------------
void CPSession::ServiceL(const RMessage2& aMessage)
{
LOGTEXT(_L("CPSession::ServiceL - Starting to process message"));
TInt err(KErrNone);
if(aMessage.Function() == RProfiler::EGetGeneralAttributes)
{
Server().GetGeneralAttributesL(aMessage);
}
else if(aMessage.Function() == RProfiler::ESetGeneralAttributes)
{
Server().SetGeneralAttributesL(aMessage);
}
else if(aMessage.Function() == RProfiler::EGetSamplerAttributes)
{
Server().GetSamplerAttributesL(aMessage);
}
else if(aMessage.Function() == RProfiler::EGetSamplerAttributeCount)
{
Server().GetSamplerAttributeCountL(aMessage);
}
else if(aMessage.Function() == RProfiler::ESetSamplerAttributes)
{
Server().SetSamplerAttributesL(aMessage);
}
else if(aMessage.Function() == RProfiler::ERefreshProfilerStatus)
{
Server().RefreshStatus(aMessage);
}
else if(aMessage.Ptr0() == 0 && aMessage.Ptr1() == 0 && aMessage.Ptr2() == 0)
{
LOGTEXT(_L("Ptr0 && Ptr1 == 0 && Ptr2 == 0"));
aMessage.Complete(Server().ControlL(RProfiler::TCommand(aMessage.Function())));
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
else if(aMessage.Ptr0() != 0 && aMessage.Ptr1() != 0 && aMessage.Ptr2() != 0)
{
LOGTEXT(_L("Error with message, all pointers contain data!"));
}
else if (aMessage.Ptr0() != 0)
{
if(aMessage.Ptr1() == 0)
{
LOGTEXT(_L("ServiceL: Ptr0 != 0 && Ptr1 == 0"));
// provided value is a descriptor
TBuf<64>* dst = new TBuf<64>;
aMessage.ReadL(0,*dst,0);
err = Server().ControlDataL(aMessage.Function(),(TAny*)dst);
delete dst;
aMessage.Complete(err);
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
else
{
LOGTEXT(_L("ServiceL: Ptr0 != 0 && Ptr1 != 0"));
// provided value is a descriptor
TBuf<64>* dst = new TBuf<64>;
aMessage.ReadL(0,*dst,0);
TUint32 num1 = (TUint32)aMessage.Ptr1();
err = Server().ControlDataL(aMessage.Function(),(TAny*)dst, (TAny*)num1);
delete dst;
aMessage.Complete(err);
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
}
else if (aMessage.Ptr1() != 0)
{
LOGTEXT(_L("ServiceL: Ptr1 != 0"));
// provided value is a TUint32
if( ((TUint32)aMessage.Ptr3()) == 0xffffffff)
{
TUint32 num = (TUint32)aMessage.Ptr1();
err = Server().ControlDataL(aMessage.Function(),(TAny*)num);
aMessage.Complete(err);
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
else
{
LOGTEXT(_L("ServiceL: Ptr3 != 0xffffffff"));
TUint32 num1 = (TUint32)aMessage.Ptr1();
TUint32 num2 = (TUint32)aMessage.Ptr3();
err = Server().ControlDataL(aMessage.Function(),(TAny*)num1,(TAny*)num2);
aMessage.Complete(err);
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
}
else if (aMessage.Ptr2() != 0)
{
// command requests for data, provided
// value should be a descriptor
if( ((TUint32)aMessage.Ptr3()) == 0xffffffff)
{
LOGTEXT(_L("ServiceL: Ptr2 != 0 && Ptr3 == 0xffffffff"));
TBuf<256>* src = new TBuf<256>;
src->Zero();
err = Server().ControlDataL(aMessage.Function(),(TAny*)src);
LOGSTRING2("Got sampler data %S",src);
aMessage.WriteL(2, *src, 0);
delete src;
aMessage.Complete(err);
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
else
{
LOGTEXT(_L("ServiceL: Ptr2 != 0 && Ptr3 != 0xffffffff"));
TUint32 num1 = (TUint32)aMessage.Ptr2(); // containing id
TBuf<256>* buffer = new TBuf<256>; // Text data, e.g. plug-in name or description
LOGSTRING3("Getting data for sampler: 0x%X, buffer max len %d",num1, aMessage.GetDesMaxLength(3));
err = Server().ControlDataL(aMessage.Function(), (TAny*)num1, (TAny*)buffer);
LOGSTRING2("Got sampler data %S",&buffer);
// write back to same parameter
aMessage.WriteL(3, *buffer, 0);
delete buffer;
aMessage.Complete(err);
LOGTEXT(_L("CPSession::ServiceL - Message completed"));
}
}
LOGTEXT(_L("CPSession::ServiceL - Message processed"));
}
// --------------------------------------------------------------------------------------------
MProfilerController* CPServer::NewL(TInt aPriority, MProfilerEngine& aEngine)
{
LOGTEXT(_L("CPServer::NewL - Enter"));
CPServer* self = new(ELeave) CPServer(aPriority, aEngine);
CleanupStack::PushL(self);
self->StartL(KProfilerName);
CleanupStack::Pop();
LOGTEXT(_L("CPServer::NewL - Exit "));
return self;
}
// --------------------------------------------------------------------------------------------
CPServer::CPServer(TInt aPriority, MProfilerEngine& aEngine)
: CServer2(aPriority), MProfilerController(aEngine)
{
}
// --------------------------------------------------------------------------------------------
void CPServer::Release()
{
delete this;
}
// --------------------------------------------------------------------------------------------
CSession2* CPServer::NewSessionL(const TVersion&,const RMessage2&) const
{
return new(ELeave) CPSession();
}
/*
*
* Static methods for controlling the profiler
* through command line
*
*/
// --------------------------------------------------------------------------------------------
static void RunEngineServerL(const TDesC& aSettingsFile, TBool aBoot)
{
LOGSTRING("Profiler: RunEngineServerL() - Install active scheduler");
CActiveScheduler* pS = new CActiveScheduler;
CActiveScheduler::Install(pS);
CProfiler* p = CProfiler::NewLC(aSettingsFile, aBoot);
LOGSTRING("Profiler: RunEngineServerL() - Starting active scheduler");
CActiveScheduler::Start();
p->Finalise();
LOGSTRING("Profiler: RunEngineServerL() - profiler finalised");
CleanupStack::PopAndDestroy(p);
delete pS;
LOGSTRING("Profiler: RunEngineServerL() - done");
}
// --------------------------------------------------------------------------------------------
static TInt TestSettingsFile(const TDesC& configFile)
{
RFs fs;
LOGSTRING2("TestSettingsFile: entry %S", &configFile);
// check if file server can be connected
if (fs.Connect() != KErrNone)
{
LOGTEXT(_L("TestSettingsFile: could not connect file server"));
// file server couldn't be connected, return false
return KErrNotFound;
}
// check if config file name length is > 0
if (configFile.Length() > 0)
{
LOGTEXT(_L("TestSettingsFile: checking location sanity"));
// check sanity of settings file location
if(BaflUtils::CheckFolder(fs, configFile) != KErrNone)
{
LOGTEXT(_L("TestSettingsFile: location sanity check failed"));
fs.Close();
return KErrGeneral;
}
}
else
{
// configFile length 0, return false
LOGTEXT(_L("TestSettingsFile: config file string null length"));
fs.Close();
return KErrNotFound;
}
// return true if tests passed
LOGTEXT(_L("TestSettingsFile: exiting..."));
fs.Close();
return KErrNone;
}
// --------------------------------------------------------------------------------------------
GLDEF_C TInt E32Main()
{
LOGSTRING("Profiler: E32Main()");
// parse command line arguments
TBuf<256> c;
TInt err(KErrNone);
_LIT(KBootMeasurement,"*boot");
TBool myBoot(EFalse);
// copy the full command line with arguments into a buffer
User::CommandLine(c);
// parse command line for boot mode. filename boot
if (c.Match(KBootMeasurement) != KErrNotFound)
{
LOGTEXT(_L("ParseCommandAndExecute Found keyword boot"));
myBoot = ETrue;
}
TBuf<256> temp;
temp.Append(c);
TLex lex(temp);
lex.Mark();
lex.SkipCharacters();
TPtrC token;
TBuf<256> fileName;
if(lex.TokenLength() != 0)
{
TPtrC token = lex.MarkedToken();
LOGSTRING2("E32 Main Token 1 %S",&token);
fileName.Append(token); // only one settings param should be
LOGSTRING3("Filename is #%S#, response %d 1", &fileName, err);
err = TestSettingsFile(fileName);
if(err != KErrNone)
{
LOGSTRING3("Filename is #%S#, response %d 2", &fileName, err);
// settings file does not exist, copy null desc to file name
fileName.Copy(KNullDesC);
}
}
else
{
LOGTEXT(_L("E32 Main Problem 1 in parsing command line"));
}
// if no command line arguments found just start the profiler process
__UHEAP_MARK;
CTrapCleanup* cleanup = CTrapCleanup::New();
TInt ret(KErrNoMemory);
err = KErrNoMemory;
if( cleanup )
{
TRAPD(err ,RunEngineServerL(fileName, myBoot));
LOGSTRING3("Profiler: E32Main() - ret %d err %d", ret,err);
if(err != KErrNone)
{
RDebug::Print(_L("if Profiler: E32Main() - ret %d err %d"), ret,err);
ret = err;
delete cleanup;
}
else
{
LOGSTRING3("else Profiler: E32Main() - ret %d err %d", ret,err);
ret = err;
delete cleanup;
}
}
__UHEAP_MARKEND;
return ret;
}