source: rtems/cpukit/libmisc/cpuuse/cpuusagetop.c @ d37adfe5

5
Last change on this file since d37adfe5 was d37adfe5, checked in by Sebastian Huber <sebastian.huber@…>, on 03/03/16 at 06:02:03

score: Fix CPU time used by executing threads

The CPU time used of a thread was previously maintained per-processor
mostly during _Thread_Dispatch(). However, on SMP configurations the
actual processor of a thread is difficult to figure out since thread
dispatching is a highly asynchronous process (e.g. via inter-processor
interrupts). Only the intended processor of a thread is known to the
scheduler easily. Do the CPU usage accounting during thread heir
updates in the context of the scheduler operations. Provide the
function _Thread_Get_CPU_time_used() to get the CPU usage of a thread
using proper locks to get a consistent value.

Close #2627.

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