kernel: update k_thread_state_str() API

When threads are in more than one state at a time, k_thread_state_str()
returns a string that lists each of its states delimited by a '+'.
This in turn necessitates a change to the API that includes both a
pointer to the buffer to use for the string and the size of the buffer.

Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
This commit is contained in:
Peter Mitsis 2022-04-11 19:54:23 -04:00 committed by Anas Nashif
commit a30cf39975
6 changed files with 103 additions and 43 deletions

View file

@ -1074,12 +1074,16 @@ __syscall int k_thread_name_copy(k_tid_t thread, char *buf,
/** /**
* @brief Get thread state string * @brief Get thread state string
* *
* Get the human friendly thread state string * This routine generates a human friendly string containing the thread's
* state, and copies as much of it as possible into @a buf.
* *
* @param thread_id Thread ID * @param thread_id Thread ID
* @retval Thread state string, empty if no state flag is set * @param buf Buffer into which to copy state strings
* @param buf_size Size of the buffer
*
* @retval Pointer to @a buf if data was copied, else a pointer to "".
*/ */
const char *k_thread_state_str(k_tid_t thread_id); const char *k_thread_state_str(k_tid_t thread_id, char *buf, size_t buf_size);
/** /**
* @} * @}

View file

@ -258,32 +258,57 @@ int z_impl_k_thread_name_copy(k_tid_t thread, char *buf, size_t size)
#endif /* CONFIG_THREAD_NAME */ #endif /* CONFIG_THREAD_NAME */
} }
const char *k_thread_state_str(k_tid_t thread_id) static size_t copy_bytes(char *dest, size_t dest_size, const char *src, size_t src_size)
{ {
switch (thread_id->base.thread_state) { size_t bytes_to_copy;
case 0:
return ""; bytes_to_copy = MIN(dest_size, src_size);
case _THREAD_DUMMY: memcpy(dest, src, bytes_to_copy);
return "dummy";
case _THREAD_PENDING: return bytes_to_copy;
return "pending";
case _THREAD_PRESTART:
return "prestart";
case _THREAD_DEAD:
return "dead";
case _THREAD_SUSPENDED:
return "suspended";
case _THREAD_ABORTING:
return "aborting";
case _THREAD_QUEUED:
return "queued";
default:
/* Add a break, some day when another case gets added at the end,
* this bit of defensive programming will be useful
*/
break;
} }
return "unknown";
const char *k_thread_state_str(k_tid_t thread_id, char *buf, size_t buf_size)
{
size_t off = 0;
uint8_t bit;
uint8_t thread_state = thread_id->base.thread_state;
static const char *states_str[8] = {"dummy", "pending", "prestart",
"dead", "suspended", "aborting",
"", "queued"};
static const size_t states_sz[8] = {5, 7, 8, 4, 9, 8, 0, 6};
if ((buf == NULL) || (buf_size == 0)) {
return "";
}
buf_size--; /* Reserve 1 byte for end-of-string character */
/*
* Loop through each bit in the thread_state. Stop once all have
* been processed. If more than one thread_state bit is set, then
* separate the descriptive strings with a '+'.
*/
for (uint8_t index = 0; thread_state != 0; index++) {
bit = BIT(index);
if ((thread_state & bit) == 0) {
continue;
}
off += copy_bytes(buf + off, buf_size - off,
states_str[index], states_sz[index]);
thread_state &= ~bit;
if (thread_state != 0) {
off += copy_bytes(buf + off, buf_size - off, "+", 1);
}
}
buf[off] = '\0';
return (const char *)buf;
} }
#ifdef CONFIG_USERSPACE #ifdef CONFIG_USERSPACE

View file

@ -62,6 +62,7 @@ static void shell_tdata_dump(const struct k_thread *cthread, void *user_data)
size_t size = thread->stack_info.size; size_t size = thread->stack_info.size;
const char *tname; const char *tname;
int ret; int ret;
char state_str[32];
#ifdef CONFIG_THREAD_RUNTIME_STATS #ifdef CONFIG_THREAD_RUNTIME_STATS
k_thread_runtime_stats_t rt_stats_thread; k_thread_runtime_stats_t rt_stats_thread;
@ -79,7 +80,8 @@ static void shell_tdata_dump(const struct k_thread *cthread, void *user_data)
thread->base.user_options, thread->base.user_options,
thread->base.prio, thread->base.prio,
(int64_t)thread->base.timeout.dticks); (int64_t)thread->base.timeout.dticks);
shell_print(shell, "\tstate: %s, entry: %p", k_thread_state_str(thread), shell_print(shell, "\tstate: %s, entry: %p",
k_thread_state_str(thread, state_str, sizeof(state_str)),
thread->entry.pEntry); thread->entry.pEntry);
#ifdef CONFIG_THREAD_RUNTIME_STATS #ifdef CONFIG_THREAD_RUNTIME_STATS

View file

