~aleteoryx/muditaos

e77b5d67028e0dfd132328b4da2d1e5a73320b4e — Dawid Wojtas 3 years ago 0b46a92
[MOS-424] Improvement of logger module

Due to losing bytes the logger has a worker
which is responsible for dumping logs to
the file. The logger also has its own timer
to dump logs every 15 minutes. EventManager
is not responsible for interval dumping logs
now.
M module-bsp/board/rt1051/os/_exit.cpp => module-bsp/board/rt1051/os/_exit.cpp +1 -1
@@ 44,7 44,7 @@
static void __attribute__((noreturn)) stop_system(void)
{
    if (!isIRQ()) {
        if (dumpLogs() != 1) {
        if (shutdownFlushLogs() != 1) {
            LOG_ERROR("Cannot dump logs");
        }
        const auto err = purefs::subsystem::unmount_all();

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +2 -14
@@ 49,25 49,14 @@
#define debug_input_events(...)
#endif

namespace
{
    constexpr auto loggerDelayMs   = 1000 * 60 * 5;
    constexpr auto loggerTimerName = "Logger";
} // namespace

EventManagerCommon::EventManagerCommon(LogDumpFunction logDumpFunction, const std::string &name)
    : sys::Service(name, "", stackDepth), loggerTimer{sys::TimerFactory::createPeriodicTimer(
                                              this,
                                              loggerTimerName,
                                              std::chrono::milliseconds{loggerDelayMs},
                                              [this](sys::Timer & /*timer*/) { dumpLogsToFile(); })},
      logDumpFunction(logDumpFunction), settings(std::make_shared<settings::Settings>())
    : sys::Service(name, "", stackDepth), logDumpFunction(std::move(logDumpFunction)),
      settings(std::make_shared<settings::Settings>())
{
    LOG_INFO("[%s] Initializing", name.c_str());
    alarmTimestamp = 0;
    alarmID        = 0;
    bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
    loggerTimer.start();
}

EventManagerCommon::~EventManagerCommon()


@@ 277,7 266,6 @@ int EventManagerCommon::dumpLogsToFile()
    if (logDumpFunction) {
        return logDumpFunction();
    }

    return 0;
}


M module-services/service-evtmgr/service-evtmgr/EventManagerCommon.hpp => module-services/service-evtmgr/service-evtmgr/EventManagerCommon.hpp +0 -2
@@ 56,8 56,6 @@ class EventManagerCommon : public sys::Service
    void processRTCFromTimestampRequest(time_t &newTime);
    void processTimezoneRequest(const std::string &timezone);

    sys::TimerHandle loggerTimer;

    LogDumpFunction logDumpFunction;

    /// @return: < 0 - error occured during log flush

M module-sys/SystemManager/SystemManagerCommon.cpp => module-sys/SystemManager/SystemManagerCommon.cpp +5 -1
@@ 10,7 10,7 @@
#include "thread.hpp"
#include "ticks.hpp"
#include "critical.hpp"
#include <algorithm>
#include "Logger.hpp"
#include <service-evtmgr/KbdMessage.hpp>
#include <service-evtmgr/BatteryMessages.hpp>
#include <service-evtmgr/Constants.hpp>


@@ 34,6 34,8 @@
#include <module-gui/gui/Common.hpp>
#include <service-eink/Common.hpp>

#include <algorithm>

const inline size_t systemManagerStack = 4096 * 2;

namespace sys


@@ 115,6 117,8 @@ namespace sys
            this, "lowBatteryShutdownDelay", lowBatteryShutdownDelayTime, [this](sys::Timer &) {
                CloseSystemHandler(CloseReason::LowBattery);
            });

        Log::Logger::get().createTimer(this);
    }

    SystemManagerCommon::~SystemManagerCommon()

M module-utils/log/CMakeLists.txt => module-utils/log/CMakeLists.txt +3 -0
@@ 9,6 9,7 @@ target_sources(log
        Logger.cpp
        log.cpp
        LoggerBuffer.cpp
        LoggerWorker.cpp
        StringCircularBuffer.cpp
)



