Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions generated/Kconfig.rpc_commands
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ config INFUSE_RPC_COMMAND_DATA_LOGGER_ERASE_REQUIRED_AUTH
depends on INFUSE_RPC_COMMAND_DATA_LOGGER_ERASE
default 2

config INFUSE_RPC_COMMAND_HEAP_STATS
bool "Enable RPC heap_stats"
depends on SYS_HEAP_RUNTIME_STATS
default y if INFUSE_SDK
help
Query stats of heaps

config INFUSE_RPC_COMMAND_HEAP_STATS_REQUIRED_AUTH
int "Required authorisation level to run heap_stats"
depends on INFUSE_RPC_COMMAND_HEAP_STATS
default 2

config INFUSE_RPC_COMMAND_LTE_AT_CMD
bool "Enable RPC lte_at_cmd"
depends on NRF_MODEM_LIB
Expand Down
9 changes: 9 additions & 0 deletions generated/include/infuse/rpc/commands_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ struct net_buf *rpc_command_infuse_states_update(struct net_buf *request);
*/
struct net_buf *rpc_command_data_logger_erase(struct net_buf *request);

/**
* @brief Run heap_stats RPC
*
* @param request @ref INFUSE_RPC_CMD packet to respond to
*
* @return struct net_buf* @ref INFUSE_RPC_RSP packet buffer
*/
struct net_buf *rpc_command_heap_stats(struct net_buf *request);

/**
* @brief Run lte_at_cmd RPC
*
Expand Down
25 changes: 25 additions & 0 deletions generated/include/infuse/rpc/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ struct rpc_struct_sockaddr {
uint8_t scope_id;
} __packed;

/** struct k_heap information */
struct rpc_struct_heap_info {
/** Address of the heap structure */
uint32_t addr;
/** Number of bytes currently free */
uint32_t free_bytes;
/** Number of bytes currently allocated */
uint32_t allocated_bytes;
/** Maximum number of bytes ever concurrently allocated */
uint32_t max_allocated_bytes;
} __packed;

/** Bluetooth LE address type */
enum rpc_enum_bt_le_addr_type {
/** Public address */
Expand Down Expand Up @@ -355,6 +367,8 @@ enum rpc_builtin_id {
RPC_ID_INFUSE_STATES_UPDATE = 17,
/** Erase all data from a data logger */
RPC_ID_DATA_LOGGER_ERASE = 18,
/** Query stats of heaps */
RPC_ID_HEAP_STATS = 19,
/** Run AT command against LTE modem */
RPC_ID_LTE_AT_CMD = 20,
/** Get current LTE interface state */
Expand Down Expand Up @@ -687,6 +701,17 @@ struct rpc_data_logger_erase_response {
struct infuse_rpc_rsp_header header;
} __packed;

/** Query stats of heaps */
struct rpc_heap_stats_request {
struct infuse_rpc_req_header header;
} __packed;

struct rpc_heap_stats_response {
struct infuse_rpc_rsp_header header;
/** Current statistics of application heaps */
struct rpc_struct_heap_info stats[];
} __packed;

/** Run AT command against LTE modem */
struct rpc_lte_at_cmd_request {
struct infuse_rpc_req_header header;
Expand Down
7 changes: 7 additions & 0 deletions generated/rpc_command_runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ void rpc_command_runner(struct net_buf *request)
}
break;
#endif /* CONFIG_INFUSE_RPC_COMMAND_DATA_LOGGER_ERASE */
#ifdef CONFIG_INFUSE_RPC_COMMAND_HEAP_STATS
case RPC_ID_HEAP_STATS:
if (AUTHORISED(auth, HEAP_STATS)) { /* GCOVR_EXCL_BR_LINE */
response = rpc_command_heap_stats(request);
}
break;
#endif /* CONFIG_INFUSE_RPC_COMMAND_HEAP_STATS */
#ifdef CONFIG_INFUSE_RPC_COMMAND_LTE_AT_CMD
case RPC_ID_LTE_AT_CMD:
if (AUTHORISED(auth, LTE_AT_CMD)) { /* GCOVR_EXCL_BR_LINE */
Expand Down
21 changes: 21 additions & 0 deletions scripts/west_commands/cloud_definitions/rpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@
{"name": "sin_addr", "type": "uint8_t", "num": 16, "description": "IPv4/IPv6 address"},
{"name": "scope_id", "type": "uint8_t", "description": "Interfaces for a scope (IPv6 only)"}
]
},
"rpc_struct_heap_info": {
"description": "struct k_heap information",
"fields": [
{"name": "addr", "type": "uint32_t", "description": "Address of the heap structure"},
{"name": "free_bytes", "type": "uint32_t", "description": "Number of bytes currently free"},
{"name": "allocated_bytes", "type": "uint32_t", "description": "Number of bytes currently allocated"},
{"name": "max_allocated_bytes", "type": "uint32_t", "description": "Maximum number of bytes ever concurrently allocated"}
]
}
},
"enums": {
Expand Down Expand Up @@ -439,6 +448,18 @@
"response_params": [
]
},
"19": {
"name": "heap_stats",
"description": "Query stats of heaps",
"depends_on": "SYS_HEAP_RUNTIME_STATS",
"default": "y if INFUSE_SDK",
"default_auth": "EPACKET_AUTH_DEVICE",
"request_params": [
],
"response_params": [
{"name": "stats", "type": "struct rpc_struct_heap_info", "num": 0, "description": "Current statistics of application heaps"}
]
},
"20": {
"name": "lte_at_cmd",
"description": "Run AT command against LTE modem",
Expand Down
1 change: 1 addition & 0 deletions subsys/rpc/commands/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_MEM_READ mem_read.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_INFUSE_STATES_QUERY infuse_states_query.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_INFUSE_STATES_UPDATE infuse_states_update.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_DATA_LOGGER_ERASE data_logger_erase.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_HEAP_STATS heap_stats.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_LTE_AT_CMD lte_at_cmd.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_LTE_STATE lte_state.c)
zephyr_library_sources_ifdef(CONFIG_INFUSE_RPC_COMMAND_COAP_DOWNLOAD coap_download.c)
Expand Down
51 changes: 51 additions & 0 deletions subsys/rpc/commands/heap_stats.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @file
* @copyright 2025 Embeint Inc
* @author Jordan Yates <[email protected]>
*
* SPDX-License-Identifier: LicenseRef-Embeint
*/

