xref: /netbsd-src/external/lgpl3/gmp/dist/tune/freq.c (revision 72c7faa4dbb41dbb0238d6b4a109da0d4b236dd4)
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
freq_environment(int help)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
freq_getsysinfo(int help)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
freq_pstat_getprocessor(int help)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
freq_sysctlbyname_i586_freq(int help)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
freq_sysctlbyname_tsc_freq(int help)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
freq_sysctl_hw_cpufrequency(int help)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
freq_sysctl_hw_model(int help)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
freq_proc_cpuinfo(int help)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
freq_sunos_sysinfo(int help)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
freq_sco_etchw(int help)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
freq_attr_get_invent(int help)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
freq_bsd_dmesg(int help)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
freq_irix_hinv(int help)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
freq_processor_info(int help)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
freq_measure_gettimeofday_one(void)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
freq_measure_getrusage_one(void)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
freq_measure(const char * name,double (* one)(void))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
freq_measure_getrusage(int help)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
freq_measure_gettimeofday(int help)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
freq_all(int help)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
speed_cycletime_init(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
speed_cycletime_fail(const char * str)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
speed_cycletime_need_cycles(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
speed_cycletime_need_seconds(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