1 /*
2  * Copyright (c) 2008-2014 Travis Geiselbrecht
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 /**
25  * @defgroup debug  Debug
26  * @{
27  */
28 
29 /**
30  * @file
31  * @brief  Debug console functions.
32  */
33 
34 #include <debug.h>
35 #include <stdio.h>
36 #include <kernel/thread.h>
37 #include <kernel/timer.h>
38 #include <kernel/debug.h>
39 #include <kernel/mp.h>
40 #include <err.h>
41 #include <platform.h>
42 
43 #if WITH_LIB_CONSOLE
44 #include <lib/console.h>
45 
46 static int cmd_threads(int argc, const cmd_args *argv);
47 static int cmd_threadstats(int argc, const cmd_args *argv);
48 static int cmd_threadload(int argc, const cmd_args *argv);
49 static int cmd_kevlog(int argc, const cmd_args *argv);
50 
51 STATIC_COMMAND_START
52 #if LK_DEBUGLEVEL > 1
53 STATIC_COMMAND_MASKED("threads", "list kernel threads", &cmd_threads, CMD_AVAIL_ALWAYS)
54 #endif
55 #if THREAD_STATS
56 STATIC_COMMAND("threadstats", "thread level statistics", &cmd_threadstats)
57 STATIC_COMMAND("threadload", "toggle thread load display", &cmd_threadload)
58 #endif
59 #if WITH_KERNEL_EVLOG
60 STATIC_COMMAND_MASKED("kevlog", "dump kernel event log", &cmd_kevlog, CMD_AVAIL_ALWAYS)
61 #endif
62 STATIC_COMMAND_END(kernel);
63 
64 #if LK_DEBUGLEVEL > 1
cmd_threads(int argc,const cmd_args * argv)65 static int cmd_threads(int argc, const cmd_args *argv)
66 {
67     printf("thread list:\n");
68     dump_all_threads();
69 
70     return 0;
71 }
72 #endif
73 
74 #if THREAD_STATS
cmd_threadstats(int argc,const cmd_args * argv)75 static int cmd_threadstats(int argc, const cmd_args *argv)
76 {
77     for (uint i = 0; i < SMP_MAX_CPUS; i++) {
78         if (!mp_is_cpu_active(i))
79             continue;
80 
81         printf("thread stats (cpu %d):\n", i);
82         printf("\ttotal idle time: %lld\n", thread_stats[i].idle_time);
83         printf("\ttotal busy time: %lld\n", current_time_ns() - thread_stats[i].idle_time);
84         printf("\treschedules: %lu\n", thread_stats[i].reschedules);
85 #if WITH_SMP
86         printf("\treschedule_ipis: %lu\n", thread_stats[i].reschedule_ipis);
87 #endif
88         printf("\tcontext_switches: %lu\n", thread_stats[i].context_switches);
89         printf("\tpreempts: %lu\n", thread_stats[i].preempts);
90         printf("\tyields: %lu\n", thread_stats[i].yields);
91         printf("\tinterrupts: %lu\n", thread_stats[i].interrupts);
92         printf("\ttimer interrupts: %lu\n", thread_stats[i].timer_ints);
93         printf("\ttimers: %lu\n", thread_stats[i].timers);
94     }
95 
96     return 0;
97 }
98 
threadload(struct timer * t,lk_time_t now,void * arg)99 static enum handler_return threadload(struct timer *t, lk_time_t now, void *arg)
100 {
101     static struct thread_stats old_stats[SMP_MAX_CPUS];
102     static lk_time_ns_t last_idle_time[SMP_MAX_CPUS];
103 
104     for (uint i = 0; i < SMP_MAX_CPUS; i++) {
105         /* dont display time for inactiv cpus */
106         if (!mp_is_cpu_active(i))
107             continue;
108 
109         lk_time_ns_t idle_time = thread_stats[i].idle_time;
110 
111         /* if the cpu is currently idle, add the time since it went idle up until now to the idle counter */
112         bool is_idle = !!mp_is_cpu_idle(i);
113         if (is_idle) {
114             idle_time += current_time_ns() - thread_stats[i].last_idle_timestamp;
115         }
116 
117         lk_time_ns_t delta_time = idle_time - last_idle_time[i];
118         lk_time_ns_t busy_time = 1000000000ULL - (delta_time > 1000000000ULL ? 1000000000ULL : delta_time);
119         uint busypercent = (busy_time * 10000) / (1000000);
120 
121         printf("cpu %u LOAD: "
122                "%u.%02u%%, "
123                "cs %lu, "
124                "pmpts %lu, "
125 #if WITH_SMP
126                "rs_ipis %lu, "
127 #endif
128                "ints %lu, "
129                "tmr ints %lu, "
130                "tmrs %lu\n",
131                i,
132                busypercent / 100, busypercent % 100,
133                thread_stats[i].context_switches - old_stats[i].context_switches,
134                thread_stats[i].preempts - old_stats[i].preempts,
135 #if WITH_SMP
136                thread_stats[i].reschedule_ipis - old_stats[i].reschedule_ipis,
137 #endif
138                thread_stats[i].interrupts - old_stats[i].interrupts,
139                thread_stats[i].timer_ints - old_stats[i].timer_ints,
140                thread_stats[i].timers - old_stats[i].timers);
141 
142         old_stats[i] = thread_stats[i];
143         last_idle_time[i] = idle_time;
144     }
145 
146     return INT_NO_RESCHEDULE;
147 }
148 
cmd_threadload(int argc,const cmd_args * argv)149 static int cmd_threadload(int argc, const cmd_args *argv)
150 {
151     static bool showthreadload = false;
152     static timer_t tltimer;
153 
154     if (showthreadload == false) {
155         // start the display
156         timer_initialize(&tltimer);
157         timer_set_periodic(&tltimer, 1000, &threadload, NULL);
158         showthreadload = true;
159     } else {
160         timer_cancel(&tltimer);
161         showthreadload = false;
162     }
163 
164     return 0;
165 }
166 
167 #endif // THREAD_STATS
168 
169 #endif // WITH_LIB_CONSOLE
170 
171 #if WITH_KERNEL_EVLOG
172 
173 #include <lib/evlog.h>
174 
175 static evlog_t kernel_evlog;
176 volatile bool kernel_evlog_enable;
177 
kernel_evlog_init(void)178 void kernel_evlog_init(void)
179 {
180     evlog_init(&kernel_evlog, KERNEL_EVLOG_LEN, 4);
181 
182     kernel_evlog_enable = true;
183 }
184 
kernel_evlog_add(uintptr_t id,uintptr_t arg0,uintptr_t arg1)185 void kernel_evlog_add(uintptr_t id, uintptr_t arg0, uintptr_t arg1)
186 {
187     if (kernel_evlog_enable) {
188         uint index = evlog_bump_head(&kernel_evlog);
189 
190         kernel_evlog.items[index] = (uintptr_t)current_time_hires();
191         kernel_evlog.items[index+1] = (arch_curr_cpu_num() << 16) | id;
192         kernel_evlog.items[index+2] = arg0;
193         kernel_evlog.items[index+3] = arg1;
194     }
195 }
196 
197 #if WITH_LIB_CONSOLE
198 
kevdump_cb(const uintptr_t * i)199 static void kevdump_cb(const uintptr_t *i)
200 {
201     switch (i[1] & 0xffff) {
202         case KERNEL_EVLOG_CONTEXT_SWITCH:
203             printf("%lu.%lu: context switch from %p to %p\n", i[0], i[1] >> 16, (void *)i[2], (void *)i[3]);
204             break;
205         case KERNEL_EVLOG_PREEMPT:
206             printf("%lu.%lu: preempt on thread %p\n", i[0], i[1] >> 16, (void *)i[2]);
207             break;
208         case KERNEL_EVLOG_TIMER_TICK:
209             printf("%lu.%lu: timer tick\n", i[0], i[1] >> 16);
210             break;
211         case KERNEL_EVLOG_TIMER_CALL:
212             printf("%lu.%lu: timer call %p, arg %p\n", i[0], i[1] >> 16, (void *)i[2], (void *)i[3]);
213             break;
214         case KERNEL_EVLOG_IRQ_ENTER:
215             printf("%lu.%lu: irq entry %lu\n", i[0], i[1] >> 16, i[2]);
216             break;
217         case KERNEL_EVLOG_IRQ_EXIT:
218             printf("%lu.%lu: irq exit  %lu\n", i[0], i[1] >> 16, i[2]);
219             break;
220         default:
221             printf("%lu: unknown id 0x%lx 0x%lx 0x%lx\n", i[0], i[1], i[2], i[3]);
222     }
223 }
224 
kernel_evlog_dump(void)225 void kernel_evlog_dump(void)
226 {
227     kernel_evlog_enable = false;
228     evlog_dump(&kernel_evlog, &kevdump_cb);
229     kernel_evlog_enable = true;
230 }
231 
cmd_kevlog(int argc,const cmd_args * argv)232 static int cmd_kevlog(int argc, const cmd_args *argv)
233 {
234     printf("kernel event log:\n");
235     kernel_evlog_dump();
236 
237     return NO_ERROR;
238 }
239 
240 #endif
241 
242 #endif // WITH_KERNEL_EVLOG
243