1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-iceoryx

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
error-handling.md 13 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 30.11.2024 01:51 01c7879

Логирование и обработка ошибок

Логирование

Логирование выполняется внутренним логгером iceoryx. Логгер используется внутри системы для записи ошибок и общей информации о работе системы. Пользователь также может использовать его в коде приложения с той же целью.

Логгер

Логгер отвечает за запись информации о состоянии системы в настраиваемый поток (включая файлы).

Логгер является потокобезопасным и, следовательно, может безопасно использоваться из нескольких потоков одновременно. В настоящее время логгер работает синхронно.

Уровни логирования

Поддерживаются следующие уровни логирования, упорядоченные по объёму отображаемой информации от наибольшего к наименьшему:

  • VERBOSE — отображается вся доступная информация;
  • DEBUG — информация для поддержки отладки со стороны разработчика;
  • INFO — информация о рабочем состоянии для пользователя;
  • WARN — указывает на потенциальную проблему, требующую исследования;
  • ERR — произошла ошибка, которая может быть обработана на стороне приложения или RouDi;
  • FATAL — произошла ошибка, и RouDi не может продолжить работу;
  • OFF — информация не записывается.

Для ERR и FATAL см. также уровни ошибок MODERATE, SEVERE (регистрируются с помощью LogErr) и FATAL (регистрируются с помощью LogFatal) в разделе «Уровни ошибок». Уровни ERR и FATAL должны использоваться только вместе с обработчиком ошибок, то есть должны сопровождаться соответствующим вызовом обработчика ошибок (в настоящее время это невозможно обеспечить).

Обработка ошибок

Ошибки считаются состояниями системы, которые не должны возникать регулярно и обычно являются результатом внешнего сбоя, например, когда ОС не может предоставить определённый ресурс (например, семафор) или приложение не отвечает. Напротив, регулярное поведение, такое как приём данных получателем, когда данные не были отправлены, не является ошибкой. С другой стороны, потеря отправленных данных будет считаться ошибкой.

Существует два основных подхода к обработке ошибок:

  1. использование исключений;
  2. коды возврата в сочетании с операторами управления потоком и центральным экземпляром для обработки ошибок, которые иначе не могут быть устранены (обработчик ошибок).

В iceoryx мы используем второй подход.

Исключения

Использование исключений в iceoryx запрещено, включая сторонний код, который может их генерировать. Это связано со следующими причинами:

  • Во многих реализациях обработка исключений требует динамической памяти (даже если сами исключения не генерируются динамически, например, через new).
  • Обработка исключений не детерминирована относительно времени выполнения.
  • Обработка исключений может вызвать (небольшое) увеличение времени выполнения даже при отсутствии генерируемых исключений.
  • В целом невозможно включить (полную) информацию обо всех исключениях, которые могут быть выброшены, в сигнатуру функций. Это затрудняет для вызывающей стороны решение о том, нужно ли обрабатывать какое-либо исключение.
  • Чрезмерное использование исключений часто приводит к запутанным блокам try-catch, что затрудняет поддержку кода.

Использование noexcept

Все функции помечены как noexcept. Обратите внимание, что это не означает, что внутри такой функции нельзя генерировать исключения. Вместо этого, когда внутри функции генерируется исключение (прямо или косвенно другой функцией), это исключение не распространяется дальше, и будет вызван std::terminate, вызывающий обработчик завершения. Цель состоит в том, чтобы это никогда не происходило во время выполнения.

Для этого необходимо исключить использование всех потенциально выбрасывающих (сторонних) функций во всей кодовой базе. Поскольку многие функции STL могут генерировать исключения, их также нельзя использовать, и их функциональность должна быть перереализована без использования исключений. В частности, всё, что выделяет динамическую память, может генерировать исключение std::bad_alloc при исчерпании памяти.

Альтернативы исключениям

В качестве альтернативы исключениям мы используем обработчик ошибок и разновидность кодов возврата в виде iox::expected, описанных ниже. iox::expected можно использовать для передачи ошибки вызывающему объекту, который должен решить, обработать ошибку самостоятельно или... errorHandler(Error::SOME_ERROR_CODE)

Более подробную информацию о том, как настроить и использовать errorHandler, можно найти в справочнике по API.

IOX_EXPECTS и IOX_ENSURES

