Changeset c3330a8 in rtems


Ignore:
Timestamp:
May 17, 2007, 10:46:45 PM (13 years ago)
Author:
Joel Sherrill <joel.sherrill@…>
Branches:
4.10, 4.11, 4.8, 4.9, 5, master
Children:
838167e
Parents:
e57739d
Message:

2007-05-17 Joel Sherrill <joel.sherrill@…>

  • ChangeLog?, configure.ac, libcsupport/src/times.c, libmisc/cpuuse/cpuuse.c, libmisc/stackchk/check.c, rtems/include/rtems/rtems/ratemon.h, rtems/src/ratemongetstatus.c, rtems/src/ratemonperiod.c, rtems/src/ratemonreportstatistics.c, rtems/src/ratemonresetall.c, rtems/src/ratemontimeout.c, score/Makefile.am, score/include/rtems/score/thread.h, score/include/rtems/score/timespec.h, score/src/threaddispatch.c, score/src/threadinitialize.c, score/src/threadtickletimeslice.c, score/src/timespecdivide.c: Add nanoseconds granularity to the rate monotonic period statistics and CPU usage statistics. This capability is enabled by default although may be conditionally disabled by the user. It could be too much overhead on small targets but it does not appear to be bad in early testing. Its impact on code size has not been evaluated either. It is possible that both forms of statistics gathering could be disabled with further tweaking of the conditional compilation.
  • score/src/timespecdividebyinteger.c: New file.
Location:
cpukit
Files:
1 added
18 edited

Legend:

