1 /*
2  * Copyright (c) 2008-2014 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 /**
10  * @defgroup debug  Debug
11  * @{
12  */
13 
14 /**
15  * @file
16  * @brief  Debug console functions.
17  */
18 
19 #include <kernel/debug.h>
20 
21 #include <kernel/mp.h>
22 #include <kernel/thread.h>
23 #include <kernel/timer.h>
24 #include <lk/console_cmd.h>
25 #include <lk/debug.h>
26 #include <lk/err.h>
27 #include <platform.h>
28 #include <stdio.h>
29 
30 static int cmd_threads(int argc, const console_cmd_args *argv);
31 static int cmd_threads_panic(int argc, const console_cmd_args *argv);
32 static int cmd_threadstats(int argc, const console_cmd_args *argv);
33 static int cmd_threadload(int argc, const console_cmd_args *argv);
34 static int cmd_kevlog(int argc, const console_cmd_args *argv);
35 
36 STATIC_COMMAND_START
37 #if LK_DEBUGLEVEL > 1
38 STATIC_COMMAND("threads", "list kernel threads", &cmd_threads)
39 STATIC_COMMAND_MASKED("threads", "list kernel threads", &cmd_threads_panic, CMD_AVAIL_PANIC)
40 #endif
41 #if THREAD_STATS
42 STATIC_COMMAND("threadstats", "thread level statistics", &cmd_threadstats)
43 STATIC_COMMAND("threadload", "toggle thread load display", &cmd_threadload)
44 #endif
45 #if WITH_KERNEL_EVLOG
46 STATIC_COMMAND_MASKED("kevlog", "dump kernel event log", &cmd_kevlog, CMD_AVAIL_ALWAYS)
47 #endif
48 STATIC_COMMAND_END(kernel);
49 
50 #if LK_DEBUGLEVEL > 1
cmd_threads(int argc,const console_cmd_args * argv)51 static int cmd_threads(int argc, const console_cmd_args *argv) {
52     printf("thread list:\n");
53     dump_all_threads();
54 
55     return 0;
56 }
57 
cmd_threads_panic(int argc,const console_cmd_args * argv)58 static int cmd_threads_panic(int argc, const console_cmd_args *argv) {
59     /* call the unsafe version of the thread dump routine since the
60      * thread lock may be held at crash time.
61      */
62     printf("thread list:\n");
63     dump_all_threads_unlocked();
64 
65     return 0;
66 }
67 #endif
68 
69 #if THREAD_STATS
cmd_threadstats(int argc,const console_cmd_args * argv)70 static int cmd_threadstats(int argc, const console_cmd_args *argv) {
71     for (uint i = 0; i < SMP_MAX_CPUS; i++) {
72         if (!mp_is_cpu_active(i))
73             continue;
74 
75         printf("thread stats (cpu %d):\n", i);
76         printf("\ttotal idle time: %lld\n", thread_stats[i].idle_time);
77         printf("\ttotal busy time: %lld\n", current_time_hires() - thread_stats[i].idle_time);
78         printf("\treschedules: %lu\n", thread_stats[i].reschedules);
79 #if WITH_SMP
80         printf("\treschedule_ipis: %lu\n", thread_stats[i].reschedule_ipis);
81 #endif
82         printf("\tcontext_switches: %lu\n", thread_stats[i].context_switches);
83         printf("\tpreempts: %lu\n", thread_stats[i].preempts);
84         printf("\tyields: %lu\n", thread_stats[i].yields);
85         printf("\tinterrupts: %lu\n", thread_stats[i].interrupts);
86         printf("\ttimer interrupts: %lu\n", thread_stats[i].timer_ints);
87         printf("\ttimers: %lu\n", thread_stats[i].timers);
88     }
89 
90     dump_threads_stats();
91     return 0;
92 }
93 
threadload(struct timer * t,lk_time_t now,void * arg)94 static enum handler_return threadload(struct timer *t, lk_time_t now, void *arg) {
95     static struct thread_stats old_stats[SMP_MAX_CPUS];
96     static lk_bigtime_t last_idle_time[SMP_MAX_CPUS];
97 
98     for (uint i = 0; i < SMP_MAX_CPUS; i++) {
99         /* dont display time for inactiv cpus */
100         if (!mp_is_cpu_active(i))
101             continue;
102 
103         lk_bigtime_t idle_time = thread_stats[i].idle_time;
104 
105         /* if the cpu is currently idle, add the time since it went idle up until now to the idle counter */
106         bool is_idle = !!mp_is_cpu_idle(i);
107         if (is_idle) {
108             idle_time += current_time_hires() - thread_stats[i].last_idle_timestamp;
109         }
110 
111         lk_bigtime_t delta_time = idle_time - last_idle_time[i];
112         lk_bigtime_t busy_time = 1000000ULL - (delta_time > 1000000ULL ? 1000000ULL : delta_time);
113         uint busypercent = (busy_time * 10000) / (1000000);
114 
115         printf("cpu %u LOAD: "
116                "%u.%02u%%, "
117                "cs %lu, "
118                "pmpts %lu, "
119 #if WITH_SMP
120                "rs_ipis %lu, "
121 #endif
122                "ints %lu, "
123                "tmr ints %lu, "
124                "tmrs %lu\n",
125                i,
126                busypercent / 100, busypercent % 100,
127                thread_stats[i].context_switches - old_stats[i].context_switches,
128                thread_stats[i].preempts - old_stats[i].preempts,
129 #if WITH_SMP
130                thread_stats[i].reschedule_ipis - old_stats[i].reschedule_ipis,
131 #endif
132                thread_stats[i].interrupts - old_stats[i].interrupts,
133                thread_stats[i].timer_ints - old_stats[i].timer_ints,
134                thread_stats[i].timers - old_stats[i].timers);
135 
136         old_stats[i] = thread_stats[i];
137         last_idle_time[i] = idle_time;
138     }
139 
140     return INT_NO_RESCHEDULE;
141 }
142 
cmd_threadload(int argc,const console_cmd_args * argv)143 static int cmd_threadload(int argc, const console_cmd_args *argv) {
144     static bool showthreadload = false;
145     static timer_t tltimer;
146 
147     if (showthreadload == false) {
148         // start the display
149         timer_initialize(&tltimer);
150         timer_set_periodic(&tltimer, 1000, &threadload, NULL);
151         showthreadload = true;
152     } else {
153         timer_cancel(&tltimer);
154         showthreadload = false;
155     }
156 
157     return 0;
158 }
159 
160 #endif // THREAD_STATS
161 
162 #if WITH_KERNEL_EVLOG
163 
164 #include <lib/evlog.h>
165 
166 static evlog_t kernel_evlog;
167 volatile bool kernel_evlog_enable;
168 
kernel_evlog_init(void)169 void kernel_evlog_init(void) {
170     evlog_init(&kernel_evlog, KERNEL_EVLOG_LEN, 4);
171 
172     kernel_evlog_enable = true;
173 }
174 
kernel_evlog_add(uintptr_t id,uintptr_t arg0,uintptr_t arg1)175 void kernel_evlog_add(uintptr_t id, uintptr_t arg0, uintptr_t arg1) {
176     if (kernel_evlog_enable) {
177         uint index = evlog_bump_head(&kernel_evlog);
178 
179         kernel_evlog.items[index] = (uintptr_t)current_time_hires();
180         kernel_evlog.items[index+1] = (arch_curr_cpu_num() << 16) | id;
181         kernel_evlog.items[index+2] = arg0;
182         kernel_evlog.items[index+3] = arg1;
183     }
184 }
185 
kevdump_cb(const uintptr_t * i)186 static void kevdump_cb(const uintptr_t *i) {
187     switch (i[1] & 0xffff) {
188         case KERNEL_EVLOG_CONTEXT_SWITCH:
189             printf("%lu.%lu: context switch from %p to %p\n", i[0], i[1] >> 16, (void *)i[2], (void *)i[3]);
190             break;
191         case KERNEL_EVLOG_PREEMPT:
192             printf("%lu.%lu: preempt on thread %p\n", i[0], i[1] >> 16, (void *)i[2]);
193             break;
194         case KERNEL_EVLOG_TIMER_TICK:
195             printf("%lu.%lu: timer tick\n", i[0], i[1] >> 16);
196             break;
197         case KERNEL_EVLOG_TIMER_CALL:
198             printf("%lu.%lu: timer call %p, arg %p\n", i[0], i[1] >> 16, (void *)i[2], (void *)i[3]);
199             break;
200         case KERNEL_EVLOG_IRQ_ENTER:
201             printf("%lu.%lu: irq entry %lu\n", i[0], i[1] >> 16, i[2]);
202             break;
203         case KERNEL_EVLOG_IRQ_EXIT:
204             printf("%lu.%lu: irq exit  %lu\n", i[0], i[1] >> 16, i[2]);
205             break;
206         default:
207             printf("%lu: unknown id 0x%lx 0x%lx 0x%lx\n", i[0], i[1], i[2], i[3]);
208     }
209 }
210 
kernel_evlog_dump(void)211 void kernel_evlog_dump(void) {
212     kernel_evlog_enable = false;
213     evlog_dump(&kernel_evlog, &kevdump_cb);
214     kernel_evlog_enable = true;
215 }
216 
cmd_kevlog(int argc,const console_cmd_args * argv)217 static int cmd_kevlog(int argc, const console_cmd_args *argv) {
218     printf("kernel event log:\n");
219     kernel_evlog_dump();
220 
221     return NO_ERROR;
222 }
223 
224 #endif // WITH_KERNEL_EVLOG
225