1 | /* SPDX-License-Identifier: BSD-2-Clause */ |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * |
---|
6 | * @ingroup RTEMSAPICPUUsageReporting |
---|
7 | * |
---|
8 | * @brief This source file contains the definition of |
---|
9 | * rtems_cpu_usage_top() and rtems_cpu_usage_top_with_plugin(). |
---|
10 | */ |
---|
11 | |
---|
12 | /* |
---|
13 | * COPYRIGHT (c) 2015, 2019. Chris Johns <chrisj@rtems.org> |
---|
14 | * |
---|
15 | * COPYRIGHT (c) 2014. |
---|
16 | * On-Line Applications Research Corporation (OAR). |
---|
17 | * |
---|
18 | * Redistribution and use in source and binary forms, with or without |
---|
19 | * modification, are permitted provided that the following conditions |
---|
20 | * are met: |
---|
21 | * 1. Redistributions of source code must retain the above copyright |
---|
22 | * notice, this list of conditions and the following disclaimer. |
---|
23 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
24 | * notice, this list of conditions and the following disclaimer in the |
---|
25 | * documentation and/or other materials provided with the distribution. |
---|
26 | * |
---|
27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
---|
31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
37 | * POSSIBILITY OF SUCH DAMAGE. |
---|
38 | */ |
---|
39 | |
---|
40 | /* |
---|
41 | * Based on the old capture engine ct-load. |
---|
42 | */ |
---|
43 | |
---|
44 | #ifdef HAVE_CONFIG_H |
---|
45 | #include "config.h" |
---|
46 | #endif |
---|
47 | |
---|
48 | #include <stdbool.h> |
---|
49 | #include <string.h> |
---|
50 | #include <stdlib.h> |
---|
51 | #include <stdio.h> |
---|
52 | #include <ctype.h> |
---|
53 | #include <inttypes.h> |
---|
54 | |
---|
55 | #include <rtems/cpuuse.h> |
---|
56 | #include <rtems/printer.h> |
---|
57 | #include <rtems/malloc.h> |
---|
58 | #include <rtems/score/objectimpl.h> |
---|
59 | #include <rtems/score/protectedheap.h> |
---|
60 | #include <rtems/score/schedulerimpl.h> |
---|
61 | #include <rtems/score/threadimpl.h> |
---|
62 | #include <rtems/score/todimpl.h> |
---|
63 | #include <rtems/score/watchdogimpl.h> |
---|
64 | #include <rtems/score/wkspace.h> |
---|
65 | #include <rtems/rtems/tasksimpl.h> |
---|
66 | |
---|
67 | #include "cpuuseimpl.h" |
---|
68 | |
---|
69 | /* |
---|
70 | * Use a struct for all data to allow more than one top and to support the |
---|
71 | * thread iterator. |
---|
72 | */ |
---|
73 | typedef struct |
---|
74 | { |
---|
75 | volatile bool thread_run; |
---|
76 | volatile bool thread_active; |
---|
77 | volatile bool single_page; |
---|
78 | volatile uint32_t sort_order; |
---|
79 | volatile uint32_t poll_rate_usecs; |
---|
80 | volatile uint32_t show; |
---|
81 | const rtems_printer* printer; |
---|
82 | Timestamp_Control zero; |
---|
83 | Timestamp_Control uptime; |
---|
84 | Timestamp_Control last_uptime; |
---|
85 | Timestamp_Control period; |
---|
86 | int task_count; /* Number of tasks. */ |
---|
87 | int last_task_count; /* Number of tasks in the previous sample. */ |
---|
88 | int task_size; /* The size of the arrays */ |
---|
89 | Thread_Control** tasks; /* List of tasks in this sample. */ |
---|
90 | Thread_Control** last_tasks; /* List of tasks in the last sample. */ |
---|
91 | Timestamp_Control* usage; /* Usage of task's in this sample. */ |
---|
92 | Timestamp_Control* last_usage; /* Usage of task's in the last sample. */ |
---|
93 | Timestamp_Control* current_usage; /* Current usage for this sample. */ |
---|
94 | Timestamp_Control total; /* Total run run, should equal the uptime. */ |
---|
95 | Timestamp_Control idle; /* Time spent in idle. */ |
---|
96 | Timestamp_Control current; /* Current time run in this period. */ |
---|
97 | Timestamp_Control current_idle; /* Current time in idle this period. */ |
---|
98 | uint32_t stack_size; /* Size of stack allocated. */ |
---|
99 | } rtems_cpu_usage_data; |
---|
100 | |
---|
101 | /* |
---|
102 | * Sort orders. |
---|
103 | */ |
---|
104 | #define RTEMS_TOP_SORT_ID (0) |
---|
105 | #define RTEMS_TOP_SORT_REAL_PRI (1) |
---|
106 | #define RTEMS_TOP_SORT_CURRENT_PRI (2) |
---|
107 | #define RTEMS_TOP_SORT_TOTAL (3) |
---|
108 | #define RTEMS_TOP_SORT_CURRENT (4) |
---|
109 | #define RTEMS_TOP_SORT_MAX (4) |
---|
110 | |
---|
111 | static inline bool equal_to_uint32_t( uint32_t * lhs, uint32_t * rhs ) |
---|
112 | { |
---|
113 | if ( *lhs == *rhs ) |
---|
114 | return true; |
---|
115 | else |
---|
116 | return false; |
---|
117 | } |
---|
118 | |
---|
119 | static inline bool less_than_uint32_t( uint32_t * lhs, uint32_t * rhs ) |
---|
120 | { |
---|
121 | if ( *lhs < *rhs ) |
---|
122 | return true; |
---|
123 | else |
---|
124 | return false; |
---|
125 | } |
---|
126 | |
---|
127 | #define CPU_usage_Equal_to( _lhs, _rhs ) _Timestamp_Equal_to( _lhs, _rhs ) |
---|
128 | #define CPU_usage_Set_to_zero( _time ) _Timestamp_Set_to_zero( _time ) |
---|
129 | #define CPU_usage_Less_than( _lhs, _rhs ) _Timestamp_Less_than( _lhs, _rhs ) |
---|
130 | |
---|
131 | static void |
---|
132 | print_memsize(rtems_cpu_usage_data* data, const uintptr_t size, const char* label) |
---|
133 | { |
---|
134 | if (size > (1024 * 1024)) |
---|
135 | rtems_printf(data->printer, "%4" PRIuPTR "M %s", size / (1024 * 1024), label); |
---|
136 | else if (size > 1024) |
---|
137 | rtems_printf(data->printer, "%4" PRIuPTR "K %s", size / 1024, label); |
---|
138 | else |
---|
139 | rtems_printf(data->printer, "%4" PRIuPTR " %s", size, label); |
---|
140 | } |
---|
141 | |
---|
142 | static int |
---|
143 | print_time(rtems_cpu_usage_data* data, |
---|
144 | const Timestamp_Control* time, |
---|
145 | const int length) |
---|
146 | { |
---|
147 | uint32_t secs = _Timestamp_Get_seconds( time ); |
---|
148 | uint32_t usecs = _Timestamp_Get_nanoseconds( time ) / TOD_NANOSECONDS_PER_MICROSECOND; |
---|
149 | int len = 0; |
---|
150 | |
---|
151 | if (secs > 60) |
---|
152 | { |
---|
153 | uint32_t mins = secs / 60; |
---|
154 | if (mins > 60) |
---|
155 | { |
---|
156 | uint32_t hours = mins / 60; |
---|
157 | if (hours > 24) |
---|
158 | { |
---|
159 | len += rtems_printf(data->printer, "%" PRIu32 "d", hours / 24); |
---|
160 | hours %= 24; |
---|
161 | } |
---|
162 | len += rtems_printf(data->printer, "%" PRIu32 "hr", hours); |
---|
163 | mins %= 60; |
---|
164 | } |
---|
165 | len += rtems_printf(data->printer, "%" PRIu32 "m", mins); |
---|
166 | secs %= 60; |
---|
167 | } |
---|
168 | len += rtems_printf(data->printer, "%" PRIu32 ".%06" PRIu32, secs, usecs); |
---|
169 | |
---|
170 | if (len < length) |
---|
171 | rtems_printf(data->printer, "%*c", length - len, ' '); |
---|
172 | |
---|
173 | return len; |
---|
174 | } |
---|
175 | |
---|
176 | /* |
---|
177 | * Count the number of tasks. |
---|
178 | */ |
---|
179 | static bool |
---|
180 | task_counter(Thread_Control *thrad, void* arg) |
---|
181 | { |
---|
182 | rtems_cpu_usage_data* data = (rtems_cpu_usage_data*) arg; |
---|
183 | ++data->task_count; |
---|
184 | |
---|
185 | return false; |
---|
186 | } |
---|
187 | |
---|
188 | /* |
---|
189 | * Create the sorted table with the current and total usage. |
---|
190 | */ |
---|
191 | static bool |
---|
192 | task_usage(Thread_Control* thread, void* arg) |
---|
193 | { |
---|
194 | rtems_cpu_usage_data* data = (rtems_cpu_usage_data*) arg; |
---|
195 | Timestamp_Control usage; |
---|
196 | Timestamp_Control current = data->zero; |
---|
197 | int j; |
---|
198 | |
---|
199 | data->stack_size += thread->Start.Initial_stack.size; |
---|
200 | |
---|
201 | usage = _Thread_Get_CPU_time_used_after_last_reset(thread); |
---|
202 | |
---|
203 | for (j = 0; j < data->last_task_count; j++) |
---|
204 | { |
---|
205 | if (thread == data->last_tasks[j]) |
---|
206 | { |
---|
207 | _Timestamp_Subtract(&data->last_usage[j], &usage, ¤t); |
---|
208 | break; |
---|
209 | } |
---|
210 | } |
---|
211 | |
---|
212 | /* |
---|
213 | * When not using nanosecond CPU usage resolution, we have to count the |
---|
214 | * number of "ticks" we gave credit for to give the user a rough guideline as |
---|
215 | * to what each number means proportionally. |
---|
216 | */ |
---|
217 | _Timestamp_Add_to(&data->total, &usage); |
---|
218 | _Timestamp_Add_to(&data->current, ¤t); |
---|
219 | |
---|
220 | if (thread->is_idle) |
---|
221 | { |
---|
222 | _Timestamp_Add_to(&data->idle, &usage); |
---|
223 | _Timestamp_Add_to(&data->current_idle, ¤t); |
---|
224 | } |
---|
225 | |
---|
226 | /* |
---|
227 | * Create the tasks to display sorting as we create. |
---|
228 | */ |
---|
229 | for (j = 0; j < data->task_count; j++) |
---|
230 | { |
---|
231 | if (data->tasks[j]) |
---|
232 | { |
---|
233 | int k; |
---|
234 | |
---|
235 | /* |
---|
236 | * Sort on the current load. |
---|
237 | */ |
---|
238 | switch (data->sort_order) |
---|
239 | { |
---|
240 | default: |
---|
241 | data->sort_order = RTEMS_TOP_SORT_CURRENT; |
---|
242 | /* drop through */ |
---|
243 | case RTEMS_TOP_SORT_CURRENT: |
---|
244 | if (CPU_usage_Equal_to(¤t, &data->zero) || |
---|
245 | CPU_usage_Less_than(¤t, &data->current_usage[j])) |
---|
246 | continue; |
---|
247 | case RTEMS_TOP_SORT_TOTAL: |
---|
248 | if (CPU_usage_Equal_to(&usage, &data->zero) || |
---|
249 | CPU_usage_Less_than(&usage, &data->usage[j])) |
---|
250 | continue; |
---|
251 | /* Fall through */ |
---|
252 | case RTEMS_TOP_SORT_REAL_PRI: |
---|
253 | if (thread->Real_priority.priority > data->tasks[j]->Real_priority.priority) |
---|
254 | continue; |
---|
255 | /* Fall through */ |
---|
256 | case RTEMS_TOP_SORT_CURRENT_PRI: |
---|
257 | if ( |
---|
258 | _Thread_Get_priority( thread ) |
---|
259 | > _Thread_Get_priority( data->tasks[j] ) |
---|
260 | ) { |
---|
261 | continue; |
---|
262 | } |
---|
263 | /* Fall through */ |
---|
264 | case RTEMS_TOP_SORT_ID: |
---|
265 | if (thread->Object.id < data->tasks[j]->Object.id) |
---|
266 | continue; |
---|
267 | } |
---|
268 | |
---|
269 | for (k = (data->task_count - 1); k >= j; k--) |
---|
270 | { |
---|
271 | data->tasks[k + 1] = data->tasks[k]; |
---|
272 | data->usage[k + 1] = data->usage[k]; |
---|
273 | data->current_usage[k + 1] = data->current_usage[k]; |
---|
274 | } |
---|
275 | } |
---|
276 | data->tasks[j] = thread; |
---|
277 | data->usage[j] = usage; |
---|
278 | data->current_usage[j] = current; |
---|
279 | break; |
---|
280 | } |
---|
281 | |
---|
282 | return false; |
---|
283 | } |
---|
284 | |
---|
285 | /* |
---|
286 | * rtems_cpuusage_top_thread |
---|
287 | * |
---|
288 | * This function displays the load of the tasks on an ANSI terminal. |
---|
289 | */ |
---|
290 | |
---|
291 | static void |
---|
292 | rtems_cpuusage_top_thread (rtems_task_argument arg) |
---|
293 | { |
---|
294 | rtems_cpu_usage_data* data = (rtems_cpu_usage_data*) arg; |
---|
295 | char name[13]; |
---|
296 | int i; |
---|
297 | Heap_Information_block wksp; |
---|
298 | uint32_t ival, fval; |
---|
299 | int task_count; |
---|
300 | rtems_event_set out; |
---|
301 | rtems_status_code sc; |
---|
302 | bool first_time = true; |
---|
303 | |
---|
304 | data->thread_active = true; |
---|
305 | |
---|
306 | _TOD_Get_uptime(&data->last_uptime); |
---|
307 | |
---|
308 | CPU_usage_Set_to_zero(&data->zero); |
---|
309 | |
---|
310 | while (data->thread_run) |
---|
311 | { |
---|
312 | Timestamp_Control uptime_at_last_reset = CPU_usage_Uptime_at_last_reset; |
---|
313 | size_t tasks_size; |
---|
314 | size_t usage_size; |
---|
315 | Timestamp_Control load; |
---|
316 | |
---|
317 | data->task_count = 0; |
---|
318 | _Thread_Iterate(task_counter, data); |
---|
319 | |
---|
320 | tasks_size = sizeof(Thread_Control*) * (data->task_count + 1); |
---|
321 | usage_size = sizeof(Timestamp_Control) * (data->task_count + 1); |
---|
322 | |
---|
323 | if (data->task_count > data->task_size) |
---|
324 | { |
---|
325 | data->tasks = realloc(data->tasks, tasks_size); |
---|
326 | data->usage = realloc(data->usage, usage_size); |
---|
327 | data->current_usage = realloc(data->current_usage, usage_size); |
---|
328 | if ((data->tasks == NULL) || (data->usage == NULL) || (data->current_usage == NULL)) |
---|
329 | { |
---|
330 | rtems_printf(data->printer, "top worker: error: no memory\n"); |
---|
331 | data->thread_run = false; |
---|
332 | break; |
---|
333 | } |
---|
334 | } |
---|
335 | |
---|
336 | memset(data->tasks, 0, tasks_size); |
---|
337 | memset(data->usage, 0, usage_size); |
---|
338 | memset(data->current_usage, 0, usage_size); |
---|
339 | |
---|
340 | _Timestamp_Set_to_zero(&data->total); |
---|
341 | _Timestamp_Set_to_zero(&data->current); |
---|
342 | _Timestamp_Set_to_zero(&data->idle); |
---|
343 | _Timestamp_Set_to_zero(&data->current_idle); |
---|
344 | |
---|
345 | data->stack_size = 0; |
---|
346 | |
---|
347 | _TOD_Get_uptime(&data->uptime); |
---|
348 | _Timestamp_Subtract(&uptime_at_last_reset, &data->uptime, &data->uptime); |
---|
349 | _Timestamp_Subtract(&data->last_uptime, &data->uptime, &data->period); |
---|
350 | data->last_uptime = data->uptime; |
---|
351 | |
---|
352 | _Thread_Iterate(task_usage, data); |
---|
353 | |
---|
354 | if (data->task_count > data->task_size) |
---|
355 | { |
---|
356 | data->last_tasks = realloc(data->last_tasks, tasks_size); |
---|
357 | data->last_usage = realloc(data->last_usage, usage_size); |
---|
358 | if ((data->last_tasks == NULL) || (data->last_usage == NULL)) |
---|
359 | { |
---|
360 | rtems_printf(data->printer, "top worker: error: no memory\n"); |
---|
361 | data->thread_run = false; |
---|
362 | break; |
---|
363 | } |
---|
364 | data->task_size = data->task_count; |
---|
365 | } |
---|
366 | |
---|
367 | memcpy(data->last_tasks, data->tasks, tasks_size); |
---|
368 | memcpy(data->last_usage, data->usage, usage_size); |
---|
369 | data->last_task_count = data->task_count; |
---|
370 | |
---|
371 | /* |
---|
372 | * We need to loop again to get suitable current usage values as we need a |
---|
373 | * last sample to work. |
---|
374 | */ |
---|
375 | if (first_time) |
---|
376 | { |
---|
377 | rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(500)); |
---|
378 | first_time = false; |
---|
379 | continue; |
---|
380 | } |
---|
381 | |
---|
382 | _Protected_heap_Get_information(&_Workspace_Area, &wksp); |
---|
383 | |
---|
384 | if (data->single_page) |
---|
385 | rtems_printf(data->printer, |
---|
386 | "\x1b[H\x1b[J" |
---|
387 | " ENTER:Exit SPACE:Refresh" |
---|
388 | " S:Scroll A:All <>:Order +/-:Lines\n"); |
---|
389 | rtems_printf(data->printer, "\n"); |
---|
390 | |
---|
391 | /* |
---|
392 | * Uptime and period of this sample. |
---|
393 | */ |
---|
394 | rtems_printf(data->printer, "Uptime: "); |
---|
395 | print_time(data, &data->uptime, 20); |
---|
396 | rtems_printf(data->printer, " Period: "); |
---|
397 | print_time(data, &data->period, 20); |
---|
398 | |
---|
399 | /* |
---|
400 | * Task count, load and idle levels. |
---|
401 | */ |
---|
402 | rtems_printf(data->printer, "\nTasks: %4i ", data->task_count); |
---|
403 | |
---|
404 | _Timestamp_Subtract(&data->idle, &data->total, &load); |
---|
405 | _Timestamp_Divide(&load, &data->uptime, &ival, &fval); |
---|
406 | rtems_printf(data->printer, |
---|
407 | "Load Average: %4" PRIu32 ".%03" PRIu32 "%%", ival, fval); |
---|
408 | _Timestamp_Subtract(&data->current_idle, &data->current, &load); |
---|
409 | _Timestamp_Divide(&load, &data->period, &ival, &fval); |
---|
410 | rtems_printf(data->printer, |
---|
411 | " Load: %4" PRIu32 ".%03" PRIu32 "%%", ival, fval); |
---|
412 | _Timestamp_Divide(&data->current_idle, &data->period, &ival, &fval); |
---|
413 | rtems_printf(data->printer, |
---|
414 | " Idle: %4" PRIu32 ".%03" PRIu32 "%%", ival, fval); |
---|
415 | |
---|
416 | /* |
---|
417 | * Memory usage. |
---|
418 | */ |
---|
419 | if (rtems_configuration_get_unified_work_area()) |
---|
420 | { |
---|
421 | rtems_printf(data->printer, "\nMem: "); |
---|
422 | print_memsize(data, wksp.Free.total, "free"); |
---|
423 | print_memsize(data, wksp.Used.total, "used"); |
---|
424 | } |
---|
425 | else |
---|
426 | { |
---|
427 | Heap_Information_block libc_heap; |
---|
428 | malloc_info(&libc_heap); |
---|
429 | rtems_printf(data->printer, "\nMem: Wksp: "); |
---|
430 | print_memsize(data, wksp.Free.total, "free"); |
---|
431 | print_memsize(data, wksp.Used.total, "used Heap: "); |
---|
432 | print_memsize(data, libc_heap.Free.total, "free"); |
---|
433 | print_memsize(data, libc_heap.Used.total, "used"); |
---|
434 | } |
---|
435 | |
---|
436 | print_memsize(data, data->stack_size, "stack\n"); |
---|
437 | |
---|
438 | rtems_printf(data->printer, |
---|
439 | "\n" |
---|
440 | " ID | NAME | RPRI | CPRI | TIME | TOTAL | CURRENT\n" |
---|
441 | "-%s---------+---------------------+-%s-----%s-----+---------------------+-%s------+--%s----\n", |
---|
442 | data->sort_order == RTEMS_TOP_SORT_ID ? "^^" : "--", |
---|
443 | data->sort_order == RTEMS_TOP_SORT_REAL_PRI ? "^^" : "--", |
---|
444 | data->sort_order == RTEMS_TOP_SORT_CURRENT_PRI ? "^^" : "--", |
---|
445 | data->sort_order == RTEMS_TOP_SORT_TOTAL ? "^^" : "--", |
---|
446 | data->sort_order == RTEMS_TOP_SORT_CURRENT ? "^^" : "--" |
---|
447 | ); |
---|
448 | |
---|
449 | task_count = 0; |
---|
450 | |
---|
451 | for (i = 0; i < data->task_count; i++) |
---|
452 | { |
---|
453 | Thread_Control* thread = data->tasks[i]; |
---|
454 | Timestamp_Control usage; |
---|
455 | Timestamp_Control current_usage; |
---|
456 | Thread_queue_Context queue_context; |
---|
457 | const Scheduler_Control *scheduler; |
---|
458 | Priority_Control real_priority; |
---|
459 | Priority_Control priority; |
---|
460 | |
---|
461 | if (thread == NULL) |
---|
462 | break; |
---|
463 | |
---|
464 | if (data->single_page && (data->show != 0) && (i >= data->show)) |
---|
465 | break; |
---|
466 | |
---|
467 | /* |
---|
468 | * We need to count the number displayed to clear the remainder of the |
---|
469 | * the display. |
---|
470 | */ |
---|
471 | ++task_count; |
---|
472 | |
---|
473 | /* |
---|
474 | * If the API os POSIX print the entry point. |
---|
475 | */ |
---|
476 | rtems_object_get_name(thread->Object.id, sizeof(name), name); |
---|
477 | if (name[0] == '\0') |
---|
478 | snprintf(name, sizeof(name) - 1, "(%p)", thread->Start.Entry.Kinds.Numeric.entry); |
---|
479 | |
---|
480 | _Thread_queue_Context_initialize(&queue_context); |
---|
481 | _Thread_Wait_acquire(thread, &queue_context); |
---|
482 | scheduler = _Thread_Scheduler_get_home(thread); |
---|
483 | real_priority = thread->Real_priority.priority; |
---|
484 | priority = _Thread_Get_priority(thread); |
---|
485 | _Thread_Wait_release(thread, &queue_context); |
---|
486 | |
---|
487 | rtems_printf(data->printer, |
---|
488 | " 0x%08" PRIx32 " | %-19s | %3" PRId32 " | %3" PRId32 " | ", |
---|
489 | thread->Object.id, |
---|
490 | name, |
---|
491 | _RTEMS_Priority_From_core(scheduler, real_priority), |
---|
492 | _RTEMS_Priority_From_core(scheduler, priority)); |
---|
493 | |
---|
494 | usage = data->usage[i]; |
---|
495 | current_usage = data->current_usage[i]; |
---|
496 | |
---|
497 | /* |
---|
498 | * Print the information |
---|
499 | */ |
---|
500 | print_time(data, &usage, 19); |
---|
501 | _Timestamp_Divide(&usage, &data->total, &ival, &fval); |
---|
502 | rtems_printf(data->printer, |
---|
503 | " |%4" PRIu32 ".%03" PRIu32, ival, fval); |
---|
504 | _Timestamp_Divide(¤t_usage, &data->period, &ival, &fval); |
---|
505 | rtems_printf(data->printer, |
---|
506 | " |%4" PRIu32 ".%03" PRIu32 "\n", ival, fval); |
---|
507 | } |
---|
508 | |
---|
509 | if (data->single_page && (data->show != 0) && (task_count < data->show)) |
---|
510 | { |
---|
511 | i = data->show - task_count; |
---|
512 | while (i > 0) |
---|
513 | { |
---|
514 | rtems_printf(data->printer, "\x1b[K\n"); |
---|
515 | i--; |
---|
516 | } |
---|
517 | } |
---|
518 | |
---|
519 | sc = rtems_event_receive(RTEMS_EVENT_1, |
---|
520 | RTEMS_EVENT_ANY, |
---|
521 | RTEMS_MILLISECONDS_TO_TICKS (data->poll_rate_usecs), |
---|
522 | &out); |
---|
523 | if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT)) |
---|
524 | { |
---|
525 | rtems_printf(data->printer, |
---|
526 | "error: event receive: %s\n", rtems_status_text(sc)); |
---|
527 | break; |
---|
528 | } |
---|
529 | } |
---|
530 | |
---|
531 | free(data->tasks); |
---|
532 | free(data->last_tasks); |
---|
533 | free(data->last_usage); |
---|
534 | free(data->current_usage); |
---|
535 | |
---|
536 | data->thread_active = false; |
---|
537 | |
---|
538 | rtems_task_exit(); |
---|
539 | } |
---|
540 | |
---|
541 | void rtems_cpu_usage_top_with_plugin( |
---|
542 | const rtems_printer *printer |
---|
543 | ) |
---|
544 | { |
---|
545 | rtems_status_code sc; |
---|
546 | rtems_task_priority priority; |
---|
547 | rtems_name name; |
---|
548 | rtems_id id; |
---|
549 | rtems_cpu_usage_data data; |
---|
550 | int show_lines = 25; |
---|
551 | |
---|
552 | memset(&data, 0, sizeof(data)); |
---|
553 | |
---|
554 | data.thread_run = true; |
---|
555 | data.single_page = true; |
---|
556 | data.sort_order = RTEMS_TOP_SORT_CURRENT; |
---|
557 | data.poll_rate_usecs = 3000; |
---|
558 | data.show = show_lines; |
---|
559 | data.printer = printer; |
---|
560 | |
---|
561 | sc = rtems_task_set_priority (RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority); |
---|
562 | |
---|
563 | if (sc != RTEMS_SUCCESSFUL) |
---|
564 | { |
---|
565 | rtems_printf (printer, |
---|
566 | "error: cannot obtain the current priority: %s\n", rtems_status_text (sc)); |
---|
567 | return; |
---|
568 | } |
---|
569 | |
---|
570 | name = rtems_build_name('C', 'P', 'l', 't'); |
---|
571 | |
---|
572 | sc = rtems_task_create (name, priority, 4 * 1024, |
---|
573 | RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR, |
---|
574 | RTEMS_FLOATING_POINT | RTEMS_LOCAL, |
---|
575 | &id); |
---|
576 | |
---|
577 | if (sc != RTEMS_SUCCESSFUL) |
---|
578 | { |
---|
579 | rtems_printf (printer, |
---|
580 | "error: cannot create helper thread: %s\n", rtems_status_text (sc)); |
---|
581 | return; |
---|
582 | } |
---|
583 | |
---|
584 | sc = rtems_task_start (id, rtems_cpuusage_top_thread, (rtems_task_argument) &data); |
---|
585 | if (sc != RTEMS_SUCCESSFUL) |
---|
586 | { |
---|
587 | rtems_printf (printer, |
---|
588 | "error: cannot start helper thread: %s\n", rtems_status_text (sc)); |
---|
589 | rtems_task_delete (id); |
---|
590 | return; |
---|
591 | } |
---|
592 | |
---|
593 | while (true) |
---|
594 | { |
---|
595 | int c = getchar (); |
---|
596 | |
---|
597 | if ((c == '\r') || (c == '\n') || (c == 'q') || (c == 'Q')) |
---|
598 | { |
---|
599 | int loops = 50; |
---|
600 | |
---|
601 | data.thread_run = false; |
---|
602 | |
---|
603 | rtems_event_send(id, RTEMS_EVENT_1); |
---|
604 | |
---|
605 | while (loops && data.thread_active) |
---|
606 | rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (100000)); |
---|
607 | |
---|
608 | rtems_printf (printer, "load monitoring stopped.\n"); |
---|
609 | return; |
---|
610 | } |
---|
611 | else if (c == '<') |
---|
612 | { |
---|
613 | if (data.sort_order == 0) |
---|
614 | data.sort_order = RTEMS_TOP_SORT_MAX; |
---|
615 | else |
---|
616 | --data.sort_order; |
---|
617 | rtems_event_send(id, RTEMS_EVENT_1); |
---|
618 | } |
---|
619 | else if (c == '>') |
---|
620 | { |
---|
621 | if (data.sort_order >= RTEMS_TOP_SORT_MAX) |
---|
622 | data.sort_order = 0; |
---|
623 | else |
---|
624 | ++data.sort_order; |
---|
625 | rtems_event_send(id, RTEMS_EVENT_1); |
---|
626 | } |
---|
627 | else if ((c == 's') || (c == 'S')) |
---|
628 | { |
---|
629 | data.single_page = !data.single_page; |
---|
630 | rtems_event_send(id, RTEMS_EVENT_1); |
---|
631 | } |
---|
632 | else if ((c == 'a') || (c == 'A')) |
---|
633 | { |
---|
634 | if (data.show == 0) |
---|
635 | data.show = show_lines; |
---|
636 | else |
---|
637 | data.show = 0; |
---|
638 | rtems_event_send(id, RTEMS_EVENT_1); |
---|
639 | } |
---|
640 | else if (c == '+') |
---|
641 | { |
---|
642 | ++show_lines; |
---|
643 | if (data.show != 0) |
---|
644 | data.show = show_lines; |
---|
645 | } |
---|
646 | else if (c == '-') |
---|
647 | { |
---|
648 | if (show_lines > 5) |
---|
649 | --show_lines; |
---|
650 | if (data.show != 0) |
---|
651 | data.show = show_lines; |
---|
652 | } |
---|
653 | else if (c == ' ') |
---|
654 | { |
---|
655 | rtems_event_send(id, RTEMS_EVENT_1); |
---|
656 | } |
---|
657 | } |
---|
658 | } |
---|
659 | |
---|
660 | void rtems_cpu_usage_top (void) |
---|
661 | { |
---|
662 | rtems_printer printer; |
---|
663 | rtems_print_printer_printk (&printer); |
---|
664 | rtems_cpu_usage_top_with_plugin (&printer); |
---|
665 | } |
---|