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