source: rtems/cpukit/libmisc/cpuuse/cpuusagetop.c @ 0340fd9

Last change on this file since 0340fd9 was 0340fd9, checked in by Sebastian Huber <sebastian.huber@…>, on 01/23/23 at 14:33:46

cpuuse: Use standard wording and group name

Use standard wording in CPU usage reporting files.

  • Property mode set to 100644
File size: 19.7 KB
Line 
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 */
73typedef 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
111static 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
119static 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
131static void
132print_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
142static int
143print_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 */
179static bool
180task_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 */
191static bool
192task_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, &current);
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, &current);
219
220  if (thread->is_idle)
221  {
222    _Timestamp_Add_to(&data->idle, &usage);
223    _Timestamp_Add_to(&data->current_idle, &current);
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(&current, &data->zero) ||
245              CPU_usage_Less_than(&current, &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
291static void
292rtems_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(&current_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
541void 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
660void 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}
Note: See TracBrowser for help on using the repository browser.