1 /*
2  * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3  */
4 
5 #if AOS_COMP_CLI
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <fcntl.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 
14 #include "k_api.h"
15 #include "debug_api.h"
16 #include "aos/cli.h"
17 
18 #if (RHINO_CONFIG_SYS_STATS > 0)
19 
20 #define CPU_USAGE_DEFAULT_PRI      7
21 #define CPU_USAGE_STACK            4048
22 
23 ktask_t *cpuusage_task;
24 int      sync_mode   = 0;
25 ksem_t   sync_sem;
26 
27 struct statistics_param {
28     uint32_t period;
29     uint32_t total;
30     uint32_t record_to_file;
31 };
32 
33 
34 typedef struct {
35     char     task_name[32];
36     uint32_t task_cpu_usage;
37 } task_cpuusage_info;
38 
39 static uint32_t task_cpu_usage_period = 0;
40 
debug_task_cpu_usage_stats(void)41 void debug_task_cpu_usage_stats(void)
42 {
43     uint32_t   i;
44     klist_t    *taskhead = &g_kobj_list.task_head;
45     klist_t    *taskend  = taskhead;
46     klist_t    *tmp;
47     ktask_t    *task;
48     lr_timer_t cur_time;
49     lr_timer_t exec_time;
50 
51     static lr_timer_t stats_start = 0;
52     lr_timer_t        stats_end;
53 
54     (void)i; /* to avoid compiler warning */
55 
56     CPSR_ALLOC();
57     RHINO_CRITICAL_ENTER();
58 #if (RHINO_CONFIG_CPU_NUM > 1)
59     for (i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
60         cur_time  = (lr_timer_t)LR_COUNT_GET();
61         exec_time = cur_time - g_active_task[i]->task_time_start;
62 
63         g_active_task[i]->task_time_total_run += (sys_time_t)exec_time;
64         g_active_task[i]->task_time_start     = cur_time;
65     }
66 #else
67     cur_time  = (lr_timer_t)LR_COUNT_GET();
68     exec_time = cur_time - g_active_task[0]->task_time_start;
69 
70     g_active_task[0]->task_time_total_run += (sys_time_t)exec_time;
71     g_active_task[0]->task_time_start     = cur_time;
72 #endif
73 
74     for (tmp = taskhead->next; tmp != taskend; tmp = tmp->next) {
75         task = krhino_list_entry(tmp, ktask_t, task_stats_item);
76         task->task_exec_time = task->task_time_total_run -
77                                task->task_time_total_run_prev;
78         task->task_time_total_run_prev = task->task_time_total_run;
79     }
80     RHINO_CRITICAL_EXIT();
81 
82     stats_end             = (lr_timer_t)LR_COUNT_GET();
83     task_cpu_usage_period = stats_end - stats_start;
84     stats_start           = stats_end;
85 }
86 
debug_total_cpu_usage_show(struct cpuusage_data * cpuusage_record,int32_t record_len,int32_t index)87 void debug_total_cpu_usage_show(struct cpuusage_data * cpuusage_record, int32_t record_len,int32_t index)
88 {
89     uint32_t    i, j;
90     klist_t    *taskhead = &g_kobj_list.task_head;
91     klist_t    *taskend  = taskhead;
92     klist_t    *tmp;
93     ktask_t    *task;
94     const char *task_name;
95     lr_timer_t  task_cpu_usage;
96     uint32_t    total_cpu_usage[RHINO_CONFIG_CPU_NUM];
97 
98     uint32_t            tasknum = 0;
99     task_cpuusage_info *taskinfo;
100     task_cpuusage_info *taskinfoeach;
101 
102     if (cpuusage_record == NULL) {
103         aos_cli_printf("-----------------------\r\n");
104     }
105 
106 #if (RHINO_CONFIG_CPU_NUM > 1)
107     for (i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
108         total_cpu_usage[i] = debug_total_cpu_usage_get(i);
109         if (cpuusage_record != NULL) {
110             if (cpuusage_record[i].taskname[0] == 0) {
111                 snprintf((char *)cpuusage_record[i].taskname, CPU_USAGE_MAX_TASKNAME_LEN, "CPU%d", (int)i);
112             }
113             cpuusage_record[i].cpuusage[index] = (float)total_cpu_usage[i] / 100;
114         } else {
115             aos_cli_printf("CPU%d usage :%3d.%02d%%  \r\n", (int)i, (int)total_cpu_usage[i] / 100,
116                            (int)total_cpu_usage[i] % 100);
117         }
118     }
119 #else
120     total_cpu_usage[0] = debug_total_cpu_usage_get(0);
121     if (cpuusage_record != NULL) {
122         if (cpuusage_record[0].taskname[0] == 0) {
123             snprintf(cpuusage_record[0].taskname, CPU_USAGE_MAX_TASKNAME_LEN, "CPU");
124         }
125         cpuusage_record[0].cpuusage[index] = (float)total_cpu_usage[0] / 100;
126     } else {
127         aos_cli_printf("CPU usage :%3d.%02d%%  \r\n", (int)total_cpu_usage[0] / 100,
128                        (int)total_cpu_usage[0] % 100);
129     }
130 #endif
131 
132     if (cpuusage_record == NULL) {
133         aos_cli_printf("---------------------------\r\n");
134         aos_cli_printf("Name               %%CPU\r\n");
135         aos_cli_printf("---------------------------\r\n");
136     }
137 
138     krhino_sched_disable();
139     for (tmp = taskhead->next; tmp != taskend; tmp = tmp->next) {
140         tasknum ++;
141     }
142     taskinfo = krhino_mm_alloc(tasknum * sizeof(task_cpuusage_info));
143     if (taskinfo ==  NULL) {
144         krhino_sched_enable();
145         return;
146     }
147     memset(taskinfo, 0, tasknum * sizeof(task_cpuusage_info));
148 
149     taskinfoeach = taskinfo;
150 
151     for (tmp = taskhead->next; tmp != taskend; tmp = tmp->next) {
152         task = krhino_list_entry(tmp, ktask_t, task_stats_item);
153 
154         if (task->task_name != NULL) {
155             task_name = task->task_name;
156         } else {
157             task_name = "anonym";
158         }
159         taskinfoeach->task_cpu_usage = debug_task_cpu_usage_get(task);
160         strncpy(taskinfoeach->task_name, task_name, sizeof(taskinfoeach->task_name) - 1);
161         taskinfoeach++;
162     }
163     krhino_sched_enable();
164 
165     for (i = 0; i < tasknum; i++) {
166         taskinfoeach   = taskinfo + i;
167         task_name      = taskinfoeach->task_name;
168         task_cpu_usage = taskinfoeach->task_cpu_usage;
169 
170         if (cpuusage_record != NULL) {
171             for (j = RHINO_CONFIG_CPU_NUM; j < record_len; j++) {
172                 if (cpuusage_record[j].taskname[0] == 0) {
173                     if (strlen(taskinfoeach->task_name) > CPU_USAGE_MAX_TASKNAME_LEN) {
174                         aos_cli_printf("task name is longer than CPU_USAGE_MAX_TASKNAME_LEN\r\n");
175                         break;
176                     }
177                     memcpy(cpuusage_record[j].taskname, taskinfoeach->task_name, strlen(taskinfoeach->task_name));
178                 }
179 
180                 if (strcmp((char *)cpuusage_record[j].taskname, taskinfoeach->task_name) != 0) {
181                     continue;
182                 }
183 
184                 cpuusage_record[j].cpuusage[index] = (float)task_cpu_usage / 100;
185                 break;
186             }
187         } else {
188             aos_cli_printf("%-19s%3d.%02d\r\n", task_name, (int)task_cpu_usage / 100,
189                            (int)task_cpu_usage % 100);
190         }
191     }
192 
193     krhino_mm_free(taskinfo);
194 
195     if (cpuusage_record == NULL) {
196         aos_cli_printf("---------------------------\r\n");
197     }
198 }
199 
200 /* one in ten thousand */
debug_task_cpu_usage_get(ktask_t * task)201 uint32_t debug_task_cpu_usage_get(ktask_t *task)
202 {
203     uint32_t task_cpu_usage = 0;
204 
205     if (task_cpu_usage_period == 0) {
206         return 0;
207     }
208 
209     task_cpu_usage = (uint64_t)(task->task_exec_time) * 10000 / task_cpu_usage_period;
210     if (task_cpu_usage > 10000) {
211         task_cpu_usage = 10000;
212     }
213 
214     return task_cpu_usage;
215 }
216 
217 /* one in ten thousand */
debug_total_cpu_usage_get(uint32_t cpuid)218 uint32_t debug_total_cpu_usage_get(uint32_t cpuid)
219 {
220     uint32_t total_cpu_usage = 0;
221     total_cpu_usage          = 10000 - debug_task_cpu_usage_get(&g_idle_task[cpuid]);
222     return total_cpu_usage;
223 }
224 
cpuusage_statistics(void * arg)225 void cpuusage_statistics(void *arg)
226 {
227     ktask_t *cur_task;
228     kstat_t  status;
229     uint32_t period     = 0;
230     uint32_t total      = 0;
231     int32_t  stat_count = 0;
232     int32_t  index      = 0;
233     struct statistics_param *param  = (struct statistics_param *)arg;
234 
235     /* used for record to file */
236     int fd  = -1;
237     int i;
238 
239 #if DEBUG_CPUUSAGE_RECODE_TO_FILE_ENABLE
240     int j;
241     int ret = -1;
242     char buf[CPU_USAGE_MAX_TASKNAME_LEN + 1];
243 #endif
244     struct  cpuusage_data cpuusage_record[DEBUG_CPUUSAGE_MAX_TASK];
245 
246     period = param->period;
247     total  = param->total;
248     stat_count = total / period;
249 
250 #if DEBUG_CPUUSAGE_RECODE_TO_FILE_ENABLE
251     if (param->record_to_file == 1) {
252         for (i = 0; i < DEBUG_CPUUSAGE_MAX_TASK; i++) {
253             memset(cpuusage_record[i].taskname, 0, CPU_USAGE_MAX_TASKNAME_LEN);
254             cpuusage_record[i].cpuusage = krhino_mm_alloc(stat_count * sizeof(float));
255             memset(cpuusage_record[i].cpuusage, 0, stat_count * sizeof(float));
256         }
257     }
258 #endif
259 
260     aos_cli_printf("Start to statistics CPU utilization, period %d ms\r\n", (int)period);
261     if (total != 0) {
262         aos_cli_printf("Total statistical time %d ms\r\n", (int)total);
263     }
264 
265     debug_task_cpu_usage_stats();
266     status = krhino_task_sleep(period * RHINO_CONFIG_TICKS_PER_SECOND / 1000);
267     if (status == RHINO_TASK_CANCELED) {
268         //krhino_task_cancel_clr();
269     }
270 
271     /* If total is 0, it will run continuously */
272     while ((index < stat_count) || (total == 0)) {
273         if (krhino_task_cancel_chk() == RHINO_TRUE) {
274             cur_task = krhino_cur_task_get();
275             krhino_task_dyn_del(cur_task);
276         }
277 
278         debug_task_cpu_usage_stats();
279         status = krhino_task_sleep(period * RHINO_CONFIG_TICKS_PER_SECOND / 1000);
280         if (status == RHINO_TASK_CANCELED) {
281             //krhino_task_cancel_clr();
282             break;
283         } else {
284             if (param->record_to_file == 1) {
285 #if DEBUG_CPUUSAGE_RECODE_TO_FILE_ENABLE
286                 debug_total_cpu_usage_show(cpuusage_record, DEBUG_CPUUSAGE_MAX_TASK, index);
287 #endif
288             } else {
289                 debug_total_cpu_usage_show(NULL, 0, 0);
290             }
291 
292             index++;
293         }
294     }
295 
296 #if DEBUG_CPUUSAGE_RECODE_TO_FILE_ENABLE
297     if (param->record_to_file == 1) {
298         fd = open(DEBUG_CPUUSAGE_FILE_NAME, O_CREAT | O_WRONLY | O_TRUNC, 0666);
299         if (fd < 0) {
300             aos_cli_printf("file open error %s, %d\r\n", __FILE__, __LINE__);
301         }
302 
303         for (i = 0; i < DEBUG_CPUUSAGE_MAX_TASK; i++) {
304             if (cpuusage_record[i].taskname[0] == 0) {
305                 break;
306             }
307             if (fd >= 0) {
308                 ret = snprintf(buf, sizeof(buf), "%-30s", cpuusage_record[i].taskname);
309                 if (ret > 0) {
310                     write(fd, buf, ret);
311                 }
312             } else {
313                 aos_cli_printf("%-30s", cpuusage_record[i].taskname);
314             }
315             for (j = 0; j < stat_count; j++) {
316                 if (fd >= 0) {
317                     ret = snprintf(buf, sizeof(buf), "%7.2f", cpuusage_record[i].cpuusage[j]);
318                     if (ret > 0) {
319                         write(fd, buf, ret);
320                     }
321                 } else {
322                     aos_cli_printf("%7.2f", cpuusage_record[i].cpuusage[j]);
323                 }
324             }
325             if (fd >= 0) {
326                 write(fd, "\r\n", sizeof("\r\n"));
327             } else {
328                 aos_cli_printf("\r\n");
329             }
330         }
331 
332         if (fd >= 0) {
333             close(fd);
334         }
335 
336         for (i = 0; i < DEBUG_CPUUSAGE_MAX_TASK; i++) {
337             krhino_mm_free(cpuusage_record[i].cpuusage);
338         }
339     }
340 #endif
341 
342     printf("====CPU utilization statistics end====\r\n");
343     if (sync_mode == 1) {
344         krhino_sem_give(&sync_sem);
345     }
346 }
347 
cpuusage_cmd(char * buf,int32_t len,int32_t argc,char ** argv)348 void cpuusage_cmd(char *buf, int32_t len, int32_t argc, char **argv)
349 {
350     static struct statistics_param param = {.period = 0, .total = 0};
351 
352     ktask_t *task_tmp   = NULL;
353     int      argv_index = 1;
354 
355     memset(&param, 0, sizeof(param));
356 
357     while (argv_index < argc) {
358         if (0 == strcmp(argv[argv_index], "-d")) {
359             argv_index++;
360             if (argv_index < argc) {
361                 param.period = atoi(argv[argv_index]);
362                 if (param.period == 0) {
363                     aos_cli_printf("warning -d parma is error\r\n");
364                 } else {
365                     argv_index++;
366                 }
367             }
368         } else if (0 == strcmp(argv[argv_index], "-t")) {
369             argv_index++;
370             if (argv_index < argc) {
371                 param.total = atoi(argv[argv_index]);
372                 if (param.total == 0) {
373                     aos_cli_printf("warning -t parma is error\r\n");
374                 } else {
375                     argv_index++;
376                 }
377             }
378         } else if (0 == strcmp(argv[argv_index], "-f")) {
379             param.record_to_file = 1;
380             argv_index++;
381         } else if (0 == strcmp(argv[argv_index], "-e")) {
382             if (cpuusage_task != NULL) {
383                 krhino_task_cancel(cpuusage_task);
384             }
385             cpuusage_task = NULL;
386             return;
387         } else if (0 == strcmp(argv[argv_index], "-s")) {
388             if (sync_mode == 0) {
389                 if ((krhino_sem_create(&sync_sem, "cpuusage_sync", 0)) != RHINO_SUCCESS) {
390                     aos_cli_printf("warning -s parma is error\r\n");
391                     continue;
392                 }
393                 argv_index++;
394                 sync_mode = 1;
395             }
396         } else {
397             /* unrecognized parameters */
398             argv_index++;
399         }
400     }
401 
402     if (param.period == 0) {
403         param.period = 1000;
404         aos_cli_printf("use default param: period %dms\r\n", param.period);
405     }
406 
407     if ((param.record_to_file == 1) && (param.total == 0)) {
408         param.total = 30000;
409         aos_cli_printf("use default param: total %dms\r\n", param.total);
410     }
411 
412     task_tmp = debug_task_find("cpuusage");
413     if (task_tmp != NULL) {
414         return;
415     }
416 
417     krhino_task_dyn_create(&cpuusage_task, "cpuusage", (void *)&param, (uint8_t)CPU_USAGE_DEFAULT_PRI, 0, CPU_USAGE_STACK,
418                            cpuusage_statistics, 1);
419 
420     if (sync_mode == 1) {
421         krhino_sem_take(&sync_sem, RHINO_WAIT_FOREVER);
422 
423         krhino_sem_del(&sync_sem);
424         sync_mode = 0;
425     }
426 }
427 
428 static const struct cli_command dumpsys_cpuusage_cmd[] = {
429     {"cpuusage", "show cpu usage", cpuusage_cmd},
430 };
431 
debug_cpuusage_cmds_register(void)432 void debug_cpuusage_cmds_register(void)
433 {
434     int32_t ret;
435 
436     ret = aos_cli_register_commands(dumpsys_cpuusage_cmd, sizeof(dumpsys_cpuusage_cmd) / sizeof(struct cli_command));
437     if (ret) {
438         printf("%s %d failed, ret = %d\r\n", __func__, __LINE__, ret);
439     }
440 }
441 
442 #endif /* #if (RHINO_CONFIG_SYS_STATS > 0) */
443 #endif /* AOS_COMP_CLI */
444