1 /*
2  * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <fcntl.h>
27 #include <kstat.h>
28 #include <procfs.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <sys/sysinfo.h>
34 #include <sys/lwp.h>
35 #include <pthread.h>
36 #include <utmpx.h>
37 #include <dlfcn.h>
38 #include <sys/loadavg.h>
39 #include <jni.h>
40 #include "jvm.h"
41 #include "com_sun_management_internal_OperatingSystemImpl.h"
42 
43 typedef struct {
44     kstat_t *kstat;
45     uint64_t  last_idle;
46     uint64_t  last_total;
47     double  last_ratio;
48 } cpuload_t;
49 
50 static cpuload_t   *cpu_loads = NULL;
51 static unsigned int num_cpus;
52 static kstat_ctl_t *kstat_ctrl = NULL;
53 
map_cpu_kstat_counters()54 static void map_cpu_kstat_counters() {
55     kstat_t     *kstat;
56     int          i;
57 
58     // Get number of CPU(s)
59     if ((num_cpus = sysconf(_SC_NPROCESSORS_ONLN)) == -1) {
60         num_cpus = 1;
61     }
62 
63     // Data structure for saving CPU load
64     if ((cpu_loads = calloc(num_cpus,sizeof(cpuload_t))) == NULL) {
65         return;
66     }
67 
68     // Get kstat cpu_stat counters for every CPU
69     // (loop over kstat to find our cpu_stat(s)
70     i = 0;
71     for (kstat = kstat_ctrl->kc_chain; kstat != NULL; kstat = kstat->ks_next) {
72         if (strncmp(kstat->ks_module, "cpu_stat", 8) == 0) {
73 
74             if (kstat_read(kstat_ctrl, kstat, NULL) == -1) {
75             // Failed to initialize kstat for this CPU so ignore it
76             continue;
77             }
78 
79             if (i == num_cpus) {
80             // Found more cpu_stats than reported CPUs
81             break;
82             }
83 
84             cpu_loads[i++].kstat = kstat;
85         }
86     }
87 }
88 
init_cpu_kstat_counters()89 static int init_cpu_kstat_counters() {
90     static int initialized = 0;
91 
92     // Concurrence in this method is prevented by the lock in
93     // the calling method get_cpu_load();
94     if(!initialized) {
95         if ((kstat_ctrl = kstat_open()) != NULL) {
96             map_cpu_kstat_counters();
97             initialized = 1;
98         }
99     }
100     return initialized ? 0 : -1;
101 }
102 
update_cpu_kstat_counters()103 static void update_cpu_kstat_counters() {
104     if(kstat_chain_update(kstat_ctrl) != 0) {
105         free(cpu_loads);
106         map_cpu_kstat_counters();
107     }
108 }
109 
read_cpustat(cpuload_t * load,cpu_stat_t * cpu_stat)110 int read_cpustat(cpuload_t *load, cpu_stat_t *cpu_stat) {
111     if (load->kstat == NULL) {
112         // no handle.
113         return -1;
114     }
115     if (kstat_read(kstat_ctrl, load->kstat, cpu_stat) == -1) {
116         //  disabling for now, a kstat chain update is likely to happen next time
117         load->kstat = NULL;
118         return -1;
119     }
120     return 0;
121 }
122 
get_single_cpu_load(unsigned int n)123 double get_single_cpu_load(unsigned int n) {
124     cpuload_t  *load;
125     cpu_stat_t  cpu_stat;
126     uint_t     *usage;
127     uint64_t          c_idle;
128     uint64_t          c_total;
129     uint64_t          d_idle;
130     uint64_t          d_total;
131     int           i;
132 
133     if (n >= num_cpus) {
134         return -1.0;
135     }
136 
137     load = &cpu_loads[n];
138     if (read_cpustat(load, &cpu_stat) < 0) {
139         return -1.0;
140     }
141 
142     usage   = cpu_stat.cpu_sysinfo.cpu;
143     c_idle  = usage[CPU_IDLE];
144 
145     for (c_total = 0, i = 0; i < CPU_STATES; i++) {
146         c_total += usage[i];
147     }
148 
149     // Calculate diff against previous snapshot
150     d_idle  = c_idle - load->last_idle;
151     d_total = c_total - load->last_total;
152 
153     /** update if weve moved */
154     if (d_total > 0) {
155         // Save current values for next time around
156         load->last_idle  = c_idle;
157         load->last_total = c_total;
158         load->last_ratio = (double) (d_total - d_idle) / d_total;
159     }
160 
161     return load->last_ratio;
162 }
163 
get_info(const char * path,void * info,size_t s,off_t o)164 int get_info(const char *path, void *info, size_t s, off_t o) {
165     int fd;
166     int ret = 0;
167     if ((fd = open(path, O_RDONLY)) < 0) {
168         return -1;
169     }
170     if (pread(fd, info, s, o) != s) {
171         ret = -1;
172     }
173     close(fd);
174     return ret;
175 }
176 
177 #define MIN(a, b)           ((a < b) ? a : b)
178 
179 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
180 
181 /**
182  * Return the cpu load (0-1) for proc number 'which' (or average all if which == -1)
183  */
get_cpu_load(int which)184 double  get_cpu_load(int which) {
185     double load =.0;
186 
187     pthread_mutex_lock(&lock);
188     if(init_cpu_kstat_counters()==0) {
189 
190         update_cpu_kstat_counters();
191 
192         if (which == -1) {
193             unsigned int i;
194             double       t;
195 
196             for (t = .0, i = 0; i < num_cpus; i++) {
197                 t += get_single_cpu_load(i);
198             }
199 
200             // Cap total systemload to 1.0
201             load = MIN((t / num_cpus), 1.0);
202         } else {
203             load = MIN(get_single_cpu_load(which), 1.0);
204         }
205     } else {
206         load = -1.0;
207     }
208     pthread_mutex_unlock(&lock);
209 
210     return load;
211 }
212 
213 /**
214  * Return the cpu load (0-1) for the current process (i.e the JVM)
215  * or -1.0 if the get_info() call failed
216  */
get_process_load(void)217 double get_process_load(void) {
218     psinfo_t info;
219 
220     // Get the percentage of "recent cpu usage" from all the lwp:s in the JVM:s
221     // process. This is returned as a value between 0.0 and 1.0 multiplied by 0x8000.
222     if (get_info("/proc/self/psinfo",&info.pr_pctcpu, sizeof(info.pr_pctcpu), offsetof(psinfo_t, pr_pctcpu)) == 0) {
223         return (double) info.pr_pctcpu / 0x8000;
224     }
225     return -1.0;
226 }
227 
228 JNIEXPORT jdouble JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getSystemCpuLoad0(JNIEnv * env,jobject dummy)229 Java_com_sun_management_internal_OperatingSystemImpl_getSystemCpuLoad0
230 (JNIEnv *env, jobject dummy)
231 {
232     return get_cpu_load(-1);
233 }
234 
235 JNIEXPORT jdouble JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0(JNIEnv * env,jobject dummy)236 Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0
237 (JNIEnv *env, jobject dummy)
238 {
239     return get_process_load();
240 }
241 
242 JNIEXPORT jdouble JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getSingleCpuLoad0(JNIEnv * env,jobject mbean,jint cpu_number)243 Java_com_sun_management_internal_OperatingSystemImpl_getSingleCpuLoad0
244 (JNIEnv *env, jobject mbean, jint cpu_number)
245 {
246     return -1.0;
247 }
248 
249 JNIEXPORT jint JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0(JNIEnv * env,jobject mbean)250 Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0
251 (JNIEnv *env, jobject mbean)
252 {
253     return -1;
254 }
255