#include <string.h>

#include <zephyr/net_buf.h>

#include <infuse/rpc/commands.h>
#include <infuse/rpc/types.h>
#include <infuse/states.h>

struct net_buf *rpc_command_heap_stats(struct net_buf *request)
{
struct rpc_heap_stats_response rsp = {0};
struct sys_memory_stats heap_stats;
int max_rsp, max_entries, num_heaps;
struct net_buf *rsp_buf;
struct k_heap *heaps;
int rc;

/* Allocate response object */
rsp_buf = rpc_response_simple_req(request, 0, &rsp, sizeof(rsp));
max_rsp = net_buf_tailroom(rsp_buf);
max_entries = max_rsp / sizeof(struct rpc_struct_heap_info);

/* Get the static heaps */
num_heaps = k_heap_array_get(&heaps);
for (int i = 0; i < MIN(max_entries, num_heaps); i++) {
/* Query info */
rc = sys_heap_runtime_stats_get(&heaps[i].heap, &heap_stats);
if ((rc != 0) || (heaps[i].heap.init_bytes == 0)) {
continue;
}

/* Push into the response */
struct rpc_struct_heap_info *info = net_buf_add(rsp_buf, sizeof(*info));

info->addr = (uintptr_t)&heaps[i];
info->free_bytes = heap_stats.free_bytes;
info->allocated_bytes = heap_stats.allocated_bytes;
info->max_allocated_bytes = heap_stats.max_allocated_bytes;
}

/* Return the response */
return rsp_buf;
}
15 changes: 15 additions & 0 deletions tests/subsys/rpc/commands/heap_stats/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

# Infuse-IoT application
if(NOT infuse IN_LIST SNIPPET)
set(SNIPPET infuse ${SNIPPET} CACHE STRING "" FORCE)
endif()

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rpc_commands_heap_stats)

target_sources(app PRIVATE
src/main.c
)
5 changes: 5 additions & 0 deletions tests/subsys/rpc/commands/heap_stats/app.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/ {
epacket_dummy: epacket_dummy {
compatible = "embeint,epacket-dummy";
};
};
3 changes: 3 additions & 0 deletions tests/subsys/rpc/commands/heap_stats/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_ZTEST=y
CONFIG_INFUSE_SDK=y
CONFIG_SYS_HEAP_RUNTIME_STATS=y
134 changes: 134 additions & 0 deletions tests/subsys/rpc/commands/heap_stats/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* @file
* @copyright 2025 Embeint Inc
* @author Jordan Yates <[email protected]>
*
* SPDX-License-Identifier: LicenseRef-Embeint
*/

#include <zephyr/ztest.h>
#include <zephyr/kernel.h>

#include <infuse/states.h>
#include <infuse/types.h>
#include <infuse/rpc/types.h>
#include <infuse/epacket/packet.h>
#include <infuse/epacket/interface/epacket_dummy.h>

static void send_heap_stats_command(uint32_t request_id)
{
const struct device *epacket_dummy = DEVICE_DT_GET(DT_NODELABEL(epacket_dummy));
struct epacket_dummy_frame header = {
.type = INFUSE_RPC_CMD,
.auth = EPACKET_AUTH_DEVICE,
.flags = 0x0000,
};
struct rpc_heap_stats_request params = {
.header =
{
.request_id = request_id,
.command_id = RPC_ID_HEAP_STATS,
},
};

/* Push command at RPC server */
epacket_dummy_receive(epacket_dummy, &header, &params, sizeof(params));
}

