sysresmonitoring/oommonitor/src/oomlog.cpp
author William Roberts <williamr@symbian.org>
Fri, 23 Apr 2010 14:37:17 +0100
branchRCL_3
changeset 22 c82a39b81a38
parent 4 0fdb7f6b0309
child 62 924385140d98
permissions -rw-r--r--
Rework addition of Symbian splash screen to reduce the source impact (uses SVG from Bug 2414) Notes: by using the OPTION SOURCEDIR parameter in the mifconv extension instructions, I can arrange to use the same source file name in sfimage, without having to export over the original Nokia file. This means that the name inside splashscreen.mbg is the same, which removes the need for the conditional compilation in SplashScreen.cpp, and gets rid of sf_splashscreen.mmp.

/*
* Copyright (c) 2006 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:  Logging functionality for OOM monitor profiling.
*
*/



#ifdef _DEBUG

#include <hal.h>
#include <w32std.h>
#include <e32std.h>
#include <apgwgnam.h>
#include <flogger.h>

#include "oomlog.h"
#include "oompanic.h"

_LIT8(KMemorySampleLoggingString, "%d");
_LIT8(KMemorySampleLoggingSeparator, ", ");

_LIT8(KCrLf, "\r\n");

_LIT8(KOomLogCancel, "Sampling triggered before previous sampling has completed. Results so far: ");

_LIT(KOomLogFile, ":\\logs\\OOM\\liveoommonitor.txt");
_LIT(KOomOldLogFile, ":\\logs\\OOM\\oommonitor.txt");

const TInt KMaxTimeStampSize = 30;
_LIT(KTimeStampFormat, "%F    %H:%T:%S");

_LIT(KDummyWgName, "20");

_LIT8(KUidPreamble, "App UIDs:");
_LIT8(KUidFormat, " 0x%x");

const TInt KPreallocatedSpaceForAppList = 50;

const TInt KMaxUidBufferSize = 1024;

COomLogger* COomLogger::NewL(RWsSession& aWs, RFs& aFs)
    {
    COomLogger* self = new (ELeave) COomLogger(aWs, aFs);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }
    
// Start logging the available memory every n micro seconds
// Firstly a list of the app IDs is written to the log (foreground app first)
// Note that the log is created in memory (to a pre-allocated buffer) and flushed out after it is complete
// the samples are saved in CSV format so that they can easily be cut and pasted to plot graphs etc.
void COomLogger::StartL()
    {
    // If the log file doesn't exist then don't attempt to sample anything
    if (!iIsOpen)
        return;
    
    // If we are already active then cancel first
    if (IsActive())
        Cancel();
    
    iWriteBuffer.Zero();
    
    iStartTime.HomeTime();
    
    // Log the timestamp
    TBuf16<KMaxTimeStampSize> timeStamp;
    iStartTime.FormatL(timeStamp, KTimeStampFormat);
    TBuf8<KMaxTimeStampSize> timeStamp8;
    timeStamp8.Copy(timeStamp);
    Write(timeStamp8);
    
    // Log all of the application IDs (foreground app first, then the other apps moving towards the back)
    LogApplicationIds();

    // Then, record the free memory
    // Note that this is done to a buffer so as not to affect the timing too much
    LogFreeMemory();
    
    // Finally, set a timer to record the memory every n microseconds
    HighRes(KTimeBetweenMemorySamples);
    }

// From CTimer / CActice
void COomLogger::RunL()
    {
    TTime currentTime;
    currentTime.HomeTime();
    TTimeIntervalMicroSeconds loggingDuration = currentTime.MicroSecondsFrom(iStartTime);
    TTimeIntervalMicroSeconds samplingDuration = KSamplingDurationUint;
    if (loggingDuration > samplingDuration)
        // If we have passed the desired logging duration then write the data we have collected
        {
        Write(iWriteBuffer);
        }
    else
        {
        // If we haven't passed the desired logging duration then record the free memory
        // Note that this is recorded into a buffer and then logged later
        iWriteBuffer.Append(KMemorySampleLoggingSeparator);
        LogFreeMemory();
        
        // Wait before taking another memory sample
        HighRes(KTimeBetweenMemorySamples);
        }
    }

void COomLogger::DoCancel()
    {
    CTimer::DoCancel();
    
    Write(KOomLogCancel);
    Write(iWriteBuffer);
    }

COomLogger::~COomLogger()
    {
    iWgIds.Close();
 // delete iWgName; // Not owned
    if (iIsOpen)
        iFile.Close();

    }

void COomLogger::Write(const TDesC8& aBuffer)
    {
    if (iIsOpen)
        {
        iFile.Write(aBuffer);
    
        // Add the line break
        iFile.Write(KCrLf);
        }
    }

