Home | History | Annotate | Line # | Download | only in tune
freq.c revision 1.1.1.3
      1 /* CPU frequency determination.
      2 
      3 Copyright 1999-2004 Free Software Foundation, Inc.
      4 
      5 This file is part of the GNU MP Library.
      6 
      7 The GNU MP Library is free software; you can redistribute it and/or modify
      8 it under the terms of either:
      9 
     10   * the GNU Lesser General Public License as published by the Free
     11     Software Foundation; either version 3 of the License, or (at your
     12     option) any later version.
     13 
     14 or
     15 
     16   * the GNU General Public License as published by the Free Software
     17     Foundation; either version 2 of the License, or (at your option) any
     18     later version.
     19 
     20 or both in parallel, as here.
     21 
     22 The GNU MP Library is distributed in the hope that it will be useful, but
     23 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     24 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     25 for more details.
     26 
     27 You should have received copies of the GNU General Public License and the
     28 GNU Lesser General Public License along with the GNU MP Library.  If not,
     29 see https://www.gnu.org/licenses/.  */
     30 
     31 
     32 /* Currently we don't get a CPU frequency on the following systems,
     33 
     34    alphaev5-cray-unicosmk2.0.6.X
     35        times() has been seen at 13.33 ns (75 MHz), which is probably not the
     36        cpu frequency.  Measuring the cycle counter against that would be
     37        possible though.  But currently we don't use the cycle counter due to
     38        unicos having int==8bytes where tune/alpha.asm assumes int==4bytes.
     39 
     40    m68040-unknown-netbsd1.4.1
     41        Not sure if the system even knows the cpu frequency.  There's no
     42        cycle counter to measure, though we could perhaps make a loop taking
     43        a known number of cycles and measure that.
     44 
     45    power-ibm-aix4.2.1.0
     46    power2-ibm-aix4.3.1.0
     47    powerpc604-ibm-aix4.3.1.0
     48    powerpc604-ibm-aix4.3.3.0
     49    powerpc630-ibm-aix4.3.3.0
     50    powerpc-unknown-netbsd1.6
     51        Don't know where any info hides on these.  mftb is not related to the
     52        cpu frequency so doesn't help.
     53 
     54    sparc-unknown-linux-gnu [maybe]
     55        Don't know where any info hides on this.
     56 
     57    t90-cray-unicos10.0.X
     58        The times() call seems to be for instance 2.22 nanoseconds, which
     59        might be the cpu frequency (450 mhz), but need to confirm that.
     60 
     61 */
     62 
     63 #include "config.h"
     64 
     65 #if HAVE_INVENT_H
     66 #include <invent.h> /* for IRIX invent_cpuinfo_t */
     67 #endif
     68 
     69 #include <stdio.h>
     70 #include <stdlib.h> /* for getenv, qsort */
     71 #include <string.h> /* for memcmp */
     72 
     73 #if HAVE_UNISTD_H
     74 #include <unistd.h> /* for sysconf */
     75 #endif
     76 
     77 #include <sys/types.h>
     78 
     79 #if HAVE_SYS_ATTRIBUTES_H
     80 #include <sys/attributes.h>   /* for IRIX attr_get(), needs sys/types.h */
     81 #endif
     82 
     83 #if HAVE_SYS_IOGRAPH_H
     84 #include <sys/iograph.h>      /* for IRIX INFO_LBL_DETAIL_INVENT */
     85 #endif
     86 
     87 #if HAVE_SYS_PARAM_H     /* for constants needed by NetBSD <sys/sysctl.h> */
     88 #include <sys/param.h>   /* and needed by HPUX <sys/pstat.h> */
     89 #endif
     90 
     91 #if HAVE_SYS_PSTAT_H
     92 #include <sys/pstat.h>   /* for HPUX pstat_getprocessor() */
     93 #endif
     94 
     95 #if HAVE_SYS_SYSCTL_H
     96 #include <sys/sysctl.h>  /* for sysctlbyname() */
     97 #endif
     98 
     99 #if TIME_WITH_SYS_TIME
    100 # include <sys/time.h>  /* for struct timeval */
    101 # include <time.h>
    102 #else
    103 # if HAVE_SYS_TIME_H
    104 #  include <sys/time.h>
    105 # else
    106 #  include <time.h>
    107 # endif
    108 #endif
    109 
    110 #if HAVE_SYS_RESOURCE_H
    111 #include <sys/resource.h>  /* for struct rusage */
    112 #endif
    113 
    114 #if HAVE_SYS_PROCESSOR_H
    115 #include <sys/processor.h>  /* for solaris processor_info_t */
    116 #endif
    117 
    118 /* On AIX 5.1 with gcc 2.9-aix51-020209 in -maix64 mode, <sys/sysinfo.h>
    119    gets an error about "fill" in "struct cpuinfo" having a negative size,
    120    apparently due to __64BIT_KERNEL not being defined because _KERNEL is not
    121    defined.  Avoid this file if we don't actually need it, which we don't on
    122    AIX since there's no getsysinfo there.  */
    123 #if HAVE_SYS_SYSINFO_H && HAVE_GETSYSINFO
    124 #include <sys/sysinfo.h>  /* for OSF getsysinfo */
    125 #endif
    126 
    127 #if HAVE_MACHINE_HAL_SYSINFO_H
    128 #include <machine/hal_sysinfo.h>  /* for OSF GSI_CPU_INFO, struct cpu_info */
    129 #endif
    130 
    131 /* Remove definitions from NetBSD <sys/param.h>, to avoid conflicts with
    132    gmp-impl.h. */
    133 #ifdef MIN
    134 #undef MIN
    135 #endif
    136 #ifdef MAX
    137 #undef MAX
    138 #endif
    139 
    140 #include "gmp-impl.h"
    141 
    142 #include "speed.h"
    143 
    144 
    145 #define HELP(str)                       \
    146   if (help)                             \
    147     {                                   \
    148       printf ("    - %s\n", str);       \
    149       return 0;                         \
    150     }
    151 
    152 
    153 /* GMP_CPU_FREQUENCY environment variable.  Should be in Hertz and can be
    154    floating point, for example "450e6". */
    155 static int
    156 freq_environment (int help)
    157 {
    158   char  *e;
    159 
    160   HELP ("environment variable GMP_CPU_FREQUENCY (in Hertz)");
    161 
    162   e = getenv ("GMP_CPU_FREQUENCY");
    163   if (e == NULL)
    164     return 0;
    165 
    166   speed_cycletime = 1.0 / atof (e);
    167 
    168   if (speed_option_verbose)
    169     printf ("Using GMP_CPU_FREQUENCY %.2f for cycle time %.3g\n",
    170             atof (e), speed_cycletime);
    171 
    172   return 1;
    173 }
    174 
    175 
    176 /* getsysinfo is available on OSF, or 4.0 and up at least.
    177    The man page (on 4.0) suggests a 0 return indicates information not
    178    available, but that seems to be the normal return for GSI_CPU_INFO.  */
    179 static int
    180 freq_getsysinfo (int help)
    181 {
    182 #if HAVE_GETSYSINFO
    183   struct cpu_info  c;
    184   int              start;
    185 
    186   HELP ("getsysinfo() GSI_CPU_INFO");
    187 
    188   start = 0;
    189   if (getsysinfo (GSI_CPU_INFO, (caddr_t) &c, sizeof (c),
    190                   &start, NULL, NULL) != -1)
    191     {
    192       speed_cycletime = 1e-6 / (double) c.mhz;
    193       if (speed_option_verbose)
    194         printf ("Using getsysinfo() GSI_CPU_INFO %u for cycle time %.3g\n",
    195                 c.mhz, speed_cycletime);
    196       return 1;
    197     }
    198 #endif
    199   return 0;
    200 }
    201 
    202 
    203 /* In HPUX 10 and up, pstat_getprocessor() psp_iticksperclktick is the
    204    number of CPU cycles (ie. the CR16 register) per CLK_TCK.  HPUX 9 doesn't
    205    have that field in pst_processor though, and has no apparent
    206    equivalent.  */
    207 
    208 static int
    209 freq_pstat_getprocessor (int help)
    210 {
    211 #if HAVE_PSTAT_GETPROCESSOR && HAVE_PSP_ITICKSPERCLKTICK
    212   struct pst_processor  p;
    213 
    214   HELP ("pstat_getprocessor() psp_iticksperclktick");
    215 
    216   if (pstat_getprocessor (&p, sizeof(p), 1, 0) != -1)
    217     {
    218       long  c = clk_tck();
    219       speed_cycletime = 1.0 / (c * p.psp_iticksperclktick);
    220       if (speed_option_verbose)
    221         printf ("Using pstat_getprocessor() psp_iticksperclktick %lu and clk_tck %ld for cycle time %.3g\n",
    222                 (unsigned long) p.psp_iticksperclktick, c,
    223                 speed_cycletime);
    224       return 1;
    225     }
    226 #endif
    227   return 0;
    228 }
    229 
    230 
    231 /* i386 FreeBSD 2.2.8 sysctlbyname machdep.i586_freq is in Hertz.
    232    There's no obvious defines available to get this from plain sysctl.  */
    233 static int
    234 freq_sysctlbyname_i586_freq (int help)
    235 {
    236 #if HAVE_SYSCTLBYNAME
    237   unsigned  val;
    238   size_t    size;
    239 
    240   HELP ("sysctlbyname() machdep.i586_freq");
    241 
    242   size = sizeof(val);
    243   if (sysctlbyname ("machdep.i586_freq", &val, &size, NULL, 0) == 0
    244       && size == sizeof(val))
    245     {
    246       speed_cycletime = 1.0 / (double) val;
    247       if (speed_option_verbose)
    248         printf ("Using sysctlbyname() machdep.i586_freq %u for cycle time %.3g\n",
    249                 val, speed_cycletime);
    250       return 1;
    251     }
    252 #endif
    253   return 0;
    254 }
    255 
    256 
    257 /* i368 FreeBSD 3.3 sysctlbyname machdep.tsc_freq is in Hertz.
    258    There's no obvious defines to get this from plain sysctl.  */
    259 
    260 static int
    261 freq_sysctlbyname_tsc_freq (int help)
    262 {
    263 #if HAVE_SYSCTLBYNAME
    264   unsigned  val;
    265   size_t    size;
    266 
    267   HELP ("sysctlbyname() machdep.tsc_freq");
    268 
    269   size = sizeof(val);
    270   if (sysctlbyname ("machdep.tsc_freq", &val, &size, NULL, 0) == 0
    271       && size == sizeof(val))
    272     {
    273       speed_cycletime = 1.0 / (double) val;
    274       if (speed_option_verbose)
    275         printf ("Using sysctlbyname() machdep.tsc_freq %u for cycle time %.3g\n",
    276                 val, speed_cycletime);
    277       return 1;
    278     }
    279 #endif
    280   return 0;
    281 }
    282 
    283 
    284 /* Apple powerpc Darwin 1.3 sysctl hw.cpufrequency is in hertz.  For some
    285    reason only seems to be available from sysctl(), not sysctlbyname().  */
    286 
    287 static int
    288 freq_sysctl_hw_cpufrequency (int help)
    289 {
    290 #if HAVE_SYSCTL && defined (CTL_HW) && defined (HW_CPU_FREQ)
    291   int       mib[2];
    292   unsigned  val;
    293   size_t    size;
    294 
    295   HELP ("sysctl() hw.cpufrequency");
    296 
    297   mib[0] = CTL_HW;
    298   mib[1] = HW_CPU_FREQ;
    299   size = sizeof(val);
    300   if (sysctl (mib, 2, &val, &size, NULL, 0) == 0)
    301     {
    302       speed_cycletime = 1.0 / (double) val;
    303       if (speed_option_verbose)
    304         printf ("Using sysctl() hw.cpufrequency %u for cycle time %.3g\n",
    305                 val, speed_cycletime);
    306       return 1;
    307     }
    308 #endif
    309   return 0;
    310 }
    311 
    312 
    313 /* The following ssyctl hw.model strings have been observed,
    314 
    315        Alpha FreeBSD 4.1:   Digital AlphaPC 164LX 599 MHz
    316        NetBSD 1.4:          Digital AlphaPC 164LX 599 MHz
    317        NetBSD 1.6.1:        CY7C601 @ 40 MHz, TMS390C602A FPU
    318 
    319    NetBSD 1.4 doesn't seem to have sysctlbyname, so sysctl() is used.  */
    320 
    321 static int
    322 freq_sysctl_hw_model (int help)
    323 {
    324 #if HAVE_SYSCTL && defined (CTL_HW) && defined (HW_MODEL)
    325   int       mib[2];
    326   char      str[128];
    327   unsigned  val;
    328   size_t    size;
    329   char      *p;
    330   int       end;
    331 
    332   HELP ("sysctl() hw.model");
    333 
    334   mib[0] = CTL_HW;
    335   mib[1] = HW_MODEL;
    336   size = sizeof(str);
    337   if (sysctl (mib, 2, str, &size, NULL, 0) == 0)
    338     {
    339       for (p = str; *p != '\0'; p++)
    340         {
    341           end = 0;
    342           if (sscanf (p, "%u MHz%n", &val, &end) == 1 && end != 0)
    343             {
    344               speed_cycletime = 1e-6 / (double) val;
    345               if (speed_option_verbose)
    346                 printf ("Using sysctl() hw.model %u for cycle time %.3g\n",
    347                         val, speed_cycletime);
    348               return 1;
    349             }
    350         }
    351     }
    352 #endif
    353   return 0;
    354 }
    355 
    356 
    357 /* /proc/cpuinfo for linux kernel.
    358 
    359    Linux doesn't seem to have any system call to get the CPU frequency, at
    360    least not in 2.0.x or 2.2.x, so it's necessary to read /proc/cpuinfo.
    361 
    362    i386 2.0.36 - "bogomips" is the CPU frequency.
    363 
    364    i386 2.2.13 - has both "cpu MHz" and "bogomips", and it's "cpu MHz" which
    365                  is the frequency.
    366 
    367    alpha 2.2.5 - "cycle frequency [Hz]" seems to be right, "BogoMIPS" is
    368                  very slightly different.
    369 
    370    alpha 2.2.18pre21 - "cycle frequency [Hz]" is 0 on at least one system,
    371                  "BogoMIPS" seems near enough.
    372 
    373    powerpc 2.2.19 - "clock" is the frequency, bogomips is something weird
    374   */
    375 
    376 static int
    377 freq_proc_cpuinfo (int help)
    378 {
    379   FILE    *fp;
    380   char    buf[128];
    381   double  val;
    382   int     ret = 0;
    383   int     end;
    384 
    385   HELP ("linux kernel /proc/cpuinfo file, cpu MHz or bogomips");
    386 
    387   if ((fp = fopen ("/proc/cpuinfo", "r")) != NULL)
    388     {
    389       while (fgets (buf, sizeof (buf), fp) != NULL)
    390         {
    391           if (sscanf (buf, "cycle frequency [Hz]    : %lf", &val) == 1
    392               && val != 0.0)
    393             {
    394               speed_cycletime = 1.0 / val;
    395               if (speed_option_verbose)
    396                 printf ("Using /proc/cpuinfo \"cycle frequency\" %.2f for cycle time %.3g\n", val, speed_cycletime);
    397               ret = 1;
    398               break;
    399             }
    400           if (sscanf (buf, "cpu MHz : %lf\n", &val) == 1)
    401             {
    402               speed_cycletime = 1e-6 / val;
    403               if (speed_option_verbose)
    404                 printf ("Using /proc/cpuinfo \"cpu MHz\" %.2f for cycle time %.3g\n", val, speed_cycletime);
    405               ret = 1;
    406               break;
    407             }
    408           end = 0;
    409           if (sscanf (buf, "clock : %lfMHz\n%n", &val, &end) == 1 && end != 0)
    410             {
    411               speed_cycletime = 1e-6 / val;
    412               if (speed_option_verbose)
    413                 printf ("Using /proc/cpuinfo \"clock\" %.2f for cycle time %.3g\n", val, speed_cycletime);
    414               ret = 1;
    415               break;
    416             }
    417           if (sscanf (buf, "bogomips : %lf\n", &val) == 1
    418               || sscanf (buf, "BogoMIPS : %lf\n", &val) == 1)
    419             {
    420               speed_cycletime = 1e-6 / val;
    421               if (speed_option_verbose)
    422                 printf ("Using /proc/cpuinfo \"bogomips\" %.2f for cycle time %.3g\n", val, speed_cycletime);
    423               ret = 1;
    424               break;
    425             }
    426         }
    427       fclose (fp);
    428     }
    429   return ret;
    430 }
    431 
    432 
    433 /* /bin/sysinfo for SunOS 4.
    434    Prints a line like: cpu0 is a "75 MHz TI,TMS390Z55" CPU */
    435 static int
    436 freq_sunos_sysinfo (int help)
    437 {
    438   int     ret = 0;
    439 #if HAVE_POPEN
    440   FILE    *fp;
    441   char    buf[128];
    442   double  val;
    443   int     end;
    444 
    445   HELP ("SunOS /bin/sysinfo program output, cpu0");
    446 
    447   /* Error messages are sent to /dev/null in case /bin/sysinfo doesn't
    448      exist.  The brackets are necessary for some shells. */
    449   if ((fp = popen ("(/bin/sysinfo) 2>/dev/null", "r")) != NULL)
    450     {
    451       while (fgets (buf, sizeof (buf), fp) != NULL)
    452         {
    453           end = 0;
    454           if (sscanf (buf, " cpu0 is a \"%lf MHz%n", &val, &end) == 1
    455               && end != 0)
    456             {
    457               speed_cycletime = 1e-6 / val;
    458               if (speed_option_verbose)
    459                 printf ("Using /bin/sysinfo \"cpu0 MHz\" %.2f for cycle time %.3g\n", val, speed_cycletime);
    460               ret = 1;
    461               break;
    462             }
    463         }
    464       pclose (fp);
    465     }
    466 #endif
    467   return ret;
    468 }
    469 
    470 
    471 /* "/etc/hw -r cpu" for SCO OpenUnix 8, printing a line like
    472 	The speed of the CPU is approximately 450MHz
    473  */
    474 static int
    475 freq_sco_etchw (int help)
    476 {
    477   int     ret = 0;
    478 #if HAVE_POPEN
    479   FILE    *fp;
    480   char    buf[128];
    481   double  val;
    482   int     end;
    483 
    484   HELP ("SCO /etc/hw program output");
    485 
    486   /* Error messages are sent to /dev/null in case /etc/hw doesn't exist.
    487      The brackets are necessary for some shells. */
    488   if ((fp = popen ("(/etc/hw -r cpu) 2>/dev/null", "r")) != NULL)
    489     {
    490       while (fgets (buf, sizeof (buf), fp) != NULL)
    491         {
    492           end = 0;
    493           if (sscanf (buf, " The speed of the CPU is approximately %lfMHz%n",
    494                       &val, &end) == 1 && end != 0)
    495             {
    496               speed_cycletime = 1e-6 / val;
    497               if (speed_option_verbose)
    498                 printf ("Using /etc/hw %.2f MHz, for cycle time %.3g\n",
    499                         val, speed_cycletime);
    500               ret = 1;
    501               break;
    502             }
    503         }
    504       pclose (fp);
    505     }
    506 #endif
    507   return ret;
    508 }
    509 
    510 
    511 /* attr_get("/hw/cpunum/0",INFO_LBL_DETAIL_INVENT) ic_cpu_info.cpufq for
    512    IRIX 6.5.  Past versions don't have INFO_LBL_DETAIL_INVENT,
    513    invent_cpuinfo_t, or /hw/cpunum/0.
    514 
    515    The same information is available from the "hinv -c processor" command,
    516    but it seems better to make a system call where possible. */
    517 
    518 static int
    519 freq_attr_get_invent (int help)
    520 {
    521   int     ret = 0;
    522 #if HAVE_ATTR_GET && HAVE_INVENT_H && defined (INFO_LBL_DETAIL_INVENT)
    523   invent_cpuinfo_t  inv;
    524   int               len, val;
    525 
    526   HELP ("attr_get(\"/hw/cpunum/0\") ic_cpu_info.cpufq");
    527 
    528   len = sizeof (inv);
    529   if (attr_get ("/hw/cpunum/0", INFO_LBL_DETAIL_INVENT,
    530                 (char *) &inv, &len, 0) == 0
    531       && len == sizeof (inv)
    532       && inv.ic_gen.ig_invclass == INV_PROCESSOR)
    533     {
    534       val = inv.ic_cpu_info.cpufq;
    535       speed_cycletime = 1e-6 / val;
    536       if (speed_option_verbose)
    537         printf ("Using attr_get(\"/hw/cpunum/0\") ic_cpu_info.cpufq %d MHz for cycle time %.3g\n", val, speed_cycletime);
    538       ret = 1;
    539     }
    540 #endif
    541   return ret;
    542 }
    543 
    544 
    545 /* FreeBSD on i386 gives a line like the following at bootup, and which can
    546    be read back from /var/run/dmesg.boot.
    547 
    548        CPU: AMD Athlon(tm) Processor (755.29-MHz 686-class CPU)
    549        CPU: Pentium 4 (1707.56-MHz 686-class CPU)
    550        CPU: i486 DX4 (486-class CPU)
    551 
    552    This is useful on FreeBSD 4.x, where there's no sysctl machdep.tsc_freq
    553    or machdep.i586_freq.
    554 
    555    It's better to use /var/run/dmesg.boot than to run /sbin/dmesg, since the
    556    latter prints the current system message buffer, which is a limited size
    557    and can wrap around if the system is up for a long time.  */
    558 
    559 static int
    560 freq_bsd_dmesg (int help)
    561 {
    562   FILE    *fp;
    563   char    buf[256], *p;
    564   double  val;
    565   int     ret = 0;
    566   int     end;
    567 
    568   HELP ("BSD /var/run/dmesg.boot file");
    569 
    570   if ((fp = fopen ("/var/run/dmesg.boot", "r")) != NULL)
    571     {
    572       while (fgets (buf, sizeof (buf), fp) != NULL)
    573         {
    574           if (memcmp (buf, "CPU:", 4) == 0)
    575             {
    576               for (p = buf; *p != '\0'; p++)
    577                 {
    578                   end = 0;
    579                   if (sscanf (p, "(%lf-MHz%n", &val, &end) == 1 && end != 0)
    580                     {
    581                       speed_cycletime = 1e-6 / val;
    582                       if (speed_option_verbose)
    583                         printf ("Using /var/run/dmesg.boot CPU: %.2f MHz for cycle time %.3g\n", val, speed_cycletime);
    584                       ret = 1;
    585                       break;
    586                     }
    587                 }
    588             }
    589         }
    590       fclose (fp);
    591     }
    592   return ret;
    593 }
    594 
    595 
    596 /* "hinv -c processor" for IRIX.  The following lines have been seen,
    597 
    598               1 150 MHZ IP20 Processor
    599               2 195 MHZ IP27 Processors
    600               Processor 0: 500 MHZ IP35
    601 
    602    This information is available from attr_get() on IRIX 6.5 (see above),
    603    but on IRIX 6.2 it's not clear where to look, so fall back on
    604    parsing.  */
    605 
    606 static int
    607 freq_irix_hinv (int help)
    608 {
    609   int     ret = 0;
    610 #if HAVE_POPEN
    611   FILE    *fp;
    612   char    buf[128];
    613   double  val;
    614   int     nproc, end;
    615 
    616   HELP ("IRIX \"hinv -c processor\" output");
    617 
    618   /* Error messages are sent to /dev/null in case hinv doesn't exist.  The
    619      brackets are necessary for some shells. */
    620   if ((fp = popen ("(hinv -c processor) 2>/dev/null", "r")) != NULL)
    621     {
    622       while (fgets (buf, sizeof (buf), fp) != NULL)
    623         {
    624           end = 0;
    625           if (sscanf (buf, "Processor 0: %lf MHZ%n", &val, &end) == 1
    626               && end != 0)
    627             {
    628             found:
    629               speed_cycletime = 1e-6 / val;
    630               if (speed_option_verbose)
    631                 printf ("Using hinv -c processor \"%.2f MHZ\" for cycle time %.3g\n", val, speed_cycletime);
    632               ret = 1;
    633               break;
    634             }
    635           end = 0;
    636           if (sscanf (buf, "%d %lf MHZ%n", &nproc, &val, &end) == 2
    637               && end != 0)
    638             goto found;
    639         }
    640       pclose (fp);
    641     }
    642 #endif
    643   return ret;
    644 }
    645 
    646 
    647 /* processor_info() for Solaris.  "psrinfo" is the command-line interface to
    648    this.  "prtconf -vp" gives similar information.
    649 
    650    Apple Darwin has a processor_info, but in an incompatible style.  It
    651    doesn't have <sys/processor.h>, so test for that.  */
    652 
    653 static int
    654 freq_processor_info (int help)
    655 {
    656 #if HAVE_PROCESSOR_INFO && HAVE_SYS_PROCESSOR_H
    657   processor_info_t  p;
    658   int  i, n, mhz = 0;
    659 
    660   HELP ("processor_info() pi_clock");
    661 
    662   n = sysconf (_SC_NPROCESSORS_CONF);
    663   for (i = 0; i < n; i++)
    664     {
    665       if (processor_info (i, &p) != 0)
    666         continue;
    667       if (p.pi_state != P_ONLINE)
    668         continue;
    669 
    670       if (mhz != 0 && p.pi_clock != mhz)
    671         {
    672           fprintf (stderr,
    673                    "freq_processor_info(): There's more than one CPU and they have different clock speeds\n");
    674           return 0;
    675         }
    676 
    677       mhz = p.pi_clock;
    678     }
    679 
    680   speed_cycletime = 1.0e-6 / (double) mhz;
    681 
    682   if (speed_option_verbose)
    683     printf ("Using processor_info() %d mhz for cycle time %.3g\n",
    684             mhz, speed_cycletime);
    685   return 1;
    686 
    687 #else
    688   return 0;
    689 #endif
    690 }
    691 
    692 
    693 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETTIMEOFDAY
    694 static double
    695 freq_measure_gettimeofday_one (void)
    696 {
    697 #define call_gettimeofday(t)   gettimeofday (&(t), NULL)
    698 #define timeval_tv_sec(t)      ((t).tv_sec)
    699 #define timeval_tv_usec(t)     ((t).tv_usec)
    700   FREQ_MEASURE_ONE ("gettimeofday", struct timeval,
    701                     call_gettimeofday, speed_cyclecounter,
    702                     timeval_tv_sec, timeval_tv_usec);
    703 }
    704 #endif
    705 
    706 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETRUSAGE
    707 static double
    708 freq_measure_getrusage_one (void)
    709 {
    710 #define call_getrusage(t)   getrusage (0, &(t))
    711 #define rusage_tv_sec(t)    ((t).ru_utime.tv_sec)
    712 #define rusage_tv_usec(t)   ((t).ru_utime.tv_usec)
    713   FREQ_MEASURE_ONE ("getrusage", struct rusage,
    714                     call_getrusage, speed_cyclecounter,
    715                     rusage_tv_sec, rusage_tv_usec);
    716 }
    717 #endif
    718 
    719 
    720 /* MEASURE_MATCH is how many readings within MEASURE_TOLERANCE of each other
    721    are required.  This must be at least 2.  */
    722 #define MEASURE_MAX_ATTEMPTS   20
    723 #define MEASURE_TOLERANCE      1.005  /* 0.5% */
    724 #define MEASURE_MATCH          3
    725 
    726 double
    727 freq_measure (const char *name, double (*one) (void))
    728 {
    729   double  t[MEASURE_MAX_ATTEMPTS];
    730   int     i, j;
    731 
    732   for (i = 0; i < numberof (t); i++)
    733     {
    734       t[i] = (*one) ();
    735 
    736       qsort (t, i+1, sizeof(t[0]), (qsort_function_t) double_cmp_ptr);
    737       if (speed_option_verbose >= 3)
    738         for (j = 0; j <= i; j++)
    739           printf ("   t[%d] is %.6g\n", j, t[j]);
    740 
    741       for (j = 0; j+MEASURE_MATCH-1 <= i; j++)
    742         {
    743           if (t[j+MEASURE_MATCH-1] <= t[j] * MEASURE_TOLERANCE)
    744             {
    745               /* use the average of the range found */
    746                 return (t[j+MEASURE_MATCH-1] + t[j]) / 2.0;
    747             }
    748         }
    749     }
    750   return -1.0;
    751 }
    752 
    753 static int
    754 freq_measure_getrusage (int help)
    755 {
    756 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETRUSAGE
    757   double  cycletime;
    758 
    759   if (! getrusage_microseconds_p ())
    760     return 0;
    761   if (! cycles_works_p ())
    762     return 0;
    763 
    764   HELP ("cycle counter measured with microsecond getrusage()");
    765 
    766   cycletime = freq_measure ("getrusage", freq_measure_getrusage_one);
    767   if (cycletime == -1.0)
    768     return 0;
    769 
    770   speed_cycletime = cycletime;
    771   if (speed_option_verbose)
    772     printf ("Using getrusage() measured cycle counter %.4g (%.2f MHz)\n",
    773             speed_cycletime, 1e-6/speed_cycletime);
    774   return 1;
    775 
    776 #else
    777   return 0;
    778 #endif
    779 }
    780 
    781 static int
    782 freq_measure_gettimeofday (int help)
    783 {
    784 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETTIMEOFDAY
    785   double  cycletime;
    786 
    787   if (! gettimeofday_microseconds_p ())
    788     return 0;
    789   if (! cycles_works_p ())
    790     return 0;
    791 
    792   HELP ("cycle counter measured with microsecond gettimeofday()");
    793 
    794   cycletime = freq_measure ("gettimeofday", freq_measure_gettimeofday_one);
    795   if (cycletime == -1.0)
    796     return 0;
    797 
    798   speed_cycletime = cycletime;
    799   if (speed_option_verbose)
    800     printf ("Using gettimeofday() measured cycle counter %.4g (%.2f MHz)\n",
    801             speed_cycletime, 1e-6/speed_cycletime);
    802   return 1;
    803 #else
    804   return 0;
    805 #endif
    806 }
    807 
    808 
    809 /* Each function returns 1 if it succeeds in setting speed_cycletime, or 0
    810    if not.
    811 
    812    In general system call tests are first since they're fast, then file
    813    tests, then tests running programs.  Necessary exceptions to this rule
    814    are noted.  The measuring is last since it's time consuming, and rather
    815    wasteful of cpu.  */
    816 
    817 static int
    818 freq_all (int help)
    819 {
    820   return
    821     /* This should be first, so an environment variable can override
    822        anything the system gives. */
    823     freq_environment (help)
    824 
    825     || freq_attr_get_invent (help)
    826     || freq_getsysinfo (help)
    827     || freq_pstat_getprocessor (help)
    828     || freq_sysctl_hw_model (help)
    829     || freq_sysctl_hw_cpufrequency (help)
    830     || freq_sysctlbyname_i586_freq (help)
    831     || freq_sysctlbyname_tsc_freq (help)
    832 
    833     /* SCO openunix 8 puts a dummy pi_clock==16 in processor_info, so be
    834        sure to check /etc/hw before that function. */
    835     || freq_sco_etchw (help)
    836 
    837     || freq_processor_info (help)
    838     || freq_proc_cpuinfo (help)
    839     || freq_bsd_dmesg (help)
    840     || freq_irix_hinv (help)
    841     || freq_sunos_sysinfo (help)
    842     || freq_measure_getrusage (help)
    843     || freq_measure_gettimeofday (help);
    844 }
    845 
    846 
    847 void
    848 speed_cycletime_init (void)
    849 {
    850   static int  attempted = 0;
    851 
    852   if (attempted)
    853     return;
    854   attempted = 1;
    855 
    856   if (freq_all (0))
    857     return;
    858 
    859   if (speed_option_verbose)
    860     printf ("CPU frequency couldn't be determined\n");
    861 }
    862 
    863 
    864 void
    865 speed_cycletime_fail (const char *str)
    866 {
    867   fprintf (stderr, "Measuring with: %s\n", speed_time_string);
    868   fprintf (stderr, "%s,\n", str);
    869   fprintf (stderr, "but none of the following are available,\n");
    870   freq_all (1);
    871   abort ();
    872 }
    873 
    874 /* speed_time_init leaves speed_cycletime set to either 0.0 or 1.0 when the
    875    CPU frequency is unknown.  0.0 is when the time base is in seconds, so
    876    that's no good if cycles are wanted.  1.0 is when the time base is in
    877    cycles, which conversely is no good if seconds are wanted.  */
    878 void
    879 speed_cycletime_need_cycles (void)
    880 {
    881   speed_time_init ();
    882   if (speed_cycletime == 0.0)
    883     speed_cycletime_fail
    884       ("Need to know CPU frequency to give times in cycles");
    885 }
    886 void
    887 speed_cycletime_need_seconds (void)
    888 {
    889   speed_time_init ();
    890   if (speed_cycletime == 1.0)
    891     speed_cycletime_fail
    892       ("Need to know CPU frequency to convert cycles to seconds");
    893 }
    894