1 /*
2  * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3  */
4 
5 #if AOS_COMP_CLI
6 #include <string.h>
7 #include <stdio.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include "aos/cli.h"
11 #include "debug_api.h"
12 
13 typedef struct {
14     char       task_name[32];
15     uint32_t   task_id;
16     uint32_t   task_state;
17     uint32_t   stack_size;
18     size_t     free_size;
19     uint64_t   time_total;
20     uint8_t    task_prio;
21     char       candidate;
22 #if (RHINO_CONFIG_CPU_NUM > 1)
23     uint8_t    cpu_binded;
24     uint8_t    cpu_num;
25     uint8_t    cur_exc;
26 #endif
27 } dumpsys_task_info_t;
28 
29 
30 #define MM_LEAK_CHECK_ROUND_SCOND 10 * 1000
31 #define RHINO_BACKTRACE_DEPTH     10
32 
33 ktimer_t g_mm_leak_check_timer;
34 
dumpsys_task_func(char * buf,uint32_t len,int32_t detail)35 uint32_t dumpsys_task_func(char *buf, uint32_t len, int32_t detail)
36 {
37     kstat_t    rst;
38     size_t     free_size  = 0;
39     uint64_t   time_total = 0;
40     /* consistent with "task_stat_t" */
41     char    *cpu_stat[] = { "ERROR",    "RDY", "PEND",    "SUS",
42                             "PEND_SUS", "SLP", "SLP_SUS", "DELETED"
43                           };
44     klist_t *taskhead   = &g_kobj_list.task_head;
45     klist_t *taskend    = taskhead;
46     klist_t *tmp;
47     ktask_t *task;
48     size_t   corenum;
49     (void)corenum;
50 
51 #if (RHINO_CONFIG_CPU_NUM > 1)
52     ktask_t *candidate[RHINO_CONFIG_CPU_NUM];
53 #else
54     ktask_t *candidate;
55 #endif
56     char    yes       = 'N';
57     int32_t taskstate = 0;
58     int32_t tasknum   = 0;
59     int32_t taskindex = 0;
60 
61     dumpsys_task_info_t *taskinfo;
62     dumpsys_task_info_t *taskinfoeach;
63 
64     aos_cli_printf("-----------------------------------------------------------"
65                    "---------------------\r\n");
66 
67     krhino_sched_disable();
68 
69 #if (RHINO_CONFIG_CPU_NUM > 1)
70     for (corenum = 0; corenum < RHINO_CONFIG_CPU_NUM; corenum++) {
71         preferred_cpu_ready_task_get(&g_ready_queue, corenum);
72         candidate[corenum] = g_preferred_ready_task[corenum];
73     }
74 
75 #else
76     preferred_cpu_ready_task_get(&g_ready_queue, cpu_cur_get());
77     candidate = g_preferred_ready_task[cpu_cur_get()];
78 #endif
79 
80     for (tmp = taskhead->next; tmp != taskend; tmp = tmp->next) {
81         tasknum ++;
82     }
83 
84     taskinfo = malloc(tasknum * sizeof(dumpsys_task_info_t));
85     if (taskinfo ==  NULL) {
86         krhino_sched_enable();
87         return RHINO_NO_MEM;
88     }
89     memset(taskinfo, 0, tasknum * sizeof(dumpsys_task_info_t));
90 
91     for (tmp = taskhead->next; tmp != taskend; tmp = tmp->next) {
92 
93         char name_cut[19];
94         taskinfoeach = taskinfo + taskindex;
95         taskindex++;
96 
97         task = krhino_list_entry(tmp, ktask_t, task_stats_item);
98         const name_t *task_name;
99         rst = krhino_task_stack_min_free(task, &free_size);
100 
101         if (rst != RHINO_SUCCESS) {
102             free_size = 0;
103         }
104 
105 #if (RHINO_CONFIG_SYS_STATS > 0)
106         time_total = task->task_time_total_run / 20;
107 #endif
108 
109         if (task->task_name != NULL) {
110             task_name = task->task_name;
111         } else {
112             task_name = "anonym";
113         }
114         strncpy(taskinfoeach->task_name, task_name, sizeof(taskinfoeach->task_name) - 1);
115         taskinfoeach->task_id = task->task_id;
116 
117 #if (RHINO_CONFIG_CPU_NUM > 1)
118         for (corenum = 0; corenum < RHINO_CONFIG_CPU_NUM; corenum++) {
119             if (candidate[corenum] == task) {
120                 yes = 'Y';
121             } else {
122                 yes = 'N';
123             }
124         }
125 
126 #else
127         if (candidate == task) {
128             yes = 'Y';
129         } else {
130             yes = 'N';
131         }
132 
133 #endif
134         taskstate = task->task_state >= sizeof(cpu_stat) / sizeof(cpu_stat[0])
135                     ? 0
136                     : task->task_state;
137         taskinfoeach->task_state = taskstate;
138         taskinfoeach->task_prio  = task->prio;
139         taskinfoeach->stack_size = task->stack_size * sizeof(cpu_stack_t);
140         taskinfoeach->free_size  = free_size * sizeof(cpu_stack_t);
141         taskinfoeach->time_total = time_total;
142         taskinfoeach->candidate  = yes;
143 #if (RHINO_CONFIG_CPU_NUM > 1)
144         taskinfoeach->cpu_binded = task->cpu_binded;
145         taskinfoeach->cpu_num    = task->cpu_num;
146         taskinfoeach->cur_exc    = task->cur_exc;
147 #endif
148 
149         /* if not support %-N.Ms,cut it manually */
150         if (strlen(task_name) > 18) {
151             memset(name_cut, 0, sizeof(name_cut));
152             memcpy(name_cut, task->task_name, 18);
153             task_name = name_cut;
154 
155             strncpy(taskinfoeach->task_name, task_name, strlen(task_name));
156         }
157     }
158 
159     krhino_sched_enable();
160 
161 #if (RHINO_CONFIG_CPU_NUM > 1)
162     aos_cli_printf("Name               ID    State    Prio StackSize MinFreesize "
163                "Runtime  Candidate cpu_binded cpu_num cur_exc\r\n");
164 #else
165     aos_cli_printf("Name               ID    State    Prio StackSize MinFreesize "
166                "Runtime  Candidate\r\n");
167 #endif
168 
169     aos_cli_printf("-----------------------------------------------------------"
170                    "---------------------\r\n");
171 
172     for (taskindex = 0; taskindex < tasknum; taskindex++) {
173         taskinfoeach = taskinfo + taskindex;
174 
175 #if (RHINO_CONFIG_CPU_NUM > 1)
176         aos_cli_printf("%-19s%-6d%-9s%-5d%-10d%-12u%-9u   %-11c%-10d%-10d%-10d\r\n",
177                    taskinfoeach->task_name, taskinfoeach->task_id, cpu_stat[taskinfoeach->task_state],
178                    taskinfoeach->task_prio, taskinfoeach->stack_size,
179                    taskinfoeach->free_size, (uint32_t)taskinfoeach->time_total,
180                    taskinfoeach->candidate, taskinfoeach->cpu_binded,
181                    taskinfoeach->cpu_num, taskinfoeach->cur_exc);
182 #else
183         aos_cli_printf("%-19s%-6d%-9s%-5d%-10d%-12u%-9u   %-11c \r\n",
184                    taskinfoeach->task_name, taskinfoeach->task_id, cpu_stat[taskinfoeach->task_state],
185                    taskinfoeach->task_prio, (int32_t)taskinfoeach->stack_size,
186                    taskinfoeach->free_size, (uint32_t)taskinfoeach->time_total,
187                    taskinfoeach->candidate);
188 #endif
189     }
190 
191     free(taskinfo);
192     aos_cli_printf("-----------------------------------------------------------"
193                    "---------------------\r\n");
194 
195     return RHINO_SUCCESS;
196 }
197 
task_cmd(char * buf,int32_t len,int32_t argc,char ** argv)198 static void task_cmd(char *buf, int32_t len, int32_t argc, char **argv)
199 {
200     dumpsys_task_func(NULL, 0, 1);
201 }
202 
dumpsys_cmd(char * buf,int len,int argc,char ** argv)203 static void dumpsys_cmd(char *buf, int len, int argc, char **argv)
204 {
205     if (argc != 2) {
206         aos_cli_printf("dumpsys help:\r\n");
207         aos_cli_printf("dumpsys mm      : show kernel memory status.\r\n");
208 #if (RHINO_CONFIG_MM_DEBUG > 0)
209         aos_cli_printf("dumpsys mm_info : show kernel memory has alloced.\r\n");
210 #endif
211         return;
212     }
213 
214     if (0 == strcmp(argv[1], "mm")) {
215         debug_mm_overview(aos_cli_printf);
216     }
217 #if (RHINO_CONFIG_MM_DEBUG > 0)
218     else if (0 == strcmp(argv[1], "mm_info")) {
219         dumpsys_mm_info_func(0);
220     }
221 #endif
222 }
223 
224 
task_bt(char * buf,int32_t len,int32_t argc,char ** argv)225 static void task_bt(char *buf, int32_t len, int32_t argc, char **argv)
226 {
227     uint32_t task_id;
228     ktask_t *task;
229 
230     switch (argc) {
231         case 2:
232             task_id = (uint32_t)strtoul(argv[1], 0, 10);
233             break;
234         default:
235             aos_cli_printf("task_bt <taskid>\r\n"
236                            "taskid : task id\r\n");
237             return;
238     }
239 
240     task = debug_task_find_by_id(task_id);
241     if (task == NULL) {
242         aos_cli_printf("taskid %d not exist\r\n", task_id);
243         return;
244     }
245 
246     if (((ktask_t *)task)->task_name) {
247         aos_cli_printf("task name : %s\r\n", ((ktask_t *)task)->task_name);
248         debug_backtrace_task((char *)(((ktask_t *)task)->task_name), aos_cli_printf);
249     }
250 }
251 
252 
task_btn(char * buf,int32_t len,int32_t argc,char ** argv)253 static void task_btn(char *buf, int32_t len, int32_t argc, char **argv)
254 {
255     if (argc == 2) {
256         debug_backtrace_task(argv[1], aos_cli_printf);
257     } else {
258         aos_cli_printf("task_btn <taskname>\r\n");
259     }
260 }
261 
262 #if (RHINO_CONFIG_MM_DEBUG > 0)
mem_leak(char * buf,int32_t len,int32_t argc,char ** argv)263 static void mem_leak(char *buf, int32_t len, int32_t argc, char **argv)
264 {
265     static int call_cnt = 0;
266     int query_index = -1;
267 
268     if (call_cnt == 0) {
269         aos_cli_printf("memory leak check start.\r\n");
270     }
271     if (argc == 2) {
272         query_index = strtoul(argv[1], NULL, 0);
273         if (query_index > call_cnt) {
274             query_index = -1;
275         }
276     } else {
277         call_cnt++;
278         printf("\r\nAdd tag %d when malloc\r\n", call_cnt);
279     }
280 
281     dumpsys_mm_leakcheck(call_cnt, query_index);
282 }
283 #endif
284 
285 static const struct cli_command dumpsys_cli_cmd[] = {
286     { "tasklist", "list all thread info", task_cmd },
287     { "dumpsys", "dump system info", dumpsys_cmd },
288     { "taskbt", "list thread backtrace", task_bt },
289     { "taskbtn", "list thread backtrace by name", task_btn },
290 #if (RHINO_CONFIG_MM_DEBUG > 0)
291     { "mmlk", "memory leak info", mem_leak },
292 #endif
293 };
294 
295 
debug_dumpsys_cmds_register(void)296 void debug_dumpsys_cmds_register(void)
297 {
298     int32_t ret;
299 
300     ret = aos_cli_register_commands(dumpsys_cli_cmd, sizeof(dumpsys_cli_cmd) / sizeof(struct cli_command));
301     if (ret) {
302         printf("%s %d failed, ret = %d\r\n", __func__, __LINE__, ret);
303     }
304 }
305 #endif /* AOS_COMP_CLI */
306