void COomLogger::LogApplicationIds()
    {
    // get all window groups, with info about parents
    TInt numGroups = iWs.NumWindowGroups(0);
    TRAPD(err, iWgIds.ReserveL(numGroups));
    
    if (err != KErrNone)
        return;
    
    if (iWs.WindowGroupList(0, &iWgIds) != KErrNone)
        return;

    // Remove all child window groups, promote parents to foremost child position
    ColapseWindowGroupTree();
    
    // Go through each window group ID in the list, get the Uid of each app then log it
    // Start with the foreground application
    TInt index = 0;
    
    TUid uid;
    
    TBuf8<KMaxUidBufferSize> uidBuffer;
    
    uidBuffer = KUidPreamble;
    
    while (index < numGroups)
        {
        __ASSERT_DEBUG(index < iWgIds.Count(), OomMonitorPanic(KWindowGroupArrayIndexOutOfBounds));    
        uid = GetUidFromWindowGroupId(iWgIds[index].iId);
        
        uidBuffer.AppendFormat(KUidFormat, uid.iUid);
        
        index++;
        }
    
    Write(uidBuffer);
    }

void COomLogger::LogFreeMemory()
    {
    TMemoryInfoV1Buf meminfo;
    UserHal::MemoryInfo(meminfo);
    TInt freeMem = meminfo().iFreeRamInBytes;

    // Save the free memory to a descriptor which will be written later
    iWriteBuffer.AppendFormat(KMemorySampleLoggingString(), freeMem);
    }

COomLogger::COomLogger(RWsSession& aWs, RFs& aFs) : CTimer(EPriorityStandard), iWs(aWs), iFs(aFs)
    {
    }

void COomLogger::ConstructL()
    {
    CActiveScheduler::Add(this);
    
    CTimer::ConstructL();
    
    TFileName oldLogFileName;
    TFileName newLogFileName;
    TChar driveChar = iFs.GetSystemDriveChar();
    oldLogFileName.Append(driveChar);
    oldLogFileName.Append(KOomOldLogFile);
    newLogFileName.Append(driveChar);
    newLogFileName.Append(KOomLogFile);
    // If there is an existing log then copy it, this will be the log that can be sent to the PC
    // Without this feature then you can't get the logs off of the device because the "live" log will always be is use.
    CFileMan* fileMan = CFileMan::NewL(iFs);
    CleanupStack::PushL(fileMan);
    fileMan->Copy(newLogFileName, oldLogFileName);
    CleanupStack::PopAndDestroy(fileMan);
    
    // Create the log file, or open it if is already exists (note that the directory must already be present
    TInt err = iFile.Create(iFs, KOomLogFile, EFileShareAny | EFileWrite);
    if (KErrNone != err)
        {
        err = iFile.Open(iFs, KOomLogFile, EFileShareAny | EFileWrite);
        }
    
    if (KErrNone == err)
        {
        iIsOpen = ETrue;
        
        // Append all new data to the end of the file
        TInt offset = 0;
        iFile.Seek(ESeekEnd, offset);
        }
    
    // Reserve enough space to build an app list later.
    iWgIds.ReserveL(KPreallocatedSpaceForAppList);
    // Reserve enough space for CApaWindowGroupName.
    iWgName = CApaWindowGroupName::NewL(iWs);
    iWgNameBuf = HBufC::NewL(CApaWindowGroupName::EMaxLength);
    (*iWgNameBuf) = KDummyWgName;
    iWgName->SetWindowGroupName(iWgNameBuf);    // iWgName takes ownership of iWgNameBuf*/

    }

void COomLogger::ColapseWindowGroupTree()
    {
    // start from the front, wg count can reduce as loop runs
    for (TInt ii=0; ii<iWgIds.Count();)
        {
        RWsSession::TWindowGroupChainInfo& info = iWgIds[ii];
        if (info.iParentId > 0)        // wg has a parent
            {
            // Look for the parent position
            TInt parentPos = ii;        // use child pos as not-found signal
            TInt count = iWgIds.Count();
            for (TInt jj=0; jj<count; jj++)
                {
                if (iWgIds[jj].iId == info.iParentId)
                    {
                    parentPos = jj;
                    break;
                    }
                }

            if (parentPos > ii)  // parent should be moved forward
                {
                iWgIds[ii] = iWgIds[parentPos];
                iWgIds.Remove(parentPos);
                }
            else if (parentPos < ii)  // parent is already ahead of child, remove child
                iWgIds.Remove(ii);
            else                    // parent not found, skip
                ii++;
            }
        else    // wg does not have a parent, skip
            ii++;
        }
    }

TUid COomLogger::GetUidFromWindowGroupId(TInt aWgId)
    {
    // get the app's details
    TPtr wgPtr(iWgNameBuf->Des());
    
    TUid uid;
    
    TInt err = iWs.GetWindowGroupNameFromIdentifier(aWgId, wgPtr);
    
    if (KErrNone != err)
        // If there is an error then set the UID to 0;
        {
        uid.iUid = 0;
        }
    else
        {
        iWgName->SetWindowGroupName(iWgNameBuf);
        uid = iWgName->AppUid(); // This UID comes from the app, not the mmp!
        }
    
    return uid;
    }

#endif //_DEBUG