@ -103,9 +103,12 @@ void wakeup_src_thread(int id)
*/ */
for (int i = 0; i < NUM_THREADS; i++) { for (int i = 0; i < NUM_THREADS; i++) {
k_tid_t th = &worker_threads[i]; k_tid_t th = &worker_threads[i];
char buffer[16];
const char *str;
zassert_equal(strcmp(k_thread_state_str(th), "pending"), str = k_thread_state_str(th, buffer, sizeof(buffer));
0, "worker thread %d not pending?", i); zassert_not_null(strstr(str, "pending"),
"worker thread %d not pending?", i);
} }
/* Wake the src worker up */ /* Wake the src worker up */

View file

@ -219,32 +219,50 @@ void test_k_thread_foreach_unlocked_null_cb(void)
*/ */
void test_k_thread_state_str(void) void test_k_thread_state_str(void)
{ {
char state_str[32];
const char *str;
k_tid_t tid = &tdata1; k_tid_t tid = &tdata1;
tid->base.thread_state = 0; tid->base.thread_state = 0;
zassert_true(strcmp(k_thread_state_str(tid), "") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "") == 0, NULL);
tid->base.thread_state = _THREAD_DUMMY; tid->base.thread_state = _THREAD_DUMMY;
zassert_true(strcmp(k_thread_state_str(tid), "dummy") == 0, NULL);
str = k_thread_state_str(tid, NULL, sizeof(state_str));
zassert_true(strcmp(str, "") == 0, NULL);
str = k_thread_state_str(tid, state_str, 0);
zassert_true(strcmp(str, "") == 0, NULL);
str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "dummy") == 0, NULL);
tid->base.thread_state = _THREAD_PENDING; tid->base.thread_state = _THREAD_PENDING;
zassert_true(strcmp(k_thread_state_str(tid), "pending") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "pending") == 0, NULL);
tid->base.thread_state = _THREAD_PRESTART; tid->base.thread_state = _THREAD_PRESTART;
zassert_true(strcmp(k_thread_state_str(tid), "prestart") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "prestart") == 0, NULL);
tid->base.thread_state = _THREAD_DEAD; tid->base.thread_state = _THREAD_DEAD;
zassert_true(strcmp(k_thread_state_str(tid), "dead") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "dead") == 0, NULL);
tid->base.thread_state = _THREAD_SUSPENDED; tid->base.thread_state = _THREAD_SUSPENDED;
zassert_true(strcmp(k_thread_state_str(tid), "suspended") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "suspended") == 0, NULL);
tid->base.thread_state = _THREAD_ABORTING; tid->base.thread_state = _THREAD_ABORTING;
zassert_true(strcmp(k_thread_state_str(tid), "aborting") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "aborting") == 0, NULL);
tid->base.thread_state = _THREAD_QUEUED; tid->base.thread_state = _THREAD_QUEUED;
zassert_true(strcmp(k_thread_state_str(tid), "queued") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "queued") == 0, NULL);
tid->base.thread_state = 0xFF; tid->base.thread_state = _THREAD_PENDING | _THREAD_SUSPENDED;
zassert_true(strcmp(k_thread_state_str(tid), "unknown") == 0, NULL); str = k_thread_state_str(tid, state_str, sizeof(state_str));
zassert_true(strcmp(str, "pending+suspended") == 0, NULL);
} }

View file

@ -156,20 +156,28 @@ void test_threads_suspend_timeout(void)
*/ */
void test_resume_unsuspend_thread(void) void test_resume_unsuspend_thread(void)
{ {
char buffer[32];
const char *str;
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE, k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
thread_entry, NULL, NULL, NULL, thread_entry, NULL, NULL, NULL,
0, K_USER, K_NO_WAIT); 0, K_USER, K_NO_WAIT);
/* Resume an unsuspend thread will not change the thread state. */ /* Resume an unsuspend thread will not change the thread state. */
zassert_true(strcmp(k_thread_state_str(tid), "queued") == 0, NULL); str = k_thread_state_str(tid, buffer, sizeof(buffer));
zassert_true(strcmp(str, "queued") == 0, NULL);
k_thread_resume(tid); k_thread_resume(tid);
zassert_true(strcmp(k_thread_state_str(tid), "queued") == 0, NULL); str = k_thread_state_str(tid, buffer, sizeof(buffer));
zassert_true(strcmp(str, "queued") == 0, NULL);
/* suspend created thread */ /* suspend created thread */
k_thread_suspend(tid); k_thread_suspend(tid);
zassert_true(strcmp(k_thread_state_str(tid), "suspended") == 0, NULL); str = k_thread_state_str(tid, buffer, sizeof(buffer));
zassert_true(strcmp(str, "suspended") == 0, NULL);
/* Resume an suspend thread will make it to be next eligible.*/ /* Resume an suspend thread will make it to be next eligible.*/
k_thread_resume(tid); k_thread_resume(tid);
zassert_true(strcmp(k_thread_state_str(tid), "queued") == 0, NULL); str = k_thread_state_str(tid, buffer, sizeof(buffer));
zassert_true(strcmp(str, "queued") == 0, NULL);
k_thread_abort(tid); k_thread_abort(tid);
} }