@@ 17,10 18,12 @@ target_include_directories(log PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(log
    PRIVATE
        utility
        purefs-paths
    PUBLIC
        module-os
        log-api
        utils-rotator    
        sys-service
)

if (${ENABLE_TESTS})

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +63 -24
@@ 1,20 1,21 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "critical.hpp"
#include <fstream>
#include "Logger.hpp"
#include "LockGuard.hpp"
#include <Logger.hpp>
#include <Utils.hpp>
#include <portmacro.h>
#include <ticks.hpp>
#include "macros.h"
#include <purefs/filesystem_paths.hpp>

#include <fstream>

namespace Log
{
    const char *Logger::levelNames[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
    Logger *Logger::_logger          = nullptr;

    using namespace std::chrono_literals;
    constexpr std::chrono::minutes writeLogsToFileInterval = 15min;

    std::ostream &operator<<(std::ostream &stream, const Application &application)
    {
        stream << application.name << ' ' << application.revision << ", " << application.tag << ", "


@@ 22,7 23,7 @@ namespace Log
        return stream;
    }

    Logger::Logger() : circularBuffer{circularBufferSize}, rotator{".log"}
    Logger::Logger() : buffer{}, rotator{".log"}
    {
        filtered = {
            {"ApplicationManager", logger_level::LOGINFO},


@@ 42,6 43,12 @@ namespace Log
            {"FileIndexer", logger_level::LOGINFO},
            {"EventManager", logger_level::LOGINFO}
        };

        std::list<sys::WorkerQueueInfo> queueInfo{
            {LoggerWorker::SignalQueueName, LoggerWorker::SignalSize, LoggerWorker::SignalQueueLenght}};
        worker = std::make_unique<LoggerWorker>(Log::workerName);
        worker->init(queueInfo);
        worker->run();
    }

    void Logger::enableColors(bool enable)


@@ 58,6 65,7 @@ namespace Log

    void Logger::destroyInstance()
    {

        delete _logger;
        _logger = nullptr;
    }


@@ 79,8 87,8 @@ namespace Log
        LockGuard lock(mutex);

        std::string logs;
        while (!circularBuffer.isEmpty()) {
            const auto [result, msg] = circularBuffer.get();
        while (!buffer.getFlushBuffer()->isEmpty()) {
            const auto [result, msg] = buffer.getFlushBuffer()->get();
            if (result) {
                logs += msg;
            }


@@ 99,23 107,21 @@ namespace Log
#endif
    }

    void Logger::createTimer(sys::Service *parent)
    {
        writeLogsTimer = sys::TimerFactory::createPeriodicTimer(
            parent, "writeLogsToFileTimer", writeLogsToFileInterval, [this](sys::Timer &) {
                buffer.nextBuffer();
                worker->notify(LoggerWorker::Signal::DumpIntervalBuffer);
            });
        writeLogsTimer.start();
    }

    auto Logger::log(Device device, const char *fmt, va_list args) -> int
    {
        LockGuard lock(mutex);

        loggerBufferCurrentPos = 0;

        const auto sizeLeft = loggerBufferSizeLeft();
        const auto result   = vsnprintf(&loggerBuffer[loggerBufferCurrentPos], sizeLeft, fmt, args);
        if (0 <= result) {
            const auto numOfBytesAddedToBuffer = static_cast<size_t>(result);
            loggerBufferCurrentPos += (numOfBytesAddedToBuffer < sizeLeft) ? numOfBytesAddedToBuffer : (sizeLeft - 1);

            logToDevice(device, loggerBuffer, loggerBufferCurrentPos);
            circularBuffer.put(std::string(loggerBuffer, loggerBufferCurrentPos));
            return loggerBufferCurrentPos;
        }
        return -1;
        return writeLog(device, fmt, args);
    }

    auto Logger::log(


@@ 125,10 131,13 @@ namespace Log
            return -1;
        }
        LockGuard lock(mutex);

        loggerBufferCurrentPos = 0;
        addLogHeader(level, file, line, function);
        return writeLog(Device::DEFAULT, fmt, args);
    }

    auto Logger::writeLog(Device device, const char *fmt, va_list args) -> int
    {
        const auto sizeLeft = loggerBufferSizeLeft();
        const auto result   = vsnprintf(&loggerBuffer[loggerBufferCurrentPos], sizeLeft, fmt, args);
        if (0 <= result) {


@@ 137,7 146,8 @@ namespace Log
            loggerBufferCurrentPos += snprintf(&loggerBuffer[loggerBufferCurrentPos], loggerBufferSizeLeft(), "\n");

            logToDevice(Device::DEFAULT, loggerBuffer, loggerBufferCurrentPos);
            circularBuffer.put(std::string(loggerBuffer, loggerBufferCurrentPos));
            buffer.getCurrentBuffer()->put(std::string(loggerBuffer, loggerBufferCurrentPos));
            checkBufferState();
            return loggerBufferCurrentPos;
        }
        return -1;


@@ 160,6 170,7 @@ namespace Log
    {
        std::error_code errorCode;
        auto firstDump = !std::filesystem::exists(logPath, errorCode);

        if (errorCode) {
            LOG_ERROR("Failed to check if file %s exists, error: %d", logPath.c_str(), errorCode.value());
            return -EIO;


@@ 203,6 214,34 @@ namespace Log
        return status;
    }

    auto Logger::diagnosticDump() -> int
    {
        buffer.nextBuffer();
        worker->notify(LoggerWorker::Signal::DumpDiagnostic);
        writeLogsTimer.restart(writeLogsToFileInterval);
        return 1;
    }

    auto Logger::flushLogs() -> int
    {
        LOG_INFO("Shutdown dump logs");
        worker->kill();
        writeLogsTimer.stop();
        buffer.nextBuffer();
        return dumpToFile(purefs::dir::getLogsPath() / LOG_FILE_NAME);
    }

    void Logger::checkBufferState()
    {
        auto size = buffer.getCurrentBuffer()->getSize();

        if (size >= buffer.getCircularBufferSize()) {
            worker->notify(LoggerWorker::Signal::DumpFilledBuffer);
            buffer.nextBuffer();
            writeLogsTimer.restart(writeLogsToFileInterval);
        }
    }

    void Logger::addFileHeader(std::ofstream &file) const
    {
        file << application;

M module-utils/log/Logger.hpp => module-utils/log/Logger.hpp +19 -7
@@ 3,15 3,18 @@

#pragma once

#include <assert.h>
#include <log/log.hpp>
#include "LoggerBuffer.hpp"
#include "LoggerWorker.hpp"
#include "LoggerBufferContainer.hpp"
#include "log_colors.hpp"
#include "Timers/TimerFactory.hpp"
#include <log/log.hpp>
#include <mutex.hpp>
#include <Service/Service.hpp>
#include <rotator/Rotator.hpp>

#include <assert.h>
#include <map>
#include <mutex.hpp>
#include <string>
#include <filesystem>

namespace Log
{


@@ 38,11 41,14 @@ namespace Log
        static void destroyInstance();
        auto getLogs() -> std::string;
        void init(Application app, size_t fileSize = MAX_LOG_FILE_SIZE);
        void createTimer(sys::Service *parent);
        auto log(Device device, const char *fmt, va_list args) -> int;
        auto log(logger_level level, const char *file, int line, const char *function, const char *fmt, va_list args)
            -> int;
        auto logAssert(const char *fmt, va_list args) -> int;
        auto dumpToFile(std::filesystem::path logPath) -> int;
        auto diagnosticDump() -> int;
        auto flushLogs() -> int;

        static constexpr auto CRIT_STR = "CRIT";
        static constexpr auto IRQ_STR  = "IRQ";


@@ 63,6 69,7 @@ namespace Log
        [[nodiscard]] auto getLogLevel(const std::string &name) -> logger_level;
        void logToDevice(const char *fmt, va_list args);
        void logToDevice(Device device, std::string_view logMsg, size_t length);
        auto writeLog(Device device, const char *fmt, va_list args) -> int;
        [[nodiscard]] size_t loggerBufferSizeLeft() const noexcept
        {
            const auto sizeLeft = LOGGER_BUFFER_SIZE - loggerBufferCurrentPos;


@@ 71,6 78,7 @@ namespace Log
        }

        void addFileHeader(std::ofstream &file) const;
        void checkBufferState();

        cpp_freertos::MutexStandard mutex;
        cpp_freertos::MutexStandard logFileMutex;


@@ 81,13 89,17 @@ namespace Log
        size_t maxFileSize                    = MAX_LOG_FILE_SIZE;

        Application application;
        LoggerBuffer circularBuffer;

        LoggerBufferContainer buffer;

        utils::Rotator<MAX_LOG_FILES_COUNT> rotator;
        static constexpr size_t circularBufferSize = 1000;

        sys::TimerHandle writeLogsTimer;

        static const char *levelNames[];
        std::map<std::string, logger_level> filtered;
        static Logger *_logger;
        std::unique_ptr<LoggerWorker> worker;
    };

    const char *getTaskDesc();

M module-utils/log/LoggerBuffer.hpp => module-utils/log/LoggerBuffer.hpp +6 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 8,11 8,12 @@
class LoggerBuffer : public StringCircularBuffer
{
  public:
    using StringCircularBuffer::StringCircularBuffer;
    explicit LoggerBuffer(size_t size) : StringCircularBuffer(size)
    {}

    [[nodiscard]] std::pair<bool, std::string> get() override;
    void put(const std::string &logMsg) override;
    void put(std::string &&logMsg) override;
    [[nodiscard]] std::pair<bool, std::string> get();
    void put(const std::string &logMsg);
    void put(std::string &&logMsg);

    static constexpr auto lostBytesMessage = "bytes was lost.";


A module-utils/log/LoggerBufferContainer.hpp => module-utils/log/LoggerBufferContainer.hpp +52 -0
@@ 0,0 1,52 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "LoggerBuffer.hpp"

class LoggerBufferContainer
{
  public:
    LoggerBufferContainer()
        : currentIndex{0}, buffer{LoggerBuffer(circularBufferSize), LoggerBuffer(circularBufferSize)},
          currentBuffer{buffer}, flushBuffer{buffer}
    {}

    LoggerBuffer *getFlushBuffer()
    {
        return flushBuffer;
    }

    LoggerBuffer *getCurrentBuffer()
    {
        return currentBuffer;
    }

    constexpr size_t getCircularBufferSize()
    {
        return circularBufferSize;
    }

    void nextBuffer()
    {
        ++currentIndex;
        flushBuffer = currentBuffer;
        currentIndex %= numberOfBuffers;
        currentBuffer = &buffer[currentIndex];
    }

    size_t getCurrentIndex()
    {
        return currentIndex;
    }

  private:
    static constexpr size_t circularBufferSize = 1024;
    static constexpr size_t numberOfBuffers    = 2;

    size_t currentIndex;
    LoggerBuffer buffer[numberOfBuffers];
    LoggerBuffer *currentBuffer;
    LoggerBuffer *flushBuffer;
};

A module-utils/log/LoggerWorker.cpp => module-utils/log/LoggerWorker.cpp +46 -0
@@ 0,0 1,46 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "LoggerWorker.hpp"
#include "Logger.hpp"
#include <log/log.hpp>
#include <magic_enum.hpp>
#include <purefs/filesystem_paths.hpp>

namespace Log
{
    LoggerWorker::LoggerWorker(const std::string name) : Worker(name, priority)
    {}

    void LoggerWorker::notify(Signal command)
    {
        if (auto queue = getQueueByName(SignalQueueName); !queue->Overwrite(&command)) {
            LOG_ERROR("Unable to overwrite the command in the commands queue.");
        }
    }

    bool LoggerWorker::handleMessage(std::uint32_t queueID)
    {
        if (const auto queue = queues[queueID]; queue->GetQueueName() == SignalQueueName) {
            if (sys::WorkerCommand command; queue->Dequeue(&command, 0)) {
                handleCommand(static_cast<Signal>(command.command));
            }
        }
        return true;
    }

    void LoggerWorker::handleCommand(Signal command)
    {
        switch (command) {
        case Signal::DumpFilledBuffer:
        case Signal::DumpIntervalBuffer:
        case Signal::DumpDiagnostic:
            LOG_INFO("Received signal: %s", magic_enum::enum_name(command).data());
            Log::Logger::get().dumpToFile(purefs::dir::getLogsPath() / LOG_FILE_NAME);
            break;
        default:
            LOG_ERROR("Command not valid: %d", static_cast<int>(command));
        }
    }

} // namespace Log

A module-utils/log/LoggerWorker.hpp => module-utils/log/LoggerWorker.hpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Worker.hpp>

namespace Log
{
    static constexpr auto workerName = "LoggerWorker";

    class LoggerWorker : public sys::Worker
    {
      public:
        enum class Signal
        {
            DumpFilledBuffer,
            DumpIntervalBuffer,
            DumpDiagnostic
        };

        static constexpr auto SignalQueueName   = "LoggerSignal";
        static constexpr auto SignalSize        = sizeof(Signal);
        static constexpr auto SignalQueueLenght = 1;

        explicit LoggerWorker(const std::string name);
        void notify(Signal command);
        bool handleMessage(std::uint32_t queueID) override;
        void handleCommand(Signal command);

      private:
        static constexpr auto priority = static_cast<UBaseType_t>(sys::ServicePriority::Idle);
    };

} // namespace Log

M module-utils/log/StringCircularBuffer.hpp => module-utils/log/StringCircularBuffer.hpp +3 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,7 11,6 @@ class StringCircularBuffer
  public:
    explicit StringCircularBuffer(size_t size) : buffer(std::make_unique<std::string[]>(size)), capacity(size)
    {}
    virtual ~StringCircularBuffer() = default;
    [[nodiscard]] size_t getCapacity() const noexcept
    {
        return capacity;


@@ 29,8 28,8 @@ class StringCircularBuffer
    {
        return full;
    }
    virtual void put(const std::string &item);
    virtual void put(std::string &&item);
    void put(const std::string &item);
    void put(std::string &&item);
    void reset();

  private:

M module-utils/log/board/rt1051/log_rt1051.cpp => module-utils/log/board/rt1051/log_rt1051.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <board.h>


@@ 27,7 27,7 @@ namespace Log

        loggerBufferCurrentPos += vsnprintf(&loggerBuffer[loggerBufferCurrentPos], loggerBufferSizeLeft(), fmt, args);
        logToDevice(Device::DEFAULT, loggerBuffer, loggerBufferCurrentPos);
        circularBuffer.put(std::string(loggerBuffer, loggerBufferCurrentPos));
        buffer.getCurrentBuffer()->put(std::string(loggerBuffer, loggerBufferCurrentPos));
    }

    void Logger::logToDevice(Device device, std::string_view logMsg, size_t length)

M module-utils/log/doc/logging_engine.md => module-utils/log/doc/logging_engine.md +10 -5
@@ 21,14 21,21 @@ to a proper device (`SEGGER_RTT`, `console output`, `SYSTEMVIEW`)
and at the same time to put them to a `circular buffer`.

`Circular buffer` has a limited size which sometimes results in losing some logs.

In such a case, proper `lost message info` is added to `msg` received from the buffer.

However, it should not happen because the logger has a worker and 2 logger buffers. When
the buffer is full the logger switch buffer and sends message to the worker to dump logs.

## Dumping to a file

Logs from `Circular buffer` are dumped to a file named `MuditaOS.log` every 10 sec by `EventManagerCommon` timer.
Logs from `Circular buffer` are dumped to a file named `MuditaOS.log` when:
- `Circular buffer` is full
- every 15 minutes from last dump
- download diagnostic from the phone
- system shutdown

Current max log file size is 50 MB (after reaching this size no more logs are dumped).
Current max log file size is 15MB. After reaching this size the `Rotator` save file and add
extension at the end of file extension eg. `MuditaOS.log.1`. Then create the new file.

Logs can be accessed using `mount_user_lfs_partition.py` script from `tools` directory.
Additionally, `test/get_os_log.py` script allows getting a log file from a running phone.


@@ 37,5 44,3 @@ Additionally, `test/get_os_log.py` script allows getting a log file from a runni

There are a series of useful system logging capabilities defined in:
`module-utils/log/api/log/debug.hpp`

Please see doxygen documentation for each parameter.

M module-utils/log/logdump/include/logdump/logdump.h => module-utils/log/logdump/include/logdump/logdump.h +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 10,7 10,8 @@ extern "C"
    /// @return: < 0 - error occurred during log flush
    /// @return:   0 - log flush did not happen
    /// @return:   1 - log flush successful
    int dumpLogs();
    int diagnosticDumpLogs();
    int shutdownFlushLogs();

#ifdef __cplusplus
}

M module-utils/log/logdump/logdump.cpp => module-utils/log/logdump/logdump.cpp +10 -4
@@ 1,11 1,17 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <logdump/logdump.h>
#include <purefs/filesystem_paths.hpp>
#include <Logger.hpp>

int dumpLogs()
using namespace Log;

int diagnosticDumpLogs()
{
    return Logger::get().diagnosticDump();
}

int shutdownFlushLogs()
{
    return Log::Logger::get().dumpToFile(purefs::dir::getLogsPath() / LOG_FILE_NAME);
    return Logger::get().flushLogs();
}

M module-utils/log/tests/test_logDumps.cpp => module-utils/log/tests/test_logDumps.cpp +21 -0
@@ 6,6 6,7 @@
#include <string>

#include <log/Logger.hpp>
#include <log/LoggerBufferContainer.hpp>

namespace
{


@@ 114,3 115,23 @@ TEST_CASE("Test if log files rotate")
    // Clean-up the environment
    std::filesystem::remove_all(logsDir);
}

TEST_CASE("Test if choose proper buffer")
{
    LoggerBufferContainer buffer;

    size_t bufferIndex = buffer.getCurrentIndex();
    REQUIRE(bufferIndex == 0);

    buffer.nextBuffer();
    bufferIndex = buffer.getCurrentIndex();
    REQUIRE(bufferIndex == 1);

    buffer.nextBuffer();
    bufferIndex = buffer.getCurrentIndex();
    REQUIRE(bufferIndex == 0);

    buffer.nextBuffer();
    bufferIndex = buffer.getCurrentIndex();
    REQUIRE(bufferIndex == 1);
}

M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +2 -2
@@ 91,7 91,7 @@ int main()
    }

    std::vector<std::unique_ptr<sys::BaseServiceCreator>> systemServices;
    systemServices.emplace_back(sys::CreatorFor<EventManager>([]() { return dumpLogs(); }));
    systemServices.emplace_back(sys::CreatorFor<EventManager>([]() { return diagnosticDumpLogs(); }));
    systemServices.emplace_back(sys::CreatorFor<service::ServiceFileIndexer>(std::move(fileIndexerAudioPaths)));
    systemServices.emplace_back(sys::CreatorFor<ServiceDB>());
    systemServices.emplace_back(sys::CreatorFor<service::Audio>());


@@ 145,7 145,7 @@ int main()
        [&platform] {
            try {
                LOG_DEBUG("System deinit");
                if (dumpLogs() != 1) {
                if (shutdownFlushLogs() != 1) {
                    LOG_ERROR("Cannot dump logs");
                }
                platform->deinit();

M products/PurePhone/PurePhoneMain.cpp => products/PurePhone/PurePhoneMain.cpp +2 -2
@@ 158,7 158,7 @@ int main()

    std::vector<std::unique_ptr<sys::BaseServiceCreator>> systemServices;
#ifdef ENABLE_SERVICE_EVTMGR
    systemServices.emplace_back(sys::CreatorFor<EventManager>([]() { return dumpLogs(); }));
    systemServices.emplace_back(sys::CreatorFor<EventManager>([]() { return diagnosticDumpLogs(); }));
#endif
#ifdef ENABLE_SERVICE_FILEINDEXER
    systemServices.emplace_back(sys::CreatorFor<service::ServiceFileIndexer>(std::move(fileIndexerAudioPaths)));


@@ 284,7 284,7 @@ int main()
        [&platform] {
            try {
                LOG_DEBUG("System deinit");
                if (dumpLogs() != 1) {
                if (shutdownFlushLogs() != 1) {
                    LOG_ERROR("Cannot dump logs");
                }
                platform->deinit();

M pure_changelog.md => pure_changelog.md +1 -0
@@ 8,6 8,7 @@
* Separated system volume from Bluetooth device volume for A2DP

### Fixed
* Fixed lost bytes in logs
* Fixed passcode lock time discrepancy between lock screen and 'Wrong password' popup
* Fixed cellular DMA errors
* Fixed order of the services while closing system