diff --git a/CHANGES.md b/CHANGES.md index a7ed80c42..e6ceb31ea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,30 @@ # Memfault Firmware SDK Changelog +## 1.3.2 - Sept 26, 2023 + +### :chart_with_upwards_trend: Improvements + +- Zephyr: + + - use `` instead of ``. Thanks @kmeihar for this change! + (see [#64](https://github.com/memfault/memfault-firmware-sdk/pull/64)) + +- nRF Connect SDK: + + - Add missing Kconfig flags `CONFIG_FLASH_MAP=y` + `CONFIG_STREAM_FLASH=y` for + the [`examples/nrf-connect-sdk/nrf5/`](examples/nrf-connect-sdk/nrf5/) + example app, for compatibility with nRF Connect SDK v2.4.1+. This was + required due to an + [upstream Zephyr change](https://github.com/zephyrproject-rtos/zephyr/commit/1b4b979f8789af6087f877c0daad0a660c1b9b28). + +- General: + - Add support for Memfault Compact Logs for C++ source files (previously only + supported in C source files). Compact logging can be enabled by setting + `MEMFAULT_LOG_COMPACT_ENABLE=1` in `memfault_platform_config.h`. See + [the docs](https://docs.memfault.com/docs/mcu/debugging/compact-logs) for + more details. + - Fix a missing include of `` required by the IAR compiler + ## 1.3.1 - Sept 21, 2023 ### :chart_with_upwards_trend: Improvements diff --git a/VERSION b/VERSION index aaae2dcbd..d695987d4 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ -BUILD ID: 3608 -GIT COMMIT: 4124d24ad -VERSION: 1.3.1 +BUILD ID: 3642 +GIT COMMIT: 5eb9f7b6a +VERSION: 1.3.2 diff --git a/components/include/memfault/components.h b/components/include/memfault/components.h index 5fab43de4..1cbf54e99 100644 --- a/components/include/memfault/components.h +++ b/components/include/memfault/components.h @@ -13,10 +13,6 @@ //! #include memfault/components.h //! -#ifdef __cplusplus -extern "C" { -#endif - #include "memfault/config.h" #include "memfault/core/arch.h" #include "memfault/core/batched_events.h" @@ -69,7 +65,3 @@ extern "C" { #include "memfault/util/rle.h" #include "memfault/util/varint.h" #include "memfault/version.h" - -#ifdef __cplusplus -} -#endif diff --git a/components/include/memfault/core/compact_log_helpers.h b/components/include/memfault/core/compact_log_helpers.h index 64174dccb..b00483ba0 100644 --- a/components/include/memfault/core/compact_log_helpers.h +++ b/components/include/memfault/core/compact_log_helpers.h @@ -27,10 +27,6 @@ #include "memfault/config.h" -#ifdef __cplusplus -extern "C" { -#endif - #if MEMFAULT_COMPACT_LOG_ENABLE #include "memfault/core/compiler.h" @@ -43,16 +39,52 @@ extern "C" { #ifdef __cplusplus +// C++ implementation of the type promotion logic +// +// Note: the C++ implementation requires the use of C++11 features and the GNU +// "##" variadic arg extension. Memfault Compact Logs in C++ require the +// compiler flag '--std=gnu++-11' or newer. -#ifdef MEMFAULT_UNITTEST - //! Note: For trace_event Memfault CppUTest tests, we pick up this header but do not actually use - //! the helpers defined so let's silence the warning generated -#else -# error "Compact logs not yet available when using CPP" -#endif +#include -#else // C Code implementation +// Default integer type is int64 +template +struct MemfaultLogArgPromotionType + : std::integral_constant {}; + +// If sizeof(T) <= 32, then it's int32 +template +struct MemfaultLogArgPromotionType< + T, typename std::enable_if::type> + : std::integral_constant {}; + +// More specific types +template <> +struct MemfaultLogArgPromotionType + : std::integral_constant {}; + +template <> +struct MemfaultLogArgPromotionType + : std::integral_constant {}; + +template <> +struct MemfaultLogArgPromotionType + : std::integral_constant {}; + +template <> +struct MemfaultLogArgPromotionType + : std::integral_constant {}; +template <> +struct MemfaultLogArgPromotionType + : std::integral_constant {}; + +// When expressing the final type via the template parameter expansion, operate +// on (arg) + 0 to force integer promotion +#define _MEMFAULT_LOG_ARG_PROMOTION_TYPE(arg) \ + MemfaultLogArgPromotionType::value + +#else // C Code implementation //! Preprocessor macro to encode promotion type info about each va_arg in a uint32_t //! @@ -94,6 +126,10 @@ extern "C" { default: sizeof((arg) + 0) <= sizeof(int) ? \ MEMFAULT_LOG_ARG_PROMOTED_TO_INT32 : MEMFAULT_LOG_ARG_PROMOTED_TO_INT64) \ +#endif // __cplusplus + +#ifdef __cplusplus +extern "C" { #endif #define _MF_FMT_0(fmt_op) 0 @@ -176,8 +212,8 @@ typedef struct MemfaultLogFmtElfSectionHeader { extern const sMemfaultLogFmtElfSectionHeader g_memfault_log_fmt_elf_section_hdr; -#endif /* MEMFAULT_COMPACT_LOG_ENABLE */ - #ifdef __cplusplus } #endif + +#endif /* MEMFAULT_COMPACT_LOG_ENABLE */ diff --git a/components/include/memfault/core/compiler_iar.h b/components/include/memfault/core/compiler_iar.h index ab820a2de..5e0bfafb6 100644 --- a/components/include/memfault/core/compiler_iar.h +++ b/components/include/memfault/core/compiler_iar.h @@ -15,6 +15,8 @@ extern "C" { #endif +#include "intrinsics.h" + #define MEMFAULT_PACKED __packed #define MEMFAULT_PACKED_STRUCT MEMFAULT_PACKED struct #define MEMFAULT_NORETURN __noreturn diff --git a/components/include/memfault/version.h b/components/include/memfault/version.h index f2e6e50d1..848539940 100644 --- a/components/include/memfault/version.h +++ b/components/include/memfault/version.h @@ -19,8 +19,8 @@ typedef struct { uint8_t patch; } sMfltSdkVersion; -#define MEMFAULT_SDK_VERSION { .major = 1, .minor = 3, .patch = 1 } -#define MEMFAULT_SDK_VERSION_STR "1.3.1" +#define MEMFAULT_SDK_VERSION { .major = 1, .minor = 3, .patch = 2 } +#define MEMFAULT_SDK_VERSION_STR "1.3.2" #ifdef __cplusplus } diff --git a/examples/freertos/Makefile b/examples/freertos/Makefile index 916929258..fa499feed 100644 --- a/examples/freertos/Makefile +++ b/examples/freertos/Makefile @@ -20,14 +20,17 @@ BUILD_DIR := build/$(BOARD) ELF = $(BUILD_DIR)/main.elf ARM_CC ?= arm-none-eabi-gcc +ARM_CXX ?= arm-none-eabi-g++ # if cc isn't set by the user, set it to ARM_CC ifeq ($(origin CC),default) CC := $(ARM_CC) +CXX := $(ARM_CXX) endif # use ccache if available CCACHE := $(shell command -v ccache 2> /dev/null) ifdef CCACHE CC := ccache $(CC) +CXX := ccache $(CXX) endif OBJCOPY ?= $(shell $(CC) -print-prog-name=objcopy) @@ -59,6 +62,7 @@ FREERTOS_SRCS = \ # Add application sources SRCS += \ main.c \ + compact_log.cpp \ $(BOARD_DIR)/startup.c \ console.c \ $(BOARD_DIR)/memfault_platform_impl.c \ @@ -79,7 +83,7 @@ SRCS += \ $(MEMFAULT_SDK_ROOT)/ports/panics/src/memfault_platform_ram_backed_coredump.c \ # Fixup build path for objects of the Memfault SDK, all build output kept within build/ -OBJS := $(subst $(MEMFAULT_SDK_ROOT),memfault-firmware-sdk,$(SRCS:%.c=$(BUILD_DIR)/%.o)) +OBJS := $(subst $(MEMFAULT_SDK_ROOT),memfault-firmware-sdk,$(SRCS:%=$(BUILD_DIR)/%.o)) INCLUDE_PATHS += \ -I$(FREERTOS_DIR)/include \ @@ -102,8 +106,10 @@ CFLAGS += \ -Og \ -MD \ +LINKER_SCRIPT = $(BOARD_DIR)/linker.ld + LDFLAGS += \ - -Wl,-T$(BOARD_DIR)/linker.ld \ + -Wl,-T$(LINKER_SCRIPT) \ -Wl,--gc-sections \ --specs=nano.specs \ --specs=rdimon.specs \ @@ -119,17 +125,22 @@ all: $(ELF) $(SRCS): $(FREERTOS_DIR)/.clonedone # Add rules for patched build objects from SDK -$(BUILD_DIR)/memfault-firmware-sdk/%.o: $(MEMFAULT_SDK_ROOT)/%.c +$(BUILD_DIR)/memfault-firmware-sdk/%.c.o: $(MEMFAULT_SDK_ROOT)/%.c mkdir -p $(dir $@) $(info Compiling $<) $(CC) $(CFLAGS) $(INCLUDE_PATHS) -c $< -o $@ -$(BUILD_DIR)/%.o: %.c +$(BUILD_DIR)/%.c.o: %.c mkdir -p $(dir $@) $(info Compiling $<) $(CC) $(CFLAGS) $(INCLUDE_PATHS) -c $< -o $@ -$(ELF).uncompressed: $(OBJS) +$(BUILD_DIR)/%.cpp.o: %.cpp + mkdir -p $(dir $@) + $(info Compiling $<) + $(CXX) -std=gnu++11 $(CFLAGS) $(INCLUDE_PATHS) -c $< -o $@ + +$(ELF).uncompressed: $(OBJS) $(LINKER_SCRIPT) $(info Linking $@) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $@ diff --git a/examples/freertos/boards/qemu_mps2_an385/Makefile b/examples/freertos/boards/qemu_mps2_an385/Makefile index 46f73ef39..717054efa 100644 --- a/examples/freertos/boards/qemu_mps2_an385/Makefile +++ b/examples/freertos/boards/qemu_mps2_an385/Makefile @@ -11,5 +11,7 @@ RUN_COMMAND = qemu-system-arm -machine mps2-an385 -monitor null -semihosting \ DEBUG_COMMAND = $(RUN_COMMAND) $(ATTACH_DEBUGGER) +# Permit overriding the gdb executable +GDB ?= gdb GDB_COMMAND = \ - gdb --ex 'target extended-remote :1234' $(ELF) + $(GDB) --ex 'target extended-remote :1234' $(ELF) diff --git a/examples/freertos/boards/qemu_mps2_an385/linker.ld b/examples/freertos/boards/qemu_mps2_an385/linker.ld index 048d28ebd..310f71778 100644 --- a/examples/freertos/boards/qemu_mps2_an385/linker.ld +++ b/examples/freertos/boards/qemu_mps2_an385/linker.ld @@ -119,6 +119,12 @@ SECTIONS __StackLimit = __StackTop - _Min_Stack_Size; PROVIDE(__stack = __StackTop); + log_fmt 0xF0000000 (INFO): + { + KEEP(*(*.log_fmt_hdr)) + KEEP(*(log_fmt)) + } + /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack") } diff --git a/examples/freertos/compact_log.cpp b/examples/freertos/compact_log.cpp new file mode 100644 index 000000000..eb9adaa3e --- /dev/null +++ b/examples/freertos/compact_log.cpp @@ -0,0 +1,22 @@ +//! @file +//! +//! Small example showcasing the use of compact logs from C++. + +#include "compact_log.h" + +#include + +#include "memfault/components.h" + +void compact_log_cpp_example(void) { +#if MEMFAULT_COMPACT_LOG_ENABLE + MEMFAULT_COMPACT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, + "This is a compact log example from c++ " + // clang-format off + "%d" " %" PRIu64 " %f" " %f" " %s", + 1234, (uint64_t)0x7, 1.0f, 2.0, "1212" +// ^int ^uint64_t ^float ^double ^string + // clang-format on + ); +#endif +} diff --git a/examples/freertos/compact_log.h b/examples/freertos/compact_log.h new file mode 100644 index 000000000..ae70d1867 --- /dev/null +++ b/examples/freertos/compact_log.h @@ -0,0 +1,16 @@ +//! @file +//! +//! Copyright (c) Memfault, Inc. +//! See License.txt for details + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void compact_log_cpp_example(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/freertos/main.c b/examples/freertos/main.c index b32ab4119..13148c320 100644 --- a/examples/freertos/main.c +++ b/examples/freertos/main.c @@ -9,6 +9,7 @@ #include #include "FreeRTOS.h" +#include "compact_log.h" #include "console.h" #include "memfault/components.h" #include "task.h" @@ -144,6 +145,17 @@ int main(void) { prv_heap_task(); prv_metrics_task(); console_init(); + + compact_log_cpp_example(); + + // Enable this to export compact log data to stdio. This is intended only for + // testing deserialization of the compact log example above and is not + // typically useful. +#if defined(DEBUG_COMPACT_LOGS) + printf(">> exporting logs...\n"); + memfault_log_export_logs(); +#endif + // Initialize the FreeRTOS kernel vTaskStartScheduler(); diff --git a/examples/freertos/memfault/memfault_platform_config.h b/examples/freertos/memfault/memfault_platform_config.h index e1943b2d1..acf039aba 100644 --- a/examples/freertos/memfault/memfault_platform_config.h +++ b/examples/freertos/memfault/memfault_platform_config.h @@ -14,3 +14,4 @@ #define MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE 1 #define MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 0 #define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS 60 +#define MEMFAULT_COMPACT_LOG_ENABLE 1 diff --git a/examples/nrf-connect-sdk/nrf5/memfault_demo_app/prj.conf b/examples/nrf-connect-sdk/nrf5/memfault_demo_app/prj.conf index 585abc25b..e56ffd5c2 100644 --- a/examples/nrf-connect-sdk/nrf5/memfault_demo_app/prj.conf +++ b/examples/nrf-connect-sdk/nrf5/memfault_demo_app/prj.conf @@ -48,6 +48,8 @@ CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_MCUBOOT_IMG_MANAGER=y CONFIG_IMG_MANAGER=y CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_STREAM_FLASH=y CONFIG_WATCHDOG=y diff --git a/examples/nrf-connect-sdk/nrf5/memfault_demo_app/west.yml b/examples/nrf-connect-sdk/nrf5/memfault_demo_app/west.yml index 8e4f1ef7b..341ce72c2 100644 --- a/examples/nrf-connect-sdk/nrf5/memfault_demo_app/west.yml +++ b/examples/nrf-connect-sdk/nrf5/memfault_demo_app/west.yml @@ -5,7 +5,7 @@ manifest: projects: - name: nrf url: https://github.com/nrfconnect/sdk-nrf - revision: v2.0.2 + revision: v2.4.2 import: true # Adding the memfault-firmware-sdk module here overrides the version the nRF diff --git a/ports/zephyr/v2.4/memfault_fault_handler.c b/ports/zephyr/v2.4/memfault_fault_handler.c index 0749c8ea0..4cbd10567 100644 --- a/ports/zephyr/v2.4/memfault_fault_handler.c +++ b/ports/zephyr/v2.4/memfault_fault_handler.c @@ -24,13 +24,10 @@ // Starting in v3.4, the handler set function was renamed and the declaration // added to a public header #if MEMFAULT_ZEPHYR_VERSION_GT(3, 3) - // The nmi.h header was moved after Zephyr v3.4. Support both locations. - #if __has_include(MEMFAULT_ZEPHYR_INCLUDE(arch/arm/aarch32/nmi.h)) - #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/aarch32/nmi.h) - #elif __has_include(MEMFAULT_ZEPHYR_INCLUDE(arch/arm/nmi.h)) - #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/nmi.h) + #if MEMFAULT_ZEPHYR_VERSION_GT(3, 4) + #include #else - #error "Missing Zephyr nmi.h header" + #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/aarch32/nmi.h) #endif #define MEMFAULT_ZEPHYR_NMI_HANDLER_SET z_arm_nmi_set_handler #else diff --git a/scripts/cmsis_pack_bundle.py b/scripts/cmsis_pack_bundle.py index fd9ac5b0e..762fe7dab 100644 --- a/scripts/cmsis_pack_bundle.py +++ b/scripts/cmsis_pack_bundle.py @@ -139,32 +139,22 @@ def files_to_link(dir_glob, common_prefix, sdk_version): yield get_file_element(file_name, common_prefix, sdk_version) -def get_latest_github_artifact(url): +def get_latest_pdsc(): + """Retrieve the contents of the latest pdsc file""" + url = f"https://github.com/{GITHUB_REPO}/releases/latest/download/Memfault.FirmwareSDK.pdsc" logging.debug("Fetching %s", url) resp = requests.get(url) # There should always be a pidx/pdsc file available. Only skip if the env # var is set and it's a 404, which is only useful before the first release. if resp.status_code == 404 or os.getenv("SKIP_MISSING_PRIOR_RELEASE"): - logging.debug("Skipping missing pidx file") + logging.debug("Skipping missing file") return None resp.raise_for_status() return resp.text -def get_latest_pdsc(): - """Retrieve the contents of the latest pdsc file""" - url = f"https://github.com/{GITHUB_REPO}/releases/latest/download/Memfault.FirmwareSDK.pdsc" - return get_latest_github_artifact(url) - - -def get_latest_pidx(): - """Retrieve the contents of the latest pidx file""" - url = f"https://github.com/{GITHUB_REPO}/releases/latest/download/Memfault.pidx" - return get_latest_github_artifact(url) - - def lxml_reindent_tree(root): """Using lxml, re-indent the tree to produce consistent output""" parser = lxml.etree.XMLParser(remove_blank_text=True) diff --git a/scripts/memfault_gdb.py b/scripts/memfault_gdb.py index 97caba617..439bb68fc 100644 --- a/scripts/memfault_gdb.py +++ b/scripts/memfault_gdb.py @@ -323,7 +323,7 @@ def _read_registers(self, core, gdb_thread, analytics_props): # Scoop up all register values vals = [] - for i in range(0, len(xtensa_gdb_idx_regs)): + for i in range(len(xtensa_gdb_idx_regs)): start_idx = i * 8 hexstr = registers[start_idx : start_idx + 8] vals.append(bytearray.fromhex(hexstr)) @@ -345,7 +345,7 @@ def _read_registers(self, core, gdb_thread, analytics_props): def get_current_registers(self, gdb_thread, analytics_props): result = [] try: - for core_id in range(0, self.num_cores): + for core_id in range(self.num_cores): result.append(self._read_registers(core_id, gdb_thread, analytics_props)) except Exception: analytics_props["core_reg_collection_error"] = {"traceback": traceback.format_exc()} @@ -433,7 +433,7 @@ def _try_collect_mpu_settings(): result += mpu_ctrl_data num_regions = (mpu_type >> 8) & 0xFF - for i in range(0, num_regions): + for i in range(num_regions): _write_register(mpu_rnr, i) _, data = _read_register(mpu_rbar) result += data diff --git a/tests/src/test_memfault_compact_log_c.c b/tests/src/test_memfault_compact_log_c.c index c65972c9b..769ffd614 100644 --- a/tests/src/test_memfault_compact_log_c.c +++ b/tests/src/test_memfault_compact_log_c.c @@ -25,77 +25,22 @@ static void prv_expect_compact_log_call(uint32_t compressed_fmt) { "compressed_fmt", compressed_fmt); } -#define MEMFAULT_COMPACT_LOG_CHECK(format, ...) \ - do { \ - MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ## __VA_ARGS__); \ - prv_compact_log_mock(MFLT_GET_COMPRESSED_LOG_FMT(__VA_ARGS__), \ - ## __VA_ARGS__); \ - } while (0) +#define MOCK_CHECK_EXPECTIONS_AND_CLEAR() \ +do { \ + mock_c()->checkExpectations(); \ + mock_c()->clear(); \ +} while (0) +#include "test_memfault_compact_log_cxx.c" void test_compact_log_c_argument_expansion(void) { - mock_c()->strictOrder(); - - // Up to 15 arguments should get serialized correctly - // compressed format code for integers is 0b00 - for (int i = 0; i <= 15; i++) { - const uint32_t compressed_fmt = 0x1 << (i *2); - prv_expect_compact_log_call(compressed_fmt); - } - MEMFAULT_COMPACT_LOG_CHECK("0 args"); - MEMFAULT_COMPACT_LOG_CHECK("1 arg %d", 1); - MEMFAULT_COMPACT_LOG_CHECK("2 arg %d %d", 1, 2); - MEMFAULT_COMPACT_LOG_CHECK("3 arg %d %d %d", 1, 2, 3); - MEMFAULT_COMPACT_LOG_CHECK("4 arg %d %d %d %d", 1, 2, 3, 4); - MEMFAULT_COMPACT_LOG_CHECK("5 arg %d %d %d %d %d", 1, 2, 3, 4, 5); - MEMFAULT_COMPACT_LOG_CHECK("6 arg %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6); - MEMFAULT_COMPACT_LOG_CHECK("7 arg %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7); - MEMFAULT_COMPACT_LOG_CHECK("8 arg %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8); - MEMFAULT_COMPACT_LOG_CHECK("9 arg %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9); - MEMFAULT_COMPACT_LOG_CHECK("10 arg %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - MEMFAULT_COMPACT_LOG_CHECK("11 arg %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); - MEMFAULT_COMPACT_LOG_CHECK("12 arg %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); - MEMFAULT_COMPACT_LOG_CHECK("13 arg %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); - MEMFAULT_COMPACT_LOG_CHECK("14 arg %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); - MEMFAULT_COMPACT_LOG_CHECK("15 arg %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + test_compact_log_cxx_argument_expansion(); } void test_compact_log_c_multi_arg(void) { - mock_c()->checkExpectations(); - mock_c()->clear(); - - prv_expect_compact_log_call(0x1C9); - const uint8_t u8 = 8; - const uint64_t u64 = 0x7fffeee10001234; - - MEMFAULT_COMPACT_LOG_CHECK("Assorted Types: %s %"PRIx8" %f %"PRIx64, - "hello", u8, (double)2.1f, u64); - mock_c()->checkExpectations(); - mock_c()->clear(); - - const char *s1 = NULL; - const char s2[] = "s2"; - char *s3 = "s3"; - char s4[] = "s4"; - - prv_expect_compact_log_call(0x1FF); - MEMFAULT_COMPACT_LOG_CHECK("Strings %s %s %s %s", s1, s2, s3, s4); + test_compact_log_cxx_multi_arg(); } void test_compact_log_c_multi_complex(void) { - // a variety of different argument types - const char *s = "s"; - uint64_t llx = 0xdeadbeef1; - double f = 1.2; - int d = 10; - int64_t lld = 1; - - // A complex formatter with all the different types interleaved - // 0b111.0010.0101.1000.1110.0111.0001.1011 - prv_expect_compact_log_call(0x7258E71B); - MEMFAULT_COMPACT_LOG_CHECK("%s %d %f %"PRIx64": " - "%"PRId64" %f %d %s: " - "%f %"PRId64" %s %d: " - "%"PRId64" %f %s", - s, d, f, llx, lld, f, d, s, f, lld, s, d, lld, f, s); + test_compact_log_cxx_multi_complex(); } diff --git a/tests/src/test_memfault_compact_log_cxx.c b/tests/src/test_memfault_compact_log_cxx.c new file mode 100644 index 000000000..580f8a518 --- /dev/null +++ b/tests/src/test_memfault_compact_log_cxx.c @@ -0,0 +1,87 @@ +//! @file +//! +//! Copyright (c) Memfault, Inc. +//! See License.txt for details +//! +//! Note: These tests are reused in C++ and C tests. See the files including +//! this one for how they're meant to be used. + +#include "memfault_test_compact_log_c.h" + +#include +#include + +#include "memfault/core/compact_log_helpers.h" +#include "memfault/core/compact_log_compile_time_checks.h" + +#include "CppUTestExt/MockSupport_c.h" + +#define MEMFAULT_COMPACT_LOG_CHECK(format, ...) \ + do { \ + MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ## __VA_ARGS__); \ + prv_compact_log_mock(MFLT_GET_COMPRESSED_LOG_FMT(__VA_ARGS__), \ + ## __VA_ARGS__); \ + } while (0) + +static void test_compact_log_cxx_argument_expansion(void) { + // Up to 15 arguments should get serialized correctly + // compressed format code for integers is 0b00 + for (int i = 0; i <= 15; i++) { + const uint32_t compressed_fmt = 0x1 << (i *2); + prv_expect_compact_log_call(compressed_fmt); + } + MEMFAULT_COMPACT_LOG_CHECK("0 args"); + MEMFAULT_COMPACT_LOG_CHECK("1 arg %d", 1); + MEMFAULT_COMPACT_LOG_CHECK("2 arg %d %d", 1, 2); + MEMFAULT_COMPACT_LOG_CHECK("3 arg %d %d %d", 1, 2, 3); + MEMFAULT_COMPACT_LOG_CHECK("4 arg %d %d %d %d", 1, 2, 3, 4); + MEMFAULT_COMPACT_LOG_CHECK("5 arg %d %d %d %d %d", 1, 2, 3, 4, 5); + MEMFAULT_COMPACT_LOG_CHECK("6 arg %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6); + MEMFAULT_COMPACT_LOG_CHECK("7 arg %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7); + MEMFAULT_COMPACT_LOG_CHECK("8 arg %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8); + MEMFAULT_COMPACT_LOG_CHECK("9 arg %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9); + MEMFAULT_COMPACT_LOG_CHECK("10 arg %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + MEMFAULT_COMPACT_LOG_CHECK("11 arg %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); + MEMFAULT_COMPACT_LOG_CHECK("12 arg %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + MEMFAULT_COMPACT_LOG_CHECK("13 arg %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); + MEMFAULT_COMPACT_LOG_CHECK("14 arg %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); + MEMFAULT_COMPACT_LOG_CHECK("15 arg %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); +} + +static void test_compact_log_cxx_multi_arg(void) { + MOCK_CHECK_EXPECTIONS_AND_CLEAR(); + + prv_expect_compact_log_call(0x1C9); + const uint8_t u8 = 8; + const uint64_t u64 = 0x7fffeee10001234; + + MEMFAULT_COMPACT_LOG_CHECK("Assorted Types: %s %" PRIx8 " %f %" PRIx64, + "hello", u8, (double)2.1f, u64); + MOCK_CHECK_EXPECTIONS_AND_CLEAR(); + + const char *s1 = NULL; + const char s2[] = "s2"; + const char *s3 = "s3"; + char s4[] = "s4"; + + prv_expect_compact_log_call(0x1FF); + MEMFAULT_COMPACT_LOG_CHECK("Strings %s %s %s %s", s1, s2, s3, s4); +} + +static void test_compact_log_cxx_multi_complex(void) { + // a variety of different argument types + const char *s = "s"; + uint64_t llx = 0xdeadbeef1; + double f = 1.2; + int d = 10; + int64_t lld = 1; + + // A complex formatter with all the different types interleaved + // 0b111.0010.0101.1000.1110.0111.0001.1011 + prv_expect_compact_log_call(0x7258E71B); + MEMFAULT_COMPACT_LOG_CHECK("%s %d %f %" PRIx64 ": " + "%" PRId64 " %f %d %s: " + "%f %" PRId64 " %s %d: " + "%" PRId64 " %f %s", + s, d, f, llx, lld, f, d, s, f, lld, s, d, lld, f, s); +} diff --git a/tests/src/test_memfault_compact_log_macros.cpp b/tests/src/test_memfault_compact_log_macros.cpp index ea1076b9d..1b6aa4500 100644 --- a/tests/src/test_memfault_compact_log_macros.cpp +++ b/tests/src/test_memfault_compact_log_macros.cpp @@ -11,6 +11,9 @@ #include "memfault_test_compact_log_c.h" +#include "memfault/core/compact_log_helpers.h" +#include "memfault/core/compact_log_compile_time_checks.h" + TEST_GROUP(MfltCompactLogMacros) { void setup() { mock().strictOrder(); @@ -33,3 +36,32 @@ TEST(MfltCompactLogMacros, Test_MfltCompactLog_MultiArg) { TEST(MfltCompactLogMacros, Test_MfltCompactLog_Complex) { test_compact_log_c_multi_complex(); } + +//! Mocks and helper for the Cxx->C++ generic tests below +static void prv_compact_log_mock(uint32_t compressed_fmt, ...) { + mock().actualCall(__func__).withUnsignedIntParameter("compressed_fmt", compressed_fmt); +} + +static void prv_expect_compact_log_call(uint32_t compressed_fmt) { + mock().expectOneCall("prv_compact_log_mock").withUnsignedIntParameter( + "compressed_fmt", compressed_fmt); +} +#define MOCK_CHECK_EXPECTIONS_AND_CLEAR() \ +do { \ + mock().checkExpectations(); \ + mock().clear(); \ +} while (0) + +#include "test_memfault_compact_log_cxx.c" + +TEST(MfltCompactLogMacros, Test_MfltCompactLog_Cpp_ArgExpansion) { + test_compact_log_cxx_argument_expansion(); +} + +TEST(MfltCompactLogMacros, Test_MfltCompactLog_Cpp_MultiArg) { + test_compact_log_cxx_multi_arg(); +} + +TEST(MfltCompactLogMacros, Test_MfltCompactLog_Cpp_Complex) { + test_compact_log_cxx_multi_complex(); +}