Skip to content

Commit

Permalink
Memfault Firmware SDK 0.24.0 (Build 283912)
Browse files Browse the repository at this point in the history
  • Loading branch information
Memfault Inc committed Jul 29, 2021
1 parent 907c880 commit 3973d90
Show file tree
Hide file tree
Showing 32 changed files with 1,068 additions and 25 deletions.
30 changes: 29 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
### Changes between Memfault SDK 0.24.0 and SDK 0.23.0 - July 27, 2021

#### :chart_with_upwards_trend: Improvements

- Added "compact log" support to trace events. When enabled, the format string will be removed at
compile time from calls to `MEMFAULT_TRACE_EVENT_WITH_LOG` and an integer along with arguments
will be serialized instead. The actual string will recovered and formatted when it arrives in the
Memfault cloud. This leads to a massive reduction in space & bandwidth needed to send trace
events. For more details about how to set up,
[check out this guide!](https://mflt.io/compact-logs)
- Fixed a `-Wshadow` compiler error that would arise in
[`memfault_coredump_regions_armv7.c`](components/panics/src/memfault_coredump_regions_armv7.c) when
`MEMFAULT_COLLECT_MPU_STATE` was enabled
- Updated debug print utility in
[`memfault_coredump_storage_debug.c`](components/panics/src/memfault_coredump_storage_debug.c) to
guard against potentially printing an uninitialized string.
- Removed unnecessary extra argument from `MEMFAULT_SOFTWARE_WATCHDOG`

#### :boom: Breaking Changes

- If you were already using `MEMFAULT_SOFTWARE_WATCHDOG`, you will need to update your call site
invocations to remove the argument being passed. i.e

```diff
- MEMFAULT_SOFTWARE_WATCHDOG(0);
+ MEMFAULT_SOFTWARE_WATCHDOG();
```

### Changes between Memfault SDK 0.23.0 and SDK 0.22.0 - July 8, 2021

#### :chart_with_upwards_trend: Improvements
Expand Down Expand Up @@ -1332,7 +1360,7 @@ void record_temperature(void) {
The feature can be disabled completely by adding
`-DMEMFAULT_COLLECT_INTERRUPT_STATE=0` to your compiler flags. More
configuration options can be found in
[memfault_coredump_regions_armv7.m](components/panics/src/memfault_coredump_regions_armv7.c).
[memfault_coredump_regions_armv7.c](components/panics/src/memfault_coredump_regions_armv7.c).
- Improve documentation about integrating the SDK within a project in README
- Update Release note summary to use markdown headings for easier referencing.
- Update try script used to collect coredumps via GDB to also collect
Expand Down
4 changes: 2 additions & 2 deletions VERSION
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
BUILD ID: 268452
GIT COMMIT: 9d1b0d8da
BUILD ID: 283912
GIT COMMIT: 7f6c0b54c
133 changes: 133 additions & 0 deletions components/core/src/memfault_compact_log_serializer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details

#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "memfault/config.h"

#if MEMFAULT_COMPACT_LOG_ENABLE

#include "memfault/core/compiler.h"
#include "memfault/core/debug_log.h"
#include "memfault/core/compact_log_helpers.h"
#include "memfault/core/compact_log_serializer.h"
#include "memfault/util/cbor.h"

//! Compact logs are placed in a linker section named "log_fmt". This is a symbol exposed by the
//! linker which points to the start of the section
extern uint32_t __start_log_fmt;

//! Note: We don't read this in the firmware but it is used during the decode
//! process to sanity check the section is being laid out as we would expect.
MEMFAULT_PUT_IN_SECTION(".log_fmt_hdr")
const sMemfaultLogFmtElfSectionHeader g_memfault_log_fmt_elf_section_hdr = {
.magic = 0x66474f4c, /* LOGf */
.version = 1,
};

bool memfault_vlog_compact_serialize(sMemfaultCborEncoder *encoder, uint32_t log_id,
uint32_t compressed_fmt, va_list args) {
const uint32_t bits_per_arg = 2;
const uint32_t bits_per_arg_mask = (1 << bits_per_arg) - 1;

const size_t num_args = (31UL - MEMFAULT_CLZ(compressed_fmt)) / bits_per_arg;

if (!memfault_cbor_encode_array_begin(encoder, 1 /* log_id */ + num_args)) {
return false;
}

// We use an offset within the section to reduce the space needed when serializing
// the log.
uint32_t log_fmt_offset = log_id - (uint32_t)(uintptr_t)&__start_log_fmt;

if (!memfault_cbor_encode_unsigned_integer(encoder, log_fmt_offset)) {
return false;
}

for (size_t i = 0; i < num_args; i++) {
// See memfault/core/compact_log_helpers.h for more details. In essence,
// the promotion type of a given va_arg is encoded within two bits of the
// compressed_fmt field.
const uint32_t type =
(compressed_fmt >> ((num_args - i - 1) * bits_per_arg)) & bits_per_arg_mask;

bool success = false;
switch (type) {

case MEMFAULT_LOG_ARG_PROMOTED_TO_INT32: {
int32_t val = va_arg(args, int32_t);
success = memfault_cbor_encode_signed_integer(encoder, val);
break;
}

case MEMFAULT_LOG_ARG_PROMOTED_TO_INT64: {
// We differentiate between an 64 bit and 32 bit integer arg
// by packing the cbor encoded int in an array.
uint64_t val = va_arg(args, uint64_t);
success = memfault_cbor_encode_array_begin(encoder, 1) &&
memfault_cbor_encode_long_signed_integer(encoder, (int64_t)val);
break;
}

case MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE: {
// Note: Per the ARM ABI, doubles are just serialized out onto the stack
// and occupy 8 bytes:
//
// Chapter 7: THE STANDARD VARIANTS
// For a variadic function the base standard is always used both for argument passing and
// result return.
//
// 6.5 Parameter Passing
// In the base standard there are no arguments that are candidates for a co-processor
// register class.
//
// So for ARM we could just do the following:
// uint64_t val = va_arg(args, uint64_t)
//
// But parsing as a double is more portable and get's optimized away by compilers
// like GCC anyway: https://godbolt.org/z/mVAS7D
MEMFAULT_STATIC_ASSERT(sizeof(uint64_t) == sizeof(double), "double does not fit in uint64_t");

// Note: We use memcpy to properly type-pun / avoid strict-aliasing violation:
// https://mflt.io/strict-aliasing-type-punning
double val = va_arg(args, double);
uint64_t double_as_uint64;
memcpy(&double_as_uint64, &val, sizeof(val));
success = memfault_cbor_encode_uint64_as_double(encoder, double_as_uint64);
break;
}

case MEMFAULT_LOG_ARG_PROMOTED_TO_STR: {
const char *str = va_arg(args, const char*);
success = memfault_cbor_encode_string(encoder, str != NULL ? str : "(null)");
break;
}

default:
break;

}

if (!success) {
return false;
}
}

return true;
}

bool memfault_log_compact_serialize(sMemfaultCborEncoder *encoder, uint32_t log_id,
uint32_t compressed_fmt, ...) {
va_list args;
va_start(args, compressed_fmt);
const bool success = memfault_vlog_compact_serialize(encoder, log_id, compressed_fmt, args);
va_end(args);

return success;
}

#endif /* MEMFAULT_COMPACT_LOG_ENABLE */
56 changes: 53 additions & 3 deletions components/core/src/memfault_trace_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
//! Copyright (c) Memfault, Inc.
//! See License.txt for details

#include "memfault/core/trace_event.h"
#include "memfault_trace_event_private.h"

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
Expand All @@ -14,12 +11,15 @@

#include "memfault/config.h"
#include "memfault/core/arch.h"
#include "memfault/core/compact_log_serializer.h"
#include "memfault/core/debug_log.h"
#include "memfault/core/event_storage.h"
#include "memfault/core/event_storage_implementation.h"
#include "memfault/core/math.h"
#include "memfault/core/serializer_helper.h"
#include "memfault/core/serializer_key_ids.h"
#include "memfault/core/trace_event.h"
#include "memfault_trace_event_private.h"

#define MEMFAULT_TRACE_EVENT_STORAGE_UNINITIALIZED (-1)
#define MEMFAULT_TRACE_EVENT_STORAGE_OUT_OF_SPACE (-2)
Expand Down Expand Up @@ -94,8 +94,13 @@ static bool prv_encode_cb(sMemfaultCborEncoder *encoder, void *ctx) {
}

if (success && log_present) {
#if !MEMFAULT_COMPACT_LOG_ENABLE
success = memfault_serializer_helper_encode_byte_string_kv_pair(encoder,
kMemfaultTraceInfoEventKey_Log, info->log, info->log_len);
#else
success = memfault_cbor_encode_unsigned_integer(encoder, kMemfaultTraceInfoEventKey_CompactLog) &&
memfault_cbor_join(encoder, info->log, info->log_len);
#endif
}

return success;
Expand Down Expand Up @@ -205,6 +210,8 @@ int memfault_trace_event_with_status_capture(eMfltTraceReasonUser reason, void *
return prv_capture_trace_event_info(&event_info);
}

#if !MEMFAULT_COMPACT_LOG_ENABLE

int memfault_trace_event_with_log_capture(
eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr, const char *fmt, ...) {

Expand Down Expand Up @@ -236,6 +243,49 @@ int memfault_trace_event_with_log_capture(
return prv_capture_trace_event_info(&event_info);
}

#else

static void prv_fill_compact_log_cb(void *ctx, uint32_t offset, const void *buf, size_t buf_len) {
uint8_t *log = (uint8_t *)ctx;
memcpy(&log[offset], buf, buf_len);
}

int memfault_trace_event_with_compact_log_capture(
eMfltTraceReasonUser reason, void *lr_addr,
uint32_t log_id, uint32_t compressed_fmt, ...) {

#if !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED
// If a log capture takes place while in an ISR we just record a normal trace event
if (memfault_arch_is_inside_isr()) {
return memfault_trace_event_capture(reason, 0, lr_addr);
}
#endif /* !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED */

va_list args;
va_start(args, compressed_fmt);
uint8_t log[MEMFAULT_TRACE_EVENT_MAX_LOG_LEN] = { 0 };

sMemfaultCborEncoder encoder;
memfault_cbor_encoder_init(&encoder, prv_fill_compact_log_cb, log, sizeof(log));
memfault_vlog_compact_serialize(&encoder, log_id, compressed_fmt, args);
size_t log_len = memfault_cbor_encoder_deinit(&encoder);

sMemfaultTraceEventInfo event_info = {
.reason = reason,
// Note: pc is recovered from file/line encoded in compact log so no need to collect!
.pc_addr = 0,
.return_addr = lr_addr,
.opt_fields = TRACE_EVENT_OPT_FIELD_LOG_MASK,
.log = &log[0],
.log_len = log_len,
};
va_end(args);

return prv_capture_trace_event_info(&event_info);
}

#endif

size_t memfault_trace_event_compute_worst_case_storage_size(void) {
sMemfaultTraceEventInfo event_info = {
.reason = kMfltTraceReasonUser_NumReasons,
Expand Down
5 changes: 2 additions & 3 deletions components/demo/src/memfault_demo_cli_trace_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
#include "memfault/core/debug_log.h"
#include "memfault/core/trace_event.h"

int memfault_demo_cli_cmd_trace_event_capture(MEMFAULT_UNUSED int argc,
MEMFAULT_UNUSED char *argv[]) {
int memfault_demo_cli_cmd_trace_event_capture(int argc, MEMFAULT_UNUSED char *argv[]) {
// For more information on user-defined error reasons, see
// the MEMFAULT_TRACE_REASON_DEFINE macro in trace_reason_user.h .
MEMFAULT_TRACE_EVENT(MemfaultCli_Test);
MEMFAULT_TRACE_EVENT_WITH_LOG(MemfaultCli_Test, "Example Trace Event. Num Args %d", argc);
return 0;
}
2 changes: 1 addition & 1 deletion components/demo/src/panics/memfault_demo_panics.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ int memfault_demo_cli_cmd_crash(int argc, char *argv[]) {
break;

case 4:
MEMFAULT_SOFTWARE_WATCHDOG(0);
MEMFAULT_SOFTWARE_WATCHDOG();
break;

default:
Expand Down
59 changes: 59 additions & 0 deletions components/include/memfault/core/compact_log_compile_time_checks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details
//!
//! @brief
//!
//! Compile time validity checks run on compact logs:
//! 1) Enables printf() style Wformat checking
//! 2) Verifies that number of args passed is <= the maximum number supported (15)

#include "memfault/config.h"

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>

#if MEMFAULT_COMPACT_LOG_ENABLE

#include "memfault/core/compiler.h"
#include "memfault/core/preprocessor.h"

#define MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS 15


static void _run_printf_like_func_check(const char* format, ...)
MEMFAULT_PRINTF_LIKE_FUNC(1, 2);

//! Mark the function as used to prevent warnings in situations where this header is included but
//! no logging is actually used
MEMFAULT_USED
static void _run_printf_like_func_check(MEMFAULT_UNUSED const char* format, ...) { }

//! Compilation time checks on log formatter
//!
//! - static asserts that argument list does not exceed allowed length.
//! - Runs printf() style format checking (behind a "if (false)" so that
//! the actual code gets optimized away)
#define MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ...) \
do { \
MEMFAULT_STATIC_ASSERT( \
MEMFAULT_ARG_COUNT_UP_TO_32(__VA_ARGS__) <= MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS , \
MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_ARG_COUNT_UP_TO_32(__VA_ARGS__)) \
" args > MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS (" \
MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS) ")!"); \
if (false) { \
_run_printf_like_func_check(format, ## __VA_ARGS__); \
} \
} while (0)

#endif /* MEMFAULT_COMPACT_LOG_ENABLE */

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 3973d90

Please sign in to comment.