blob: d41b750f17699c96152ba804b54c5404571ac6ff [file]
//! @file
#include <string.h>
#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"
#include "fakes/fake_memfault_event_storage.h"
#include "memfault/core/arch.h"
#include "memfault/core/compact_log_serializer.h"
#include "memfault/core/compiler.h"
#include "memfault/core/data_packetizer_source.h"
#include "memfault/core/event_storage.h"
#include "memfault/core/trace_event.h"
#include "memfault_trace_event_private.h"
bool memfault_arch_is_inside_isr(void) {
return mock().actualCall(__func__).returnBoolValueOrDefault(false);
}
bool memfault_vlog_compact_serialize(sMemfaultCborEncoder *encoder, MEMFAULT_UNUSED uint32_t log_id,
MEMFAULT_UNUSED uint32_t compressed_fmt,
MEMFAULT_UNUSED va_list args) {
const uint8_t cbor[] = { 0x84, 0x08, 0x01, 0x02, 0x03 };
return memfault_cbor_join(encoder, &cbor, sizeof(cbor));
}
bool memfault_vlog_compact_serialize_fallback_entry(MEMFAULT_UNUSED sMemfaultCborEncoder *encoder,
MEMFAULT_UNUSED uint32_t log_id,
MEMFAULT_UNUSED uint32_t serialized_len) {
return false;
}
static const sMemfaultEventStorageImpl *s_fake_event_storage_impl;
#define MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES (59)
TEST_GROUP(MfltTraceEvent) {
void setup() {
static uint8_t s_storage[100];
fake_memfault_event_storage_clear();
s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage));
mock().strictOrder();
}
void teardown() {
mock().checkExpectations();
mock().clear();
memfault_trace_event_reset();
}
};
TEST(MfltTraceEvent, Test_BootNullStorage) {
const int rv = memfault_trace_event_boot(NULL);
CHECK_EQUAL(rv, -4);
CHECK_FALSE(memfault_trace_event_booted());
}
TEST(MfltTraceEvent, Test_BootStorageTooSmall) {
fake_memfault_event_storage_set_available_space(MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES - 1);
const int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(rv, -3);
CHECK_FALSE(memfault_trace_event_booted());
}
TEST(MfltTraceEvent, Test_CaptureButStorageUninitialized) {
const int rv = memfault_trace_event_capture(kMfltTraceReasonUser_Unknown, NULL, NULL);
CHECK_EQUAL(rv, -1);
}
static void prv_run_capture_test(void *pc, void *lr, int32_t *status_code, size_t storage_size,
size_t expected_encoded_size) {
fake_memfault_event_storage_clear();
fake_memfault_event_storage_set_available_space(storage_size);
mock().expectOneCall("memfault_arch_is_inside_isr");
mock().expectOneCall("prv_begin_write");
const bool expect_rollback = (storage_size < expected_encoded_size);
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
const int rv =
(status_code == NULL) ?
memfault_trace_event_capture(kMfltTraceReasonUser_test, pc, lr) :
memfault_trace_event_with_status_capture(kMfltTraceReasonUser_test, pc, lr, *status_code);
CHECK_EQUAL(expect_rollback ? -2 : 0, rv);
mock().checkExpectations();
}
TEST(MfltTraceEvent, Test_CaptureOk_PcAndLr) {
fake_memfault_event_storage_clear();
CHECK_FALSE(memfault_trace_event_booted());
const int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
CHECK(memfault_trace_event_booted());
const uint8_t expected_data[] = {
0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43,
0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E,
0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA3, 0x06, 0x03, 0x02,
0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD,
};
// anything less than the expected size should fail to encode
for (size_t i = 1; i <= sizeof(expected_data); i++) {
void *pc = (void *)0x12345678;
void *lr = (void *)0xaabbccdd;
prv_run_capture_test(pc, lr, NULL, i, sizeof(expected_data));
}
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndStatus) {
fake_memfault_event_storage_clear();
const int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A',
'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a',
'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06,
0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA4, 0x06,
0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA,
0xBB, 0xCC, 0xDD, 0x07, 0x20 };
// anything less than the expected size should fail to encode
for (size_t i = 1; i <= sizeof(expected_data); i++) {
void *pc = (void *)0x12345678;
void *lr = (void *)0xaabbccdd;
int32_t status_code = -1;
prv_run_capture_test(pc, lr, &status_code, i, sizeof(expected_data));
}
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
#if !MEMFAULT_COMPACT_LOG_ENABLE
TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndLog) {
fake_memfault_event_storage_clear();
int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A',
'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a',
'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06,
0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA4, 0x06,
0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA,
0xBB, 0xCC, 0xDD, 0x08, 0x4E, '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e' };
void *pc = (void *)0x12345678;
void *lr = (void *)0xaabbccdd;
#if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \
!MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED
const size_t expected_check_isr_calls = 2;
#else
const size_t expected_check_isr_calls = 1;
#endif
// anything less than the expected size should fail to encode
for (size_t i = 1; i <= sizeof(expected_data); i++) {
const size_t storage_size = i;
fake_memfault_event_storage_clear();
fake_memfault_event_storage_set_available_space(storage_size);
mock().expectNCalls(expected_check_isr_calls, "memfault_arch_is_inside_isr");
mock().expectOneCall("prv_begin_write");
const bool expect_rollback = (storage_size < sizeof(expected_data));
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
rv = memfault_trace_event_with_log_capture(kMfltTraceReasonUser_test, pc, lr,
"%d%d%d%d%d%d789abcdetruncated", 1, 2, 3, 4, 5, 6);
CHECK_EQUAL(expect_rollback ? -2 : 0, rv);
mock().checkExpectations();
}
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndLogFromIsr) {
const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A',
'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a',
'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06,
0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA4, 0x06,
0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA,
0xBB, 0xCC, 0xDD, 0x08, 0x4E, '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e' };
void *pc = (void *)0x12345678;
void *lr = (void *)0xaabbccdd;
#if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \
!MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED
const size_t expected_check_isr_calls = 2;
#else
const size_t expected_check_isr_calls = 1;
#endif
// let's also make sure collection from an isr works as expected
fake_memfault_event_storage_clear();
int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
fake_memfault_event_storage_set_available_space(sizeof(expected_data));
mock().expectNCalls(expected_check_isr_calls, "memfault_arch_is_inside_isr").andReturnValue(true);
rv = memfault_trace_event_with_log_capture(kMfltTraceReasonUser_test, pc, lr,
"%d%d%d%d%d%d789abcdetruncated", 1, 2, 3, 4, 5, 6);
CHECK_EQUAL(0, rv);
mock().expectOneCall("prv_begin_write");
mock().expectOneCall("prv_finish_write").withParameter("rollback", false);
rv = memfault_trace_event_try_flush_isr_event();
CHECK_EQUAL(0, rv);
mock().checkExpectations();
size_t expected_isr_data_size = sizeof(expected_data);
#if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \
!MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED
expected_isr_data_size -= 16;
uint8_t expected_data_no_log_from_isr[expected_isr_data_size];
memcpy(expected_data_no_log_from_isr, expected_data, expected_isr_data_size);
expected_data_no_log_from_isr[38] = 0xA3; // array of 3 items instead of 4
fake_event_storage_assert_contents_match(expected_data_no_log_from_isr,
sizeof(expected_data_no_log_from_isr));
#else
fake_event_storage_assert_contents_match(expected_data, expected_isr_data_size);
#endif
}
#else
TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndCompactLog) {
fake_memfault_event_storage_clear();
int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
const uint8_t expected_data[] = {
0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C',
'D', 'D', 0x0A, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.',
'3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA3, 0x06, 0x03, 0x03,
0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0x09, 0x84, 0x08, 0x01, 0x02, 0x03,
};
void *lr = (void *)0xaabbccdd;
#if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \
!MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED
const size_t expected_check_isr_calls = 2;
#else
const size_t expected_check_isr_calls = 1;
#endif
// anything less than the expected size should fail to encode
for (size_t i = 1; i <= sizeof(expected_data); i++) {
const size_t storage_size = i;
fake_memfault_event_storage_clear();
fake_memfault_event_storage_set_available_space(storage_size);
mock().expectNCalls(expected_check_isr_calls, "memfault_arch_is_inside_isr");
mock().expectOneCall("prv_begin_write");
const bool expect_rollback = (storage_size < sizeof(expected_data));
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
rv = memfault_trace_event_with_compact_log_capture(kMfltTraceReasonUser_test, lr, 0x40, 8, 1, 2,
3);
CHECK_EQUAL(expect_rollback ? -2 : 0, rv);
mock().checkExpectations();
}
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
#endif /* MEMFAULT_COMPACT_LOG_ENABLE */
static void prv_setup_isr_test(void) {
fake_memfault_event_storage_clear();
int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
mock().expectOneCall("memfault_arch_is_inside_isr").andReturnValue(true);
void *pc = (void *)0x12345678;
void *lr = (void *)0xaabbccdd;
rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc, lr);
CHECK_EQUAL(0, rv);
mock().checkExpectations();
mock().expectOneCall("memfault_arch_is_inside_isr").andReturnValue(true);
void *pc2 = (void *)2;
void *lr2 = (void *)1;
rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc2, lr2);
CHECK_EQUAL(-2, rv);
mock().checkExpectations();
}
TEST(MfltTraceEvent, Test_CaptureOk_FromIsrWithForceFlush) {
prv_setup_isr_test();
mock().expectOneCall("prv_begin_write");
const bool expect_rollback = false;
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
const int rv = memfault_trace_event_try_flush_isr_event();
CHECK_EQUAL(0, rv);
mock().checkExpectations();
const uint8_t expected_data[] = {
0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43,
0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E,
0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA3, 0x06, 0x03, 0x02,
0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD,
};
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
TEST(MfltTraceEvent, Test_CaptureOk_FromIsrWithLazyFlush) {
prv_setup_isr_test();
bool expect_rollback = true;
fake_memfault_event_storage_set_available_space(10);
mock().expectOneCall("memfault_arch_is_inside_isr");
mock().expectOneCall("prv_begin_write");
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
void *pc2 = (void *)NULL;
void *lr2 = (void *)1;
int rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc2, lr2);
CHECK_EQUAL(-2, rv);
mock().checkExpectations();
const uint8_t expected_data[] = {
// event logged from ISR
0xA7,
0x02,
0x02,
0x03,
0x01,
0x07,
0x69,
0x44,
0x41,
0x41,
0x42,
0x42,
0x43,
0x43,
0x44,
0x44,
0x0A,
0x64,
0x6D,
0x61,
0x69,
0x6E,
0x09,
0x65,
0x31,
0x2E,
0x32,
0x2E,
0x33,
0x06,
0x66,
0x65,
0x76,
0x74,
0x5F,
0x32,
0x34,
0x04,
0xA3,
0x06,
0x03,
0x02,
0x1A,
0x12,
0x34,
0x56,
0x78,
0x03,
0x1A,
0xAA,
0xBB,
0xCC,
0xDD,
// event not logged from ISR
0xA7,
0x02,
0x02,
0x03,
0x01,
0x07,
0x69,
0x44,
0x41,
0x41,
0x42,
0x42,
0x43,
0x43,
0x44,
0x44,
0x0A,
0x64,
0x6D,
0x61,
0x69,
0x6E,
0x09,
0x65,
0x31,
0x2E,
0x32,
0x2E,
0x33,
0x06,
0x66,
0x65,
0x76,
0x74,
0x5F,
0x32,
0x34,
0x04,
0xA2,
0x06,
0x03,
0x03,
0x01,
};
fake_memfault_event_storage_set_available_space(sizeof(expected_data));
expect_rollback = false;
// first the ISR event should be flushed now that we aren't in an ISR
mock().expectOneCall("memfault_arch_is_inside_isr");
mock().expectOneCall("prv_begin_write");
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
// then the second event we want to record should be stored
mock().expectOneCall("prv_begin_write");
mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback);
rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc2, lr2);
CHECK_EQUAL(0, rv);
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
TEST(MfltTraceEvent, Test_CaptureOk_LrOnly) {
fake_memfault_event_storage_clear();
const int rv = memfault_trace_event_boot(s_fake_event_storage_impl);
CHECK_EQUAL(0, rv);
const uint8_t expected_data[] = {
0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44,
0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x06,
0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA2, 0x06, 0x03, 0x03, 0x01,
};
// anything less than the expected size should fail to encode
for (size_t i = 1; i <= sizeof(expected_data); i++) {
void *pc = (void *)NULL;
void *lr = (void *)1;
prv_run_capture_test(pc, lr, NULL, i, sizeof(expected_data));
}
fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data));
}
TEST(MfltTraceEvent, Test_CaptureStorageFull) {
fake_memfault_event_storage_clear();
fake_memfault_event_storage_set_available_space(MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES);
CHECK_EQUAL(memfault_trace_event_boot(s_fake_event_storage_impl), 0);
fake_memfault_event_storage_set_available_space(0);
mock().expectOneCall("memfault_arch_is_inside_isr");
mock().expectOneCall("prv_begin_write");
// Expect rollback!
mock().expectOneCall("prv_finish_write").withParameter("rollback", true);
const int rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, 0, 0);
CHECK_EQUAL(-2, rv);
}
TEST(MfltTraceEvent, Test_GetWorstCaseSerializeSize) {
const size_t worst_case_size = memfault_trace_event_compute_worst_case_storage_size();
LONGS_EQUAL(MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES, worst_case_size);
}