src/hbcore/core/hbsharedmemorymanager_p.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 17 Sep 2010 08:32:10 +0300
changeset 28 b7da29130b0e
parent 23 e6ad4ef83b23
permissions -rw-r--r--
Revision: 201035 Kit: 201037

/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbCore module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

#include "hbsharedmemorymanager_p.h"
#include "hbsharedmemoryallocators_p.h"
#include "hbmemoryutils_p.h"
#include <QSharedMemory>
#include <QString>
#include <QList>
#include <QDebug>
#ifdef HB_THEME_SERVER_MEMORY_REPORT
#include <QDate>
#include <QDir>
#include <QFile>
#endif
#include "hbsharedcache_p.h"

#ifdef HB_BIN_CSS
#include "hbcssconverterutils_p.h"
#endif // HB_BIN_CSS

#define USE_SUBALLOCATOR

#ifdef HB_THEME_SERVER_FULL_MEMORY_REPORT
// identifier and size are packed in one quint32
static const int allocIdentifier =   0x80000000;
static const int freeIdentifier =    0x40000000;
static const int reallocIdentifier = 0xC0000000;
#endif

HbSharedMemoryManager *HbSharedMemoryManager::memManager = 0;

#if defined(HB_HAVE_PROTECTED_CHUNK) && defined(Q_OS_SYMBIAN)
HbSharedMemoryWrapper::HbSharedMemoryWrapper(const QString &key, QObject *parent) :
    wrapperError(QSharedMemory::NoError),        
    key(key),
    memorySize(0),
    memory(0)
{
    Q_UNUSED(parent);
}

HbSharedMemoryWrapper::~HbSharedMemoryWrapper()
{
    chunk.Close();

    memory = 0;
    memorySize = 0;

}

void HbSharedMemoryWrapper::setErrorString(const QString &function, TInt errorCode)
{
    if (errorCode == KErrNone)
        return;
    switch (errorCode) {
    case KErrAlreadyExists:
        wrapperError = QSharedMemory::AlreadyExists;
        errorString = QSharedMemory::tr("%1: already exists").arg(function);
    break;
    case KErrNotFound:
        wrapperError = QSharedMemory::NotFound;
        errorString = QSharedMemory::tr("%1: doesn't exists").arg(function);
        break;
    case KErrArgument:
        wrapperError = QSharedMemory::InvalidSize;
        errorString = QSharedMemory::tr("%1: invalid size").arg(function);
        break;
    case KErrNoMemory:
        wrapperError = QSharedMemory::OutOfResources;
        errorString = QSharedMemory::tr("%1: out of resources").arg(function);
        break;
    case KErrPermissionDenied:
        wrapperError = QSharedMemory::PermissionDenied;
        errorString = QSharedMemory::tr("%1: permission denied").arg(function);
        break;
    default:
        errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errorCode);
        wrapperError = QSharedMemory::UnknownError;
    }
}

bool HbSharedMemoryWrapper::create(int size, QSharedMemory::AccessMode mode)
{
    Q_UNUSED(mode);
    TPtrC ptr(TPtrC16(static_cast<const TUint16*>(key.utf16()), key.length()));

    TChunkCreateInfo info;
    info.SetReadOnly();
    info.SetGlobal(ptr);
    info.SetNormal(size, size);
    
    //TInt err = chunk.CreateGlobal(ptr, size, size); // Original Qt version
    TInt err = chunk.Create(info);

    QString function = QLatin1String("HbSharedMemoryWrapper::create");    
    setErrorString(function, err);

    if (err != KErrNone)
        return false;

    // Zero out the created chunk
    Mem::FillZ(chunk.Base(), chunk.Size());

    memorySize = chunk.Size();
    memory = chunk.Base();
    
    return true;
}

QSharedMemory::SharedMemoryError HbSharedMemoryWrapper::error() const
{
    return wrapperError;
}

bool HbSharedMemoryWrapper::attach(QSharedMemory::AccessMode mode)
{
    Q_UNUSED(mode);    
    // Grab a pointer to the memory block
    if (!chunk.Handle()) {
        TPtrC ptr(TPtrC16(static_cast<const TUint16*>(key.utf16()), key.length()));        

        TInt err = KErrNoMemory;

        err = chunk.OpenGlobal(ptr, false);

        if (err != KErrNone) {
            QString function = QLatin1String("HbSharedMemoryWrapper::attach");        
            setErrorString(function, err);
            return false;
        }
    }

    memorySize = chunk.Size();
    memory = chunk.Base();

    return true;
}

