Skip to content

Commit efc5fe0

Browse files
Andrew Boiejhedberg
authored andcommitted
kernel: overhaul unused stack measurement
The existing stack_analyze APIs had some problems: 1. Not properly namespaced 2. Accepted the stack object as a parameter, yet the stack object does not contain the necessary information to get the associated buffer region, the thread object is needed for this 3. Caused a crash on certain platforms that do not allow inspection of unused stack space for the currently running thread 4. No user mode access 5. Separately passed in thread name We deprecate these functions and add a new API k_thread_stack_space_get() which addresses all of these issues. A helper API log_stack_usage() also added which resembles STACK_ANALYZE() in functionality. Fixes: #17852 Signed-off-by: Andrew Boie <[email protected]>
1 parent f02fdd2 commit efc5fe0

File tree

16 files changed

+229
-46
lines changed

16 files changed

+229
-46
lines changed

arch/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ config STACK_GROWS_UP
243243
Select this option if the architecture has upward growing thread
244244
stacks. This is not common.
245245

246+
config NO_UNUSED_STACK_INSPECTION
247+
bool
248+
help
249+
Selected if the architecture will generate a fault if unused stack
250+
memory is examined, which is the region between the current stack
251+
pointer and the deepest available address in the current stack
252+
region.
253+
246254
config MAX_THREAD_BYTES
247255
int "Bytes to use when tracking object thread permissions"
248256
default 2

arch/arc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ config ARC_CONNECT
145145

146146
config ARC_STACK_CHECKING
147147
bool
148+
select NO_UNUSED_STACK_INSPECTION
148149
help
149150
Use ARC STACK_CHECKING to do stack protection
150151

drivers/bluetooth/hci/h5.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,8 @@ static void ack_timeout(struct k_work *work)
362362
h5_send(NULL, HCI_3WIRE_ACK_PKT, 0);
363363

364364
/* Analyze stacks */
365-
STACK_ANALYZE("tx_stack", tx_stack);
366-
STACK_ANALYZE("rx_stack", rx_stack);
365+
log_stack_usage(&tx_thread_data);
366+
log_stack_usage(&rx_thread_data);
367367
}
368368

369369
static void h5_process_complete_packet(u8_t *hdr)
@@ -713,13 +713,15 @@ static void h5_init(void)
713713
(k_thread_entry_t)tx_thread, NULL, NULL, NULL,
714714
K_PRIO_COOP(CONFIG_BT_HCI_TX_PRIO),
715715
0, K_NO_WAIT);
716+
k_thread_name_set(&tx_thread_data, "tx_thread");
716717

717718
k_fifo_init(&h5.rx_queue);
718719
k_thread_create(&rx_thread_data, rx_stack,
719720
K_THREAD_STACK_SIZEOF(rx_stack),
720721
(k_thread_entry_t)rx_thread, NULL, NULL, NULL,
721722
K_PRIO_COOP(CONFIG_BT_RX_PRIO),
722723
0, K_NO_WAIT);
724+
k_thread_name_set(&rx_thread_data, "rx_thread");
723725

724726
/* Unack queue */
725727
k_fifo_init(&h5.unack_queue);

include/debug/stack.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static inline size_t stack_unused_space_get(const char *stack, size_t size)
6060
return unused;
6161
}
6262

