From 772b34b8d268d334968f37578ba27bdbf8e0bbc7 Mon Sep 17 00:00:00 2001 From: kongr45gpen <electrovesta@gmail.com> Date: Mon, 17 Oct 2022 20:38:04 +0000 Subject: [PATCH] =?UTF-8?q?Add=20forbidden=20enum=20magic=20=F0=9F=A7=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 4 +- inc/Platform/x86/Helpers/EnumMagic.hpp | 114 ++++++++++++++++++ src/Platform/x86/ErrorHandler.cpp | 5 +- src/Platform/x86/main.cpp | 4 +- test/Services/DummyService.cpp | 2 +- .../Services/MemoryManagementServiceTests.cpp | 2 + 6 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 inc/Platform/x86/Helpers/EnumMagic.hpp diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5227898..fd23279a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -51,6 +51,7 @@ clang-tidy: - clang-tidy-html clang-tidy-output.log - mv clang.html clang-tidy-html-report artifacts: + when: always paths: - ./clang-tidy-html-report @@ -85,9 +86,8 @@ ikos: script: - cd $CI_PROJECT_DIR - ikos-scan cmake . - - ikos-scan make + - ikos-scan make tests - ikos tests.bc - - ikos ecss_services.bc - ikos-report -o=ikos-report.txt output.db after_script: - mv ikos-report.txt ikos-report diff --git a/inc/Platform/x86/Helpers/EnumMagic.hpp b/inc/Platform/x86/Helpers/EnumMagic.hpp new file mode 100644 index 00000000..6e558240 --- /dev/null +++ b/inc/Platform/x86/Helpers/EnumMagic.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include <string> +#include <utility> + +#if !defined(__GNUC__) && !defined(__clang__) +#define __PRETTY_FUNCTION__ "E V = Sorry, feature not supported!" +#endif + +/** + * A simplified version of https://github.com/Neargye/magic_enum, this file uses some gcc compiler options to provide an @ref enumName function. + * This function converts an enum value to a string. It's used for debugging purposes only, to improve the readability of values that are hidden + * behind enums. + * + * Use with care. NOT intended for a microcontroller. + * + * @note This functionality is designed for simple, sequence-like enums. Their values should start from 0 and increase 1-by-1 until the end. It will probably + * result in slow compilation or incorrect results if used for other, "weirder" enums. + * + * This hack aims to be safe from errors by using `constexpr` lavishly. Enum values which are out of bounds (e.g. not defined or beyond + * EnumMagic_::MaximumValues) will not throw errors or cause unexpected events, but will probably display some kind of string. + * + * @license MIT, Daniil Goncharov + */ +namespace EnumMagic_ { + /** + * The highest enum value that can be handled. + * + * Values higher than this will just result in a number or malformed text, but _not_ a compilation or runtime error. + */ + constexpr auto MaximumValues = 256; + + /** + * Returns the identifier of an enum value. + * + * For example, if you define an enum: + * @code + * enum Something { + * First = 0, + * Second = 1 + * } + * @endcode + * then calling this function with `Something::First` will return the string `"Something::First"`. + * + * Internally, this function uses the [`__PRETTY_FUNCTION__`](https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html) + * magic constant defined by GCC. The constant contains the template arguments of the function in a string, + * which themselves contain the identifier of the enum value. + * + * @note Tested with GCC version 11 only, other versions or compilers might return incorrect values + * + * @tparam E Enum type + * @tparam V Enum value (of type E) + * @return The identifier of the enum value + */ + template <typename E, E V> + constexpr std::basic_string_view<char> enumHack() { + static_assert(std::is_enum_v<E>, "enumHack() requires enum type."); + + std::string_view function = __PRETTY_FUNCTION__; + auto pos = function.find("E V = "); + std::string_view output = function.substr(pos + 6); + output.remove_suffix(1); + return output; + } + + /** + * Shortcut definition of enumHack for easier access + */ + template <typename Enum, Enum Value> + inline constexpr auto enumNameV = enumHack<Enum, Value>(); + + /** + * Given an enum type and a sequence of values, this function returns a list + * + * @tparam Indexes A list of potential enum values in numerical format + * @return An std::array of names for every enum value in the indexes + */ + template <typename Enum, std::size_t... Indexes> + constexpr auto names(std::index_sequence<Indexes...>) noexcept { + static_assert(std::is_enum_v<Enum>, "names() requires enum type."); + + return std::array<std::string_view, sizeof...(Indexes)>{{enumNameV<Enum, static_cast<Enum>(Indexes)>...}}; + } + + /** + * For the specified Enum enumeration, this variable will contain a list of the names of all its values + */ + template <typename Enum> + inline constexpr std::array namesV = names<Enum>(std::make_index_sequence<MaximumValues>{}); + + /** + * Given a nested name (e.g. `Services::Parameter::value`), this function returns the innermost name (e.g. `value`) + */ + constexpr std::string_view removeNamespace(std::string_view in) { + auto pos = in.find_last_of("::"); + return in.substr(pos + 1); + } +} // namespace EnumMagic_ + +/** + * Get the name of an enum value, as defined in the source code. + */ +template <typename Enum> +std::string enumName(Enum value) { + using namespace EnumMagic_; + + auto index = static_cast<size_t>(value); + + if (index >= namesV<Enum>.size()) { + return "(Unknown)"; + } + + return std::string(removeNamespace(namesV<Enum>[index])); +} diff --git a/src/Platform/x86/ErrorHandler.cpp b/src/Platform/x86/ErrorHandler.cpp index 06ad3859..572d3424 100644 --- a/src/Platform/x86/ErrorHandler.cpp +++ b/src/Platform/x86/ErrorHandler.cpp @@ -9,6 +9,7 @@ #include <Message.hpp> #include <Logger.hpp> #include <type_traits> +#include "Helpers/EnumMagic.hpp" // TODO: Find a way to reduce the number of copies of this chunk template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType); @@ -27,7 +28,7 @@ void ErrorHandler::logError(const Message& message, ErrorType errorType) { */ << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error " << "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType) - << "]: " << std::underlying_type_t<ErrorType>(errorType); + << "]: " << enumName(errorType) << " (" << static_cast<std::underlying_type_t<ErrorType>>(errorType) << ")"; } template <typename ErrorType> @@ -39,5 +40,5 @@ void ErrorHandler::logError(ErrorType errorType) { */ << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " - << std::underlying_type_t<ErrorType>(errorType); + << enumName(errorType) << " (" << static_cast<std::underlying_type_t<ErrorType>>(errorType) << ")"; } diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp index d8be71a1..4928e91d 100644 --- a/src/Platform/x86/main.cpp +++ b/src/Platform/x86/main.cpp @@ -363,6 +363,8 @@ int main() { LOG_NOTICE << "ECSS Services test complete"; + ErrorHandler::reportInternalError(static_cast<ErrorHandler::InternalErrorType>(254)); + std::cout << UTCTimestamp() << std::endl; return 0; -} \ No newline at end of file +} diff --git a/test/Services/DummyService.cpp b/test/Services/DummyService.cpp index 188d1899..ff354935 100644 --- a/test/Services/DummyService.cpp +++ b/test/Services/DummyService.cpp @@ -13,5 +13,5 @@ TEST_CASE("Log string as message TM[128, 128]", "[service][st128]") { CHECK(report.messageType == DummyService::MessageType::LogString); char logOutput[39]; report.readString(logOutput, 39); - CHECK(strcmp(logOutput, "An amazing log that is very informative") == 0); + CHECK(memcmp(logOutput, "An amazing log that is very informative", 39) == 0); } diff --git a/test/Services/MemoryManagementServiceTests.cpp b/test/Services/MemoryManagementServiceTests.cpp index d37ac4c4..2ad8097e 100644 --- a/test/Services/MemoryManagementServiceTests.cpp +++ b/test/Services/MemoryManagementServiceTests.cpp @@ -28,6 +28,8 @@ TEST_CASE("TC[6,2]", "[service][st06]") { CHECK(pStr[0] == 'h'); CHECK(pStr[1] == 'R'); CHECK(pStr[2] == 'h'); + + free(pStr); } TEST_CASE("TC[6,5]", "[service][st06]") { -- GitLab