void *HbSharedMemoryWrapper::data()
{
    return memory;
}

int HbSharedMemoryWrapper::size() const
{
    return memorySize;
}
#else // use QSharedMemory
HbSharedMemoryWrapper::HbSharedMemoryWrapper(const QString &key, QObject *parent)
{
    chunk = new QSharedMemory(key, parent);
}

HbSharedMemoryWrapper::~HbSharedMemoryWrapper()
{
    delete chunk;
    chunk = 0;
}

bool HbSharedMemoryWrapper::create(int size, QSharedMemory::AccessMode mode)
{
    if (chunk) {
        return chunk->create(size, mode);
    }
    return false;
}

QSharedMemory::SharedMemoryError HbSharedMemoryWrapper::error() const
{
    return chunk->error();
}

bool HbSharedMemoryWrapper::attach(QSharedMemory::AccessMode mode)
{
    if (chunk) {
        return chunk->attach(mode);
    }
    return false;
}

void *HbSharedMemoryWrapper::data()
{
    if (chunk) {
        return chunk->data();
    }
    return 0;
}

int HbSharedMemoryWrapper::size() const
{
    if (chunk) {
        return chunk->size();
    }
    return 0;
}
#endif

/* Functions implementation of HbSharedMemoryManager class */

/**
 * HbSharedMemoryManager::initialize
 * 
 * If due to OOM scenario, the initialization of HbSharedMemoryManager fails,
 * it handles the exception and returns false. 
 */
bool HbSharedMemoryManager::initialize()
{
    if (chunk) {
        return true;
    }
    bool success = false;
    chunk = new HbSharedMemoryWrapper(HB_THEME_SHARED_PIXMAP_CHUNK);
    // check if app filename is same as server filename ..
    // ToDo: improve server identification logic.. UID on symbian?
    const QString &appName = HbMemoryUtils::getCleanAppName();
    bool binCSSConverterApp = (appName == BIN_CSS_APP || appName == BIN_CSS_APP_SYMBIAN);
    // Testability support: allowing unit test to write to shared chunk also
    if (appName == THEME_SERVER_NAME || appName == SHARED_MEMORY_MANAGER_UNIT_TEST || binCSSConverterApp) {
        // This is server, create shared memory chunk
        success = chunk->create(CACHE_SIZE, QSharedMemory::ReadWrite);
        // If sharedMemory already exists.
        // (This can happpen if ThemeServer crashed without releasing QSharedMemory)
        if (!success && QSharedMemory::AlreadyExists == chunk->error()) {
            success = chunk->attach(QSharedMemory::ReadWrite);
        }
        writable = true;
    } else {
        // this is not server so just attach to shared memory chunk in ReadOnly mode
        success = chunk->attach(QSharedMemory::ReadOnly);
        writable = false;
    }
    if ( !success ) {
        THEME_GENERIC_DEBUG() << "HbSharedMemoryManager:: Could not initialize shared memory chunk";
        delete chunk; 
        chunk = 0;
    }
    if (success && isWritable()) {
        // if we are recovering from theme server crash, shared chunk may
        // already be ready
        bool enableRecovery = false;
        if (binCSSConverterApp) {
            enableRecovery = false;
        }
        HbSharedChunkHeader *chunkHeader = static_cast<HbSharedChunkHeader*>(chunk->data());
        HbSharedCache *cachePtr = 0;
        if (enableRecovery && chunkHeader->identifier == INITIALIZED_CHUNK_IDENTIFIER) {
            // just reconnect allocators to the shared chunk
            mainAllocator->initialize(chunk, chunkHeader->mainAllocatorOffset);
            subAllocator->initialize(chunk, chunkHeader->subAllocatorOffset, mainAllocator);
        } else {
            memset(chunkHeader, 0, sizeof(HbSharedChunkHeader));
            // Load memory file in the beginning of the chunk first.
            int memoryFileSize = 0;

#ifdef Q_OS_SYMBIAN
            if (!binCSSConverterApp) {
                QString memoryFile("z:/resource/hb/themes/hbdefault.cssbin");
                memoryFileSize = loadMemoryFile(memoryFile);
            }
#endif
            // Put main allocator after the memory file or if memory file was not loaded, after chunk header.
            chunkHeader->mainAllocatorOffset = memoryFileSize ? ALIGN(memoryFileSize)
                                                              : sizeof(HbSharedChunkHeader);
            // Clear also allocator identifier so that they will not try to re-connect
            quint32 *mainAllocatorIdentifier = address<quint32>(chunkHeader->mainAllocatorOffset);
            *mainAllocatorIdentifier = 0;
            mainAllocator->initialize(chunk, chunkHeader->mainAllocatorOffset);
            chunkHeader->subAllocatorOffset = alloc(SPACE_NEEDED_FOR_MULTISEGMENT_ALLOCATOR);
            quint32 *subAllocatorIdentifier = address<quint32>(chunkHeader->subAllocatorOffset);
            *subAllocatorIdentifier = 0;
            subAllocator->initialize(chunk, chunkHeader->subAllocatorOffset, mainAllocator);
            chunkHeader->identifier = INITIALIZED_CHUNK_IDENTIFIER;
            
            if (!binCSSConverterApp) {
                if (memoryFileSize == 0) {
                    cachePtr = createSharedCache(0, 0, 0, -1);
                }
            }
        }
        if (!cachePtr) {
            cachePtr = cache();
        }
        if (cachePtr && !binCSSConverterApp) {
            cachePtr->initServer();
        }

        success = true;
    } else {
        HbSharedCache *cachePtr = cache();
        if (cachePtr) {
            cachePtr->initClient();
        }
    }
    return success;
}