63+
__deprecated
6364
static inline void stack_analyze(const char *name, const char *stack,
6465
unsigned int size)
6566
{
@@ -82,11 +83,33 @@ static inline void stack_analyze(const char *name, const char *stack,
8283
* @param name Name of the stack
8384
* @param sym The symbol of the stack
8485
*/
85-
#define STACK_ANALYZE(name, sym) \
86+
#define STACK_ANALYZE(name, sym) DEPRECATED_MACRO \
8687
do { \
8788
stack_analyze(name, \
8889
Z_THREAD_STACK_BUFFER(sym), \
8990
K_THREAD_STACK_SIZEOF(sym)); \
9091
} while (false)
9192

93+
static inline void log_stack_usage(const struct k_thread *thread)
94+
{
95+
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
96+
size_t unused, size = thread->stack_info.size;
97+
98+
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
99+
100+
if (k_thread_stack_space_get(thread, &unused) == 0) {
101+
unsigned int pcnt = ((size - unused) * 100U) / size;
102+
const char *tname;
103+
104+
tname = k_thread_name_get((k_tid_t)thread);
105+
if (tname == NULL) {
106+
tname = "unknown";
107+
}
108+
109+
LOG_INF("%p (%s):\tunused %zu\tusage %zu / %zu (%u %%)",
110+
thread, tname, unused, size - unused, size,
111+
pcnt);
112+
}
113+
#endif
114+
}
92115
#endif /* ZEPHYR_INCLUDE_DEBUG_STACK_H_ */

include/kernel.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,31 @@ static inline void k_thread_resource_pool_assign(struct k_thread *thread,
834834
thread->resource_pool = pool;
835835
}
836836

837+
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
838+
/**
839+
* @brief Obtain stack usage information for the specified thread
840+
*
841+
* User threads will need to have permission on the target thread object.
842+
*
843+
* Some hardware may prevent inspection of a stack buffer currently in use.
844+
* If this API is called from supervisor mode, on the currently running thread,
845+
* on a platform which selects CONFIG_NO_UNUSED_STACK_INSPECTION, an error
846+
* will be generated.
847+
*
848+
* @param thread Thread to inspect stack information
849+
* @param unused_ptr Output parameter, filled in with the unused stack space
850+
* of the target thread in bytes.
851+
* @return 0 on success
852+
* @return -EBADF Bad thread object (user mode only)
853+
* @return -EPERM No permissions on thread object (user mode only)
854+
* #return -ENOTSUP Forbidden by hardware policy
855+
* @return -EINVAL Thread is uninitialized or exited (user mode only)
856+
* @return -EFAULT Bad memory address for unused_ptr (user mode only)
857+
*/
858+
__syscall int k_thread_stack_space_get(const struct k_thread *thread,
859+
size_t *unused_ptr);
860+
#endif
861+
837862
#if (CONFIG_HEAP_MEM_POOL_SIZE > 0)
838863
/**
839864
* @brief Assign the system heap as a thread's resource pool

kernel/thread.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <string.h>
2828
#include <stdbool.h>
2929
#include <irq_offload.h>
30+
#include <sys/check.h>
3031

3132
static struct k_spinlock lock;
3233

@@ -882,3 +883,90 @@ void irq_offload(irq_offload_routine_t routine, void *parameter)
882883
k_sem_give(&offload_sem);
883884
}
884885
#endif
886+
887+
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
888+
#ifdef CONFIG_STACK_GROWS_UP
889+
#error "Unsupported configuration for stack analysis"
890+
#endif
891+
892+
int z_impl_k_thread_stack_space_get(const struct k_thread *thread,
893+
size_t *unused_ptr)
894+
{
895+
const u8_t *start = (u8_t *)thread->stack_info.start;
896+
size_t size = thread->stack_info.size;
897+
size_t unused = 0;
898+
const u8_t *checked_stack = start;
899+
/* Take the address of any local variable as a shallow bound for the
900+
* stack pointer. Addresses above it are guaranteed to be
901+
* accessible.
902+
*/
903+
const u8_t *stack_pointer = (const u8_t *)&start;
904+
905+
/* If we are currently running on the stack being analyzed, some
906+
* memory management hardware will generate an exception if we
907+
* read unused stack memory.
908+
*
909+
* This never happens when invoked from user mode, as user mode
910+
* will always run this function on the privilege elevation stack.
911+
*/
912+
if ((stack_pointer > start) && (stack_pointer <= (start + size)) &&
913+
IS_ENABLED(CONFIG_NO_UNUSED_STACK_INSPECTION)) {
914+
/* TODO: We could add an arch_ API call to temporarily
915+
* disable the stack checking in the CPU, but this would
916+
* need to be properly managed wrt context switches/interrupts
917+
*/
918+
return -ENOTSUP;
919+
}
920+
921+
if (IS_ENABLED(CONFIG_STACK_SENTINEL)) {
922+
/* First 4 bytes of the stack buffer reserved for the
923+
* sentinel value, it won't be 0xAAAAAAAA for thread
924+
* stacks.
925+
*
926+
* FIXME: thread->stack_info.start ought to reflect
927+
* this!
928+
*/
929+
checked_stack += 4;
930+
size -= 4;
931+
}
932+
933+
for (size_t i = 0; i < size; i++) {
934+
if ((checked_stack[i]) == 0xaaU) {
935+
unused++;
936+
} else {
937+
break;
938+
}
939+
}
940+
941+
*unused_ptr = unused;
942+
943+
return 0;
944+
}
945+
946+
#ifdef CONFIG_USERSPACE
947+
int z_vrfy_k_thread_stack_space_get(const struct k_thread *thread,
948+
size_t *unused_ptr)
949+
{
950+
size_t unused;
951+
int ret;
952+
953+
ret = Z_SYSCALL_OBJ(thread, K_OBJ_THREAD);
954+
CHECKIF(ret != 0) {
955+
return ret;
956+
}
957+
958+
ret = z_impl_k_thread_stack_space_get(thread, &unused);
959+
CHECKIF(ret != 0) {
960+
return ret;
961+
}
962+
963+
ret = z_user_to_copy(unused_ptr, &unused, sizeof(size_t));
964+
CHECKIF(ret != 0) {
965+
return ret;
966+
}
967+
968+
return 0;
969+
}
970+
#include <syscalls/k_thread_stack_space_get_mrsh.c>
971+
#endif /* CONFIG_USERSPACE */
972+
#endif /* CONFIG_INIT_STACKS && CONFIG_THREAD_STACK_INFO */

lib/cmsis_rtos_v2/thread.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,17 +362,19 @@ uint32_t osThreadGetStackSize(osThreadId_t thread_id)
362362
uint32_t osThreadGetStackSpace(osThreadId_t thread_id)
363363
{
364364
struct cv2_thread *tid = (struct cv2_thread *)thread_id;
365-
u32_t size = tid->z_thread.stack_info.size;
366-
u32_t unused = 0U;
365+
size_t unused;
366+
int ret;
367367

368368
__ASSERT(tid, "");
369369
__ASSERT(is_cmsis_rtos_v2_thread(tid), "");
370370
__ASSERT(!k_is_in_isr(), "");
371371

372-
unused = stack_unused_space_get((char *)tid->z_thread.stack_info.start,
373-
size);
372+
ret = k_thread_stack_space_get(&tid->z_thread, &unused);
373+
if (ret != 0) {
374+
unused = 0;
375+
}
374376

375-
return unused;
377+
return (uint32_t)unused;
376378
}
377379

378380
/**

samples/bluetooth/hci_spi/src/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ static void bt_tx_thread(void *p1, void *p2, void *p3)
259259
net_buf_unref(buf);
260260
}
261261

262-
STACK_ANALYZE("tx_stack", bt_tx_thread_stack);
262+
log_stack_usage(&bt_tx_thread_data);
263263

264264
/* Make sure other threads get a chance to run */
265265
k_yield();
@@ -312,6 +312,7 @@ void main(void)
312312
K_THREAD_STACK_SIZEOF(bt_tx_thread_stack),
313313
bt_tx_thread, NULL, NULL, NULL, K_PRIO_COOP(7),
314314
0, K_NO_WAIT);
315+
k_thread_name_set(&bt_tx_thread_data, "bt_tx_thread");
315316

