From 8753a09a4c69d775cead1f1febafa5c44ac5a936 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 16 Jun 2023 16:05:04 -0400 Subject: [PATCH] Fix #2379, implement SB bulk transfer test Add a functional test that moves a large number of messages across the bus as fast as possible (i.e. with no delay). The process is timed, thus offering some metric as to the single thread/single core performance of the software bus implementation underneath. Note that due to vast differences between hardware, results should only be compared between successive runs on the exact same hardware, such as before and after a change was made. --- modules/cfe_assert/inc/cfe_assert.h | 28 ++++ modules/cfe_assert/src/cfe_assert_runner.c | 7 +- modules/cfe_testcase/CMakeLists.txt | 3 +- modules/cfe_testcase/src/cfe_test.c | 1 + modules/cfe_testcase/src/cfe_test.h | 1 + .../cfe_testcase/src/sb_performance_test.c | 147 ++++++++++++++++++ 6 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 modules/cfe_testcase/src/sb_performance_test.c diff --git a/modules/cfe_assert/inc/cfe_assert.h b/modules/cfe_assert/inc/cfe_assert.h index 082b0872e..a464432ac 100644 --- a/modules/cfe_assert/inc/cfe_assert.h +++ b/modules/cfe_assert/inc/cfe_assert.h @@ -163,6 +163,23 @@ typedef void (*CFE_Assert_StatusCallback_t)(uint8 MessageType, const char *Prefi */ #define CFE_Assert_STATUS_STORE(FN) CFE_Assert_Status_Store(FN, __FILE__, __LINE__, #FN) +/** +** \brief Check if the stored status matches a possible value +** +** Allows the caller to check the value from a previous invocation of CFE_Assert_STATUS_STORE() +** without directly asserting on the value or changing test state. +** +** This may be useful in situations where a large volume of tests are being performed, such as +** during performance or load testing, where reporting all "PASS" cases may add signficant +** extra CPU usage and log volume, interfering with the result. This macro can be followed +** or combined with CFE_Assert_STATUS_MAY_BE / CFE_Assert_STATUS_MUST_BE to do actual reporting. + +** \sa #CFE_Assert_STATUS_STORE, #CFE_Assert_STATUS_MAY_BE, #CFE_Assert_STATUS_MUST_BE +** +** \returns Actual CFE_Status_t value from the call +*/ +#define CFE_Assert_STATUS_SILENTCHECK(expected) CFE_Assert_Status_SilentCheck(expected) + /*****************************************************************************/ /** ** \brief Retroactively check for an acceptable status value from CFE_Assert_STATUS_STORE @@ -357,6 +374,17 @@ bool CFE_Assert_StatusCheck(CFE_Status_t Status, bool ExpectSuccess, UtAssert_Ca */ CFE_Status_t CFE_Assert_Status_Store(CFE_Status_t Status, const char *File, uint32 Line, const char *Text); +/** +** \brief Helper function to silently check status of a previously stored result +** +** \par Description +** This helper function will check the status previously stored to a +** temporary holding area, but does not assert on it. +** +** \returns true if status code matched, false if it did not match. +*/ +bool CFE_Assert_Status_SilentCheck(CFE_Status_t Status); + /** ** \brief Helper function for nominal CFE calls with deferred check ** diff --git a/modules/cfe_assert/src/cfe_assert_runner.c b/modules/cfe_assert/src/cfe_assert_runner.c index 5c5216d23..74a0b53a7 100644 --- a/modules/cfe_assert/src/cfe_assert_runner.c +++ b/modules/cfe_assert/src/cfe_assert_runner.c @@ -111,6 +111,11 @@ CFE_Status_t CFE_Assert_Status_Store(CFE_Status_t Status, const char *File, uint return Status; } +bool CFE_Assert_Status_SilentCheck(CFE_Status_t Status) +{ + return (Status == CFE_Assert_Global.StoredStatus); +} + bool CFE_Assert_Status_DeferredCheck(CFE_Status_t Status, UtAssert_CaseType_t CaseType, const char *File, uint32 Line, const char *Text) { @@ -125,7 +130,7 @@ bool CFE_Assert_Status_DeferredCheck(CFE_Status_t Status, UtAssert_CaseType_t Ca } else { - Result = (Status == CFE_Assert_Global.StoredStatus); + Result = CFE_Assert_Status_SilentCheck(Status); if (Result) { /* no extra tag added to "true" conditions */ diff --git a/modules/cfe_testcase/CMakeLists.txt b/modules/cfe_testcase/CMakeLists.txt index bed726c1a..6c83749ca 100644 --- a/modules/cfe_testcase/CMakeLists.txt +++ b/modules/cfe_testcase/CMakeLists.txt @@ -2,7 +2,7 @@ include_directories(inc) # Filenames based on doxygen groups. # Create the app module -add_cfe_app(cfe_testcase +add_cfe_app(cfe_testcase src/cfe_test.c src/cfe_test_table.c src/es_application_control_test.c @@ -23,6 +23,7 @@ add_cfe_app(cfe_testcase src/message_id_test.c src/msg_api_test.c src/resource_id_misc_test.c + src/sb_performance_test.c src/sb_pipe_mang_test.c src/sb_sendrecv_test.c src/sb_subscription_test.c diff --git a/modules/cfe_testcase/src/cfe_test.c b/modules/cfe_testcase/src/cfe_test.c index 9a655f0ea..388a289bf 100644 --- a/modules/cfe_testcase/src/cfe_test.c +++ b/modules/cfe_testcase/src/cfe_test.c @@ -78,6 +78,7 @@ void CFE_TestMain(void) SBPipeMangSetup(); SBSendRecvTestSetup(); SBSubscriptionTestSetup(); + SBPerformanceTestSetup(); TBLContentAccessTestSetup(); TBLContentMangTestSetup(); TBLInformationTestSetup(); diff --git a/modules/cfe_testcase/src/cfe_test.h b/modules/cfe_testcase/src/cfe_test.h index 1bd1c4e65..c31e83a5a 100644 --- a/modules/cfe_testcase/src/cfe_test.h +++ b/modules/cfe_testcase/src/cfe_test.h @@ -89,6 +89,7 @@ void FSUtilTestSetup(void); void MessageIdTestSetup(void); void MsgApiTestSetup(void); void ResourceIdMiscTestSetup(void); +void SBPerformanceTestSetup(void); void SBPipeMangSetup(void); void SBSendRecvTestSetup(void); void SBSubscriptionTestSetup(void); diff --git a/modules/cfe_testcase/src/sb_performance_test.c b/modules/cfe_testcase/src/sb_performance_test.c new file mode 100644 index 000000000..e56b1e09d --- /dev/null +++ b/modules/cfe_testcase/src/sb_performance_test.c @@ -0,0 +1,147 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * @file + * + * Functional test of SB transmit/receive API performance + * + * The intent of this test is to perform a set of SB message transfers + * at a sufficiently high rate / volume such that the performance of + * the implementation can be characterized. Note that this + * cannot (currently) measure the performance directly, it merely implements a + * scenario that allows the performance to be measured by an external test + * harness. + */ + +#include "cfe_test.h" +#include "cfe_msgids.h" +#include "cfe_test_msgids.h" + +/* A simple command message */ +typedef struct +{ + CFE_MSG_CommandHeader_t CommandHeader; + uint32 CmdPayload; +} CFE_FT_TestCmdMessage_t; + +/* A simple telemetry message */ +typedef struct +{ + CFE_MSG_TelemetryHeader_t TelemetryHeader; + uint32 TlmPayload; +} CFE_FT_TestTlmMessage_t; + +/* + * This test procedure should be agnostic to specific MID values, but it should + * not overlap/interfere with real MIDs used by other apps. + */ +static const CFE_SB_MsgId_t CFE_FT_CMD_MSGID = CFE_SB_MSGID_WRAP_VALUE(CFE_TEST_CMD_MID); +static const CFE_SB_MsgId_t CFE_FT_TLM_MSGID = CFE_SB_MSGID_WRAP_VALUE(CFE_TEST_HK_TLM_MID); + +void TestBulkTransmitRecv(void) +{ + CFE_SB_PipeId_t PipeId1 = CFE_SB_INVALID_PIPE; + CFE_SB_PipeId_t PipeId2 = CFE_SB_INVALID_PIPE; + CFE_FT_TestCmdMessage_t CmdMsg; + CFE_FT_TestTlmMessage_t TlmMsg; + CFE_SB_Buffer_t * MsgBuf; + const CFE_FT_TestCmdMessage_t *CmdPtr; + const CFE_FT_TestTlmMessage_t *TlmPtr; + uint32 SendCount; + OS_time_t StartTime; + OS_time_t ElapsedTime; + + memset(&CmdMsg, 0, sizeof(CmdMsg)); + memset(&TlmMsg, 0, sizeof(TlmMsg)); + + UtPrintf("Testing: Bulk SB Transmit/Receive"); + CFE_PSP_GetTime(&StartTime); + + /* Setup, create a pipe and subscribe (one cmd, one tlm) */ + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&PipeId1, 5, "TestPipe1"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&PipeId2, 5, "TestPipe2"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_CMD_MSGID, PipeId1, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_TLM_MSGID, PipeId2, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + + /* Initialize the message content */ + UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(CmdMsg.CommandHeader), CFE_FT_CMD_MSGID, sizeof(CmdMsg)), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(TlmMsg.TelemetryHeader), CFE_FT_TLM_MSGID, sizeof(TlmMsg)), CFE_SUCCESS); + + for (SendCount = 0; SendCount < 1000000; ++SendCount) + { + CmdMsg.CmdPayload = SendCount; + TlmMsg.TlmPayload = ~SendCount; + + /* In order to not "flood" with test results, this should be silent unless a failure occurs */ + CFE_Assert_STATUS_STORE(CFE_SB_TransmitMsg(CFE_MSG_PTR(CmdMsg.CommandHeader), true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + CFE_Assert_STATUS_STORE(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmMsg.TelemetryHeader), true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, PipeId1, CFE_SB_POLL)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + /* As above, to avoid flooding of test cases, only report mismatch here */ + CmdPtr = (const void *)MsgBuf; + if (CmdPtr->CmdPayload != CmdMsg.CmdPayload) + { + UtAssert_UINT32_EQ(CmdPtr->CmdPayload, CmdMsg.CmdPayload); + break; + } + + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, PipeId2, CFE_SB_POLL)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + TlmPtr = (const void *)MsgBuf; + if (TlmPtr->TlmPayload != TlmMsg.TlmPayload) + { + UtAssert_UINT32_EQ(TlmPtr->TlmPayload, TlmMsg.TlmPayload); + break; + } + + /* report progress periodically */ + if ((SendCount % 50000) == 0) + { + UtPrintf("Success after %lu messages", (unsigned long)SendCount); + } + } + + CFE_PSP_GetTime(&ElapsedTime); + ElapsedTime = OS_TimeSubtract(ElapsedTime, StartTime); + + UtAssert_MIR("Elapsed time for SB bulk message test: %lu usec", OS_TimeGetTotalMicroseconds(ElapsedTime)); +} + +void SBPerformanceTestSetup(void) +{ + UtTest_Add(TestBulkTransmitRecv, NULL, NULL, "Test Bulk SB Transmit/Receive"); +}