Difference between revisions of "Zephyr"
m (Zephyr macro to create thread, Zephyr API routine to give existing thread a name.) |
m |
||
| Line 68: | Line 68: | ||
* https://docs.zephyrproject.org/latest/reference/kernel/threads/index.html?highlight=k_thread_create#c.k_thread_name_set | * https://docs.zephyrproject.org/latest/reference/kernel/threads/index.html?highlight=k_thread_create#c.k_thread_name_set | ||
| + | |||
| + | <!-- comentario --> | ||
| + | |||
| + | == [[#top|^]] Zephyr thread_analyzer_cb And Related == | ||
| + | |||
| + | Zephyr 2.6.0's thread_analyzer_cb() routine gathers and populates a structure with all pertinent, reported run-time thread statistics. This is however a static routine so we cannot call it directly. Nor does it return the summary thread resource use data to its direct calling routine. There is yet hope, and to understand a path forward here excerpted is the definition of this important thread reporting routine from file `./zephyr/subsys/debug/thread_analyzer.c`: | ||
| + | |||
| + | <pre> | ||
| + | 59 static void thread_analyze_cb(const struct k_thread *cthread, void *user_data) | ||
| + | 60 { | ||
| + | 61 struct k_thread *thread = (struct k_thread *)cthread; | ||
| + | 62 #ifdef CONFIG_THREAD_RUNTIME_STATS | ||
| + | 63 k_thread_runtime_stats_t rt_stats_all; | ||
| + | 64 k_thread_runtime_stats_t rt_stats_thread; | ||
| + | 65 int ret; | ||
| + | 66 #endif | ||
| + | 67 size_t size = thread->stack_info.size; | ||
| + | 68 thread_analyzer_cb cb = user_data; | ||
| + | 69 struct thread_analyzer_info info; | ||
| + | 70 char hexname[PTR_STR_MAXLEN + 1]; | ||
| + | 71 const char *name; | ||
| + | 72 size_t unused; | ||
| + | 73 int err; | ||
| + | 74 | ||
| + | 75 | ||
| + | 76 | ||
| + | 77 name = k_thread_name_get((k_tid_t)thread); | ||
| + | 78 if (!name || name[0] == '\0') { | ||
| + | 79 name = hexname; | ||
| + | 80 snprintk(hexname, sizeof(hexname), "%p", (void *)thread); | ||
| + | 81 } | ||
| + | 82 | ||
| + | 83 err = k_thread_stack_space_get(thread, &unused); | ||
| + | 84 if (err) { | ||
| + | 85 THREAD_ANALYZER_PRINT( | ||
| + | 86 THREAD_ANALYZER_FMT( | ||
| + | 87 " %-20s: unable to get stack space (%d)"), | ||
| + | 88 name, err); | ||
| + | |||
| + | 88 name, err); | ||
| + | 89 | ||
| + | 90 unused = 0; | ||
| + | 91 } | ||
| + | 92 | ||
| + | 93 info.name = name; | ||
| + | 94 info.stack_size = size; | ||
| + | 95 info.stack_used = size - unused; | ||
| + | 96 | ||
| + | 97 #ifdef CONFIG_THREAD_RUNTIME_STATS | ||
| + | 98 ret = 0; | ||
| + | 99 | ||
| + | 100 if (k_thread_runtime_stats_get(thread, &rt_stats_thread) != 0) { | ||
| + | 101 ret++; | ||
| + | 102 } | ||
| + | 103 | ||
| + | 104 if (k_thread_runtime_stats_all_get(&rt_stats_all) != 0) { | ||
| + | 105 ret++; | ||
| + | 106 } | ||
| + | 107 if (ret == 0) { | ||
| + | 108 info.utilization = (rt_stats_thread.execution_cycles * 100U) / | ||
| + | 109 rt_stats_all.execution_cycles; | ||
| + | 110 } | ||
| + | 111 #endif | ||
| + | 112 cb(&info); | ||
| + | 113 } | ||
| + | </pre> | ||
| + | |||
| + | The routine which calls this is: | ||
| + | |||
| + | <pre> | ||
| + | 115 void thread_analyzer_run(thread_analyzer_cb cb) | ||
| + | 116 { | ||
| + | 117 if (IS_ENABLED(CONFIG_THREAD_ANALYZER_RUN_UNLOCKED)) { | ||
| + | 118 k_thread_foreach_unlocked(thread_analyze_cb, cb); | ||
| + | 119 } else { | ||
| + | 120 k_thread_foreach(thread_analyze_cb, cb); | ||
| + | 121 } | ||
| + | 122 } | ||
| + | </pre> | ||
| + | |||
| + | We should be able to directly call <code>thread_analyzer_run(thread_analyzer_cb cb)</code>. If yes, and if we can learn how &info is defined, we should be able to redirect thread_analyzer reports to an arbitrary UART . . . | ||
<!-- comentario --> | <!-- comentario --> | ||
Revision as of 06:51, 13 November 2021
Zephyr RTOS Logger and Backend Features
- https://docs.zephyrproject.org/2.6.0/reference/logging/index.html#logger-backend-interface
- https://docs.zephyrproject.org/2.6.0/reference/logging/index.html#default-frontend
Zephyr's thread_analyzer when not configured to dump reports to printk() instead sends thread data to LOG_INF(). Excerpt from Zephyr file `./zephyr/subsys/debug/thread_analyzer.c`:
11 #include <kernel.h> 12 #include <debug/thread_analyzer.h> 13 #include <debug/stack.h> 14 #include <kernel.h> 15 #include <logging/log.h> 16 #include <stdio.h> 17 18 LOG_MODULE_REGISTER(thread_analyzer, CONFIG_THREAD_ANALYZER_LOG_LEVEL); 19 20 #if IS_ENABLED(CONFIG_THREAD_ANALYZER_USE_PRINTK) 21 #define THREAD_ANALYZER_PRINT(...) printk(__VA_ARGS__) 22 #define THREAD_ANALYZER_FMT(str) str "\n" 23 #define THREAD_ANALYZER_VSTR(str) (str) 24 #else 25 #define THREAD_ANALYZER_PRINT(...) LOG_INF(__VA_ARGS__) 26 #define THREAD_ANALYZER_FMT(str) str 27 #define THREAD_ANALYZER_VSTR(str) log_strdup(str) 28 #endif
Zephyr macro `LOG_INF` is in turn defined:
ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$ grep -nr LOG_INF ./* | grep define ./include/logging/log.h:61:#define LOG_INF(...) Z_LOG(LOG_LEVEL_INF, __VA_ARGS__)
Define of Z_LOG:
ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$ grep -nr 'Z_LOG[^_]' ./*
./include/logging/log_core.h:295:#define Z_LOG2(_level, _source, _dsource, ...) do { \
./include/logging/log_core.h:329:#define Z_LOG(_level, ...) \
$ grep -nr 'Z_LOG[^_]' ./* $ vi ./include/logging/log_core.h 329 #define Z_LOG(_level, ...) \ 330 Z_LOG2(_level, __log_current_const_data, __log_current_dynamic_data, __VA_ARGS__)
A Zephyr RTOS sample to check out:
`ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr/samples/subsys/logging/logger$`
^ To Create And To Name Zephyr Threads
^ Zephyr thread_analyzer_cb And Related
Zephyr 2.6.0's thread_analyzer_cb() routine gathers and populates a structure with all pertinent, reported run-time thread statistics. This is however a static routine so we cannot call it directly. Nor does it return the summary thread resource use data to its direct calling routine. There is yet hope, and to understand a path forward here excerpted is the definition of this important thread reporting routine from file `./zephyr/subsys/debug/thread_analyzer.c`:
59 static void thread_analyze_cb(const struct k_thread *cthread, void *user_data)
60 {
61 struct k_thread *thread = (struct k_thread *)cthread;
62 #ifdef CONFIG_THREAD_RUNTIME_STATS
63 k_thread_runtime_stats_t rt_stats_all;
64 k_thread_runtime_stats_t rt_stats_thread;
65 int ret;
66 #endif
67 size_t size = thread->stack_info.size;
68 thread_analyzer_cb cb = user_data;
69 struct thread_analyzer_info info;
70 char hexname[PTR_STR_MAXLEN + 1];
71 const char *name;
72 size_t unused;
73 int err;
74
75
76
77 name = k_thread_name_get((k_tid_t)thread);
78 if (!name || name[0] == '\0') {
79 name = hexname;
80 snprintk(hexname, sizeof(hexname), "%p", (void *)thread);
81 }
82
83 err = k_thread_stack_space_get(thread, &unused);
84 if (err) {
85 THREAD_ANALYZER_PRINT(
86 THREAD_ANALYZER_FMT(
87 " %-20s: unable to get stack space (%d)"),
88 name, err);
88 name, err);
89
90 unused = 0;
91 }
92
93 info.name = name;
94 info.stack_size = size;
95 info.stack_used = size - unused;
96
97 #ifdef CONFIG_THREAD_RUNTIME_STATS
98 ret = 0;
99
100 if (k_thread_runtime_stats_get(thread, &rt_stats_thread) != 0) {
101 ret++;
102 }
103
104 if (k_thread_runtime_stats_all_get(&rt_stats_all) != 0) {
105 ret++;
106 }
107 if (ret == 0) {
108 info.utilization = (rt_stats_thread.execution_cycles * 100U) /
109 rt_stats_all.execution_cycles;
110 }
111 #endif
112 cb(&info);
113 }
The routine which calls this is:
115 void thread_analyzer_run(thread_analyzer_cb cb)
116 {
117 if (IS_ENABLED(CONFIG_THREAD_ANALYZER_RUN_UNLOCKED)) {
118 k_thread_foreach_unlocked(thread_analyze_cb, cb);
119 } else {
120 k_thread_foreach(thread_analyze_cb, cb);
121 }
122 }
We should be able to directly call thread_analyzer_run(thread_analyzer_cb cb). If yes, and if we can learn how &info is defined, we should be able to redirect thread_analyzer reports to an arbitrary UART . . .