~aleteoryx/muditaos

9cf11913a180628cbb96a398a6c191ed62a7fe40 — Alek Rudnik 4 years ago 9ea80ab
[EGD-8129] Add atexit functionality

Added atexit functionality. All global destructors and functions
registered with atexit() should be called at program exit.
It was possbile to reuse original newlib implementation with
MALLOC_PROVIDED flag set.

Made sure logger destructructor is called as last one.

Due to mudita OS legacy, it was not possible to simply fix dependencies
in global objectes and hence there are neither global objects
destructors nor functions registered with atexit() called when exit()
is called.
Possibly it will be changed later.
M Target_RT1051.cmake => Target_RT1051.cmake +2 -0
@@ 60,6 60,8 @@ add_compile_options(
set(TARGET_SOURCES

        ${CMAKE_CURRENT_LIST_DIR}/module-os/board/rt1051/_exit.cpp
        ${CMAKE_CURRENT_LIST_DIR}/module-os/board/rt1051/__atexit.c
        ${CMAKE_CURRENT_LIST_DIR}/module-os/board/rt1051/__call_atexit.c
        CACHE INTERNAL ""
)


M board/rt1051/ldscripts/sections.ld => board/rt1051/ldscripts/sections.ld +11 -1
@@ 24,6 24,9 @@ EXTERN(image_vector_table);
EXTERN(boot_data);
EXTERN(hyperflash_config);

/* Ensure that newlib runs the finalizers.  */
__libc_fini = _fini;

SECTIONS
{
    /* Image Vector Table and Boot Data for booting from external flash */


@@ 123,9 126,16 @@ SECTIONS
        KEEP (*(.init_array))
        __init_array_end = .;

        KEEP(*(.fini));
        . = ALIGN(4);
        KEEP(*(.fini))

        . = ALIGN(4);
        __fini_array_start = .;
        KEEP (*(.fini_array))
        KEEP (*(SORT(.fini_array.*)))
        __fini_array_end = .;
        
        . = ALIGN(4);
        KEEP (*crtbegin.o(.ctors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
        KEEP (*(SORT(.ctors.*)))

M module-bsp/board/rt1051/common/board.cpp => module-bsp/board/rt1051/common/board.cpp +27 -0
@@ 188,9 188,36 @@ namespace bsp
        rebootProgress = rebootState::reboot;
    }

    struct PlatformExitObject
    {
        void (*func)();
    };
    static unsigned short iObject = 0;
    constexpr auto MAX_PLATFORM_EXIT_OBJECTS = 16u;

    static PlatformExitObject objects[MAX_PLATFORM_EXIT_OBJECTS];

    int RegisterPlatformExitFunction(void (*func)())
    {
        if (iObject >= sizeof(objects))
            return -1;
        objects[iObject].func = func;
        ++iObject;
        return 0;
    }

    void CallPlatformExitFunctions()
    {
        while (iObject > 0) {
            objects[--iObject].func();
        }
    }

    extern "C" {
        void _platform_exit(void)
        {
            CallPlatformExitFunctions();

            static constexpr auto POWER_SWITCH_PIN = 7;
            static const auto POWER_SWITCH_PORT = GPIO2;
            switch(rebootProgress)

M module-bsp/board/rt1051/common/startup_mimxrt1052.cpp => module-bsp/board/rt1051/common/startup_mimxrt1052.cpp +4 -1
@@ 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

//*****************************************************************************


@@ 58,6 58,7 @@
extern "C"
{
    extern void __libc_init_array(void);
    extern void __call_exitprocs(int code, void *d);
}
#endif
#endif


@@ 750,6 751,8 @@ __attribute__((section(".after_vectors.reset"))) void ResetISR(void)
#else
    main();
#endif
    // call global destructors and functions registered using atexit
    __call_exitprocs(0, 0);
    _platform_exit();
    //
    // main() shouldn't return, but if it does, we'll just enter an infinite loop

M module-bsp/bsp/bsp.hpp => module-bsp/bsp/bsp.hpp +3 -0
@@ 11,6 11,9 @@ namespace bsp

    // Board system reset
    void BoardReboot();

    // Register platform exit functions
    int RegisterPlatformExitFunction(void (*func)());
}



A module-os/board/rt1051/__atexit.c => module-os/board/rt1051/__atexit.c +159 -0
@@ 0,0 1,159 @@
/*
 *  Common routine to implement atexit-like functionality.
 *
 *  This is also the key function to be configured as lite exit, a size-reduced
 *  implementation of exit that doesn't invoke clean-up functions such as _fini
 *  or global destructors.
 *
 *  Default (without lite exit) call graph is like:
 *  _start -> atexit -> __register_exitproc
 *  _start -> __libc_init_array -> __cxa_atexit -> __register_exitproc
 *  on_exit -> __register_exitproc
 *  _start -> exit -> __call_exitprocs
 *
 *  Here an -> means arrow tail invokes arrow head. All invocations here
 *  are non-weak reference in current newlib/libgloss.
 *
 *  Lite exit makes some of above calls as weak reference, so that size expansive
 *  functions __register_exitproc and __call_exitprocs may not be linked. These
 *  calls are:
 *    _start w-> atexit
 *    __cxa_atexit w-> __register_exitproc
 *    exit w-> __call_exitprocs
 *
 *  Lite exit also makes sure that __call_exitprocs will be referenced as non-weak
 *  whenever __register_exitproc is referenced as non-weak.
 *
 *  Thus with lite exit libs, a program not explicitly calling atexit or on_exit
 *  will escape from the burden of cleaning up code. A program with atexit or on_exit
 *  will work consistently to normal libs.
 *
 *  Lite exit is enabled with --enable-lite-exit, and is controlled with macro
 *  _LITE_EXIT.
 */

#include <stddef.h>
#include <stdlib.h>
#include <reent.h>
#include <sys/lock.h>
#include "atexit.h"

/* Make this a weak reference to avoid pulling in malloc.  */
#ifndef MALLOC_PROVIDED
void * malloc(size_t) _ATTRIBUTE((__weak__));
#endif

#ifdef _LITE_EXIT
/* As __call_exitprocs is weak reference in lite exit, make a
   non-weak reference to it here.  */
const void * __atexit_dummy = &__call_exitprocs;
#endif

#ifndef __SINGLE_THREAD__
extern _LOCK_RECURSIVE_T __atexit_recursive_mutex;
#endif

#ifdef _REENT_GLOBAL_ATEXIT
static struct _atexit _global_atexit0 = _ATEXIT_INIT;
# define _GLOBAL_ATEXIT0 (&_global_atexit0)
#else
# define _GLOBAL_ATEXIT0 (&_GLOBAL_REENT->_atexit0)
#endif

/*
 * Register a function to be performed at exit or on shared library unload.
 */

int
__register_exitproc (int type,
	void (*fn) (void),
	void *arg,
	void *d)
{
  struct _on_exit_args * args;
  register struct _atexit *p;

#ifndef __SINGLE_THREAD__
  __lock_acquire_recursive(__atexit_recursive_mutex);
#endif

  p = _GLOBAL_ATEXIT;
  if (p == NULL)
    {
      _GLOBAL_ATEXIT = p = _GLOBAL_ATEXIT0;
#ifdef _REENT_SMALL
      extern struct _on_exit_args * const __on_exit_args _ATTRIBUTE ((weak));
      if (&__on_exit_args != NULL)
	p->_on_exit_args_ptr = __on_exit_args;
#endif	/* def _REENT_SMALL */
    }
  if (p->_ind >= _ATEXIT_SIZE)
    {
#if !defined (_ATEXIT_DYNAMIC_ALLOC) || !defined (MALLOC_PROVIDED)
#ifndef __SINGLE_THREAD__
      __lock_release_recursive(__atexit_recursive_mutex);
#endif
      return -1;
#else
      p = (struct _atexit *) malloc (sizeof *p);
      if (p == NULL)
	{
#ifndef __SINGLE_THREAD__
	  __lock_release_recursive(__atexit_recursive_mutex);
#endif
	  return -1;
	}
      p->_ind = 0;
      p->_next = _GLOBAL_ATEXIT;
      _GLOBAL_ATEXIT = p;
#ifndef _REENT_SMALL
      p->_on_exit_args._fntypes = 0;
      p->_on_exit_args._is_cxa = 0;
#else
      p->_on_exit_args_ptr = NULL;
#endif
#endif
    }

  if (type != __et_atexit)
    {
#ifdef _REENT_SMALL
      args = p->_on_exit_args_ptr;
      if (args == NULL)
	{
#ifndef _ATEXIT_DYNAMIC_ALLOC
#ifndef __SINGLE_THREAD__
	  __lock_release_recursive(__atexit_recursive_mutex);
#endif
	  return -1;
#else
	  if (malloc)
	    args = malloc (sizeof * p->_on_exit_args_ptr);

	  if (args == NULL)
	    {
#ifndef __SINGLE_THREAD__
	      __lock_release(__atexit_recursive_mutex);
#endif
	      return -1;
	    }
	  args->_fntypes = 0;
	  args->_is_cxa = 0;
	  p->_on_exit_args_ptr = args;
#endif
	}
#else
      args = &p->_on_exit_args;
#endif
      args->_fnargs[p->_ind] = arg;
      args->_fntypes |= (1 << p->_ind);
      args->_dso_handle[p->_ind] = d;
      if (type == __et_cxa)
	args->_is_cxa |= (1 << p->_ind);
    }
  p->_fns[p->_ind++] = fn;
#ifndef __SINGLE_THREAD__
  __lock_release_recursive(__atexit_recursive_mutex);
#endif
  return 0;
}

A module-os/board/rt1051/__call_atexit.c => module-os/board/rt1051/__call_atexit.c +162 -0
@@ 0,0 1,162 @@
/*
 * COmmon routine to call call registered atexit-like routines.
 */


#include <stdlib.h>
#include <reent.h>
#include <sys/lock.h>
#include "atexit.h"

/* Make this a weak reference to avoid pulling in free.  */
#ifndef MALLOC_PROVIDED
void free(void *) _ATTRIBUTE((__weak__));
#endif

#ifndef __SINGLE_THREAD__
__LOCK_INIT_RECURSIVE(, __atexit_recursive_mutex);
#endif

#ifdef _REENT_GLOBAL_ATEXIT
struct _atexit *_global_atexit = _NULL;
#endif

//#undef _WANT_REGISTER_FINI
#ifdef _WANT_REGISTER_FINI

/* If "__libc_fini" is defined, finalizers (either
   "__libc_fini_array", or "_fini", as appropriate) will be run after
   all user-specified atexit handlers.  For example, you can define
   "__libc_fini" to "_fini" in your linker script if you want the C
   library, rather than startup code, to register finalizers.  If you
   do that, then your startup code need not contain references to
   "atexit" or "exit".  As a result, only applications that reference
   "exit" explicitly will pull in finalization code.

   The choice of whether to register finalizers from libc or from
   startup code is deferred to link-time, rather than being a
   configure-time option, so that the same C library binary can be
   used with multiple BSPs, some of which register finalizers from
   startup code, while others defer to the C library.  */
extern char __libc_fini __attribute__((weak));

/* Register the application finalization function with atexit.  These
   finalizers should run last.  Therefore, we want to call atexit as
   soon as possible.  */
static void 
register_fini(void) __attribute__((constructor (0)));

static void 
register_fini(void)
{
  if (&__libc_fini) {
#ifdef HAVE_INITFINI_ARRAY
    extern void __libc_fini_array (void);
    atexit (__libc_fini_array);
#else
    extern void _fini (void);
    atexit (_fini);
#endif
  }
}

#endif /* _WANT_REGISTER_FINI  */

/*
 * Call registered exit handlers.  If D is null then all handlers are called,
 * otherwise only the handlers from that DSO are called.
 */

void 
__call_exitprocs (int code, void *d)
{
  register struct _atexit *p;
  struct _atexit **lastp;
  register struct _on_exit_args * args;
  register int n;
  int i;
  void (*fn) (void);


#ifndef __SINGLE_THREAD__
  __lock_acquire_recursive(__atexit_recursive_mutex);
#endif

 restart:

  p = _GLOBAL_ATEXIT;
  lastp = &_GLOBAL_ATEXIT;
  while (p)
    {
#ifdef _REENT_SMALL
      args = p->_on_exit_args_ptr;
#else
      args = &p->_on_exit_args;
#endif
      for (n = p->_ind - 1; n >= 0; n--)
	{
	  int ind;

	  i = 1 << n;

	  /* Skip functions not from this dso.  */
	  if (d && (!args || args->_dso_handle[n] != d))
	    continue;

	  /* Remove the function now to protect against the
	     function calling exit recursively.  */
	  fn = p->_fns[n];
	  if (n == p->_ind - 1)
	    p->_ind--;
	  else
	    p->_fns[n] = NULL;

	  /* Skip functions that have already been called.  */
	  if (!fn)
	    continue;

	  ind = p->_ind;

	  /* Call the function.  */
	  if (!args || (args->_fntypes & i) == 0)
	    fn ();
	  else if ((args->_is_cxa & i) == 0)
	    (*((void (*)(int, void *)) fn))(code, args->_fnargs[n]);
	  else
	    (*((void (*)(void *)) fn))(args->_fnargs[n]);

	  /* The function we called call atexit and registered another
	     function (or functions).  Call these new functions before
	     continuing with the already registered functions.  */
	  if (ind != p->_ind || *lastp != p)
	    goto restart;
	}

#if !defined (_ATEXIT_DYNAMIC_ALLOC) || !defined (MALLOC_PROVIDED)
      break;
#else
      /* Move to the next block.  Free empty blocks except the last one,
	 which is part of _GLOBAL_REENT.  */
      if (p->_ind == 0 && p->_next)
	{
	  /* Remove empty block from the list.  */
	  *lastp = p->_next;
#ifdef _REENT_SMALL
	  if (args)
	    free (args);
#endif
	  free (p);
	  p = *lastp;
	}
      else
	{
	  lastp = &p->_next;
	  p = p->_next;
	}
#endif
    }
#ifndef __SINGLE_THREAD__
  __lock_release_recursive(__atexit_recursive_mutex);
#endif

}

M module-os/board/rt1051/_exit.cpp => module-os/board/rt1051/_exit.cpp +13 -8
@@ 40,26 40,25 @@
#include <string.h>
#include <exit_backtrace.h>
#include <purefs/vfs_subsystem.hpp>
#include <bsp/bsp.hpp>


static void __attribute__((noreturn)) stop_system(void)
{
    if(!isIRQ()) {
    if (!isIRQ()) {
        if (dumpLogs() != 1) {
            LOG_ERROR("Cannot dump logs");
        }
        const auto err = purefs::subsystem::unmount_all();
        if(err) {
        if (err) {
            LOG_WARN("Unable unmount all filesystems with error: %i.", err);
        } else {
        }
        else {
            LOG_INFO("Filesystems unmounted successfully...");
        }
    }
    LOG_INFO("Restarting the system...");
    vTaskSuspendAll();
    haltIfDebugging();
    bsp::BoardReboot();
    vTaskEndScheduler();
    LOG_INFO("Restarting the system...");
    NVIC_SystemReset();
    // waiting for system reset
    while (1) {
#ifndef DEBUG


@@ 82,3 81,9 @@ void __attribute__((noreturn, used)) _exit(int code)
{
     _exit_backtrace(code, code!=0);
}

void __attribute__((noreturn, used)) exit(int code)
{
    // do not call global destructors and atexit registered functions
    _exit(code);
}

A module-os/board/rt1051/atexit.h => module-os/board/rt1051/atexit.h +15 -0
@@ 0,0 1,15 @@
/*
 *	Common definitions for atexit-like routines
 */

enum __atexit_types
{
  __et_atexit,
  __et_onexit,
  __et_cxa
};

#define MALLOC_PROVIDED

void __call_exitprocs (int, void *);
int __register_exitproc (int, void (*fn) (void), void *, void *);

M module-os/board/rt1051/include/exit_backtrace.h => module-os/board/rt1051/include/exit_backtrace.h +1 -2
@@ 24,6 24,7 @@ void __attribute__((noreturn, used)) _exit_backtrace(int code, bool bt_dump);
 * @note Function never returns and dump backtrace when code is not equal EXIT_SUCCESS
 */
void __attribute__((noreturn, used)) _exit(int code);
void __attribute__((noreturn, used)) exit(int code);


/** This is internal backtrce function


@@ 52,5 53,3 @@ static inline void __attribute__((always_inline)) _StackTrace_Dump_And_Abort(voi
#ifdef __cplusplus
}
#endif



M module-platform/rt1051/CMakeLists.txt => module-platform/rt1051/CMakeLists.txt +1 -1
@@ 28,5 28,5 @@ target_link_libraries(
        platform

    PRIVATE
        module-vfs
        module-vfs log
)

M module-platform/rt1051/src/RT1051Platform.cpp => module-platform/rt1051/src/RT1051Platform.cpp +3 -1
@@ 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 <platform/rt1051/RT1051Platform.hpp>


@@ 8,6 8,7 @@
#include <bsp/bsp.hpp>
#include <purefs/vfs_subsystem.hpp>
#include <exception>
#include <Logger.hpp>

using platform::rt1051::BlockDeviceFactory;
using platform::rt1051::RT1051Platform;


@@ 15,6 16,7 @@ using platform::rt1051::RT1051Platform;
RT1051Platform::RT1051Platform()
{
    bsp::BoardInit();
    bsp::RegisterPlatformExitFunction(Log::Logger::destroyInstance);
}

void RT1051Platform::init()

M module-sys/SystemManager/SystemManagerCommon.cpp => module-sys/SystemManager/SystemManagerCommon.cpp +1 -1
@@ 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 <SystemManager/SystemManagerCommon.hpp>

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +37 -21
@@ 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 "critical.hpp"


@@ 12,25 12,8 @@

namespace Log
{
    std::map<std::string, logger_level> Logger::filtered = {{"ApplicationManager", logger_level::LOGINFO},
#if (!LOG_SENSITIVE_DATA_ENABLED)
                                                            {"CellularMux", logger_level::LOGINFO},
                                                            {"ServiceCellular", logger_level::LOGINFO},
#endif
                                                            {"ServiceAntenna", logger_level::LOGERROR},
                                                            {"ServiceAudio", logger_level::LOGINFO},
                                                            {"ServiceBluetooth", logger_level::LOGINFO},
                                                            {"ServiceBluetooth_w1", logger_level::LOGINFO},
                                                            {"ServiceFota", logger_level::LOGINFO},
                                                            {"ServiceEink", logger_level::LOGINFO},
                                                            {"ServiceDB", logger_level::LOGINFO},
                                                            {CRIT_STR, logger_level::LOGTRACE},
                                                            {IRQ_STR, logger_level::LOGTRACE},
                                                            {"FileIndexer", logger_level::LOGINFO},
                                                            {"ServiceAudio", logger_level::LOGERROR},
                                                            {"EventManager", logger_level::LOGINFO}};

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

    std::ostream &operator<<(std::ostream &stream, const Application &application)
    {


@@ 40,7 23,27 @@ namespace Log
    }

    Logger::Logger() : circularBuffer{circularBufferSize}, rotator{".log"}
    {}
    {
        filtered = {
            {"ApplicationManager", logger_level::LOGINFO},
#if (!LOG_SENSITIVE_DATA_ENABLED)
            {"CellularMux", logger_level::LOGINFO},
            {"ServiceCellular", logger_level::LOGINFO},
#endif
            {"ServiceAntenna", logger_level::LOGERROR},
            {"ServiceAudio", logger_level::LOGINFO},
            {"ServiceBluetooth", logger_level::LOGINFO},
            {"ServiceBluetooth_w1", logger_level::LOGINFO},
            {"ServiceFota", logger_level::LOGINFO},
            {"ServiceEink", logger_level::LOGINFO},
            {"ServiceDB", logger_level::LOGINFO},
            {CRIT_STR, logger_level::LOGTRACE},
            {IRQ_STR, logger_level::LOGTRACE},
            {"FileIndexer", logger_level::LOGINFO},
            {"ServiceAudio", logger_level::LOGERROR},
            {"EventManager", logger_level::LOGINFO}
        };
    }

    void Logger::enableColors(bool enable)
    {


@@ 54,6 57,19 @@ namespace Log
        }
    }

    void Logger::destroyInstance()
    {
        delete _logger;
        _logger = nullptr;
    }

    Logger &Logger::get()
    {
        static auto *logger = new Logger;
        _logger             = logger;
        return *logger;
    }

    auto Logger::getLogLevel(const std::string &name) -> logger_level
    {
        return filtered[name];

M module-utils/log/Logger.hpp => module-utils/log/Logger.hpp +5 -7
@@ 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


@@ 34,11 34,8 @@ namespace Log
    {
      public:
        void enableColors(bool enable);
        [[nodiscard]] static Logger &get()
        {
            static Logger logger;
            return logger;
        }
        [[nodiscard]] static Logger &get();
        static void destroyInstance();
        auto getLogs() -> std::string;
        void init(Application app, size_t fileSize = MAX_LOG_FILE_SIZE);
        auto log(Device device, const char *fmt, va_list args) -> int;


@@ 89,7 86,8 @@ namespace Log
        static constexpr size_t circularBufferSize = 1000;

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

    const char *getTaskDesc();

M products/BellHybrid/CMakeLists.txt => products/BellHybrid/CMakeLists.txt +1 -0
@@ 12,6 12,7 @@ target_compile_options(BellHybrid
    PRIVATE
        $<$<COMPILE_LANGUAGE:C>:-Wno-discarded-qualifiers>
        "-Wno-error=deprecated-declarations"
        "-Wno-prio-ctor-dtor"
    )



M products/PurePhone/CMakeLists.txt => products/PurePhone/CMakeLists.txt +1 -0
@@ 19,6 19,7 @@ target_compile_options(PurePhone
    PRIVATE
        $<$<COMPILE_LANGUAGE:C>:-Wno-discarded-qualifiers>
        "-Wno-error=deprecated-declarations"
        "-Wno-prio-ctor-dtor"
    )

target_sources(PurePhone

M products/PurePhone/PurePhoneMain.cpp => products/PurePhone/PurePhoneMain.cpp +24 -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

#include "config.h"


@@ 70,6 70,24 @@

#include <memory>
#include <vector>
#include <cstdlib>

void atexit_cleanup_handler()
{
    LOG_INFO("Starting clean up");
}

class Cleanup
{
  public:
    ~Cleanup()
    {
        const int result_2 = std::atexit(atexit_cleanup_handler);
        if (result_2 != 0) {
            LOG_FATAL("at exit failed");
        }
    }
};

int main()
{


@@ 204,9 222,11 @@ int main()
            return true;
        });

    LOG_PRINTF("Launching %s \n", ApplicationName);
    LOG_PRINTF("commit: %s version: %s branch: %s\n", GIT_REV, VERSION, GIT_BRANCH);
    LOG_INFO("Launching %s ", ApplicationName);
    LOG_INFO("commit: %s version: %s branch: %s", GIT_REV, VERSION, GIT_BRANCH);
    cpp_freertos::Thread::StartScheduler();
    LOG_PRINTF("Scheduler is terminated properly\n");
    LOG_INFO("Scheduler is terminated properly");
    Cleanup cleanup;

    return 0;
}