static struct net_buf *expect_heap_stats_response(uint32_t request_id)
{
struct k_fifo *response_queue = epacket_dummmy_transmit_fifo_get();
struct rpc_heap_stats_response *response;
struct net_buf *rsp;

zassert_not_null(response_queue);

/* Response was sent */
rsp = k_fifo_get(response_queue, K_MSEC(100));
zassert_not_null(rsp);
net_buf_pull_mem(rsp, sizeof(struct epacket_dummy_frame));
response = (void *)rsp->data;

/* Parameters match what we expect */
zassert_equal(request_id, response->header.request_id);
zassert_equal(0, response->header.return_code);

/* Return the response */
return rsp;
}

/* Two arbitrary heaps for testing */
K_HEAP_DEFINE(heap1, 512);
K_HEAP_DEFINE(heap2, 1024);

ZTEST(rpc_command_heap_stats, test_basic)
{
struct rpc_heap_stats_response *response;
struct net_buf *rsp;
size_t num_heaps;
uint32_t free1, free2;
void *p;

/* Initial state (no allocations) */
send_heap_stats_command(3);
rsp = expect_heap_stats_response(3);
response = (void *)rsp->data;
num_heaps = (rsp->len - sizeof(*response)) / sizeof(struct rpc_struct_heap_info);
zassert_equal(2, num_heaps);

/* Expected states */
zassert_equal((uintptr_t)&heap1, response->stats[0].addr);
zassert_within(512, response->stats[0].free_bytes, 128);
zassert_equal(0, response->stats[0].allocated_bytes);
zassert_equal(0, response->stats[0].max_allocated_bytes);
zassert_equal((uintptr_t)&heap2, response->stats[1].addr);
zassert_within(1024, response->stats[1].free_bytes, 128);
zassert_equal(0, response->stats[1].allocated_bytes);
zassert_equal(0, response->stats[1].max_allocated_bytes);
free1 = response->stats[0].free_bytes;
free2 = response->stats[1].free_bytes;
net_buf_unref(rsp);

/* Allocate some bytes from each heap */
p = k_heap_alloc(&heap1, 128, K_FOREVER);
p = k_heap_alloc(&heap2, 256, K_FOREVER);

send_heap_stats_command(4);
rsp = expect_heap_stats_response(4);
response = (void *)rsp->data;
num_heaps = (rsp->len - sizeof(*response)) / sizeof(struct rpc_struct_heap_info);
zassert_equal(2, num_heaps);

/* Expected states */
zassert_equal((uintptr_t)&heap1, response->stats[0].addr);
zassert_within(free1 - 128, response->stats[0].free_bytes, 8);
zassert_within(128, response->stats[0].allocated_bytes, 8);
zassert_within(128, response->stats[0].max_allocated_bytes, 8);
zassert_equal((uintptr_t)&heap2, response->stats[1].addr);
zassert_within(free2 - 256, response->stats[1].free_bytes, 8);
zassert_within(256, response->stats[1].allocated_bytes, 8);
zassert_within(256, response->stats[1].max_allocated_bytes, 8);
net_buf_unref(rsp);

/* Free one of the buffers */
k_heap_free(&heap2, p);

send_heap_stats_command(4);
rsp = expect_heap_stats_response(4);
response = (void *)rsp->data;
num_heaps = (rsp->len - sizeof(*response)) / sizeof(struct rpc_struct_heap_info);
zassert_equal(2, num_heaps);

/* Expected states */
zassert_equal((uintptr_t)&heap1, response->stats[0].addr);
zassert_within(free1 - 128, response->stats[0].free_bytes, 8);
zassert_within(128, response->stats[0].allocated_bytes, 8);
zassert_within(128, response->stats[0].max_allocated_bytes, 8);
zassert_equal((uintptr_t)&heap2, response->stats[1].addr);
zassert_equal(free2, response->stats[1].free_bytes);
zassert_equal(0, response->stats[1].allocated_bytes);
zassert_within(256, response->stats[1].max_allocated_bytes, 8);
net_buf_unref(rsp);
}

ZTEST_SUITE(rpc_command_heap_stats, NULL, NULL, NULL, NULL, NULL);
12 changes: 12 additions & 0 deletions tests/subsys/rpc/commands/heap_stats/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
common:
tags: infuse
min_flash: 64
min_ram: 32
tests:
rpc.commands.heap_stats:
required_snippets:
- infuse
platform_allow:
- mps2/an385
integration_platforms:
- mps2/an385
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ manifest:

projects:
- name: zephyr
revision: 82063fdbc7ad1c289c6a3709ebb709a40f1ff3de
revision: fe8ef68db4ece8467d1b1f09ea3aeb29771c8f68
# Limit imported repositories to reduce clone time
import:
name-allowlist:
Expand Down
Loading