From c8fa483b68c6c1149c2a7a8bc1e901b38860ec9b Mon Sep 17 00:00:00 2001 From: Archit Gupta <71798289+archigup@users.noreply.github.com> Date: Fri, 5 Mar 2021 17:56:28 -0800 Subject: [PATCH] Add custom metrics to defender demo (#507) Adds custom metrics to the defender demo. The metrics added are a list of the task ids and the stack high water mark. --- .../DemoTasks/DefenderDemoExample.c | 74 +++- .../Device_Defender_Demo/FreeRTOSConfig.h | 12 +- .../Device_Defender_Demo/defender_config.h | 40 ++ .../Device_Defender_Demo/demo_config.h | 7 + .../Device_Defender_Demo/metrics_collector.c | 3 - .../Device_Defender_Demo/report_builder.c | 351 +++++++++++++----- .../Device_Defender_Demo/report_builder.h | 12 +- FreeRTOS-Plus/Source/AWS/device-defender | 2 +- 8 files changed, 380 insertions(+), 121 deletions(-) diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/DemoTasks/DefenderDemoExample.c b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/DemoTasks/DefenderDemoExample.c index 732538bc11..96e4908858 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/DemoTasks/DefenderDemoExample.c +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/DemoTasks/DefenderDemoExample.c @@ -35,18 +35,20 @@ * with another MQTT library. This demo requires using the AWS IoT broker as * Device Defender is an AWS service. * - * This demo connects to the AWS IoT broker and subscribes to the device - * defender topics. It then collects metrics for the open ports and sockets on - * the device using FreeRTOS+TCP, and generates a device defender report. The + * This demo subscribes to the device defender topics. It then collects metrics + * for the open ports and sockets on the device using FreeRTOS+TCP. Additonally + * the stack high water mark and task IDs are collected for custom metrics. + * These metrics are used to generate a device defender report. The * report is then published, and the demo waits for a response from the device - * defender service. Upon receiving the response or timing out, the demo - * finishes. + * defender service. Upon receiving an accepted response, the demo finishes. + * If the demo receives a rejected response or times out, the demo repeats up to + * a maximum of DEFENDER_MAX_DEMO_LOOP_COUNT times. * * This demo sets the report ID to xTaskGetTickCount(), which may collide if * the device is reset. Reports for a Thing with a previously used report ID * will be assumed to be duplicates and discarded by the Device Defender * service. The report ID needs to be unique per report sent with a given - * Thing. We recommend using an increasing unique id such as the current + * Thing. We recommend using an increasing unique ID such as the current * timestamp. */ @@ -97,7 +99,7 @@ #define DEFENDER_RESPONSE_WAIT_SECONDS ( 2 ) /** - * @brief Name of the report id field in the response from the AWS IoT Device + * @brief Name of the report ID field in the response from the AWS IoT Device * Defender service. */ #define DEFENDER_RESPONSE_REPORT_ID_FIELD "reportId" @@ -133,8 +135,8 @@ typedef enum ReportStatusRejected } ReportStatus_t; -/** - * @brief Each compilation unit that consumes the NetworkContext must define it. +/** + * @brief Each compilation unit that consumes the NetworkContext must define it. * It should contain a single pointer to the type of your desired transport. * When using multiple transports in the same compilation unit, define this pointer as void *. * @@ -195,6 +197,16 @@ static uint16_t pusOpenUdpPorts[ democonfigOPEN_UDP_PORTS_ARRAY_SIZE ]; */ static Connection_t pxEstablishedConnections[ democonfigESTABLISHED_CONNECTIONS_ARRAY_SIZE ]; +/** + * @brief Array of task statuses, used to generate custom metrics. + */ +static TaskStatus_t pxTaskStatusList[ democonfigCUSTOM_METRICS_TASKS_ARRAY_SIZE ]; + +/** + * @brief Task numbers custom metric array. + */ +static uint32_t pulCustomMetricsTaskNumbers[ democonfigCUSTOM_METRICS_TASKS_ARRAY_SIZE ]; + /** * @brief All the metrics sent in the device defender report. */ @@ -211,9 +223,10 @@ static ReportStatus_t xReportStatus; static char pcDeviceMetricsJsonReport[ democonfigDEVICE_METRICS_REPORT_BUFFER_SIZE ]; /** - * @brief Report Id sent in the defender report. + * @brief Report ID sent in the defender report. */ static uint32_t ulReportId = 0UL; + /*-----------------------------------------------------------*/ /** @@ -463,7 +476,9 @@ static bool prvCollectDeviceMetrics( void ) { bool xStatus = false; eMetricsCollectorStatus eMetricsCollectorStatus; - uint32_t ulNumOpenTcpPorts = 0UL, ulNumOpenUdpPorts = 0UL, ulNumEstablishedConnections = 0UL; + uint32_t ulNumOpenTcpPorts = 0UL, ulNumOpenUdpPorts = 0UL, ulNumEstablishedConnections = 0UL, i; + UBaseType_t uxTasksWritten = { 0 }; + TaskStatus_t pxTaskStatus = { 0 }; /* Collect bytes and packets sent and received. */ eMetricsCollectorStatus = eGetNetworkStats( &( xNetworkStats ) ); @@ -516,6 +531,36 @@ static bool prvCollectDeviceMetrics( void ) } } + /* Collect custom metrics. This demo sends this task's stack high water mark + * as a number type custom metric and the current task IDs as a list of + * numbers type custom metric. */ + if( eMetricsCollectorStatus == eMetricsCollectorSuccess ) + { + vTaskGetInfo( + /* Query this task. */ + NULL, + &pxTaskStatus, + /* Include the stack high water mark value. */ + pdTRUE, + /* Don't include the task state in the TaskStatus_t structure. */ + 0 ); + uxTasksWritten = uxTaskGetSystemState( pxTaskStatusList, democonfigCUSTOM_METRICS_TASKS_ARRAY_SIZE, NULL ); + + if( uxTasksWritten == 0 ) + { + eMetricsCollectorStatus = eMetricsCollectorCollectionFailed; + LogError( ( "Failed to collect system state. uxTaskGetSystemState() failed due to insufficient buffer space.", + eMetricsCollectorStatus ) ); + } + else + { + for( i = 0; i < uxTasksWritten; i++ ) + { + pulCustomMetricsTaskNumbers[ i ] = pxTaskStatusList[ i ].xTaskNumber; + } + } + } + /* Populate device metrics. */ if( eMetricsCollectorStatus == eMetricsCollectorSuccess ) { @@ -527,6 +572,9 @@ static bool prvCollectDeviceMetrics( void ) xDeviceMetrics.ulOpenUdpPortsArrayLength = ulNumOpenUdpPorts; xDeviceMetrics.pxEstablishedConnectionsArray = &( pxEstablishedConnections[ 0 ] ); xDeviceMetrics.ulEstablishedConnectionsArrayLength = ulNumEstablishedConnections; + xDeviceMetrics.ulStackHighWaterMark = pxTaskStatus.usStackHighWaterMark; + xDeviceMetrics.pulTaskIdArray = pulCustomMetricsTaskNumbers; + xDeviceMetrics.ulTaskIdArrayLength = uxTasksWritten; } return xStatus; @@ -671,14 +719,14 @@ void prvDefenderDemoTask( void * pvParameters ) * DEFENDER_MAX_DEMO_LOOP_COUNT times. */ do { - /* Set a report Id to be used. + /* Set a report ID to be used. * * !!!NOTE!!! * This demo sets the report ID to xTaskGetTickCount(), which may collide * if the device is reset. Reports for a Thing with a previously used * report ID will be assumed to be duplicates and discarded by the Device * Defender service. The report ID needs to be unique per report sent with - * a given Thing. We recommend using an increasing unique id such as the + * a given Thing. We recommend using an increasing unique ID such as the * current timestamp. */ ulReportId = ( uint32_t ) xTaskGetTickCount(); diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/FreeRTOSConfig.h b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/FreeRTOSConfig.h index 7aa77d93f2..05f20147aa 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/FreeRTOSConfig.h +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/FreeRTOSConfig.h @@ -48,7 +48,8 @@ #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 60 ) /* In this simulated case, the stack only has to hold one small structure as the real stack is part of the Win32 thread. */ #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 2048U * 1024U ) ) #define configMAX_TASK_NAME_LEN ( 15 ) -#define configUSE_TRACE_FACILITY 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_CO_ROUTINES 0 @@ -103,15 +104,6 @@ #define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_pcTaskGetTaskName 1 -/* This demo makes use of one or more example stats formatting functions. These - * format the raw data provided by the uxTaskGetSystemState() function in to human - * readable ASCII form. See the notes in the implementation of vTaskList() within - * FreeRTOS/Source/tasks.c for limitations. configUSE_STATS_FORMATTING_FUNCTIONS - * is set to 2 so the formatting functions are included without the stdio.h being - * included in tasks.c. That is because this project defines its own sprintf() - * functions. */ -#define configUSE_STATS_FORMATTING_FUNCTIONS 1 - /* Assert call defined for debug builds. */ #ifdef _DEBUG extern void vAssertCalled( const char * pcFile, diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/defender_config.h b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/defender_config.h index 496881a265..22c7d116fd 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/defender_config.h +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/defender_config.h @@ -52,4 +52,44 @@ /************ End of logging configuration ****************/ +/** + * AWS IoT Device Defender Service supports both long and short names for keys + * in the report sent by a device. For example, + * + * A device defender report using long key names: + * { + * "header": { + * "report_id": 1530304554, + * "version": "1.0" + * }, + * "metrics": { + * "network_stats": { + * "bytes_in": 29358693495, + * "bytes_out": 26485035, + * "packets_in": 10013573555, + * "packets_out": 11382615 + * } + * } + * } + * + * An equivalent report using short key names: + * { + * "hed": { + * "rid": 1530304554, + * "v": "1.0" + * }, + * "met": { + * "ns": { + * "bi": 29358693495, + * "bo": 26485035, + * "pi": 10013573555, + * "po": 11382615 + * } + * } + * } + * + * Set to 1 to enable use of long key names in the defender report. + */ +#define DEFENDER_USE_LONG_KEYS 0 + #endif /* ifndef DEFENDER_CONFIG_H_ */ diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/demo_config.h b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/demo_config.h index d9bd3cbad8..123ed48881 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/demo_config.h +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/demo_config.h @@ -261,6 +261,13 @@ extern void vLoggingPrintf( const char * pcFormatString, */ #define democonfigESTABLISHED_CONNECTIONS_ARRAY_SIZE 10 +/** + * @brief Size of the task numbers array. + * + * This must be at least the number of tasks used. + */ +#define democonfigCUSTOM_METRICS_TASKS_ARRAY_SIZE 10 + /** * @brief Size of the buffer which contains the generated device defender report. * diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/metrics_collector.c b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/metrics_collector.c index 426bc49a2c..02437bf678 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/metrics_collector.c +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/metrics_collector.c @@ -43,9 +43,6 @@ #include "FreeRTOS.h" #include "FreeRTOS_IP.h" -/* FreeRTOS+TCP tcp_netstat utility include. */ -#include "tcp_netstat.h" - /* Demo config. */ #include "demo_config.h" diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.c b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.c index 3d467287d2..d5906cf0b2 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.c +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.c @@ -36,6 +36,9 @@ /* Demo config. */ #include "demo_config.h" +/* Device Defender Client Library. */ +#include "defender.h" + /* Interface include. */ #include "report_builder.h" @@ -50,53 +53,68 @@ /* Formats used to generate the JSON report. */ #define reportbuilderJSON_PORT_OBJECT_FORMAT \ "{" \ - "\"port\": %u" \ + "\"%s\": %u" \ "}," #define reportbuilderJSON_CONNECTION_OBJECT_FORMAT \ "{" \ - "\"local_port\": %u," \ - "\"remote_addr\": \"%u.%u.%u.%u:%u\"" \ + "\"%s\": %u," \ + "\"%s\": \"%u.%u.%u.%u:%u\"" \ "}," #define reportbuilderJSON_REPORT_FORMAT_PART1 \ "{" \ - "\"header\": {" \ - "\"report_id\": %u," \ - "\"version\": \"%u.%u\"" \ + "\"%s\": {" \ + "\"%s\": %u," \ + "\"%s\": \"%u.%u\"" \ "}," \ - "\"metrics\": {" \ - "\"listening_tcp_ports\": {" \ - "\"ports\": " + "\"%s\": {" \ + "\"%s\": {" \ + "\"%s\": " #define reportbuilderJSON_REPORT_FORMAT_PART2 \ "," \ - "\"total\": %u" \ + "\"%s\": %u" \ "}," \ - "\"listening_udp_ports\": {" \ - "\"ports\": " + "\"%s\": {" \ + "\"%s\": " #define reportbuilderJSON_REPORT_FORMAT_PART3 \ "," \ - "\"total\": %u" \ + "\"%s\": %u" \ "}," \ - "\"network_stats\": {" \ - "\"bytes_in\": %u," \ - "\"bytes_out\": %u," \ - "\"packets_in\": %u," \ - "\"packets_out\": %u" \ + "\"%s\": {" \ + "\"%s\": %u," \ + "\"%s\": %u," \ + "\"%s\": %u," \ + "\"%s\": %u" \ "}," \ - "\"tcp_connections\": {" \ - "\"established_connections\": {" \ - "\"connections\": " + "\"%s\": {" \ + "\"%s\": {" \ + "\"%s\": " #define reportbuilderJSON_REPORT_FORMAT_PART4 \ "," \ - "\"total\": %u" \ + "\"%s\": %u" \ + "}" \ "}" \ + "}," \ + "\"%s\": {" \ + "\"stack_high_water_mark\": [" \ + "{" \ + "\"%s\": %u" \ + "}" \ + "]," \ + "\"task_numbers\": [" \ + "{" \ + "\"%s\": " + +#define reportbuilderJSON_REPORT_FORMAT_PART5 \ "}" \ + "]" \ "}" \ "}" + /*-----------------------------------------------------------*/ /** @@ -158,6 +176,24 @@ static eReportBuilderStatus prvWriteConnectionsArray( char * pcBuffer, const Connection_t * pxConnectionsArray, uint32_t ulConnectionsArrayLength, uint32_t * pulOutCharsWritten ); + +/** + * @brief Write task ID array to the given buffer as a JSON array. + * + * @param[in] pcBuffer The buffer to write the array of task IDs. + * @param[in] ulBufferLength The length of the buffer. + * @param[in] pulTaskIdArray The array containing the task IDs. + * @param[in] pulTaskIdArrayLength Length of the pulTaskIdsArray array. + * @param[out] pulOutCharsWritten Number of characters written to the buffer. + * + * @return #ReportBuilderSuccess if the array is successfully written; + * #ReportBuilderBufferTooSmall if the buffer cannot hold the full array. + */ +static eReportBuilderStatus prvWriteTaskIdArray( char * pcBuffer, + uint32_t ulBufferLength, + const uint32_t * pulTaskIdArray, + uint32_t pulTaskIdArrayLength, + uint32_t * pulOutCharsWritten ); /*-----------------------------------------------------------*/ static eReportBuilderStatus prvWritePortsArray( char * pcBuffer, @@ -168,7 +204,7 @@ static eReportBuilderStatus prvWritePortsArray( char * pcBuffer, { char * pcCurrentWritePos = pcBuffer; uint32_t i, ulRemainingBufferLength = ulBufferLength; - int32_t ulCharactersWritten; + int32_t lCharactersWritten; eReportBuilderStatus eStatus = eReportBuilderSuccess; configASSERT( pcBuffer != NULL ); @@ -190,20 +226,20 @@ static eReportBuilderStatus prvWritePortsArray( char * pcBuffer, /* Write the array elements. */ for( i = 0; ( ( i < ulOpenPortsArrayLength ) && ( eStatus == eReportBuilderSuccess ) ); i++ ) { - ulCharactersWritten = snprintf( pcCurrentWritePos, - ulRemainingBufferLength, - reportbuilderJSON_PORT_OBJECT_FORMAT, - pusOpenPortsArray[ i ] ); + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_PORT_OBJECT_FORMAT, + DEFENDER_REPORT_PORT_KEY, + pusOpenPortsArray[ i ] ); - if( !reportbuilderSNPRINTF_SUCCESS( ulCharactersWritten, ulRemainingBufferLength ) ) + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) { eStatus = eReportBuilderBufferTooSmall; - break; } else { - ulRemainingBufferLength -= ( uint32_t ) ulCharactersWritten; - pcCurrentWritePos += ulCharactersWritten; + ulRemainingBufferLength -= ( uint32_t ) lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; } } @@ -222,6 +258,7 @@ static eReportBuilderStatus prvWritePortsArray( char * pcBuffer, *pcCurrentWritePos = reportbuilderJSON_ARRAY_CLOSE_MARKER; ulRemainingBufferLength -= 1; pcCurrentWritePos += 1; + *pulOutCharsWritten = ulBufferLength - ulRemainingBufferLength; } else { @@ -229,11 +266,6 @@ static eReportBuilderStatus prvWritePortsArray( char * pcBuffer, } } - if( eStatus == eReportBuilderSuccess ) - { - *pulOutCharsWritten = ulBufferLength - ulRemainingBufferLength; - } - return eStatus; } /*-----------------------------------------------------------*/ @@ -246,7 +278,7 @@ static eReportBuilderStatus prvWriteConnectionsArray( char * pcBuffer, { char * pcCurrentWritePos = pcBuffer; uint32_t i, ulRemainingBufferLength = ulBufferLength; - int32_t ulCharactersWritten; + int32_t lCharactersWritten; eReportBuilderStatus eStatus = eReportBuilderSuccess; const Connection_t * pxConn; @@ -270,25 +302,26 @@ static eReportBuilderStatus prvWriteConnectionsArray( char * pcBuffer, for( i = 0; ( ( i < ulConnectionsArrayLength ) && ( eStatus == eReportBuilderSuccess ) ); i++ ) { pxConn = &( pxConnectionsArray[ i ] ); - ulCharactersWritten = snprintf( pcCurrentWritePos, - ulRemainingBufferLength, - reportbuilderJSON_CONNECTION_OBJECT_FORMAT, - pxConn->usLocalPort, - ( pxConn->ulRemoteIp >> 24 ) & 0xFF, - ( pxConn->ulRemoteIp >> 16 ) & 0xFF, - ( pxConn->ulRemoteIp >> 8 ) & 0xFF, - ( pxConn->ulRemoteIp ) & 0xFF, - pxConn->usRemotePort ); - - if( !reportbuilderSNPRINTF_SUCCESS( ulCharactersWritten, ulRemainingBufferLength ) ) + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_CONNECTION_OBJECT_FORMAT, + DEFENDER_REPORT_LOCAL_PORT_KEY, + pxConn->usLocalPort, + DEFENDER_REPORT_REMOTE_ADDR_KEY, + ( pxConn->ulRemoteIp >> 24 ) & 0xFF, + ( pxConn->ulRemoteIp >> 16 ) & 0xFF, + ( pxConn->ulRemoteIp >> 8 ) & 0xFF, + ( pxConn->ulRemoteIp ) & 0xFF, + pxConn->usRemotePort ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) { eStatus = eReportBuilderBufferTooSmall; - break; } else { - ulRemainingBufferLength -= ulCharactersWritten; - pcCurrentWritePos += ulCharactersWritten; + ulRemainingBufferLength -= lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; } } @@ -307,6 +340,7 @@ static eReportBuilderStatus prvWriteConnectionsArray( char * pcBuffer, *pcCurrentWritePos = reportbuilderJSON_ARRAY_CLOSE_MARKER; ulRemainingBufferLength -= 1; pcCurrentWritePos += 1; + *pulOutCharsWritten = ulBufferLength - ulRemainingBufferLength; } else { @@ -314,9 +348,77 @@ static eReportBuilderStatus prvWriteConnectionsArray( char * pcBuffer, } } + return eStatus; +} +/*-----------------------------------------------------------*/ + +static eReportBuilderStatus prvWriteTaskIdArray( char * pcBuffer, + uint32_t ulBufferLength, + const uint32_t * pulTaskIdArray, + uint32_t pulTaskIdArrayLength, + uint32_t * pulOutCharsWritten ) +{ + char * pcCurrentWritePos = pcBuffer; + uint32_t i, ulRemainingBufferLength = ulBufferLength; + int32_t lCharactersWritten; + eReportBuilderStatus eStatus = eReportBuilderSuccess; + + configASSERT( pcBuffer != NULL ); + configASSERT( pulTaskIdArray != NULL ); + configASSERT( pulOutCharsWritten != NULL ); + + /* Write the JSON array open marker. */ + if( ulRemainingBufferLength > 1 ) + { + *pcCurrentWritePos = reportbuilderJSON_ARRAY_OPEN_MARKER; + ulRemainingBufferLength -= 1; + pcCurrentWritePos += 1; + } + else + { + eStatus = eReportBuilderBufferTooSmall; + } + + /* Write the array elements. */ + for( i = 0; ( ( i < pulTaskIdArrayLength ) && ( eStatus == eReportBuilderSuccess ) ); i++ ) + { + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + "%u,", + pulTaskIdArray[ i ] ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) + { + eStatus = eReportBuilderBufferTooSmall; + } + else + { + ulRemainingBufferLength -= ( uint32_t ) lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; + } + } + if( eStatus == eReportBuilderSuccess ) { - *pulOutCharsWritten = ulBufferLength - ulRemainingBufferLength; + /* Discard the last comma. */ + if( pulTaskIdArrayLength > 0 ) + { + pcCurrentWritePos -= 1; + ulRemainingBufferLength += 1; + } + + /* Write the JSON array close marker. */ + if( ulRemainingBufferLength > 1 ) + { + *pcCurrentWritePos = reportbuilderJSON_ARRAY_CLOSE_MARKER; + ulRemainingBufferLength -= 1; + pcCurrentWritePos += 1; + *pulOutCharsWritten = ulBufferLength - ulRemainingBufferLength; + } + else + { + eStatus = eReportBuilderBufferTooSmall; + } } return eStatus; @@ -332,9 +434,10 @@ eReportBuilderStatus eGenerateJsonReport( char * pcBuffer, uint32_t * pulOutReportLength ) { char * pcCurrentWritePos = pcBuffer; - uint32_t ulRemainingBufferLength = ulBufferLength, bufferWritten; + uint32_t ulRemainingBufferLength = ulBufferLength; + uint32_t bufferWritten; eReportBuilderStatus eStatus = eReportBuilderSuccess; - int32_t ulCharactersWritten; + int32_t lCharactersWritten; configASSERT( pcBuffer != NULL ); configASSERT( pxMetrics != NULL ); @@ -358,22 +461,29 @@ eReportBuilderStatus eGenerateJsonReport( char * pcBuffer, /* Write part1. */ if( eStatus == eReportBuilderSuccess ) { - ulCharactersWritten = snprintf( pcCurrentWritePos, - ulRemainingBufferLength, - reportbuilderJSON_REPORT_FORMAT_PART1, - ulReportId, - ulMajorReportVersion, - ulMinorReportVersion ); - - if( !reportbuilderSNPRINTF_SUCCESS( ulCharactersWritten, ulRemainingBufferLength ) ) + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_REPORT_FORMAT_PART1, + DEFENDER_REPORT_HEADER_KEY, + DEFENDER_REPORT_ID_KEY, + ulReportId, + DEFENDER_REPORT_VERSION_KEY, + ulMajorReportVersion, + ulMinorReportVersion, + DEFENDER_REPORT_METRICS_KEY, + DEFENDER_REPORT_TCP_LISTENING_PORTS_KEY, + DEFENDER_REPORT_PORTS_KEY + ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) { LogError( ( "Failed to write part 1." ) ); eStatus = eReportBuilderBufferTooSmall; } else { - ulRemainingBufferLength -= ulCharactersWritten; - pcCurrentWritePos += ulCharactersWritten; + ulRemainingBufferLength -= lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; } } @@ -400,20 +510,24 @@ eReportBuilderStatus eGenerateJsonReport( char * pcBuffer, /* Write part2. */ if( eStatus == eReportBuilderSuccess ) { - ulCharactersWritten = snprintf( pcCurrentWritePos, - ulRemainingBufferLength, - reportbuilderJSON_REPORT_FORMAT_PART2, - pxMetrics->ulOpenTcpPortsArrayLength ); - - if( !reportbuilderSNPRINTF_SUCCESS( ulCharactersWritten, ulRemainingBufferLength ) ) + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_REPORT_FORMAT_PART2, + DEFENDER_REPORT_TOTAL_KEY, + pxMetrics->ulOpenTcpPortsArrayLength, + DEFENDER_REPORT_UDP_LISTENING_PORTS_KEY, + DEFENDER_REPORT_PORTS_KEY + ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) { LogError( ( "Failed to write part 2." ) ); eStatus = eReportBuilderBufferTooSmall; } else { - ulRemainingBufferLength -= ulCharactersWritten; - pcCurrentWritePos += ulCharactersWritten; + ulRemainingBufferLength -= lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; } } @@ -440,24 +554,34 @@ eReportBuilderStatus eGenerateJsonReport( char * pcBuffer, /* Write part3. */ if( eStatus == eReportBuilderSuccess ) { - ulCharactersWritten = snprintf( pcCurrentWritePos, - ulRemainingBufferLength, - reportbuilderJSON_REPORT_FORMAT_PART3, - pxMetrics->ulOpenUdpPortsArrayLength, - pxMetrics->pxNetworkStats->ulBytesReceived, - pxMetrics->pxNetworkStats->ulBytesSent, - pxMetrics->pxNetworkStats->ulPacketsReceived, - pxMetrics->pxNetworkStats->ulPacketsSent ); - - if( !reportbuilderSNPRINTF_SUCCESS( ulCharactersWritten, ulRemainingBufferLength ) ) + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_REPORT_FORMAT_PART3, + DEFENDER_REPORT_TOTAL_KEY, + pxMetrics->ulOpenUdpPortsArrayLength, + DEFENDER_REPORT_NETWORK_STATS_KEY, + DEFENDER_REPORT_BYTES_IN_KEY, + pxMetrics->pxNetworkStats->ulBytesReceived, + DEFENDER_REPORT_BYTES_OUT_KEY, + pxMetrics->pxNetworkStats->ulBytesSent, + DEFENDER_REPORT_PKTS_IN_KEY, + pxMetrics->pxNetworkStats->ulPacketsReceived, + DEFENDER_REPORT_PKTS_OUT_KEY, + pxMetrics->pxNetworkStats->ulPacketsSent, + DEFENDER_REPORT_TCP_CONNECTIONS_KEY, + DEFENDER_REPORT_ESTABLISHED_CONNECTIONS_KEY, + DEFENDER_REPORT_CONNECTIONS_KEY + ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) { LogError( ( "Failed to write part 3." ) ); eStatus = eReportBuilderBufferTooSmall; } else { - ulRemainingBufferLength -= ulCharactersWritten; - pcCurrentWritePos += ulCharactersWritten; + ulRemainingBufferLength -= lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; } } @@ -484,26 +608,67 @@ eReportBuilderStatus eGenerateJsonReport( char * pcBuffer, /* Write part4. */ if( eStatus == eReportBuilderSuccess ) { - ulCharactersWritten = snprintf( pcCurrentWritePos, - ulRemainingBufferLength, - reportbuilderJSON_REPORT_FORMAT_PART4, - pxMetrics->ulEstablishedConnectionsArrayLength ); - - if( !reportbuilderSNPRINTF_SUCCESS( ulCharactersWritten, ulRemainingBufferLength ) ) + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_REPORT_FORMAT_PART4, + DEFENDER_REPORT_TOTAL_KEY, + pxMetrics->ulEstablishedConnectionsArrayLength, + DEFENDER_REPORT_CUSTOM_METRICS_KEY, + DEFENDER_REPORT_NUMBER_KEY, + pxMetrics->ulStackHighWaterMark, + DEFENDER_REPORT_NUMBER_LIST_KEY + ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) { LogError( ( "Failed to write part 4." ) ); eStatus = eReportBuilderBufferTooSmall; } else { - ulRemainingBufferLength -= ulCharactersWritten; - pcCurrentWritePos += ulCharactersWritten; + ulRemainingBufferLength -= lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; + } + } + + /* Write task ids array. */ + if( eStatus == eReportBuilderSuccess ) + { + eStatus = prvWriteTaskIdArray( pcCurrentWritePos, + ulRemainingBufferLength, + pxMetrics->pulTaskIdArray, + pxMetrics->ulTaskIdArrayLength, + &( bufferWritten ) ); + + if( eStatus == eReportBuilderSuccess ) + { + pcCurrentWritePos += bufferWritten; + ulRemainingBufferLength -= bufferWritten; + } + else + { + LogError( ( "Failed to write task ID array." ) ); } } + /* Write part5. */ if( eStatus == eReportBuilderSuccess ) { - *pulOutReportLength = ulBufferLength - ulRemainingBufferLength; + lCharactersWritten = snprintf( pcCurrentWritePos, + ulRemainingBufferLength, + reportbuilderJSON_REPORT_FORMAT_PART5 ); + + if( !reportbuilderSNPRINTF_SUCCESS( lCharactersWritten, ulRemainingBufferLength ) ) + { + LogError( ( "Failed to write part 5." ) ); + eStatus = eReportBuilderBufferTooSmall; + } + else + { + ulRemainingBufferLength -= lCharactersWritten; + pcCurrentWritePos += lCharactersWritten; + *pulOutReportLength = ulBufferLength - ulRemainingBufferLength; + } } return eStatus; diff --git a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.h b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.h index c9b54ae15c..f89d8b9e20 100644 --- a/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.h +++ b/FreeRTOS-Plus/Demo/AWS/Device_Defender_Windows_Simulator/Device_Defender_Demo/report_builder.h @@ -41,7 +41,13 @@ typedef enum } eReportBuilderStatus; /** - * @brief Represents metrics to be included in the report. + * @brief Represents metrics to be included in the report, including custom metrics. + * + * This demo demonstrates the use of the stack high water mark and list of + * running task ids as custom metrics sent to AWS IoT Device Defender service. + * + * For more information on custom metrics, refer to the following AWS document: + * https://docs.aws.amazon.com/iot/latest/developerguide/dd-detect-custom-metrics.html */ typedef struct ReportMetrics { @@ -52,6 +58,10 @@ typedef struct ReportMetrics uint32_t ulOpenUdpPortsArrayLength; Connection_t * pxEstablishedConnectionsArray; uint32_t ulEstablishedConnectionsArrayLength; + /* Custom metrics */ + uint32_t ulStackHighWaterMark; + uint32_t * pulTaskIdArray; + uint32_t ulTaskIdArrayLength; } ReportMetrics_t; /** diff --git a/FreeRTOS-Plus/Source/AWS/device-defender b/FreeRTOS-Plus/Source/AWS/device-defender index e15db4c22c..ce98843504 160000 --- a/FreeRTOS-Plus/Source/AWS/device-defender +++ b/FreeRTOS-Plus/Source/AWS/device-defender @@ -1 +1 @@ -Subproject commit e15db4c22cfdd534d1e0693cdbdf467c7b09e897 +Subproject commit ce98843504b037a81fbfbccd90d90d6f2bb1f417