Austin Schuh | dace2a6 | 2020-08-18 10:56:48 -0700 | [diff] [blame] | 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 | } |