Предположим, что myAlgorithm является частью внутреннего API и не должен вызываться с nullptr. Мы могли бы проверять это каждый раз, но этого можно избежать, если мы укажем, что вызывающий объект отвечает за обеспечение выполнения этих условий.

int32_t myAlgorithm(int32_t* ptr) {
    IOX_EXPECTS(ptr!=nullptr);
    //наблюдаем порядок, разыменовываем только после проверки nullptr
    IOX_EXPECTS(*ptr > -1024 && *ptr < 1024);

    int32_t intermediate = timesTwo(*ptr);
    //здесь может не потребоваться проверка следующего вызова функции,
    //но она ясно выражает наши ожидания
    IOX_ENSURES(intermediate % 2 == 0);

    int32_t result = abs(intermediate);

    IOX_ENSURES(result % 2 == 0);
    IOX_ENSURES(result >= 0);
    IOX_ENSURES(result < 2048);

    return result;
}

Обратите внимание, что в случае проверок nullptr также можно использовать ссылки в аргументах (или iox::not_null, если предполагается его сохранение, поскольку ссылки не копируются). Следует учитывать, что iox::not_null влечёт за собой затраты во время выполнения, которые могут быть нежелательны. Когда IOX_EXPECTS и IOX_ENSURES реализованы так, чтобы не оставлять следов в режиме выпуска, мы не несём затрат во время выполнения при их использовании. По этой причине рекомендуется использовать их для документирования и проверки предположений там, где это уместно.

expected

Этот пример проверяет аргументы и, если они действительны, продолжает вычислять результат и возвращает его. В противном случае он создаёт объект Error из errorCode и возвращает его.

expected<SomeType, Error> func(Arg arg) {
    int32_t errorCode = checkArg(arg);
    if(isNoError(errorCode)) {
        SomeType result = computeResult(arg);
        // опционально делаем что-то с результатом
        return result;
    }
    return Error(errorCode);
}

Вызывающий объект отвечает за обработку (или распространение) ошибки.

auto result = func(arg);
if(result.has_error()) {
    auto& error = result.error();
    //обрабатываем или распространяем ошибку
} else {
    auto& value = result.value();
    //продолжаем, используя значение
}

В качестве альтернативы можно использовать функциональный подход.

auto successFunc = [](SomeType& value) {
    //продолжаем, используя значение
};

auto errorFunc = [](Error& error) {
    //обрабатываем ошибку
};

func(arg).and_then(successFunc).or_else(errorFunc);

Тестирование фатальной ошибки

Для фатальных ошибок обработчик ошибок завершает выполнение двоичного файла. Чтобы протестировать эти пути, следует использовать функцию iox::testing::IOX_EXPECT_FATAL_FAILURE вместо макроса gTest EXPECT_DEATH. Макрос gTest EXPECT_DEATH разветвляет процесс, что замедляет выполнение теста (особенно при включённом ThreadSanitizer) и вызывает проблемы с запуском потока. Функция IOX_EXPECT_FATAL_FAILURE регистрирует временный обработчик ошибок и запускает предоставленную функцию в отдельном потоке. Когда вызывается обработчик ошибок, используется longjmp, чтобы предотвратить завершение работы и вместо этого обеспечить корректное завершение потока.

#include "iceoryx_hoofs/testing/fatal_failure.hpp"
TEST(MyTest, valueOnNulloptIsFatal) {
    iox::optional<bool> sut;
    IOX_EXPECT_FATAL_FAILURE<iox::HoofsError>([&] { sut.value(); }, iox::HoofsError::EXPECTS_ENSURES_FAILED);
}

Если нельзя использовать IOX_EXPECT_FATAL_FAILURE, например, при тестировании PoshRuntime с его синглтоном, следует использовать EXPECT_DEATH с GTEST_FLAG(death_test_style) = "threadsafe";.

GTEST_FLAG(death_test_style) = "threadsafe";
EXPECT_DEATH(bad_call(), ".*");

Открытые вопросы

Централизованная обработка ошибок

Может быть желательно иметь централизованный обработчик ошибок, где ошибки времени выполнения на стороне приложения регистрируются и (возможно) обрабатываются. Это также можно сделать в RouDi (отправив информацию в RouDi), но RouDi уже...

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-iceoryx.git
git@api.gitlife.ru:oschina-mirror/mirrors-iceoryx.git
oschina-mirror
mirrors-iceoryx
mirrors-iceoryx
main