/**
 * HbSharedMemoryManager::alloc
 * 
 * This function throws std::bad_alloc in case of OOM condition, else
 * proper offset in case of a successful memory allocation
 */
qptrdiff HbSharedMemoryManager::alloc(int size)
{
#ifdef HB_THEME_SERVER_MEMORY_REPORT
// some code copied, but much more readable this way
    if (isWritable() && size > 0) {
        int offset = -1;
#ifdef USE_SUBALLOCATOR
        if (size <= MAXIMUM_ALLOC_SIZE_FOR_SUBALLOCATOR) {
            offset = subAllocator->alloc(size);
        } else {
            try{
                // main allocator may throw bad_alloc
                offset = mainAllocator->alloc(size);
            }
            catch(std::bad_alloc &badAlloc){
                Q_UNUSED(badAlloc);
                OOMCount++;
                if (!OOMReportCreated) {
                    OOMReportCreated = true;
                    createReport();
                }
                throw;
            }
        }
#else
        try{
            // main allocator may throw bad_alloc
            offset = mainAllocator->alloc(size);
        }
        catch(std::bad_alloc &badAlloc){
            OOMCount++;
            throw;
        }
#endif
        if (offset == -1) {
            // we ran out of memory
            OOMCount++;
            if (!OOMReportCreated) {
                OOMReportCreated = true;
                createReport();
            }
            throw std::bad_alloc();
        }
    if (allocations.contains(size)) {
        allocations[size].first++;
    } else {
        allocations.insert(size, QPair<quint32, quint32>(1,0));
    }

#else // normal alloc without reporting
    if (isWritable() && size > 0) {
        qptrdiff offset = -1;
#ifdef USE_SUBALLOCATOR
        if (size <= MAXIMUM_ALLOC_SIZE_FOR_SUBALLOCATOR) {
            offset = subAllocator->alloc(size);
        } else {
            offset = mainAllocator->alloc(size);
        }
#else
        offset = mainAllocator->alloc(size);
#endif
        if (offset == -1) {
            // we ran out of memory
            throw std::bad_alloc();
        }
#endif // HB_THEME_SERVER_MEMORY_REPORT

#ifdef HB_THEME_SERVER_FULL_MEMORY_REPORT
        fullAllocationHistory.append(QPair<quint32,quint32>(size | allocIdentifier, offset));
#endif

#ifdef HB_BIN_CSS
        HbCssConverterUtils::cellAllocated(offset, size);
#endif
        return offset;
    } else {
        // memory manager attached in read only mode, cannot allocate memory
        throw std::bad_alloc();
    }
}

/**
 * free
 */
void HbSharedMemoryManager::free(qptrdiff offset)
{
    // don't do anything when freeing NULL (pointer)offset
    if (isWritable() && (offset > 0)) {
        qptrdiff metaData = *address<qptrdiff>(offset - sizeof(qptrdiff));
#ifdef HB_THEME_SERVER_MEMORY_REPORT
        int size = 0;
        if (metaData & MAIN_ALLOCATOR_IDENTIFIER) {
            size = mainAllocator->allocatedSize(offset);
        } else {
            size = subAllocator->allocatedSize(offset);
        }

        if (allocations.contains(size)) {
            allocations[size].second++;
        }
#ifdef HB_THEME_SERVER_FULL_MEMORY_REPORT
        fullAllocationHistory.append(QPair<quint32,quint32>(size | freeIdentifier, offset));
#endif

#endif //HB_THEME_SERVER_MEMORY_REPORT
        if (metaData & MAIN_ALLOCATOR_IDENTIFIER) {
            mainAllocator->free(offset);
        } else {
            subAllocator->free(offset);
        }
#ifdef HB_BIN_CSS
        HbCssConverterUtils::cellFreed(offset);
#endif
    }
}

/**
 * realloc
 * 
 * This function can throw if alloc fails
 */
qptrdiff HbSharedMemoryManager::realloc(qptrdiff offset, int newSize)
{
    qptrdiff newOffset = -1;
    if (isWritable()) {
#ifdef HB_THEME_SERVER_FULL_MEMORY_REPORT
        if (offset > 0) { // if offset == -1, just do normal alloc and not report realloc
            fullAllocationHistory.append(QPair<quint32, quint32>(newSize | reallocIdentifier, offset));
        }
#endif
        newOffset = alloc(newSize);
        int allocatedSize = ALIGN(newSize);
        if (offset > 0) {
#ifdef HB_BIN_CSS
            HbCssConverterUtils::cellMoved(offset, newOffset);
#endif
            unsigned char *scrPtr = address<unsigned char>(offset);
            qptrdiff metaData = *address<qptrdiff>(offset - sizeof(qptrdiff));
            if (metaData & MAIN_ALLOCATOR_IDENTIFIER) {
                int oldSize = mainAllocator->allocatedSize(offset);
                memcpy(address<unsigned char>(newOffset), scrPtr, qMin(oldSize, allocatedSize));
#ifdef HB_THEME_SERVER_MEMORY_REPORT
                free(offset);
#else
                mainAllocator->free(offset);
#endif
            } else {
                int oldSize = subAllocator->allocatedSize(offset);
                memcpy(address<unsigned char>(newOffset), scrPtr, qMin(oldSize, allocatedSize));
#ifdef HB_THEME_SERVER_MEMORY_REPORT
                free(offset);
#else
                subAllocator->free(offset);
#endif
            }
#if HB_BIN_CSS
            // Does not matter if already called when calling free() above.
            HbCssConverterUtils::cellFreed(offset);
#endif
        }
    } else {
        // tried to free non-writable memory???
        throw std::bad_alloc();
    }

    return newOffset;
}

/**
 * base
 */
void *HbSharedMemoryManager::base()
{
    return chunk->data();
}

/**
 * constructor
 */
HbSharedMemoryManager::HbSharedMemoryManager()
    : writable(true),
     mainAllocator(new HbSplayTreeAllocator),
     subAllocator(new HbMultiSegmentAllocator),
     chunk(0)
#ifdef HB_THEME_SERVER_MEMORY_REPORT
     ,totalAllocated(0),
     OOMCount(0),
     OOMReportCreated(false),
     ordinal(1)
#endif
{
}

HbSharedCache *HbSharedMemoryManager::createSharedCache(
    const char *dataArray, int size, int offsetItemCount, int globalParametersOffset, qptrdiff sharedCacheOffset)
{
    HbSharedCache *cache = 0;
    HbSharedChunkHeader *chunkHeader = static_cast<HbSharedChunkHeader*>(chunk->data());
    if (chunkHeader->sharedCacheOffset != 0) {
        free(chunkHeader->sharedCacheOffset);
        chunkHeader->sharedCacheOffset = 0;
    }
    if (!dataArray) {
        size = 0;
    }

    int sharedCacheSize = sizeof(HbSharedCache) + size;
    // If shared cache offset is not given as parameter, alloc it
    if (sharedCacheOffset < 0) {
        sharedCacheOffset = alloc(sharedCacheSize);
    }

    if (sharedCacheOffset >= 0) {
        cache = new (address<char>(sharedCacheOffset)) HbSharedCache();
        cache->setContent(dataArray, size, offsetItemCount, globalParametersOffset);
        chunkHeader->sharedCacheOffset = sharedCacheOffset;
    }
    return cache;
}

int HbSharedMemoryManager::size()
{
    if(mainAllocator) {
        return (static_cast<HbSplayTreeAllocator*>(mainAllocator))->size();
    }
    return -1;
}

HbSharedCache *HbSharedMemoryManager::cache()
{
    HbSharedCache *cachePtr = 0;
    if (chunk) {
        const HbSharedChunkHeader *chunkHeader =
                static_cast<const HbSharedChunkHeader*>(chunk->data());
        cachePtr = address<HbSharedCache>(chunkHeader->sharedCacheOffset);
    }
    return cachePtr;
}

/**
 * destructor
 */
HbSharedMemoryManager::~HbSharedMemoryManager()
{
#ifdef HB_THEME_SERVER_MEMORY_REPORT
    allocations.clear();
#endif
    if (chunk) {
        const HbSharedChunkHeader *chunkHeader =
                static_cast<const HbSharedChunkHeader*>(chunk->data());
        if (chunkHeader->sharedCacheOffset > 0) {
            HbSharedCache *cachePtr = address<HbSharedCache>(chunkHeader->sharedCacheOffset);
            cachePtr->freeResources();
        }
    }
    delete subAllocator;
    delete mainAllocator;
    delete chunk;
}

/**
 * to get instance of HbSharedMemoryManager
 */
HbMemoryManager *HbSharedMemoryManager::instance()
{
    if (!memManager) {
        memManager = new HbSharedMemoryManager();
        if (!memManager->initialize()) {
            THEME_GENERIC_DEBUG() << "HbSharedMemoryManager:Could not initialize shared memory";
            delete memManager;
            memManager = 0;
        }
    }
    return memManager;
}

/**
 * release the HbSharedMemoryManager-instance.
 */
void HbSharedMemoryManager::releaseInstance()
{
    delete memManager;
    memManager = 0;
}

/**
 * gets the free memory reported by main allocator
 */
int HbSharedMemoryManager::freeSharedMemory()
{
    HbSplayTreeAllocator *splayAllocator = static_cast<HbSplayTreeAllocator*>(mainAllocator);
    return splayAllocator->freeBytes();
}

/**
 * gets the allocated memory reported by main allocator
 */
int HbSharedMemoryManager::allocatedSharedMemory()
{
    HbSplayTreeAllocator *splayAllocator = static_cast<HbSplayTreeAllocator*>(mainAllocator);
    return splayAllocator->allocatedBytes();
}

int HbSharedMemoryManager::loadMemoryFile(const QString &filePath)
{
    int loadedSize = 0;
    THEME_CSSBIN_DEBUG() << "loading: " << filePath;
    
    QFile file(filePath);
    if(file.open(QFile::ReadOnly)) {
        qint64 fileSize = file.size();
        file.read(static_cast<char*>(chunk->data()), fileSize);
        file.close();
        loadedSize = (int)fileSize;
    }
    THEME_CSSBIN_DEBUG() << "Loading memory file status: " << (loadedSize > 0 ? "no error" : file.errorString());
    return loadedSize;
}

#ifdef HB_THEME_SERVER_MEMORY_REPORT
bool pairGreaterThan(const QPair<quint32, QPair<quint32, quint32> > &p1,
                     const QPair<quint32, QPair<quint32, quint32> > &p2)
{
     return p1.first > p2.first;
}

void HbSharedMemoryManager::createReport()
{
#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WIN)
    QString filePath("C:\\data\\log\\themeserver\\");
#else
    QString filePath(QDir::tempPath());
    filePath.append("\\data\\log\\themeserver\\");
#endif

    filePath = QDir::toNativeSeparators(filePath);

    QDir dir(filePath);
    if (!dir.exists()) {
        dir.mkpath(filePath);
    }

    filePath.append(QString::number(QDate::currentDate().year()));
    filePath.append("wk");
    filePath.append(QString::number(QDate::currentDate().weekNumber()));
#ifdef HB_THEME_SERVER_FULL_MEMORY_REPORT
    filePath.append("_");
    filePath.append(QString::number(ordinal));
    ordinal++;
#endif
    filePath.append(".txt");

    QFile file(filePath);
    if (!file.open(QFile::WriteOnly | QFile::Append | QFile::Text)) {
        hbWarning() << "Create shared memory report error - can't write to file " << filePath;
        return;
    }

    QTextStream reportWriter(&file);

    reportWriter << "********************************************************************************\n";
    reportWriter << "HbSharedMemoryManager: memory report\n";
    reportWriter << "********************************************************************************\n\n";

    // list for sorting allocations and frees
    QList<QPair<quint32, QPair<quint32, quint32> > > valueList;
    QMap<quint32, QPair<quint32, quint32> >::const_iterator i; // size, <allocated, freed>
    for (i = allocations.constBegin(); i != allocations.constEnd(); ++i) {
        valueList.append(QPair<quint32, QPair<quint32, quint32> >
                         (i.value().first, QPair<quint32, quint32>(i.key(), i.value().second)));
    }
    qSort(valueList.begin(), valueList.end(), pairGreaterThan);

    reportWriter << "Top 30 allocation counts:\n";
    reportWriter << "(allocation sizes for small allocations are chunk sizes from multisegment algorithm, not actual original allocation sizes)\n";
    reportWriter << "times allocated - times released - size\n";
    int count = 0;
    for (int i = 0; i < valueList.size(); i++) {
        if (count > 30) {
            break; // only report top 30 sizes
        }
        reportWriter << valueList.at(i).first << " - "
                     << valueList.at(i).second.second << " - "
                     << valueList.at(i).second.first << "\n";
        count++;
    }
    reportWriter << "\n";

    valueList.clear();
    for (i = allocations.constBegin(); i != allocations.constEnd(); ++i) {
        valueList.append(QPair<quint32, QPair<quint32, quint32> >
                         (i.key(), QPair<quint32, quint32>(i.value().first, i.value().second)));
    }
    qSort(valueList.begin(), valueList.end(), pairGreaterThan);
    reportWriter << "Top 30 allocated sizes:\n";
    reportWriter << "size - times allocated - times released\n";
    count = 0;
    for (int i = 0; i < valueList.size(); i++) {
        if (count > 30) {
            break; // only report top 30 sizes
        }
        reportWriter << valueList.at(i).first << " - "  << valueList.at(i).second.first << " - "
                     << valueList.at(i).second.second << "\n";
        count++;
    }
    reportWriter << "\n";

    mainAllocator->writeReport(reportWriter);
#ifdef USE_SUBALLOCATOR
    subAllocator->writeReport(reportWriter);
#endif

#ifdef HB_THEME_SERVER_FULL_MEMORY_REPORT
    reportWriter << "\n********************* FULL ALLOCATION HISTORY ***********************\n\n";
    for (int i = 0; i < fullAllocationHistory.size(); i++) {
        quint32 size = fullAllocationHistory.at(i).first & 0x3FFFFFFF;
        quint32 identifier = fullAllocationHistory.at(i).first & 0xC0000000;
        quint32 offset = fullAllocationHistory.at(i).second;
        switch (identifier) {
            case allocIdentifier:
                reportWriter << "allocated " << size << " bytes from offset " << offset << "\n";
                break;
            case freeIdentifier:
                reportWriter << "freed " << size << " bytes from offset " << offset << "\n";
                break;
            case reallocIdentifier:
                reportWriter << "reallocation from offset " << offset << " with "
                             << size << " bytes" << "\n";
                i++;
                if (i <= fullAllocationHistory.size()) {
                    size = fullAllocationHistory.at(i).first & 0x3FFFFFFF;
                    identifier = fullAllocationHistory.at(i).first & 0xC0000000;
                    offset = fullAllocationHistory.at(i).second;
                    if (identifier == allocIdentifier) { // should come right after realloc
                        reportWriter << "    from realloc: allocated " << size
                                     << " bytes from offset " << offset << "\n";
                    } else {
                        reportWriter << "ERROR: no alloc after realloc! How is this possible?\n";
                    }
                }
                i++;
                if (i <= fullAllocationHistory.size()) {
                    size = fullAllocationHistory.at(i).first & 0x3FFFFFFF;
                    identifier = fullAllocationHistory.at(i).first & 0xC0000000;
                    offset = fullAllocationHistory.at(i).second;
                    if (identifier == freeIdentifier) { // should come right after realloc and alloc
                        reportWriter << "    from realloc: freed " << size
                                     << " bytes from offset " << offset << "\n";
                    } else {
                        reportWriter << "ERROR: no free after realloc and alloc! How is this possible?\n";
                    }
                }
                break;
            default:
                break;
        }
    }
#endif
    file.close();
    if (file.error()) {
        hbWarning() << "Create shared memory report error - error with file " << filePath;
    }
}
#endif