Unmodified
Added
Removed
  • cpukit/ChangeLog

    re57739d rc3330a8  
     12007-05-17      Joel Sherrill <joel.sherrill@oarcorp.com>
     2
     3        * ChangeLog, configure.ac, libcsupport/src/__times.c,
     4        libmisc/cpuuse/cpuuse.c, libmisc/stackchk/check.c,
     5        rtems/include/rtems/rtems/ratemon.h, rtems/src/ratemongetstatus.c,
     6        rtems/src/ratemonperiod.c, rtems/src/ratemonreportstatistics.c,
     7        rtems/src/ratemonresetall.c, rtems/src/ratemontimeout.c,
     8        score/Makefile.am, score/include/rtems/score/thread.h,
     9        score/include/rtems/score/timespec.h, score/src/threaddispatch.c,
     10        score/src/threadinitialize.c, score/src/threadtickletimeslice.c,
     11        score/src/timespecdivide.c: Add nanoseconds granularity to the rate
     12        monotonic period statistics and CPU usage statistics. This capability
     13        is enabled by default although may be conditionally disabled by the
     14        user. It could be too much overhead on small targets but it does not
     15        appear to be bad in early testing. Its impact on code size has not
     16        been evaluated either. It is possible that both forms of statistics
     17        gathering could be disabled with further tweaking of the conditional
     18        compilation.
     19        * score/src/timespecdividebyinteger.c: New file.
     20
    1212007-05-16      Joel Sherrill <joel.sherrill@oarcorp.com>
    222
     
    3757        Added the following directives: rtems_rate_monotonic_get_statistics,
    3858        rtems_rate_monotonic_reset_statistics,
    39         rtems_rate_montonic_reset_all_statistics,
    40         rtems_rate_montonic_report_statistics, and rtems_object_get_name.
     59        rtems_rate_monotonic_reset_all_statistics,
     60        rtems_rate_monotonic_report_statistics, and rtems_object_get_name.
    4161        Obsoleted the rtems/rtmonuse.h file as a public interface.
    4262        * rtems/src/ratemongetstatistics.c,
  • cpukit/configure.ac

    re57739d rc3330a8  
    208208  [$ac_cv_sizeof_CPU_CONTEXT],
    209209  [The size of a 'CPU_CONTEXT', as computed by sizeof])
     210
     211RTEMS_CPUOPT([__RTEMS_USE_TICKS_CPU_USAGE_STATISTICS__],
     212  [test x"${USE_TICKS_FOR_CPU_USAGE_STATISTICS}" = x"1"],
     213  [1],
     214  [disable nanosecond granularity for cpu usage statistics]
     215)
     216
     217RTEMS_CPUOPT([__RTEMS_USE_TICKS_RATE_MONOTONIC_STATISTICS__],
     218  [test x"${USE_TICKS_FOR_RATE_MONOTONIC_STATISTICS}" = x"1"],
     219  [1],
     220  [disable nanosecond granularity for period statistics]
     221)
    210222
    211223RTEMS_CPUOPT([__RTEMS_MAJOR__],
  • cpukit/libcsupport/src/__times.c

    re57739d rc3330a8  
    2323#include <errno.h>
    2424#include <assert.h>
     25#ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     26  #include <rtems/score/timespec.h>
     27#endif
    2528
    2629clock_t _times(
     
    4952   */
    5053
    51   ptms->tms_utime  = _Thread_Executing->ticks_executed;
     54  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     55    {
     56      struct timespec per_tick;
     57      uint32_t ticks;
     58      uint32_t fractional_ticks;
     59     
     60      per_tick.tv_sec =
     61        _TOD_Microseconds_per_tick / TOD_MILLISECONDS_PER_SECOND;
     62      per_tick.tv_nsec =
     63        (_TOD_Microseconds_per_tick % TOD_MILLISECONDS_PER_SECOND) / 1000;
     64
     65      _Timespec_Divide(
     66        &_Thread_Executing->cpu_time_used,
     67        &per_tick,
     68        &ticks,
     69        &fractional_ticks
     70      );
     71      ptms->tms_utime = ticks;
     72    }
     73  #else
     74    ptms->tms_utime  = _Thread_Executing->ticks_executed;
     75  #endif
    5276  ptms->tms_stime  = ticks;
    5377  ptms->tms_cutime = 0;
  • cpukit/libmisc/cpuuse/cpuuse.c

    re57739d rc3330a8  
    2727#include <rtems/bspIo.h>
    2828
    29 uint32_t   CPU_usage_Ticks_at_last_reset;
     29#if defined(RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS) || \
     30    defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS)
     31  #include <rtems/score/timespec.h>
     32
     33  /* We print to 1/10's of milliseconds */
     34  #define NANOSECONDS_DIVIDER 100000
     35  #define PERCENT_FMT "%04" PRId32
     36#endif
     37
     38#ifndef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     39  uint32_t   CPU_usage_Ticks_at_last_reset;
     40#endif
    3041
    3142/*PAGE
     
    4253  char                 name[5];
    4354  uint32_t             ival, fval;
    44   uint32_t             total_units = 0;
     55  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     56    struct timespec    uptime;
     57  #else
     58    uint32_t           total_units = 0;
     59  #endif
    4560
    46   for ( api_index = 1 ;
    47         api_index <= OBJECTS_APIS_LAST ;
    48         api_index++ ) {
    49     if ( !_Objects_Information_table[ api_index ] )
    50       continue;
    51     information = _Objects_Information_table[ api_index ][ 1 ];
    52     if ( information ) {
    53       for ( i=1 ; i <= information->maximum ; i++ ) {
    54         the_thread = (Thread_Control *)information->local_table[ i ];
     61  /*
     62   *  When not using nanosecond CPU usage resolution, we have to count
     63   *  the number of "ticks" we gave credit for to give the user a rough
     64   *  guideline as to what each number means proportionally.
     65   */
     66  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     67    _TOD_Get_uptime( &uptime );
     68  #else
     69    for ( api_index = 1 ; api_index <= OBJECTS_APIS_LAST ; api_index++ ) {
     70      if ( !_Objects_Information_table[ api_index ] )
     71        continue;
     72      information = _Objects_Information_table[ api_index ][ 1 ];
     73      if ( information ) {
     74        for ( i=1 ; i <= information->maximum ; i++ ) {
     75          the_thread = (Thread_Control *)information->local_table[ i ];
    5576
    56         if ( the_thread )
    57           total_units += the_thread->ticks_executed;
     77          if ( the_thread )
     78            total_units += the_thread->ticks_executed;
     79        }
    5880      }
    5981    }
    60   }
    61 
     82  #endif
     83 
    6284  printk( "CPU Usage by thread\n"
    63           "   ID        NAME        TICKS    PERCENT\n"
    64    );
     85          "   ID        NAME     TICKS    PERCENT\n"
     86  );
    6587
    6688  for ( api_index = 1 ;
     
    78100
    79101        rtems_object_get_name( the_thread->Object.id, sizeof(name), name );
     102 
     103        printk( "0x%08" PRIx32 "   %4s    ", the_thread->Object.id, name );
    80104
    81         ival = total_units ?
    82                  the_thread->ticks_executed * 10000 / total_units : 0;
    83         fval = ival % 100;
    84         ival /= 100;
    85         printk(
    86           "0x%08" PRIx32 "   %4s    %8" PRId32 "     %3" PRId32
    87              ".%02" PRId32"\n",
    88           the_thread->Object.id,
    89           name,
    90           the_thread->ticks_executed,
    91           ival,
    92           fval
    93         );
     105        #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     106          _Timespec_Divide( &the_thread->cpu_time_used, &uptime, &ival, &fval );
     107
     108          printk(
     109            "%" PRId32 ".%06d"                 /* cpu time used */     
     110            " %3" PRId32 ".%02" PRId32 "\n",  /* percentage */
     111            the_thread->cpu_time_used.tv_sec,
     112              the_thread->cpu_time_used.tv_nsec /
     113                 TOD_NANOSECONDS_PER_MICROSECOND,
     114            ival,
     115            fval
     116          );
     117        #else
     118          ival = (total_units) ?
     119                   the_thread->ticks_executed * 10000 / total_units : 0;
     120          fval = ival % 100;
     121          ival /= 100;
     122          printk(
     123            "%8" PRId32 "     %3" PRId32 ".%02" PRId32"\n",
     124            the_thread->ticks_executed,
     125            ival,
     126            fval
     127          );
     128        #endif
    94129      }
    95130    }
    96131  }
    97132
    98   printk(
    99     "\nTicks since last reset = %" PRId32 "\n",
    100     _Watchdog_Ticks_since_boot - CPU_usage_Ticks_at_last_reset
    101   );
    102   printk( "\nTotal Units = %" PRId32 "\n", total_units );
     133  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     134    printk( "Uptime %d.%06d seconds\n\n",
     135       uptime.tv_sec,
     136       uptime.tv_nsec / TOD_NANOSECONDS_PER_MICROSECOND
     137    );
     138  #else
     139    printk(
     140      "Ticks since last reset = %" PRId32 "\n",
     141      _Watchdog_Ticks_since_boot - CPU_usage_Ticks_at_last_reset
     142    );
     143    printk( "Total Units = %" PRId32 "\n\n", total_units );
     144  #endif
    103145}
    104146
     
    107149)
    108150{
    109   the_thread->ticks_executed = 0;
     151  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     152    the_thread->cpu_time_used.tv_sec  = 0;
     153    the_thread->cpu_time_used.tv_nsec = 0;
     154  #else
     155    the_thread->ticks_executed = 0;
     156  #endif
    110157}
    111158
     
    115162void rtems_cpu_usage_reset( void )
    116163{
    117   CPU_usage_Ticks_at_last_reset = _Watchdog_Ticks_since_boot;
     164  #ifndef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     165    CPU_usage_Ticks_at_last_reset = _Watchdog_Ticks_since_boot;
     166  #endif
    118167
    119168  rtems_iterate_over_all_threads(CPU_usage_Per_thread_handler);
  • cpukit/libmisc/stackchk/check.c

    re57739d rc3330a8  
    216216
    217217  printk(
    218     "BLOWN STACK!!! Offending task(%p): id=0x%08" PRIx32 "; name=0x%08" PRIx32,
     218    "BLOWN STACK!!! Offending task(0x%p): "
     219        "id=0x%08" PRIx32 "; name=0x%08" PRIx32,
    219220    running,
    220221    running->Object.id,
     
    234235
    235236  printk(
    236     "  stack covers range %p - %p (%d bytes)\n",
     237    "  stack covers range 0x%p - 0x%p (%d bytes)\n",
    237238    stack->area,
    238239    stack->area + stack->size - 1,
     
    326327    base += length - 1;
    327328    for (ebase = s; base > ebase; base--)
    328         if (*base != U32_PATTERN)
    329             return (void *) base;
     329      if (*base != U32_PATTERN)
     330        return (void *) base;
    330331  #else
    331332    /*
     
    336337    base += PATTERN_SIZE_WORDS;
    337338    for (ebase = base + length; base < ebase; base++)
    338         if (*base != U32_PATTERN)
    339             return (void *) base;
     339      if (*base != U32_PATTERN)
     340        return (void *) base;
    340341  #endif
    341342
    342343  return (void *)0;
    343 }
    344 
    345 /*
    346  *  Report Name
    347  */
    348 
    349 char *Stack_check_Get_object_name(
    350   Objects_Control  *the_object,
    351   char            **name
    352 )
    353 {
    354   Objects_Information *info;
    355 
    356   info = _Objects_Get_information(the_object->id);
    357   if ( info->is_string ) {
    358     *name = (char *) the_object->name;
    359   } else {
    360     uint32_t  u32_name = (uint32_t) the_object->name;
    361     (*name)[ 0 ] = (u32_name >> 24) & 0xff;
    362     (*name)[ 1 ] = (u32_name >> 16) & 0xff;
    363     (*name)[ 2 ] = (u32_name >>  8) & 0xff;
    364     (*name)[ 3 ] = (u32_name >>  0) & 0xff;
    365     (*name)[ 4 ] = '\0';
    366   }
    367   return *name;
    368344}
    369345
     
    381357  void           *high_water_mark;
    382358  Stack_Control  *stack;
    383   uint32_t        u32_name;
    384   char            name_str[5];
    385   char           *name;
     359  char            name[5];
    386360
    387361  if ( !the_thread )
     
    412386    used = 0;
    413387
    414   name = name_str;
    415388  if ( the_thread ) {
    416     name = Stack_check_Get_object_name( &the_thread->Object, &name );
     389    rtems_object_get_name( the_thread->Object.id, sizeof(name), name );
    417390  } else {
    418     u32_name = rtems_build_name('I', 'N', 'T', 'R');
    419     name[ 0 ] = (u32_name >> 24) & 0xff;
    420     name[ 1 ] = (u32_name >> 16) & 0xff;
    421     name[ 2 ] = (u32_name >>  8) & 0xff;
    422     name[ 3 ] = (u32_name >>  0) & 0xff;
     391    name[ 0 ] = 'I';
     392    name[ 1 ] = 'N';
     393    name[ 2 ] = 'T';
     394    name[ 3 ] = 'R';
    423395    name[ 4 ] = '\0';
    424396  }
    425397
    426   printk("0x%08" PRIx32 "  %4s  %p - %p   %8" PRId32 "   %8" PRId32 "\n",
     398  printk("0x%08" PRIx32 "  %4s  0x%p - 0x%p   %8" PRId32 "   %8" PRId32 "\n",
    427399    the_thread ? the_thread->Object.id : ~0,
    428400    name,
  • cpukit/rtems/include/rtems/rtems/ratemon.h

    re57739d rc3330a8  
    3131#ifdef __cplusplus
    3232extern "C" {
     33#endif
     34
     35/*
     36 *  The user can define this at configure time and go back to ticks
     37 *  resolution.
     38 */
     39#ifndef __RTEMS_USE_TICKS_RATE_MONOTONIC_STATISTICS__
     40  /*
     41   *  Enable the nanosecond accurate statistics
     42   *
     43   *  When not defined, the older style tick accurate granularity
     44   *  is used.
     45   */
     46  #define RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
    3347#endif
    3448
     
    6983  uint32_t     count;
    7084  uint32_t     missed_count;
    71   uint32_t     min_cpu_time;
    72   uint32_t     max_cpu_time;
    73   uint32_t     total_cpu_time;
    74   uint32_t     min_wall_time;
    75   uint32_t     max_wall_time;
    76   uint32_t     total_wall_time;
     85  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     86    struct timespec min_cpu_time;
     87    struct timespec max_cpu_time;
     88    struct timespec total_cpu_time;
     89  #else
     90    uint32_t  min_cpu_time;
     91    uint32_t  max_cpu_time;
     92    uint32_t  total_cpu_time;
     93  #endif
     94  #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     95    struct timespec min_wall_time;
     96    struct timespec max_wall_time;
     97    struct timespec total_wall_time;
     98  #else
     99    uint32_t  min_wall_time;
     100    uint32_t  max_wall_time;
     101    uint32_t  total_wall_time;
     102  #endif
    77103}  rtems_rate_monotonic_period_statistics;
    78104
     
    84110  Objects_Id                          owner;
    85111  rtems_rate_monotonic_period_states  state;
    86   uint32_t                            ticks_since_last_period;
    87   uint32_t                            ticks_executed_since_last_period;
     112  #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     113    struct timespec                   since_last_period;
     114  #else
     115    uint32_t                          ticks_since_last_period;
     116  #endif
     117  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     118    struct timespec                   executed_since_last_period;
     119  #else
     120    uint32_t                          ticks_executed_since_last_period;
     121  #endif
    88122}  rtems_rate_monotonic_period_status;
    89123
     
    97131  Watchdog_Control                        Timer;
    98132  rtems_rate_monotonic_period_states      state;
    99   uint32_t                                owner_ticks_executed_at_period;
    100   uint32_t                                time_at_period;
     133  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     134    struct timespec                       owner_executed_at_period;
     135  #else
     136    uint32_t                              owner_ticks_executed_at_period;
     137  #endif
     138  #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     139    struct timespec                       time_at_period;
     140  #else
     141    uint32_t                              time_at_period;
     142  #endif
    101143  uint32_t                                next_length;
    102144  Thread_Control                         *owner;
     
    218260
    219261/*
    220  *  rtems_rate_montonic_reset_all_statistics
     262 *  rtems_rate_monotonic_reset_all_statistics
    221263 *
    222264 *  DESCRIPTION:
     
    225267 *  on ALL period instances.
    226268 */
    227 void rtems_rate_montonic_reset_all_statistics( void );
    228 
    229 /*
    230  *  rtems_rate_montonic_report_statistics
     269void rtems_rate_monotonic_reset_all_statistics( void );
     270
     271/*
     272 *  rtems_rate_monotonic_report_statistics
    231273 *
    232274 *  DESCRIPTION:
     
    235277 *  on ALL period instances which have non-zero counts using printk.
    236278 */
    237 void rtems_rate_montonic_report_statistics( void );
     279void rtems_rate_monotonic_report_statistics( void );
    238280
    239281/*
     
    278320 *  This method resets the statistics information for a period instance.
    279321 */
     322
     323#ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     324  #define _Rate_monotonic_Reset_wall_time_statistics( _the_period ) \
     325     do { \
     326        /* set the minimums to a large value */ \
     327        (_the_period)->Statistics.min_wall_time.tv_sec = 0x7fffffff; \
     328        (_the_period)->Statistics.min_wall_time.tv_nsec = 0x7fffffff; \
     329     } while (0)
     330#else
     331  #define _Rate_monotonic_Reset_wall_time_statistics( _the_period )
     332#endif
     333
     334#ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     335  #define _Rate_monotonic_Reset_cpu_use_statistics( _the_period ) \
     336     do { \
     337        /* set the minimums to a large value */ \
     338        (_the_period)->Statistics.min_cpu_time.tv_sec = 0x7fffffff; \
     339        (_the_period)->Statistics.min_cpu_time.tv_nsec = 0x7fffffff; \
     340     } while (0)
     341#else
     342  #define _Rate_monotonic_Reset_cpu_use_statistics( _the_period )
     343#endif
    280344
    281345#define _Rate_monotonic_Reset_statistics( _the_period ) \
     
    286350      sizeof( rtems_rate_monotonic_period_statistics ) \
    287351    ); \
     352    _Rate_monotonic_Reset_cpu_use_statistics( _the_period ); \
     353    _Rate_monotonic_Reset_wall_time_statistics( _the_period ); \
    288354  } while (0)
    289  
     355
    290356#ifndef __RTEMS_APPLICATION__
    291357#include <rtems/rtems/ratemon.inl>
  • cpukit/rtems/src/ratemongetstatus.c

    re57739d rc3330a8  
    2323#include <rtems/rtems/ratemon.h>
    2424#include <rtems/score/thread.h>
     25
     26#if defined(RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS) || \
     27    defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS)
     28  #include <rtems/score/timespec.h>
     29#endif
    2530
    2631/*PAGE
     
    6570
    6671      if ( status->state == RATE_MONOTONIC_INACTIVE ) {
    67         status->ticks_since_last_period = 0;
    68         status->ticks_executed_since_last_period = 0;
     72        #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     73          status->since_last_period.tv_sec = 0;
     74          status->since_last_period.tv_nsec = 0;
     75        #else
     76          status->ticks_since_last_period = 0;
     77        #endif
     78        #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     79          status->executed_since_last_period.tv_sec = 0;
     80          status->executed_since_last_period.tv_nsec = 0;
     81        #else
     82          status->ticks_executed_since_last_period = 0;
     83        #endif
    6984      } else {
    70         status->ticks_since_last_period =
    71           _Watchdog_Ticks_since_boot - the_period->time_at_period;
     85        /*
     86         *  Both nanoseconds granularity options have to know the uptime.
     87         *  This lets them share one single invocation of _TOD_Get_uptime().
     88         */
     89        #if defined(RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS) || \
     90            defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS)
     91          struct timespec uptime;
     92          _TOD_Get_uptime( &uptime );
     93        #endif
    7294
    73         status->ticks_executed_since_last_period =
    74           the_period->owner->ticks_executed -
    75             the_period->owner_ticks_executed_at_period;
     95        #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     96          _Timespec_Subtract(
     97            &the_period->time_at_period,
     98            &uptime,
     99            &status->since_last_period
     100          );
     101        #else
     102          status->ticks_since_last_period =
     103            _Watchdog_Ticks_since_boot - the_period->time_at_period;
     104        #endif
     105
     106        #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     107          _Timespec_Subtract(
     108            &_Thread_Time_of_last_context_switch,
     109            &uptime,
     110            &status->executed_since_last_period
     111          );
     112        #else
     113          status->ticks_executed_since_last_period =
     114            the_period->owner->ticks_executed -
     115              the_period->owner_ticks_executed_at_period;
     116        #endif
    76117      }
    77118
  • cpukit/rtems/src/ratemonperiod.c

    re57739d rc3330a8  
    2323#include <rtems/rtems/ratemon.h>
    2424#include <rtems/score/thread.h>
     25#if defined(RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS) || \
     26    defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS)
     27  #include <rtems/score/timespec.h>
     28  extern struct timespec _Thread_Time_of_last_context_switch;
     29#endif
    2530
    2631void _Rate_monotonic_Update_statistics(
     
    2833)
    2934{
    30   uint32_t  ticks_since_last_period;
    31   uint32_t  ticks_executed_since_last_period;
     35  rtems_rate_monotonic_period_statistics *stats;
     36  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     37    struct timespec                       executed;
     38  #else
     39    uint32_t                              ticks_executed_since_last_period;
     40  #endif
     41  #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     42    struct timespec                       period_start;
     43    struct timespec                       since_last_period;
     44  #else
     45    uint32_t                              ticks_since_last_period;
     46  #endif
     47  #if defined(RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS) || \
     48      defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS)
     49    struct timespec  uptime;
     50
     51    /*
     52     * Obtain the current time since boot
     53     */
     54    _TOD_Get_uptime( &uptime );
     55  #endif
    3256
    3357  /*
     
    4165   */
    4266
    43   ticks_since_last_period =
    44       _Watchdog_Ticks_since_boot - the_period->time_at_period;
    45 
    46   ticks_executed_since_last_period = the_period->owner->ticks_executed -
    47         the_period->owner_ticks_executed_at_period;
     67  #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     68    period_start               = the_period->time_at_period;
     69    the_period->time_at_period = uptime;
     70    _Timespec_Subtract( &period_start, &uptime, &since_last_period );
     71  #else
     72    ticks_since_last_period =
     73        _Watchdog_Ticks_since_boot - the_period->time_at_period;
     74    the_period->time_at_period = _Watchdog_Ticks_since_boot;
     75  #endif
     76
     77  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     78    {
     79      struct timespec ran;
     80       
     81       /* executed = current cpu usage - value at start of period */
     82      _Timespec_Subtract(
     83         &the_period->owner_executed_at_period,
     84         &_Thread_Executing->cpu_time_used,
     85         &executed
     86      );
     87
     88      /* How much time time since last context switch */
     89      _Timespec_Subtract(&_Thread_Time_of_last_context_switch, &uptime, &ran);
     90
     91      /* executed += ran */
     92      _Timespec_Add_to( &executed, &ran );
     93    }
     94  #else
     95      ticks_executed_since_last_period = the_period->owner->ticks_executed -
     96        the_period->owner_ticks_executed_at_period;
     97  #endif
    4898
    4999  /*
     
    51101   */
    52102
    53   the_period->Statistics.count++;
     103  stats = &the_period->Statistics;
     104  stats->count++;
     105
    54106  if ( the_period->state == RATE_MONOTONIC_EXPIRED )
    55     the_period->Statistics.missed_count++;
    56   the_period->Statistics.total_cpu_time  += ticks_executed_since_last_period;
    57   the_period->Statistics.total_wall_time += ticks_since_last_period;
     107    stats->missed_count++;
    58108
    59109  /*
     
    61111   */
    62112
    63   if ( ticks_executed_since_last_period < the_period->Statistics.min_cpu_time )
    64     the_period->Statistics.min_cpu_time = ticks_executed_since_last_period;
    65 
    66   if ( ticks_executed_since_last_period > the_period->Statistics.max_cpu_time )
    67     the_period->Statistics.max_cpu_time = ticks_executed_since_last_period;
     113  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     114    _Timespec_Add_to( &stats->total_cpu_time, &executed );
     115
     116    if ( _Timespec_Less_than( &executed, &stats->min_cpu_time ) )
     117      stats->min_cpu_time = executed;
     118
     119    if ( _Timespec_Greater_than( &executed, &stats->max_cpu_time ) )
     120      stats->max_cpu_time = executed;
     121
     122  #else
     123    stats->total_cpu_time  += ticks_executed_since_last_period;
     124
     125    if ( ticks_executed_since_last_period < stats->min_cpu_time )
     126      stats->min_cpu_time = ticks_executed_since_last_period;
     127
     128    if ( ticks_executed_since_last_period > stats->max_cpu_time )
     129      stats->max_cpu_time = ticks_executed_since_last_period;
     130  #endif
    68131
    69132  /*
     
    71134   */
    72135
    73   if ( ticks_since_last_period < the_period->Statistics.min_wall_time )
    74     the_period->Statistics.min_wall_time = ticks_since_last_period;
    75 
    76   if ( ticks_since_last_period > the_period->Statistics.max_wall_time )
    77     the_period->Statistics.max_wall_time = ticks_since_last_period;
     136  #ifndef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     137    stats->total_wall_time += ticks_since_last_period;
     138
     139    if ( ticks_since_last_period < stats->min_wall_time )
     140      stats->min_wall_time = ticks_since_last_period;
     141
     142    if ( ticks_since_last_period > stats->max_wall_time )
     143      stats->max_wall_time = ticks_since_last_period;
     144  #else
     145    _Timespec_Add_to( &stats->total_wall_time, &since_last_period );
     146
     147    if ( _Timespec_Less_than( &since_last_period, &stats->min_wall_time ) )
     148      stats->min_wall_time = since_last_period;
     149
     150    if ( _Timespec_Greater_than( &since_last_period, &stats->max_wall_time ) )
     151      stats->max_wall_time = since_last_period;
     152  #endif
    78153}
    79154
     
    147222          _ISR_Enable( level );
    148223
     224          #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     225            /*
     226             * Since the statistics didn't update the starting time,
     227             * we do it here.
     228             */
     229            _TOD_Get_uptime( &the_period->time_at_period );
     230          #else
     231            the_period->time_at_period = _Watchdog_Ticks_since_boot;
     232          #endif
     233
     234          #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     235            {
     236              struct timespec ran, uptime;
     237
     238              _TOD_Get_uptime( &uptime );
     239             
     240              the_period->owner_executed_at_period =
     241                _Thread_Executing->cpu_time_used;
     242
     243              /* How much time time since last context switch */
     244              _Timespec_Subtract(
     245                &_Thread_Time_of_last_context_switch,
     246                &uptime,
     247                &ran
     248              );
     249
     250              /* thread had executed before the last context switch also */
     251              _Timespec_Add_to( &the_period->owner_executed_at_period, &ran );
     252            }
     253          #else
     254            the_period->owner_ticks_executed_at_period =
     255              _Thread_Executing->ticks_executed;
     256          #endif
     257
    149258          the_period->state = RATE_MONOTONIC_ACTIVE;
    150259          _Watchdog_Initialize(
     
    155264          );
    156265
    157           the_period->owner_ticks_executed_at_period =
    158             _Thread_Executing->ticks_executed;
    159 
    160           the_period->time_at_period = _Watchdog_Ticks_since_boot;
    161266          the_period->next_length = length;
    162267
     
    217322
    218323          the_period->state = RATE_MONOTONIC_ACTIVE;
    219           the_period->owner_ticks_executed_at_period =
    220             _Thread_Executing->ticks_executed;
    221           the_period->time_at_period = _Watchdog_Ticks_since_boot;
    222324          the_period->next_length = length;
    223325
  • cpukit/rtems/src/ratemonreportstatistics.c

    re57739d rc3330a8  
    2323#include <rtems/bspIo.h>
    2424
     25#if defined(RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS) || \
     26    defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS)
     27  #include <rtems/score/timespec.h>
     28
     29  /* We print to 1/10's of milliseconds */
     30  #define NANOSECONDS_DIVIDER 100000
     31  #define PERCENT_FMT "%04" PRId32
     32#endif
     33
    2534/*
    2635 *  This directive allows a thread to print the statistics information
     
    3140 *  but actually uses other services of the Manager.
    3241 */
    33 void rtems_rate_montonic_report_statistics( void )
     42void rtems_rate_monotonic_report_statistics( void )
    3443{
    3544  rtems_status_code                      status;
     
    3847  rtems_rate_monotonic_period_status     the_status;
    3948  char                                   name[5];
    40   uint32_t                               ival_cpu, fval_cpu;
    41   uint32_t                               ival_wall, fval_wall;
    4249
    43   printk(
    44     "Period information by period\n"
    45    "   ID      OWNER   PERIODS  MISSED    CPU TIME    WALL TIME\n"
     50  printk( "Period information by period\n" );
     51/*
     52Layout by columns -- in memory of Hollerith :)
     53
     541234567890123456789012345678901234567890123456789012345678901234567890123456789\
     55   ID     OWNER COUNT MISSED X
     56ididididid NNNN ccccc mmmmmm X
     57
     58  Uncomment the following if you are tinkering with the formatting.
     59  Be sure to test the various cases.
     60  printk("\
     611234567890123456789012345678901234567890123456789012345678901234567890123456789\
     62\n");
     63*/
     64  printk( "   ID     OWNER COUNT MISSED     CPU TIME     "
     65       #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     66          "    "
     67       #endif
     68       #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     69          "   "
     70       #endif
     71          "   WALL TIME\n"
    4672  );
    4773
     
    6591      continue;
    6692
    67     ival_cpu = the_stats.total_cpu_time * 100 / the_stats.count;
    68 
    6993    name[ 0 ] = '\0';
    7094
     
    7397    }
    7498
    75     fval_cpu = ival_cpu % 100;
    76     ival_cpu /= 100;
    77     ival_wall = the_stats.total_wall_time * 100 / the_stats.count;
    78     fval_wall = ival_wall % 100;
    79     ival_wall /= 100;
     99    /*
     100     *  Print part of report line that is not dependent on granularity
     101     */
     102
    80103    printk(
    81       "0x%08" PRIx32 "  %4s   %6" PRId32 "   %3" PRId32 "       "
    82          "%" PRId32 "/%" PRId32 "/%" PRId32 ".%02" PRId32 "    "
    83          "%" PRId32 "/%" PRId32 "/%" PRId32 ".%02" PRId32 "\n",
     104      "0x%08" PRIx32 " %4s %5" PRId32 " %6" PRId32 " ",
    84105      id, name,
    85       the_stats.count, the_stats.missed_count,
    86       the_stats.min_cpu_time, the_stats.max_cpu_time, ival_cpu, fval_cpu,
    87       the_stats.min_wall_time, the_stats.max_wall_time, ival_wall, fval_wall
     106      the_stats.count, the_stats.missed_count
    88107    );
     108
     109    /*
     110     *  print CPU Usage part of statistics
     111     */
     112    {
     113    #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     114      struct timespec   cpu_average;
     115
     116      _Timespec_Divide_by_integer(
     117         &the_stats.total_cpu_time,
     118         the_stats.count,
     119         &cpu_average
     120      );
     121      printk(
     122        "%" PRId32 "." PERCENT_FMT "/"        /* min cpu time */
     123        "%" PRId32 "." PERCENT_FMT "/"        /* max cpu time */
     124        "%" PRId32 "." PERCENT_FMT " ",       /* avg cpu time */
     125        the_stats.min_cpu_time.tv_sec,
     126          the_stats.min_cpu_time.tv_nsec / NANOSECONDS_DIVIDER,
     127        the_stats.max_cpu_time.tv_sec,
     128          the_stats.max_cpu_time.tv_nsec / NANOSECONDS_DIVIDER,
     129        cpu_average.tv_sec,
     130          cpu_average.tv_nsec / NANOSECONDS_DIVIDER
     131       );
     132    #else
     133      uint32_t ival_cpu, fval_cpu;
     134
     135      ival_cpu = the_stats.total_cpu_time * 100 / the_stats.count;
     136      fval_cpu = ival_cpu % 100;
     137      ival_cpu /= 100;
     138
     139      printk(
     140        "%3" PRId32 "/%4" PRId32 "/%3" PRId32 ".%02" PRId32 " ",
     141        the_stats.min_cpu_time, the_stats.max_cpu_time, ival_cpu, fval_cpu
     142      );
     143    #endif
     144    }
     145
     146    /*
     147     *  print Wall time part of statistics
     148     */
     149    {
     150    #ifdef RTEMS_ENABLE_NANOSECOND_RATE_MONOTONIC_STATISTICS
     151      struct timespec  wall_average;
     152      _Timespec_Divide_by_integer(
     153         &the_stats.total_wall_time,
     154         the_stats.count,
     155         &wall_average
     156      );
     157      printk(
     158        "%" PRId32 "." PERCENT_FMT "/"        /* min wall time */
     159        "%" PRId32 "." PERCENT_FMT "/"        /* max wall time */
     160        "%" PRId32 "." PERCENT_FMT "\n",      /* avg wall time */
     161        the_stats.min_wall_time.tv_sec,
     162          the_stats.min_wall_time.tv_nsec / NANOSECONDS_DIVIDER,
     163        the_stats.max_wall_time.tv_sec,
     164          the_stats.max_wall_time.tv_nsec / NANOSECONDS_DIVIDER,
     165        wall_average.tv_sec,
     166          wall_average.tv_nsec / NANOSECONDS_DIVIDER
     167      );
     168    #else
     169      uint32_t  ival_wall, fval_wall;
     170
     171      ival_wall = the_stats.total_wall_time * 100 / the_stats.count;
     172      fval_wall = ival_wall % 100;
     173      ival_wall /= 100;
     174      printk(
     175        "%3" PRId32 "/%4" PRId32 "/%3" PRId32 ".%02" PRId32 "\n",
     176        the_stats.min_wall_time, the_stats.max_wall_time, ival_wall, fval_wall
     177      );
     178    #endif
     179    }
    89180  }
    90181}
  • cpukit/rtems/src/ratemonresetall.c

    re57739d rc3330a8  
    2424
    2525/*
    26  *  rtems_rate_montonic_reset_all_statistics
     26 *  rtems_rate_monotonic_reset_all_statistics
    2727 */
    28 void rtems_rate_montonic_reset_all_statistics( void )
     28void rtems_rate_monotonic_reset_all_statistics( void )
    2929{
    3030  Objects_Id        id;
  • cpukit/rtems/src/ratemontimeout.c

    re57739d rc3330a8  
    6464            the_thread->Wait.id == the_period->Object.id ) {
    6565        _Thread_Unblock( the_thread );
    66         the_period->owner_ticks_executed_at_period =
    67           the_thread->ticks_executed;
    68 
    69         the_period->time_at_period = _Watchdog_Ticks_since_boot;
    7066
    7167        _Watchdog_Insert_ticks( &the_period->Timer, the_period->next_length );
    7268      } else if ( the_period->state == RATE_MONOTONIC_OWNER_IS_BLOCKING ) {
    7369        the_period->state = RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING;
    74         the_period->owner_ticks_executed_at_period =
    75           the_thread->ticks_executed;
    7670
    77         the_period->time_at_period = _Watchdog_Ticks_since_boot;
    7871        _Watchdog_Insert_ticks( &the_period->Timer, the_period->next_length );
    7972      } else
  • cpukit/score/Makefile.am

    re57739d rc3330a8  
    149149libscore_a_SOURCES += src/timespecaddto.c src/timespecfromticks.c \
    150150    src/timespecisvalid.c src/timespeclessthan.c src/timespecgreaterthan.c \
    151     src/timespecsubtract.c src/timespectoticks.c src/timespecdivide.c
     151    src/timespecsubtract.c src/timespectoticks.c src/timespecdivide.c \
     152    src/timespecdividebyinteger.c
    152153
    153154## TOD_C_FILES
  • cpukit/score/include/rtems/score/thread.h

    re57739d rc3330a8  
    3030#ifdef __cplusplus
    3131extern "C" {
     32#endif
     33
     34/*
     35 *  The user can define this at configure time and go back to ticks
     36 *  resolution.
     37 */
     38#ifndef __RTEMS_USE_TICKS_CPU_USAGE_STATISTICS__
     39  /*
     40   *  Enable the nanosecond accurate statistics
     41   *
     42   *  When not defined, the older style tick accurate granularity
     43   *  is used.
     44   */
     45  #define RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
    3246#endif
    3347
     
    4458#include <rtems/score/tqdata.h>
    4559#include <rtems/score/watchdog.h>
     60
     61#ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     62  /* XXX include something for timespec */
     63#endif
    4664
    4765/**
     
    319337  Thread_CPU_budget_algorithm_callout   budget_callout;
    320338
    321   /** This field is the number of clock ticks executed by this thread
     339  /** This field is the amount of CPU time consumed by this thread
    322340   *  since it was created.
    323341   */
    324   uint32_t                              ticks_executed;
     342  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     343    struct timespec                       cpu_time_used;
     344  #else
     345    uint32_t                              ticks_executed;
     346  #endif
    325347  /** This field points to the Ready FIFO for this priority. */
    326348  Chain_Control                        *ready;
     
    435457 */
    436458SCORE_EXTERN struct _reent **_Thread_libc_reent;
     459
     460#ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     461
     462  /**
     463   * This contains the time since boot when the last context switch occurred.
     464   * By placing it in the BSS, it will automatically be zeroed out at
     465   * system initialization and does not need to be known outside this
     466   * file.
     467   */
     468  SCORE_EXTERN struct timespec _Thread_Time_of_last_context_switch;
     469#endif
    437470
    438471/**
  • cpukit/score/include/rtems/score/timespec.h

    re57739d rc3330a8  
    136136);
    137137
    138 /** @brief Divide Timespec
     138/** @brief Divide Timespec By Integet
    139139 *
    140140 *  This routine divides a timespec by an integer value.  The expected
     
    148148 *  @return This method fills in @a result.
    149149 */
    150 void _Timespec_Divide(
     150void _Timespec_Divide_by_integer(
    151151  const struct timespec *time,
    152152  uint32_t               iterations,
    153153  struct timespec       *result
     154);
     155
     156/** @brief Divide Timespec
     157 *
     158 *  This routine divides a timespec by another timespec.  The
     159 *  intended use is for calculating percentages to three decimal points.
     160 *
     161 *  @param[in] lhs is the left hand number
     162 *  @param[in] rhs is the righ hand number
     163 *  @param[in] ival_percentage is the integer portion of the average
     164 *  @param[in] fval_percentage is the thousandths of percentage
     165 *
     166 *  @return This method fills in @a result.
     167 */
     168void _Timespec_Divide(
     169  const struct timespec *lhs,
     170  const struct timespec *rhs,
     171  uint32_t              *ival_percentage,
     172  uint32_t              *fval_percentage
    154173);
    155174
  • cpukit/score/src/threaddispatch.c

    re57739d rc3330a8  
    33 *
    44 *
    5  *  COPYRIGHT (c) 1989-1999.
     5 *  COPYRIGHT (c) 1989-2007.
    66 *  On-Line Applications Research Corporation (OAR).
    77 *
     
    3030#include <rtems/score/userext.h>
    3131#include <rtems/score/wkspace.h>
     32
     33#ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     34
     35  #include <rtems/score/timespec.h>
     36#endif
    3237
    3338/*PAGE
     
    7984    _ISR_Enable( level );
    8085
    81     heir->ticks_executed++;
     86    #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     87      {
     88        struct timespec uptime, ran;
     89        _TOD_Get_uptime( &uptime );
     90        _Timespec_Subtract(&_Thread_Time_of_last_context_switch, &uptime, &ran);
     91        _Timespec_Add_to( &executing->cpu_time_used, &ran );
     92        _Thread_Time_of_last_context_switch = uptime;
     93      }
     94    #else
     95      heir->ticks_executed++;
     96    #endif
    8297
    8398    /*
  • cpukit/score/src/threadinitialize.c

    re57739d rc3330a8  
    188188  }
    189189
    190   the_thread->Start.isr_level        = isr_level;
    191 
    192   the_thread->current_state          = STATES_DORMANT;
    193   the_thread->Wait.queue             = NULL;
    194   the_thread->resource_count         = 0;
    195   the_thread->suspend_count          = 0;
    196   the_thread->real_priority          = priority;
    197   the_thread->Start.initial_priority = priority;
    198   the_thread->ticks_executed         = 0;
    199 
     190  the_thread->Start.isr_level         = isr_level;
     191
     192  the_thread->current_state           = STATES_DORMANT;
     193  the_thread->Wait.queue              = NULL;
     194  the_thread->resource_count          = 0;
     195  the_thread->suspend_count           = 0;
     196  the_thread->real_priority           = priority;
     197  the_thread->Start.initial_priority  = priority;
    200198  _Thread_Set_priority( the_thread, priority );
     199
     200  /*
     201   *  Initialize the CPU usage statistics
     202   */
     203
     204  #ifdef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     205    the_thread->cpu_time_used.tv_sec  = 0;
     206    the_thread->cpu_time_used.tv_nsec = 0;
     207  #else
     208    the_thread->ticks_executed        = 0;
     209  #endif
    201210
    202211  /*
  • cpukit/score/src/threadtickletimeslice.c

    re57739d rc3330a8  
    5050  executing = _Thread_Executing;
    5151
    52   /*
    53    *  Increment the number of ticks this thread has been executing
    54    */
    55 
    56   executing->ticks_executed++;
     52  #ifndef RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS
     53    /*
     54     *  Increment the number of ticks this thread has been executing
     55     */
     56    executing->ticks_executed++;
     57  #endif
    5758
    5859  /*
  • cpukit/score/src/timespecdivide.c

    re57739d rc3330a8  
    2424
    2525void _Timespec_Divide(
    26   const struct timespec *time,
    27   uint32_t               iterations,
    28   struct timespec       *result
     26  const struct timespec *lhs,
     27  const struct timespec *rhs,
     28  uint32_t              *ival_percentage,
     29  uint32_t              *fval_percentage
    2930)
    3031{
    31   uint64_t t;
     32  uint64_t left, right, answer;
    3233
    3334  /*
     
    3536   *  in a 64-bit integer.
    3637   */
    37   t  = time->tv_sec * TOD_NANOSECONDS_PER_SECOND;
    38   t += time->tv_nsec;
     38  left   = lhs->tv_sec * (uint64_t)TOD_NANOSECONDS_PER_SECOND;
     39  left  += lhs->tv_nsec;
     40  right  = rhs->tv_sec * (uint64_t)TOD_NANOSECONDS_PER_SECOND;
     41  right += rhs->tv_nsec;
    3942
    40   /*
    41    *  Divide to get nanoseconds per iteration
    42    */
    43 
    44   t /= iterations;
     43  if ( rhs == 0 ) {
     44    *ival_percentage = 0;
     45    *ival_percentage = 0;
     46    return;
     47  }
    4548
    4649  /*
     
    4851   */
    4952
    50   result->tv_sec  = t / TOD_NANOSECONDS_PER_SECOND;
    51   result->tv_nsec = t % TOD_NANOSECONDS_PER_SECOND;
     53  answer = (left * 1000) / right;
     54
     55  *fval_percentage = answer % 1000;
     56  *ival_percentage = answer / 1000;
    5257}
Note: See TracChangeset for help on using the changeset viewer.