perfapps/perfmon/engine/src/perfmon_engine.cpp
changeset 51 b048e15729d6
child 52 36d60d12b4af
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/perfapps/perfmon/engine/src/perfmon_engine.cpp	Fri Sep 03 17:11:21 2010 +0300
@@ -0,0 +1,1129 @@
+/*
+ * 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 FILES
+#include "perfmon_engine.h"
+#include "perfmon_powerlistener.h"
+#include <coeutils.h>
+#include <bautils.h>
+#include <eikenv.h>
+#include <e32hal.h>
+#include <u32std.h>
+#include <s32file.h>
+#include <akntitle.h> 
+#include <eikspane.h>
+#include <aknnotewrappers.h>
+_LIT(KDefaultLogFilePath, "c:\\data\\PerfMon.log");
+
+const TInt KCalibrationLength = 2;
+const TInt KMinimumSamplesLength = 16;
+const TInt KCPUTimeMultiplier = 1000000; // used to avoid TReal conversions
+
+const TInt KSettingsDrive = EDriveC;
+_LIT(KSettingsFileName, "perfmon_settings.ini");
+
+// --------------------------------------------------------------------------------------------
+
+TInt CPULoadCount(TAny* aPtr)
+    {
+    TPerfMonNOPCounter& atts = *((TPerfMonNOPCounter*) aPtr);
+
+    // increase the counter
+    atts.iCounterValue++;
+    return 1;
+    }
+
+TInt CPULoadNOPThread(TAny* aPtr)
+    {
+    // set the thread running in correct CPU
+    TPerfMonNOPCounter& atts = *((TPerfMonNOPCounter*) aPtr);
+    
+    if (atts.iAmountOfCPUs > 1)
+        {
+        // request via HAL that the current thread will run in CPU defined in atts.iCPUNumber
+        TInt err = UserSvr::HalFunction(EHalGroupKernel, 19 /*EKernelHalLockThreadToCpu*/, (TAny*) atts.iCPUNumber, 0);
+
+        if (err != KErrNone)
+            {
+            // error, stop this thread
+            return err;
+            }
+        }
+
+    // init trap clean and install active scheduler
+    CTrapCleanup* pC = CTrapCleanup::New();
+    CActiveScheduler* pS = new CActiveScheduler;
+    CActiveScheduler::Install(pS);
+
+    // call the counter to simulate polling the null thread for cpu time
+    CIdle* idle = CIdle::NewL(CActive::EPriorityStandard);
+    TCallBack cb(CPULoadCount, aPtr);
+    idle->Start(cb);
+
+    pS->Start();
+
+    delete idle;
+    delete pS;
+    delete pC;
+
+    return KErrNone;
+    }
+
+// ===================================== MEMBER FUNCTIONS =====================================
+
+CPerfMonEngine::CPerfMonEngine() :
+    CActive(EPriorityUserInput),
+    iPowerClient(0)
+    {
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::ConstructL()
+    {
+    iCurrentCPUMode = ECPUModeNotSet;
+    iLogFileInitialized = EFalse;
+    iAmountOfCPUs = 1;
+
+    iEnv = CEikonEnv::Static();
+    User::LeaveIfError(iLs.Connect());
+
+    User::LeaveIfError(iTimer.CreateLocal());
+    CActiveScheduler::Add(this);
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::ActivateEngineL()
+    {
+    // load settings
+    TRAP_IGNORE(LoadSettingsL());
+
+    // create data storages for the samples
+    CreateSamplesDataArrayL();
+
+    // set default modes
+    HandleSettingsChangeL();
+
+    // start sampling data immediately (jump to RunL)    
+    iTimer.After(iStatus, 100);
+    SetActive();
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::DeActivateEngineL()
+    {
+    Cancel();
+
+    DeActivatePowerMonitoring();
+    DeActivateCPUMonitoring();
+
+    // close log file
+    OpenLogFile(EFalse);
+    }
+
+// --------------------------------------------------------------------------------------------
+
+CPerfMonEngine::~CPerfMonEngine()
+    {
+    DeActivatePowerMonitoring();
+
+    if (iPowerClient)
+        {
+        delete iPowerClient;
+        iPowerClient = 0;
+        }
+
+    iTimer.Close();
+
+    // clear data storages
+    if (iSampleEntryArray)
+        {
+        for (TInt i = 0; i < iSampleEntryArray->Count(); i++)
+            {
+            delete iSampleEntryArray->At(i).iSampleDataArray;
+            }
+
+        delete iSampleEntryArray;
+        }
+
+    iLs.Close();
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::DoCancel()
+    {
+    iTimer.Cancel();
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::RunL()
+    {
+    // calculate new values 
+    UpdateSamplesDataL();
+
+    // log changes
+    AppendLatestSamplesToLogsL();
+
+    // redraw views
+    SendDrawEventToContainersL();
+
+    // continue
+    iTimer.After(iStatus, iSettings.iHeartBeat * 1000); // convert from milliseconds to microseconds
+    SetActive();
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::HandleSettingsChangeL()
+    {
+    // set priority of the thread
+    RThread().SetPriority(SettingItemToThreadPriority(iSettings.iPriority));
+
+    // init cpu monitor if setting has been changed
+    if (iCurrentCPUMode != iSettings.iCPUMode)
+        {
+        DeActivateCPUMonitoring();
+        ActivateCPUMonitoringL();
+        }
+
+    // Check for power setting changes every time
+    if (iSettings.iPowerMonitoringEnabled)
+        {
+        ActivatePowerMonitoringL();
+        }
+    else
+        {
+        DeActivatePowerMonitoring();
+        }
+
+    // close log file
+    OpenLogFile(EFalse);
+
+    // enable log file
+    if (iSettings.iLoggingEnabled && (iSettings.iLoggingMode
+            == ELoggingModeLogFile || iSettings.iLoggingMode
+            == ELoggingModeRDebugLogFile))
+        OpenLogFile(ETrue);
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::EnableLogging(TBool aEnable)
+    {
+    if (aEnable)
+        {
+        if (iSettings.iLoggingMode == ELoggingModeLogFile
+                || iSettings.iLoggingMode == ELoggingModeRDebugLogFile)
+            OpenLogFile(ETrue);
+
+        iSettings.iLoggingEnabled = ETrue;
+        }
+    else // disable
+        {
+        iSettings.iLoggingEnabled = EFalse;
+        OpenLogFile(EFalse);
+        }
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::OpenLogFile(TBool aOpen)
+    {
+    // open log file for writing
+    if (aOpen)
+        {
+        if (!iLogFileInitialized)
+            {
+            TInt err(KErrNone);
+
+            // open the log file for writing
+            if (iLogFile.Open(iEnv->FsSession(), iSettings.iLoggingFilePath,
+                    EFileWrite) != KErrNone)
+                {
+                iEnv->FsSession().MkDirAll(iSettings.iLoggingFilePath);
+                err = iLogFile.Replace(iEnv->FsSession(),
+                        iSettings.iLoggingFilePath, EFileWrite);
+                }
+            else
+                {
+                // file opens correctly, seek to the end
+                TInt fileSize = 0;
+                iLogFile.Size(fileSize);
+                err = iLogFile.Seek(ESeekCurrent, fileSize);
+                }
+
+            if (err == KErrNone)
+                {
+                iLogFileInitialized = ETrue;
+                }
+            else
+                {
+                // show error
+                CAknErrorNote* note = new (ELeave) CAknErrorNote();
+                note->ExecuteLD(_L("Unable to create log file, check settings"));
+                }
+            }
+        }
+
+    // close handle to log file
+    else
+        {
+        if (iLogFileInitialized)
+            {
+            iLogFile.Flush();
+            iLogFile.Close();
+
+            iLogFileInitialized = EFalse;
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::ActivateCPUMonitoringL()
+    {
+    // reset counter variables
+    iCPULoadCalibrating = ETrue;
+    iCPULoadCalibrationCounter = 0;
+    iCPULoadMaxValue = 999999999;
+
+    for (TInt i=0; i<KMaxCPUs; i++)
+        {
+        iCPULoadPreviousValue[i] = 1;
+        }
+
+    // use null thread is cpu time is supported and the setting is on
+    if (CPUTimeSupported() && iSettings.iCPUMode == ECPUModeCPUTime)
+        {
+        // try to open handle to null thread
+        if (OpenHandleToNullThread())
+            {
+            // handle is open, get initial value for each CPU
+            for (TInt i=0; i<iAmountOfCPUs; i++)
+                {
+                TTimeIntervalMicroSeconds time;
+                iNullThreads[i].GetCpuTime(time);
+                iCPULoadPreviousValue[i] = time.Int64();
+                }
+
+            iPreviousTime.HomeTime();
+
+            iCurrentCPUMode = ECPUModeCPUTime;
+            return; // cpu time is succesfully in use
+            }
+        }
+
+    // otherwise use normal sampling with nops
+    iCurrentCPUMode = ECPUModeNotSet;
+
+    // show a warning if cpu time cannot be taken in use
+    if (iSettings.iCPUMode == ECPUModeCPUTime)
+        {
+        CAknInformationNote* note = new (ELeave) CAknInformationNote();
+        note->ExecuteLD(
+                _L("CPU Time not supported in this system, using NOPs sampling"));
+        }
+
+    // get the amount of CPUs
+    iAmountOfCPUs = GetAmountOfCPUs();
+
+    // create a thread for CPU load monitoring
+    for (TInt i=0; i<iAmountOfCPUs; i++)
+        {
+        // initialize the NOP counter variables
+        iCPULoadNOPCounter[i].iCounterValue = 0;
+        iCPULoadNOPCounter[i].iCPUNumber = i;
+        iCPULoadNOPCounter[i].iAmountOfCPUs = iAmountOfCPUs;
+
+        // create a name for the thread
+        TFileName cpuLoadThreadName;
+        cpuLoadThreadName.Copy(_L("PerfMonCPULoadNOPThread"));
+        cpuLoadThreadName.AppendNum(i);
+        
+        // create the thread and resume it
+        User::LeaveIfError(iCPULoadNOPThread[i].Create(cpuLoadThreadName,
+                CPULoadNOPThread, 0x1000, 0x1000, 0x100000, &iCPULoadNOPCounter[i]));
+        iCPULoadNOPThread[i].SetPriority(EPriorityLess);
+        iCPULoadNOPThread[i].Resume();
+        }
+
+    iCurrentCPUMode = ECPUModeNOPs; // NOPs taken succesfully in use
+    }
+
+// --------------------------------------------------------------------------------------------
+
+TBool CPerfMonEngine::OpenHandleToNullThread()
+    {
+    // find the kernel process and then the null thread
+    TFindProcess fp(_L("ekern.exe*"));
+
+    TFullName kernelName;
+    if (fp.Next(kernelName) == KErrNone)
+        {
+        iAmountOfCPUs = 0;
+
+        // find all null threads in the system by following the order ie "Null", "Null1", "Null2", "Null3"
+        for (TInt i(0); i<KMaxCPUs; i++)
+            {
+            // construct name of the null thread from the process name
+            TFullName nullThreadName(kernelName);
+            nullThreadName.Append(_L("::Null"));
+            
+            if (i >= 1) // add identifier for CPUs higher than zero
+                nullThreadName.AppendNum(i);
+
+            TFindThread ft(nullThreadName);
+            TFullName threadName;
+
+            if (ft.Next(threadName) == KErrNone)
+                {
+                if (iNullThreads[i].Open(threadName) != KErrNone)
+                    return EFalse;
+                
+                iAmountOfCPUs++;
+                }
+            else
+                {
+                break;  // break the loop, no more matches can be found
+                }
+            }
+        }
+
+    // process not found
+    else
+        return EFalse;
+
+    // success!
+    return ETrue;
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::DeActivateCPUMonitoring()
+    {
+    if (iCurrentCPUMode == ECPUModeCPUTime)
+        {
+        // close handles to null threads
+        for (TInt i=0;i<iAmountOfCPUs;i++)
+            {
+            iNullThreads[i].Close();
+            }
+        }
+
+    else if (iCurrentCPUMode == ECPUModeNOPs)
+        {
+        // kill the cpu load thread
+        for (TInt i=0;i<iAmountOfCPUs;i++)
+            {
+            iCPULoadNOPThread[i].Kill(0);
+            iCPULoadNOPThread[i].Close();
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------------------------
+
+TBool CPerfMonEngine::CPUTimeSupported()
+    {
+    TTimeIntervalMicroSeconds time;
+    TInt err = RThread().GetCpuTime(time);
+
+    if (err == KErrNone && time.Int64() > 0)
+        return ETrue;
+    else
+        return EFalse;
+    }
+
+// --------------------------------------------------------------------------------------------
+
+TInt CPerfMonEngine::GetAmountOfCPUs()
+    {
+    // query from HAL the amount of CPUs and then check it returns a valid value
+    TInt amountOfCPUs = UserSvr::HalFunction(EHalGroupKernel, 16 /*EKernelHalNumLogicalCpus*/, 0, 0);
+    
+    if (amountOfCPUs >= 1 && amountOfCPUs <= KMaxCPUs)
+        return amountOfCPUs;
+    else
+        return 1; // HAL may not support this feature, so just return one CPU
+    }
+
+// --------------------------------------------------------------------------------------------
+
+TThreadPriority CPerfMonEngine::SettingItemToThreadPriority(TInt aIndex)
+    {
+    TThreadPriority threadPriority = EPriorityNull;
+
+    switch (aIndex)
+        {
+        case EThreadPriorityTypeMuchLess:
+            {
+            threadPriority = EPriorityMuchLess;
+            break;
+            }
+        case EThreadPriorityTypeLess:
+            {
+            threadPriority = EPriorityLess;
+            break;
+            }
+        case EThreadPriorityTypeNormal:
+            {
+            threadPriority = EPriorityNormal;
+            break;
+            }
+        case EThreadPriorityTypeMore:
+            {
+            threadPriority = EPriorityMore;
+            break;
+            }
+        case EThreadPriorityTypeMuchMore:
+            {
+            threadPriority = EPriorityMuchMore;
+            break;
+            }
+        case EThreadPriorityTypeRealTime:
+            {
+            threadPriority = EPriorityRealTime;
+            break;
+            }
+        case EThreadPriorityTypeAbsoluteVeryLow:
+            {
+            threadPriority = EPriorityAbsoluteVeryLow;
+            break;
+            }
+        case EThreadPriorityTypeAbsoluteLow:
+            {
+            threadPriority = EPriorityAbsoluteLow;
+            break;
+            }
+        case EThreadPriorityTypeAbsoluteBackground:
+            {
+            threadPriority = EPriorityAbsoluteBackground;
+            break;
+            }
+        case EThreadPriorityTypeAbsoluteForeground:
+            {
+            threadPriority = EPriorityAbsoluteForeground;
+            break;
+            }
+        case EThreadPriorityTypeAbsoluteHigh:
+            {
+            threadPriority = EPriorityAbsoluteHigh;
+            break;
+            }
+
+        default:
+            {
+            User::Panic(_L("Wrong tp index"), 276);
+            break;
+            }
+        }
+
+    return threadPriority;
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::CreateSamplesDataArrayL()
+    {
+    TInt maxSamples =
+            iSettings.iMaxSamples >= KMinimumSamplesLength ? iSettings.iMaxSamples
+                    : KMinimumSamplesLength;
+
+    // create the data structure to store all samples
+    iSampleEntryArray = new (ELeave) CSampleEntryArray(16);
+
+    // set sample data array positions for each type
+    iCPU0PositionInSamples = 0;
+    iRAMPositionInSamples = KMaxCPUs;
+    iCDrivePositionInSamples = KMaxCPUs+1;
+    iPowerPositionInSamples = iCDrivePositionInSamples + ( ESourceI - ESourceC ) + 1;
+
+    // add all CPU source entries
+    for (TInt i=0; i<KMaxCPUs; i++)
+        {
+        TSampleEntry newSampleEntry;
+
+        newSampleEntry.iDescription.Copy(_L("CPU"));
+        newSampleEntry.iDescription.AppendNum(i);
+
+        newSampleEntry.iUnitTypeShort.Copy(KNullDesC);
+        newSampleEntry.iUnitTypeLong.Copy(KNullDesC);
+        newSampleEntry.iDriveNumber = -1;
+        newSampleEntry.iGraphColor = KRgbYellow;
+        newSampleEntry.iGraphColor.SetGreen(255-i*75);
+        newSampleEntry.iGraphColor.SetBlue(i*75);
+
+        newSampleEntry.iSampleDataArray = new(ELeave) CSampleDataArray(maxSamples);
+        iSampleEntryArray->AppendL(newSampleEntry);
+        }
+
+    // add RAM entry
+    {
+    TSampleEntry newSampleEntry;
+
+    newSampleEntry.iDescription.Copy(_L("RAM"));
+    newSampleEntry.iUnitTypeShort.Copy(_L("b"));
+    newSampleEntry.iUnitTypeLong.Copy(_L("bytes"));
+    newSampleEntry.iDriveNumber = -1;
+    newSampleEntry.iGraphColor = KRgbGreen;
+
+    newSampleEntry.iSampleDataArray = new(ELeave) CSampleDataArray(maxSamples);
+    iSampleEntryArray->AppendL(newSampleEntry);
+    }
+
+    // add all disk drives
+    for (TInt i=0; i<ESourceI-ESourceC+1; i++)
+        {
+        TSampleEntry newSampleEntry;
+
+        TChar driveLetter = 'C' + i; // C is the first drive
+
+        newSampleEntry.iDescription.Append(driveLetter);
+        newSampleEntry.iDescription.Append(_L(":"));
+        newSampleEntry.iUnitTypeShort.Copy(_L("b"));
+        newSampleEntry.iUnitTypeLong.Copy(_L("bytes"));
+
+        iEnv->FsSession().CharToDrive(driveLetter,
+                newSampleEntry.iDriveNumber);
+
+        newSampleEntry.iGraphColor = KRgbCyan;
+        newSampleEntry.iGraphColor.SetGreen(255 - (i - ESourceC) * 30);
+        newSampleEntry.iGraphColor.SetRed(i * 30);
+
+        newSampleEntry.iSampleDataArray = new (ELeave) CSampleDataArray(
+                maxSamples);
+
+        iSampleEntryArray->AppendL(newSampleEntry);
+        }
+
+    // add power entry
+    {
+    TSampleEntry newSampleEntry;
+
+    newSampleEntry.iDescription.Copy(_L("Power"));
+    newSampleEntry.iUnitTypeShort.Copy(_L("mW"));
+    newSampleEntry.iUnitTypeLong.Copy(_L("milliwatts"));
+    newSampleEntry.iDriveNumber = -1;
+    newSampleEntry.iGraphColor = KRgbRed;
+
+    newSampleEntry.iSampleDataArray = new(ELeave) CSampleDataArray(maxSamples);
+    iSampleEntryArray->AppendL(newSampleEntry);
+    }
+
+    // save current time as start time
+    iStartTime.HomeTime();
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::UpdateSamplesDataL()
+    {
+    // reset inactivity timers
+    if (iSettings.iKeepBacklightOn)
+        User::ResetInactivityTime();
+
+    // get current time
+    TTime currentTime;
+    currentTime.HomeTime();
+
+    // calculate time difference
+    TTimeIntervalMicroSeconds timeDeltaFromPreviousSample =
+            currentTime.MicroSecondsFrom(iPreviousTime);
+
+    // remember current time as previous
+    iPreviousTime = currentTime;
+
+    // get CPU load value for each CPU
+    for (TInt i=0;i<iAmountOfCPUs;i++)
+        {
+        TInt64 cpuLoadDelta(0);
+        TInt64 cpuLoadFree(0);
+        TInt64 currentCPUValue(0);
+        
+        if (iCurrentCPUMode == ECPUModeCPUTime || iCurrentCPUMode == ECPUModeNOPs)
+            {
+            if (iCurrentCPUMode == ECPUModeCPUTime)
+                {
+                // get CPU time of null thread
+                TTimeIntervalMicroSeconds time;
+                iNullThreads[i].GetCpuTime(time);
+                currentCPUValue = time.Int64();
+                }
+            else if (iCurrentCPUMode == ECPUModeNOPs)
+                {
+                // get CPU time from sampler
+                currentCPUValue = iCPULoadNOPCounter[i].iCounterValue;
+                }
+
+            // get delta and store the previous value
+            cpuLoadDelta = currentCPUValue - iCPULoadPreviousValue[i];
+            iCPULoadPreviousValue[i] = currentCPUValue;
+            
+            // velocity = distance / time
+            cpuLoadFree = cpuLoadDelta * KCPUTimeMultiplier / timeDeltaFromPreviousSample.Int64();
+
+            // detect maximum value (this is common to all CPUs)
+            if (cpuLoadFree > iCPULoadMaxValue)
+                {
+                iCPULoadMaxValue = cpuLoadFree;
+                }
+            }
+
+        // save cpu sample data
+        TSampleData cpuSample;
+        cpuSample.iFree = cpuLoadFree;
+        cpuSample.iSize = iCPULoadCalibrating ? cpuLoadFree : iCPULoadMaxValue;  // if calibrating, size==free, otherwise store the correct size value
+        cpuSample.iTimeFromStart = currentTime.MicroSecondsFrom(iStartTime);
+
+        iSampleEntryArray->At(i).iSampleDataArray->InsertL(0, cpuSample);
+        }
+        
+    // check calibration status, the calibration will be only done against CPU0
+    if (iCPULoadCalibrating)
+        {
+        iCPULoadCalibrationCounter++;
+        TInt64 cpuLoadSize = iSampleEntryArray->At(iCPU0PositionInSamples).iSampleDataArray->At(0).iSize;
+        
+        // check if need to calibrate anymore
+        if (iCPULoadCalibrationCounter > KCalibrationLength)
+            {
+            iCPULoadCalibrating = EFalse;
+            
+            // from the samples, get the minimum value, and let it be the max value
+            for (TInt i=0; i<iSampleEntryArray->At(0).iSampleDataArray->Count(); i++)
+                {
+                TInt64 newCPULoadMaxValue = iCPULoadMaxValue;
+                
+                if (iSampleEntryArray->At(0).iSampleDataArray->At(i).iFree < newCPULoadMaxValue)
+                    {
+                    newCPULoadMaxValue = iSampleEntryArray->At(0).iSampleDataArray->At(i).iFree;
+                    }
+                
+                iCPULoadMaxValue = newCPULoadMaxValue;
+                }
+            
+            // adjust priority of the poller thread
+            if (iCurrentCPUMode == ECPUModeNOPs)
+                {
+                for (TInt i=0; i<iAmountOfCPUs; i++)
+                    {
+                    iCPULoadNOPThread[i].SetPriority(EPriorityAbsoluteVeryLow);
+                    }
+                }
+
+            }
+        }
+
+    // get ram memory
+    TMemoryInfoV1Buf ramMemory;
+    UserHal::MemoryInfo(ramMemory);
+
+    TSampleData memorySample;
+    memorySample.iFree = ramMemory().iFreeRamInBytes;
+    memorySample.iSize = ramMemory().iMaxFreeRamInBytes;
+    memorySample.iTimeFromStart = currentTime.MicroSecondsFrom(iStartTime);
+
+    iSampleEntryArray->At(iRAMPositionInSamples).iSampleDataArray->InsertL(0, memorySample);
+    
+    // all drives
+    for (TInt i = iCDrivePositionInSamples; i < iPowerPositionInSamples; i++)
+        {
+        TSampleData driveSample;
+
+        // get volume info from RFs
+        TVolumeInfo volumeInfo;
+        if (iEnv->FsSession().Volume(volumeInfo,
+                iSampleEntryArray->At(i).iDriveNumber) == KErrNone)
+            {
+            driveSample.iFree = volumeInfo.iFree;
+            driveSample.iSize = volumeInfo.iSize;
+            }
+        else
+            {
+            driveSample.iFree = 0;
+            driveSample.iSize = 0;
+            }
+
+        driveSample.iTimeFromStart = currentTime.MicroSecondsFrom(iStartTime);
+
+        iSampleEntryArray->At(i).iSampleDataArray->InsertL(0, driveSample);
+        }
+
+    // Power data
+    TSampleData powerSample;
+    if (iSettings.iPowerMonitoringEnabled)
+        {
+        // Values in milliwatts
+        powerSample.iFree = ( iPowerClient->GetMaxPower() - iPowerClient->GetPower() ) / 1000;
+        powerSample.iSize = iPowerClient->GetMaxPower() / 1000;
+        }
+    else
+        {
+        powerSample.iFree = 0;
+        powerSample.iSize = 0;
+        }
+
+    powerSample.iTimeFromStart = currentTime.MicroSecondsFrom(iStartTime);
+
+    iSampleEntryArray->At(iPowerPositionInSamples).iSampleDataArray->InsertL(0, powerSample);
+
+    // compress sample data arrays to save memory
+    TInt curLength(iSampleEntryArray->At(0).iSampleDataArray->Count());
+
+    TInt maxSamples =
+            iSettings.iMaxSamples >= KMinimumSamplesLength ? iSettings.iMaxSamples
+                    : KMinimumSamplesLength;
+
+    if (curLength > maxSamples && curLength % 5 == 0)
+        {
+        for (TInt i = 0; i < iSampleEntryArray->Count(); i++)
+            {
+            if (SampleEntryArray()->At(i).iSampleDataArray->Count() > 0)
+                {
+                iSampleEntryArray->At(i).iSampleDataArray->ResizeL(maxSamples); // looses old samples
+                iSampleEntryArray->At(i).iSampleDataArray->Compress();
+                }
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::AppendLatestSamplesToLogsL()
+    {
+    if (iSettings.iLoggingEnabled && SampleEntryArray())
+        {
+        // loop all sources
+        for (TInt i = 0; i < SampleEntryArray()->Count(); i++)
+            {
+            // check if this setting has been enabled and it has some data
+            if (iSettings.iLoggingSources.iSrcEnabled[SampleEntryPosToSettingPos(i)]
+                    && SampleEntryArray()->At(i).iSampleDataArray->Count() > 0)
+                {
+                // get current sample
+                TSampleData& currentSample =
+                        SampleEntryArray()->At(i).iSampleDataArray->At(0);
+
+                TBuf<128> buf;
+                buf.Append(_L("PERFMON;"));
+                buf.Append(SampleEntryArray()->At(i).iDescription);
+                buf.Append(_L(";"));
+                buf.AppendNum(currentSample.iTimeFromStart.Int64());
+                buf.Append(_L(";"));
+                buf.AppendNum(currentSample.iFree);
+                buf.Append(_L(";"));
+                buf.AppendNum(currentSample.iSize);
+
+                // print to RDebug
+                if (iSettings.iLoggingMode == ELoggingModeRDebug
+                        || iSettings.iLoggingMode == ELoggingModeRDebugLogFile)
+                    {
+                    RDebug::Print(buf);
+                    }
+
+                // print to log file
+                if (iSettings.iLoggingMode == ELoggingModeLogFile
+                        || iSettings.iLoggingMode == ELoggingModeRDebugLogFile)
+                    {
+                    buf.Append(_L("\r\n"));
+
+                    TBuf8<128> buf8;
+                    buf8.Copy(buf);
+
+                    iLogFile.Write(buf8);
+                    }
+                }
+            }
+        }
+    }
+
+void CPerfMonEngine::LoadSettingsL()
+    {
+    // set defaults
+    iSettings.iHeartBeat = 600;
+    iSettings.iMaxSamples = 64;
+    iSettings.iPriority = EThreadPriorityTypeNormal;
+    iSettings.iCPUMode = ECPUModeCPUTime;
+    iSettings.iKeepBacklightOn = ETrue;
+
+    iSettings.iDataPopupVisibility = EDataPopupVisbilityAlwaysOn;
+    iSettings.iDataPopupLocation = EDataPopupLocationTopRight;
+    iSettings.iDataPopupSources.SetDefaults1();
+
+    iSettings.iGraphsVerticalBarPeriod = 5;
+    iSettings.iGraphsSources.SetDefaults2();
+
+    iSettings.iLoggingMode = ELoggingModeRDebug;
+    iSettings.iLoggingFilePath.Copy(KDefaultLogFilePath);
+    iSettings.iLoggingSources.SetDefaults2();
+
+    iSettings.iLoggingEnabled = EFalse;
+
+    iSettings.iPowerMonitoringEnabled = ETrue;
+
+    // make sure that the private path of this app in c-drive exists
+    iEnv->FsSession().CreatePrivatePath(KSettingsDrive); // c:\\private\\20011385\\
+    
+    // handle settings always in the private directory 
+    if (iEnv->FsSession().SetSessionToPrivate(KSettingsDrive) == KErrNone)
+        {
+        const TUid KUidPerfMon =
+            {
+            0x20011385
+            };
+        // open or create a dictionary file store
+        CDictionaryFileStore* settingsStore = CDictionaryFileStore::OpenLC(
+                iEnv->FsSession(), KSettingsFileName, KUidPerfMon);
+
+        LoadDFSValueL(settingsStore, KPMSettingHeartBeat, iSettings.iHeartBeat);
+        LoadDFSValueL(settingsStore, KPMSettingMaxSamples,
+                iSettings.iMaxSamples);
+        LoadDFSValueL(settingsStore, KPMSettingPriority, iSettings.iPriority);
+        
+        LoadDFSValueL(settingsStore, KPMSettingCPUMode, iSettings.iCPUMode);
+        
+        LoadDFSValueL(settingsStore, KPMSettingKeepBackLightOn,
+                iSettings.iKeepBacklightOn);
+
+        LoadDFSValueL(settingsStore, KPMSettingDataPopupVisbility,
+                iSettings.iDataPopupVisibility);
+        LoadDFSValueL(settingsStore, KPMSettingDataPopupLocation,
+                iSettings.iDataPopupLocation);
+        LoadDFSValueL(settingsStore, KPMSettingDataPopupSources,
+                iSettings.iDataPopupSources);
+
+        LoadDFSValueL(settingsStore, KPMSettingGraphsVerticalBarPeriod,
+                iSettings.iGraphsVerticalBarPeriod);
+        LoadDFSValueL(settingsStore, KPMSettingGraphsSources,
+                iSettings.iGraphsSources);
+
+        LoadDFSValueL(settingsStore, KPMSettingLoggingMode,
+                iSettings.iLoggingMode);
+        LoadDFSValueL(settingsStore, KPMSettingLoggingFilePath,
+                iSettings.iLoggingFilePath);
+        LoadDFSValueL(settingsStore, KPMSettingLoggingSources,
+                iSettings.iLoggingSources);
+
+        LoadDFSValueL(settingsStore, KPMSettingPowerMonitoringEnabled,
+                iSettings.iPowerMonitoringEnabled);
+
+        CleanupStack::PopAndDestroy(); // settingsStore
+        }
+    }
+
+// --------------------------------------------------------------------------------------------
+
+void CPerfMonEngine::SaveSettingsL()
+    {
+    // handle settings always in c:\\private\\20011385\\ 
+    if (iEnv->FsSession().SetSessionToPrivate( KSettingsDrive ) == KErrNone)
+        {
+        // delete existing store to make sure that it is clean and not eg corrupted
+        if (BaflUtils::FileExists(iEnv->FsSession(), KSettingsFileName))
+            {
+            iEnv->FsSession().Delete(KSettingsFileName);
+            }
+        const TUid KUidPerfMon =
+            {
+            0x20011385
+            };
+        // create a dictionary file store
+        CDictionaryFileStore* settingsStore = CDictionaryFileStore::OpenLC(
+                iEnv->FsSession(), KSettingsFileName, KUidPerfMon);
+
+        SaveDFSValueL(settingsStore, KPMSettingHeartBeat, iSettings.iHeartBeat);
+        SaveDFSValueL(settingsStore, KPMSettingMaxSamples,
+                iSettings.iMaxSamples);
+        SaveDFSValueL(settingsStore, KPMSettingPriority, iSettings.iPriority);
+        SaveDFSValueL(settingsStore, KPMSettingCPUMode, iSettings.iCPUMode);
+        SaveDFSValueL(settingsStore, KPMSettingKeepBackLightOn,
+                iSettings.iKeepBacklightOn);
+
+        SaveDFSValueL(settingsStore, KPMSettingDataPopupVisbility,
+                iSettings.iDataPopupVisibility);
+        SaveDFSValueL(settingsStore, KPMSettingDataPopupLocation,
+                iSettings.iDataPopupLocation);
+        SaveDFSValueL(settingsStore, KPMSettingDataPopupSources,
+                iSettings.iDataPopupSources);
+
+        SaveDFSValueL(settingsStore, KPMSettingGraphsVerticalBarPeriod,
+                iSettings.iGraphsVerticalBarPeriod);
+        SaveDFSValueL(settingsStore, KPMSettingGraphsSources,
+                iSettings.iGraphsSources);
+
+        SaveDFSValueL(settingsStore, KPMSettingLoggingMode,
+                iSettings.iLoggingMode);
+        SaveDFSValueL(settingsStore, KPMSettingLoggingFilePath,
+                iSettings.iLoggingFilePath);
+        SaveDFSValueL(settingsStore, KPMSettingLoggingSources,
+                iSettings.iLoggingSources);
+
+        SaveDFSValueL(settingsStore, KPMSettingPowerMonitoringEnabled,
+                iSettings.iPowerMonitoringEnabled);
+
+        settingsStore->CommitL();
+        CleanupStack::PopAndDestroy(); // settingsStore
+        }
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::LoadDFSValueL(CDictionaryFileStore* aDicFS,
+        const TUid& aUid, TInt& aValue)
+    {
+    if (aDicFS->IsPresentL(aUid))
+        {
+        RDictionaryReadStream in;
+        in.OpenLC(*aDicFS, aUid);
+        aValue = in.ReadInt16L();
+        CleanupStack::PopAndDestroy(); // in        
+        }
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::LoadDFSValueL(CDictionaryFileStore* aDicFS,
+        const TUid& aUid, TDes& aValue)
+    {
+    if (aDicFS->IsPresentL(aUid))
+        {
+        RDictionaryReadStream in;
+        in.OpenLC(*aDicFS, aUid);
+        TInt bufLength = in.ReadInt16L(); // get length of descriptor
+        in.ReadL(aValue, bufLength); // get the descriptor itself
+        CleanupStack::PopAndDestroy(); // in
+        }
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::LoadDFSValueL(CDictionaryFileStore* aDicFS,
+        const TUid& aUid, TPerfMonSources& aValue)
+    {
+    if (aDicFS->IsPresentL(aUid))
+        {
+        RDictionaryReadStream in;
+        in.OpenLC(*aDicFS, aUid);
+        TInt bufLength = in.ReadInt16L(); // get length of the array
+
+        if (bufLength < 0 || bufLength > ESourcesLength) // check for validaty
+            User::Leave(KErrNotSupported);
+
+        for (TInt i = 0; i < bufLength; i++) // get all items
+            aValue.iSrcEnabled[i] = in.ReadInt16L();
+
+        CleanupStack::PopAndDestroy(); // in
+        }
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::SaveDFSValueL(CDictionaryFileStore* aDicFS,
+        const TUid& aUid, const TInt& aValue)
+    {
+    RDictionaryWriteStream out;
+    out.AssignLC(*aDicFS, aUid);
+    out.WriteInt16L(aValue);
+    out.CommitL();
+    CleanupStack::PopAndDestroy(); // out
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::SaveDFSValueL(CDictionaryFileStore* aDicFS,
+        const TUid& aUid, const TDes& aValue)
+    {
+    RDictionaryWriteStream out;
+    out.AssignLC(*aDicFS, aUid);
+    out.WriteInt16L(aValue.Length()); // write length of the descriptor
+    out.WriteL(aValue, aValue.Length()); // write the descriptor itself
+    out.CommitL();
+    CleanupStack::PopAndDestroy(); // out
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::SaveDFSValueL(CDictionaryFileStore* aDicFS,
+        const TUid& aUid, const TPerfMonSources& aValue)
+    {
+    RDictionaryWriteStream out;
+    out.AssignLC(*aDicFS, aUid);
+
+    out.WriteInt16L(ESourcesLength); // write length of the array
+
+    for (TInt i = 0; i < ESourcesLength; i++) // write all items
+        out.WriteInt16L(aValue.iSrcEnabled[i]);
+
+    out.CommitL();
+    CleanupStack::PopAndDestroy(); // out
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::ActivatePowerMonitoringL()
+    {
+    if (!iPowerClient)
+        {
+        iPowerClient = CPerfMonPowerListener::NewL();
+        }
+
+    // disable power monitoring if initialization fails
+    TInt err = iPowerClient->Activate();
+    if (err != KErrNone )
+        {
+        DeActivatePowerMonitoring();
+
+        iSettings.iPowerMonitoringEnabled = EFalse;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+
+void CPerfMonEngine::DeActivatePowerMonitoring()
+    {
+    if (iPowerClient)
+        {
+        iPowerClient->DeActivate();
+        }
+    }
+
+// ---------------------------------------------------------------------------
+
+TInt CPerfMonEngine::SampleEntryPosToSettingPos(TInt aSampleEntryPos)
+    {
+    TInt settingPos(0); // return position of aSampleEntryPos in settings
+    
+    if (aSampleEntryPos >= iCPU0PositionInSamples && aSampleEntryPos < iRAMPositionInSamples)
+        {
+        settingPos = ESourceCPU;
+        }
+    else if (aSampleEntryPos == iRAMPositionInSamples)
+        {
+        settingPos = ESourceRAM;
+        }
+    else
+        {
+        settingPos = ESourceC + (aSampleEntryPos-iCDrivePositionInSamples);
+        }
+    
+    return settingPos;
+    }
+
+// End of File