1 /* Get the system load averages.
2 Copyright (C) 1985, 86, 87, 88, 89, 91, 92, 93, 1994, 1995
3 Free Software Foundation, Inc.
5 This file is part of SXEmacs
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 /* Compile-time symbols that this file uses:
23 FIXUP_KERNEL_SYMBOL_ADDR() Adjust address in returned struct nlist.
24 KERNEL_FILE Pathname of the kernel to nlist.
25 LDAV_CVT() Scale the load average from the kernel.
27 LDAV_SYMBOL Name of kernel symbol giving load average.
28 LOAD_AVE_TYPE Type of the load average array in the kernel.
29 Must be defined unless one of
30 apollo, DGUX, NeXT, or UMAX is defined;
31 otherwise, no load average is available.
32 NLIST_STRUCT Include nlist.h, not a.out.h, and
33 the nlist n_name element is a pointer,
35 NLIST_NAME_UNION struct nlist has an n_un member, not n_name.
36 LINUX_LDAV_FILE [__linux__]: File containing load averages.
38 Specific system predefines this file uses, aside from setting
39 default values if not emacs:
42 BSD Real BSD, not just BSD-like.
48 sequent Sequent Dynix 3.x.x (BSD)
49 _SEQUENT_ Sequent DYNIX/ptx 1.x.x (SYSV)
50 sony_news NEWS-OS (works at least for 4.1C)
53 __linux__ Linux: assumes /proc filesystem mounted.
54 Support from Michael K. Johnson.
55 __NetBSD__ NetBSD: assumes /kern filesystem mounted.
56 __OpenBSD__ OpenBSD: ditto.
58 In addition, to avoid nesting many #ifdefs, we internally set
59 LDAV_DONE to indicate that the load average has been computed.
61 We also #define LDAV_PRIVILEGED if a program will require
62 special installation to be able to call getloadavg. */
64 /* This should always be first. */
70 #include "sysfile.h" /* for encapsulated open, close, read, write */
72 #ifndef HAVE_GETLOADAVG
74 /* The existing Emacs configuration files define a macro called
75 LOAD_AVE_CVT, which accepts a value of type LOAD_AVE_TYPE, and
76 returns the load average multiplied by 100. What we actually want
77 is a macro called LDAV_CVT, which returns the load average as an
80 For backwards compatibility, we'll define LDAV_CVT in terms of
81 LOAD_AVE_CVT, but future machine config files should just define
84 #if !defined(LDAV_CVT) && defined(LOAD_AVE_CVT)
85 #define LDAV_CVT(n) (LOAD_AVE_CVT (n) / 100.0)
88 #if defined (HAVE_KSTAT_H)
90 #endif /* HAVE_KSTAT_H */
92 #if !defined (BSD) && defined (ultrix)
93 /* Ultrix behaves like BSD on Vaxen. */
98 /* NeXT in the 2.{0,1,2} releases defines BSD in <sys/param.h>, which
99 conflicts with the definition understood in this file, that this
103 /* NeXT defines FSCALE in <sys/param.h>. However, we take FSCALE being
104 defined to mean that the nlist method should be used, which is not true. */
108 /* Set values that are different from the defaults, which are
109 set a little farther down with #ifndef. */
111 /* Some shorthands. */
113 #if defined (HPUX) && !defined (hpux)
117 #if defined(hp300) && !defined(hpux)
121 #if defined(ultrix) && defined(mips)
125 #if (defined(sun) && defined(SVR4)) || defined (SOLARIS2)
129 #if defined (__osf__) && (defined (__alpha) || defined (__alpha__))
132 #include <netinet/in.h> /* Needed for Digital UNIX V3 */
133 #include <net/proto_net.h>
134 #include <sys/table.h>
137 #if defined (__osf__) && (defined (mips) || defined (__mips__))
139 #include <sys/table.h>
142 /* UTek's /bin/cc on the 4300 has no architecture specific cpp define by
143 default, but _MACH_IND_SYS_TYPES is defined in <sys/types.h>. Combine
144 that with a couple of other things and we'll have a unique match. */
145 #if !defined (tek4300) && defined (unix) && defined (m68k) && defined (mc68000) && defined (mc68020) && defined (_MACH_IND_SYS_TYPES)
146 #define tek4300 /* Define by emacs, but not by other users. */
149 /* VAX C can't handle multi-line #ifs, or lines longer than 256 chars. */
150 #ifndef LOAD_AVE_TYPE
153 #define LOAD_AVE_TYPE long
157 #define LOAD_AVE_TYPE long
161 #define LOAD_AVE_TYPE long
165 #define LOAD_AVE_TYPE long
169 #define LOAD_AVE_TYPE long
173 #define LOAD_AVE_TYPE long
177 #define LOAD_AVE_TYPE long
181 #define LOAD_AVE_TYPE long
185 #define LOAD_AVE_TYPE long
188 #if defined (ardent) && defined (titan)
189 #define LOAD_AVE_TYPE long
193 #define LOAD_AVE_TYPE long
196 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
197 #define LOAD_AVE_TYPE long
201 #define LOAD_AVE_TYPE long
205 #define LOAD_AVE_TYPE double
207 #define LDAV_CVT(n) (n)
211 #endif /* No LOAD_AVE_TYPE. */
214 /* <sys/param.h> defines an incorrect value for FSCALE on Alpha OSF/1,
215 according to ghazi@noc.rutgers.edu. */
217 #define FSCALE 1024.0
220 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
221 /* <sys/param.h> defines an incorrect value for FSCALE on an
222 Alliant FX/2800 Concentrix 2.2, according to ghazi@noc.rutgers.edu. */
229 /* SunOS and some others define FSCALE in sys/param.h. */
232 #define FSCALE 2048.0
235 #if defined(MIPS) || defined(SVR4) || defined(decstation)
239 #if defined (sgi) || defined (sequent)
240 /* Sometimes both MIPS and sgi are defined, so FSCALE was just defined
241 above under #ifdef MIPS. But we want the sgi value. */
243 #define FSCALE 1000.0
246 #if defined (ardent) && defined (titan)
247 #define FSCALE 65536.0
255 #define FSCALE 65536.0
258 #endif /* Not FSCALE. */
260 #if !defined (LDAV_CVT) && defined (FSCALE)
261 #define LDAV_CVT(n) (((double) (n)) / FSCALE)
264 /* VAX C can't handle multi-line #ifs, or lines longer that 256 characters. */
283 #if defined (_SEQUENT_) || defined (sequent)
303 #if defined (ardent) && defined (titan)
315 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
323 #endif /* defined (NLIST_STRUCT) */
325 #if defined(sgi) || (defined(mips) && !defined(BSD))
326 #define FIXUP_KERNEL_SYMBOL_ADDR(nl) ((nl)[0].n_value &= ~(1 << 31))
329 #if !defined (KERNEL_FILE) && defined (sequent)
330 #define KERNEL_FILE "/dynix"
333 #if !defined (KERNEL_FILE) && defined (hpux)
334 #define KERNEL_FILE "/hp-ux"
337 #if !defined(KERNEL_FILE) && (defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) || defined(SVR4) || (defined (ardent) && defined (titan)))
338 #define KERNEL_FILE "/unix"
341 #if !defined (LDAV_SYMBOL) && defined (alliant)
342 #define LDAV_SYMBOL "_Loadavg"
345 #if !defined(LDAV_SYMBOL) && ((defined(hpux) && !defined(hp9000s300)) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) || (defined (ardent) && defined (titan)) || defined (_AIX))
346 #define LDAV_SYMBOL "avenrun"
355 /* LOAD_AVE_TYPE should only get defined if we're going to use the
357 #if !defined(LOAD_AVE_TYPE) && (defined(BSD) || defined(LDAV_CVT) || defined(KERNEL_FILE) || defined(LDAV_SYMBOL))
358 #define LOAD_AVE_TYPE double
365 #else /* NLIST_STRUCT */
367 #endif /* NLIST_STRUCT */
375 #define KERNEL_FILE "/vmunix"
376 #endif /* KERNEL_FILE */
379 #define LDAV_SYMBOL "_avenrun"
380 #endif /* LDAV_SYMBOL */
383 #define LDAV_CVT(n) ((double) (n))
384 #endif /* !LDAV_CVT */
386 #endif /* LOAD_AVE_TYPE */
389 #ifdef HAVE_MACH_MACH_H
390 #include <mach/mach.h>
397 #include <sys/sysmp.h>
403 #include <sys/time.h>
404 #include <sys/wait.h>
405 #include <sys/syscall.h>
408 #include <machine/cpu.h>
409 #include <inq_stats/statistics.h>
410 #include <inq_stats/sysstats.h>
411 #include <inq_stats/cpustats.h>
412 #include <inq_stats/procstats.h>
413 #else /* Not UMAX_43. */
414 #include <sys/sysdefs.h>
415 #include <sys/statistics.h>
416 #include <sys/sysstats.h>
417 #include <sys/cpudefs.h>
418 #include <sys/cpustats.h>
419 #include <sys/procstats.h>
420 #endif /* Not UMAX_43. */
424 #include <sys/dg_sys_info.h>
427 #if defined (HAVE_SYS_PSTAT_H)
428 #include <sys/pstat.h>
429 #endif /* HAVE_SYS_PSTAT_H (on HPUX) */
431 /* Avoid static vars inside a function since in HPUX they dump as pure. */
434 static processor_set_t default_set;
435 static int getloadavg_initialized;
439 static unsigned int cpus = 0;
440 static unsigned int samples;
444 static struct dg_sys_info_load_info load_info; /* what-a-mouthful! */
448 /* File descriptor open to /dev/kmem */
450 /* Nonzero iff channel is valid. */
451 static int getloadavg_initialized;
452 /* Offset in kmem to seek to read load average, or 0 means invalid. */
456 static struct nlist nl[2];
464 # define countof(x) (sizeof (x) / sizeof (*(x)))
467 #endif /* LOAD_AVE_TYPE */
469 /* Put the 1 minute, 5 minute and 15 minute load averages
470 into the first NELEM elements of LOADAVG.
471 Return the number written (never more than 3, but may be less than NELEM),
472 or -1 if an error occurred. */
474 int getloadavg(double loadavg[], int nelem);
476 int getloadavg(double loadavg[], int nelem)
478 int elem = 0; /* Return value. */
480 #ifdef NO_GET_LOAD_AVG
482 /* Set errno to zero to indicate that there was no particular error;
483 this function just can't work at all on this system. */
486 #endif /* NO_GET_LOAD_AVG */
488 #if ! defined (LDAV_DONE) && defined (HAVE_KSTAT_H) && defined (HAVE_LIBKSTAT)
490 /* getloadavg is best implemented using kstat (kernel stats), on
491 systems (like SunOS5) that support it, since you don't need special
492 privileges to use it.
494 Initial implementation courtesy Zlatko Calusic <zcalusic@carnet.hr>.
495 Integrated to XEmacs by Hrvoje Niksic <hniksic@xemacs.org>.
496 Additional cleanup by Hrvoje Niksic, based on code published by
497 Casper Dik <Casper.Dik@Holland.Sun.Com>. */
500 static char *avestrings[] = { "avenrun_1min",
505 if (nelem > countof(avestrings))
506 nelem = countof(avestrings);
511 ksp = kstat_lookup(kc, "unix", 0, "system_misc");
516 if (kstat_read(kc, ksp, 0) < 0) {
520 for (elem = 0; elem < nelem; elem++) {
522 (kstat_named_t *) kstat_data_lookup(ksp, avestrings[elem]);
527 loadavg[elem] = (double)kn->value.ul / FSCALE;
530 #endif /* HAVE_KSTAT_H && HAVE_LIBKSTAT */
532 #if !defined (LDAV_DONE) && defined (HAVE_SYS_PSTAT_H)
534 /* This is totally undocumented, and is not guaranteed to work, but
535 mayhap it might .... If it does work, it will work only on HP-UX
536 8.0 or later. -- Darryl Okahata <darrylo@sr.hp.com> */
537 #undef LOAD_AVE_TYPE /* Make sure these don't exist. */
540 struct pst_dynamic procinfo;
543 statbuf.pst_dynamic = &procinfo;
544 if (pstat(PSTAT_DYNAMIC, statbuf, sizeof(struct pst_dynamic), 0, 0) ==
547 loadavg[elem++] = procinfo.psd_avg_1_min;
548 loadavg[elem++] = procinfo.psd_avg_5_min;
549 loadavg[elem++] = procinfo.psd_avg_15_min;
552 #if !defined (LDAV_DONE) && defined (__linux__)
556 #ifndef LINUX_LDAV_FILE
557 #define LINUX_LDAV_FILE "/proc/loadavg"
564 fd = open(LINUX_LDAV_FILE, O_RDONLY);
567 count = read(fd, ldavgbuf, 40);
572 count = sscanf(ldavgbuf, "%lf %lf %lf",
573 &load_ave[0], &load_ave[1], &load_ave[2]);
577 for (elem = 0; elem < nelem && elem < count; elem++)
578 loadavg[elem] = load_ave[elem];
579 #endif /* __linux__ */
581 #if !defined (LDAV_DONE) && defined (__NetBSD__) || defined (__OpenBSD__)
585 #ifndef NETBSD_LDAV_FILE
586 #define NETBSD_LDAV_FILE "/kern/loadavg"
589 unsigned long int load_ave[3], scale;
593 fp = fopen(NETBSD_LDAV_FILE, "r");
596 count = fscanf(fp, "%lu %lu %lu %lu\n",
597 &load_ave[0], &load_ave[1], &load_ave[2], &scale);
602 for (elem = 0; elem < nelem; elem++)
603 loadavg[elem] = (double)load_ave[elem] / (double)scale;
604 #endif /* __NetBSD__ or __OpenBSD__ */
606 #if !defined (LDAV_DONE) && defined (NeXT)
608 /* The NeXT code was adapted from iscreen 3.2. */
611 struct processor_set_basic_info info;
614 /* We only know how to get the 1-minute average for this system,
615 so even if the caller asks for more than 1, we only return 1. */
617 if (!getloadavg_initialized) {
618 if (processor_set_default(host_self(), &default_set) ==
620 getloadavg_initialized = 1;
623 if (getloadavg_initialized) {
624 info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
625 if (processor_set_info
626 (default_set, PROCESSOR_SET_BASIC_INFO, &host,
627 (processor_set_info_t) & info, &info_count)
629 getloadavg_initialized = 0;
633 (double)info.load_average / LOAD_SCALE;
637 if (!getloadavg_initialized)
641 #if !defined (LDAV_DONE) && defined (UMAX)
643 /* UMAX 4.2, which runs on the Encore Multimax multiprocessor, does not
644 have a /dev/kmem. Information about the workings of the running kernel
645 can be gathered with inq_stats system calls.
646 We only know how to get the 1-minute average for this system. */
648 struct proc_summary proc_sum_data;
649 struct stat_descr proc_info;
651 REGISTER unsigned int i, j;
654 REGISTER unsigned int c, i;
655 struct cpu_config conf;
656 struct stat_descr desc;
659 desc.sd_subsys = SUBSYS_CPU;
660 desc.sd_type = CPUTYPE_CONFIG;
661 desc.sd_addr = (char *)&conf;
662 desc.sd_size = sizeof conf;
664 if (inq_stats(1, &desc))
668 for (i = 0; i < conf.config_maxclass; ++i) {
669 struct class_stats stats;
670 memset((char *)&stats, 0, sizeof stats);
672 desc.sd_type = CPUTYPE_CLASS;
674 desc.sd_addr = (char *)&stats;
675 desc.sd_size = sizeof stats;
677 if (inq_stats(1, &desc))
680 c += stats.class_numcpus;
683 samples = cpus < 2 ? 3 : (2 * cpus / 3);
686 proc_info.sd_next = 0;
687 proc_info.sd_subsys = SUBSYS_PROC;
688 proc_info.sd_type = PROCTYPE_SUMMARY;
689 proc_info.sd_addr = (char *)&proc_sum_data;
690 proc_info.sd_size = sizeof(struct proc_summary);
691 proc_info.sd_sizeused = 0;
693 if (inq_stats(1, &proc_info) != 0)
696 load = proc_sum_data.ps_nrunnable;
698 for (i = samples - 1; i > 0; --i) {
699 load += proc_sum_data.ps_nrun[j];
700 if (j++ == PS_NRUNSIZE)
705 loadavg[elem++] = load / samples / cpus;
708 #if !defined (LDAV_DONE) && defined (DGUX)
710 /* This call can return -1 for an error, but with good args
711 it's not supposed to fail. The first argument is for no
712 apparent reason of type `long int *'. */
713 dg_sys_info((long int *)&load_info,
714 DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
717 loadavg[elem++] = load_info.one_minute;
719 loadavg[elem++] = load_info.five_minute;
721 loadavg[elem++] = load_info.fifteen_minute;
724 #if !defined (LDAV_DONE) && defined (OSF_MIPS)
727 struct tbl_loadavg load_ave;
728 table(TBL_LOADAVG, 0, &load_ave, 1, sizeof(load_ave));
730 = (load_ave.tl_lscale == 0 ? load_ave.tl_avenrun.d[0]
731 : (load_ave.tl_avenrun.l[0] / (double)load_ave.tl_lscale));
732 #endif /* OSF_MIPS */
735 #if !defined (LDAV_DONE) && defined (OSF_ALPHA)
738 struct tbl_loadavg load_ave;
739 table(TBL_LOADAVG, 0, &load_ave, 1, sizeof(load_ave));
740 for (elem = 0; elem < nelem; elem++)
742 = (load_ave.tl_lscale == 0 ? load_ave.tl_avenrun.d[elem]
743 : (load_ave.tl_avenrun.l[elem] /
744 (double)load_ave.tl_lscale));
745 #endif /* OSF_ALPHA */
747 #if !defined (LDAV_DONE) && defined(LOAD_AVE_TYPE)
749 /* UNIX-specific code -- read the average from /dev/kmem. */
751 #define LDAV_PRIVILEGED /* This code requires special installation. */
753 LOAD_AVE_TYPE load_ave[3];
755 /* Get the address of LDAV_SYMBOL. */
759 strcpy(nl[0].n_name, LDAV_SYMBOL);
760 strcpy(nl[1].n_name, "");
761 #else /* NLIST_STRUCT */
762 #ifdef NLIST_NAME_UNION
763 nl[0].n_un.n_name = LDAV_SYMBOL;
764 nl[1].n_un.n_name = 0;
765 #else /* not NLIST_NAME_UNION */
766 nl[0].n_name = (char *)LDAV_SYMBOL;
768 #endif /* not NLIST_NAME_UNION */
769 #endif /* NLIST_STRUCT */
773 #if !(defined (_AIX) && !defined (ps2))
774 nlist(KERNEL_FILE, nl)
776 knlist(nl, 1, sizeof(nl[0]))
779 /* Omit "&& nl[0].n_type != 0 " -- it breaks on Sun386i. */
781 #ifdef FIXUP_KERNEL_SYMBOL_ADDR
782 FIXUP_KERNEL_SYMBOL_ADDR(nl);
784 offset = nl[0].n_value;
786 #endif /* !SUNOS_5 */
790 ldav_off = sysmp(MP_KERNADDR, MPKA_AVENRUN);
792 offset = (long)ldav_off & 0x7fffffff;
796 /* Make sure we have /dev/kmem open. */
797 if (!getloadavg_initialized) {
799 channel = open("/dev/kmem", 0);
801 /* Set the channel to close on exec, so it does not
802 litter any child's descriptor table. */
807 (void)fcntl(channel, F_SETFD, FD_CLOEXEC);
809 getloadavg_initialized = 1;
812 /* We pass 0 for the kernel, corefile, and swapfile names
813 to use the currently running kernel. */
814 kd = kvm_open(0, 0, 0, O_RDONLY, 0);
816 /* nlist the currently running kernel. */
818 offset = nl[0].n_value;
819 getloadavg_initialized = 1;
824 /* If we can, get the load average values. */
825 if (offset && getloadavg_initialized) {
826 /* Try to read the load. */
828 if (lseek(channel, offset, 0) == -1L
829 || read(channel, (char *)load_ave, sizeof(load_ave))
830 != sizeof(load_ave)) {
832 getloadavg_initialized = 0;
835 if (kvm_read(kd, offset, (char *)load_ave, sizeof(load_ave))
836 != sizeof(load_ave)) {
838 getloadavg_initialized = 0;
843 if (offset == 0 || !getloadavg_initialized)
847 loadavg[elem++] = LDAV_CVT(load_ave[0]);
849 loadavg[elem++] = LDAV_CVT(load_ave[1]);
851 loadavg[elem++] = LDAV_CVT(load_ave[2]);
854 #endif /* !LDAV_DONE && LOAD_AVE_TYPE */
859 #endif /* ! HAVE_GETLOADAVG */
862 void main(int argc, char **argv)
867 naptime = atoi(argv[1]);
873 errno = 0; /* Don't be misled if it doesn't set errno. */
874 loads = getloadavg(avg, 3);
876 perror("Error getting load average");
880 printf("1-minute: %f ", avg[0]);
882 printf("5-minute: %f ", avg[1]);
884 printf("15-minute: %f ", avg[2]);