1 /*
2  * Copyright (c) 2011, 2020, 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 <stdio.h>
27 #include <stdint.h>
28 #include <stdarg.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/resource.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35 #include <stdlib.h>
36 #include <dlfcn.h>
37 #include <pthread.h>
38 #include <inttypes.h>
39 #include "sun_management_OperatingSystemImpl.h"
40 
41 struct ticks {
42     uint64_t  used;
43     uint64_t  usedKernel;
44     uint64_t  total;
45 };
46 
47 typedef struct ticks ticks;
48 
49 typedef enum {
50     CPU_LOAD_VM_ONLY,
51     CPU_LOAD_GLOBAL,
52 } CpuLoadTarget;
53 
54 static struct perfbuf {
55     int   nProcs;
56     ticks jvmTicks;
57     ticks cpuTicks;
58     ticks *cpus;
59 } counters;
60 
61 #define DEC_64 "%"SCNd64
62 #define NS_PER_SEC 1000000000
63 
next_line(FILE * f)64 static void next_line(FILE *f) {
65     while (fgetc(f) != '\n');
66 }
67 
68 /**
69  * Return the total number of ticks since the system was booted.
70  * If the usedTicks parameter is not NULL, it will be filled with
71  * the number of ticks spent on actual processes (user, system or
72  * nice processes) since system boot. Note that this is the total number
73  * of "executed" ticks on _all_ CPU:s, that is on a n-way system it is
74  * n times the number of ticks that has passed in clock time.
75  *
76  * Returns a negative value if the reading of the ticks failed.
77  */
get_totalticks(int which,ticks * pticks)78 static int get_totalticks(int which, ticks *pticks) {
79     FILE         *fh;
80     uint64_t        userTicks, niceTicks, systemTicks, idleTicks;
81     uint64_t        iowTicks = 0, irqTicks = 0, sirqTicks= 0;
82     int             n;
83 
84     if((fh = fopen("/proc/stat", "r")) == NULL) {
85         return -1;
86     }
87 
88     n = fscanf(fh, "cpu " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64 " "
89                    DEC_64 " " DEC_64,
90            &userTicks, &niceTicks, &systemTicks, &idleTicks,
91            &iowTicks, &irqTicks, &sirqTicks);
92 
93     // Move to next line
94     next_line(fh);
95 
96     //find the line for requested cpu faster to just iterate linefeeds?
97     if (which != -1) {
98         int i;
99         for (i = 0; i < which; i++) {
100             if (fscanf(fh, "cpu%*d " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64 " "
101                             DEC_64 " " DEC_64 " " DEC_64,
102                    &userTicks, &niceTicks, &systemTicks, &idleTicks,
103                    &iowTicks, &irqTicks, &sirqTicks) < 4) {
104                 fclose(fh);
105                 return -2;
106             }
107             next_line(fh);
108         }
109         n = fscanf(fh, "cpu%*d " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64 " "
110                        DEC_64 " " DEC_64 " " DEC_64 "\n",
111            &userTicks, &niceTicks, &systemTicks, &idleTicks,
112            &iowTicks, &irqTicks, &sirqTicks);
113     }
114 
115     fclose(fh);
116     if (n < 4) {
117         return -2;
118     }
119 
120     pticks->used       = userTicks + niceTicks;
121     pticks->usedKernel = systemTicks + irqTicks + sirqTicks;
122     pticks->total      = userTicks + niceTicks + systemTicks + idleTicks +
123                          iowTicks + irqTicks + sirqTicks;
124 
125     return 0;
126 }
127 
vread_statdata(const char * procfile,const char * fmt,va_list args)128 static int vread_statdata(const char *procfile, const char *fmt, va_list args) {
129     FILE    *f;
130     int     n;
131     char     buf[2048];
132 
133     if ((f = fopen(procfile, "r")) == NULL) {
134         return -1;
135     }
136 
137     if ((n = fread(buf, 1, sizeof(buf), f)) != -1) {
138     char *tmp;
139 
140     buf[n-1] = '\0';
141     /** skip through pid and exec name. the exec name _could be wacky_ (renamed) and
142      *  make scanf go mupp.
143      */
144     if ((tmp = strrchr(buf, ')')) != NULL) {
145         // skip the ')' and the following space but check that the buffer is long enough
146         tmp += 2;
147         if (tmp < buf + n) {
148         n = vsscanf(tmp, fmt, args);
149         }
150     }
151     }
152 
153     fclose(f);
154 
155     return n;
156 }
157 
read_statdata(const char * procfile,const char * fmt,...)158 static int read_statdata(const char *procfile, const char *fmt, ...) {
159     int       n;
160     va_list args;
161 
162     va_start(args, fmt);
163     n = vread_statdata(procfile, fmt, args);
164     va_end(args);
165     return n;
166 }
167 
168 /** read user and system ticks from a named procfile, assumed to be in 'stat' format then. */
read_ticks(const char * procfile,uint64_t * userTicks,uint64_t * systemTicks)169 static int read_ticks(const char *procfile, uint64_t *userTicks, uint64_t *systemTicks) {
170     return read_statdata(procfile, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u "DEC_64" "DEC_64,
171              userTicks, systemTicks
172              );
173 }
174 
175 /**
176  * Return the number of ticks spent in any of the processes belonging
177  * to the JVM on any CPU.
178  */
get_jvmticks(ticks * pticks)179 static int get_jvmticks(ticks *pticks) {
180     uint64_t userTicks;
181     uint64_t systemTicks;
182 
183     if (read_ticks("/proc/self/stat", &userTicks, &systemTicks) < 0) {
184         return -1;
185     }
186 
187     // get the total
188     if (get_totalticks(-1, pticks) < 0) {
189         return -1;
190     }
191 
192     pticks->used       = userTicks;
193     pticks->usedKernel = systemTicks;
194 
195     return 0;
196 }
197 
198 /**
199  * This method must be called first, before any data can be gathererd.
200  */
perfInit()201 int perfInit() {
202     static int initialized = 0;
203 
204     if (!initialized) {
205         int  i;
206 
207         // We need to allocate counters for all CPUs, including ones that
208         // are currently offline as they could be turned online later.
209         int n = sysconf(_SC_NPROCESSORS_CONF);
210         if (n <= 0) {
211             n = 1;
212         }
213 
214         counters.cpus = calloc(n,sizeof(ticks));
215         counters.nProcs = n;
216         if (counters.cpus != NULL)  {
217             // For the CPU load
218             get_totalticks(-1, &counters.cpuTicks);
219 
220             for (i = 0; i < n; i++) {
221                 get_totalticks(i, &counters.cpus[i]);
222             }
223             // For JVM load
224             get_jvmticks(&counters.jvmTicks);
225             initialized = 1;
226         }
227     }
228 
229     return initialized ? 0 : -1;
230 }
231 
232 #define MAX(a,b) (a>b?a:b)
233 #define MIN(a,b) (a<b?a:b)
234 
235 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
236 
237 /**
238  * Return the load of the CPU as a double. 1.0 means the CPU process uses all
239  * available time for user or system processes, 0.0 means the CPU uses all time
240  * being idle.
241  *
242  * Returns a negative value if there is a problem in determining the CPU load.
243  */
244 
get_cpuload_internal(int which,double * pkernelLoad,CpuLoadTarget target)245 static double get_cpuload_internal(int which, double *pkernelLoad, CpuLoadTarget target) {
246     uint64_t udiff, kdiff, tdiff;
247     ticks *pticks, tmp;
248     double user_load = -1.0;
249     int failed = 0;
250 
251     *pkernelLoad = 0.0;
252 
253     pthread_mutex_lock(&lock);
254 
255     if(perfInit() == 0) {
256 
257         if (target == CPU_LOAD_VM_ONLY) {
258             pticks = &counters.jvmTicks;
259         } else if (which == -1) {
260             pticks = &counters.cpuTicks;
261         } else {
262             pticks = &counters.cpus[which];
263         }
264 
265         tmp = *pticks;
266 
267         if (target == CPU_LOAD_VM_ONLY) {
268             if (get_jvmticks(pticks) != 0) {
269                 failed = 1;
270             }
271         } else if (get_totalticks(which, pticks) < 0) {
272             failed = 1;
273         }
274 
275         if(!failed) {
276             // seems like we sometimes end up with less kernel ticks when
277             // reading /proc/self/stat a second time, timing issue between cpus?
278             if (pticks->usedKernel < tmp.usedKernel) {
279                 kdiff = 0;
280             } else {
281                 kdiff = pticks->usedKernel - tmp.usedKernel;
282             }
283             tdiff = pticks->total - tmp.total;
284             udiff = pticks->used - tmp.used;
285 
286             if (tdiff == 0) {
287                 user_load = 0;
288             } else {
289                 if (tdiff < (udiff + kdiff)) {
290                     tdiff = udiff + kdiff;
291                 }
292                 *pkernelLoad = (kdiff / (double)tdiff);
293                 // BUG9044876, normalize return values to sane values
294                 *pkernelLoad = MAX(*pkernelLoad, 0.0);
295                 *pkernelLoad = MIN(*pkernelLoad, 1.0);
296 
297                 user_load = (udiff / (double)tdiff);
298                 user_load = MAX(user_load, 0.0);
299                 user_load = MIN(user_load, 1.0);
300             }
301         }
302     }
303     pthread_mutex_unlock(&lock);
304     return user_load;
305 }
306 
get_cpu_load(int which)307 double get_cpu_load(int which) {
308     double u, s;
309     u = get_cpuload_internal(which, &s, CPU_LOAD_GLOBAL);
310     if (u < 0) {
311         return -1.0;
312     }
313     // Cap total systemload to 1.0
314     return MIN((u + s), 1.0);
315 }
316 
get_process_load()317 double get_process_load() {
318     double u, s;
319     u = get_cpuload_internal(-1, &s, CPU_LOAD_VM_ONLY);
320     if (u < 0) {
321         return -1.0;
322     }
323     return u + s;
324 }
325 
326 JNIEXPORT jdouble JNICALL
Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0(JNIEnv * env,jobject dummy)327 Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0
328 (JNIEnv *env, jobject dummy)
329 {
330     if (perfInit() == 0) {
331         return get_cpu_load(-1);
332     } else {
333         return -1.0;
334     }
335 }
336 
337 JNIEXPORT jdouble JNICALL
Java_sun_management_OperatingSystemImpl_getProcessCpuLoad0(JNIEnv * env,jobject dummy)338 Java_sun_management_OperatingSystemImpl_getProcessCpuLoad0
339 (JNIEnv *env, jobject dummy)
340 {
341     if (perfInit() == 0) {
342         return get_process_load();
343     } else {
344         return -1.0;
345     }
346 }
347 
348 JNIEXPORT jdouble JNICALL
Java_sun_management_OperatingSystemImpl_getSingleCpuLoad0(JNIEnv * env,jobject mbean,jint cpu_number)349 Java_sun_management_OperatingSystemImpl_getSingleCpuLoad0
350 (JNIEnv *env, jobject mbean, jint cpu_number)
351 {
352     if (perfInit() == 0 && cpu_number >= 0 && cpu_number < counters.nProcs) {
353         return get_cpu_load(cpu_number);
354     } else {
355         return -1.0;
356     }
357 }
358 
359 JNIEXPORT jint JNICALL
Java_sun_management_OperatingSystemImpl_getHostConfiguredCpuCount0(JNIEnv * env,jobject mbean)360 Java_sun_management_OperatingSystemImpl_getHostConfiguredCpuCount0
361 (JNIEnv *env, jobject mbean)
362 {
363     if (perfInit() == 0) {
364         return counters.nProcs;
365     } else {
366        return -1;
367     }
368 }
369 
370 // Return the host cpu ticks since boot in nanoseconds
371 JNIEXPORT jlong JNICALL
Java_sun_management_OperatingSystemImpl_getHostTotalCpuTicks0(JNIEnv * env,jobject mbean)372 Java_sun_management_OperatingSystemImpl_getHostTotalCpuTicks0
373 (JNIEnv *env, jobject mbean)
374 {
375     if (perfInit() == 0) {
376         if (get_totalticks(-1, &counters.cpuTicks) < 0) {
377             return -1;
378         } else {
379             long ticks_per_sec = sysconf(_SC_CLK_TCK);
380             jlong result = (jlong)counters.cpuTicks.total;
381             if (ticks_per_sec <= NS_PER_SEC) {
382                 long scale_factor = NS_PER_SEC/ticks_per_sec;
383                 result = result * scale_factor;
384             } else {
385                 long scale_factor = ticks_per_sec/NS_PER_SEC;
386                 result = result / scale_factor;
387             }
388             return result;
389         }
390     } else {
391         return -1;
392     }
393 }
394 
395 JNIEXPORT jint JNICALL
Java_sun_management_OperatingSystemImpl_getHostOnlineCpuCount0(JNIEnv * env,jobject mbean)396 Java_sun_management_OperatingSystemImpl_getHostOnlineCpuCount0
397 (JNIEnv *env, jobject mbean)
398 {
399     int n = sysconf(_SC_NPROCESSORS_ONLN);
400     if (n <= 0) {
401         n = 1;
402     }
403     return n;
404 }
405