316317
/* Send a vendor event to announce that the slave is initialized */
317318
buf = net_buf_alloc(&cmd_tx_pool, K_FOREVER);

subsys/bluetooth/controller/hci/hci_driver.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ static K_THREAD_STACK_DEFINE(prio_recv_thread_stack,
6262
struct k_thread recv_thread_data;
6363
static K_THREAD_STACK_DEFINE(recv_thread_stack, CONFIG_BT_RX_STACK_SIZE);
6464

65-
#if defined(CONFIG_INIT_STACKS)
65+
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
6666
static u32_t prio_ts;
6767
static u32_t rx_ts;
6868
#endif
@@ -154,10 +154,9 @@ static void prio_recv_thread(void *p1, void *p2, void *p3)
154154
/* Now, ULL mayfly has something to give to us */
155155
BT_DBG("sem taken");
156156

157-
#if defined(CONFIG_INIT_STACKS)
157+
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
158158
if (k_uptime_get_32() - prio_ts > K_SECONDS(5)) {
159-
STACK_ANALYZE("prio recv thread stack",
160-
prio_recv_thread_stack);
159+
log_stack_usage(&prio_recv_thread_data);
161160
prio_ts = k_uptime_get_32();
162161
}
163162
#endif
@@ -399,9 +398,9 @@ static void recv_thread(void *p1, void *p2, void *p3)
399398

400399
k_yield();
401400

402-
#if defined(CONFIG_INIT_STACKS)
401+
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
403402
if (k_uptime_get_32() - rx_ts > K_SECONDS(5)) {
404-
STACK_ANALYZE("recv thread stack", recv_thread_stack);
403+
log_stack_usage(&recv_thread_data);
405404
rx_ts = k_uptime_get_32();
406405
}
407406
#endif

subsys/bluetooth/host/hci_core.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,9 +1022,9 @@ static void hci_disconn_complete(struct net_buf *buf)
10221022

10231023
/* Check stacks usage */
10241024
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
1025-
STACK_ANALYZE("rx stack", rx_thread_stack);
1025+
log_stack_usage(&rx_thread_data);
10261026
#endif
1027-
STACK_ANALYZE("tx stack", tx_thread_stack);
1027+
log_stack_usage(&tx_thread_data);
10281028

10291029
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
10301030
conn->handle = 0U;

0 commit comments

Comments
 (0)