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 "com_sun_management_internal_OperatingSystemImpl.h"
27 
28 #include <sys/resource.h>
29 #include <sys/types.h>
30 #include <sys/sysctl.h>
31 #include <sys/time.h>
32 #ifndef __NetBSD__
33   #include <sys/user.h>
34 #endif
35 #include <unistd.h>
36 #ifdef __FreeBSD__
37   #include <stdlib.h>
38   #include <malloc_np.h>
39 #endif
40 
41 #include "jvm.h"
42 
43 struct ticks {
44     jlong used;
45     jlong total;
46 };
47 
48 typedef struct ticks ticks;
49 
50 static struct perfbuf {
51     int   nProcs;
52     ticks jvmTicks;
53     ticks cpuTicks;
54     ticks *cpus;
55 } counters;
56 
57 /**
58  * This method must be called first, before any data can be gathererd.
59  */
perfInit()60 int perfInit() {
61     static int initialized = 0;
62 
63     if (!initialized) {
64         int mib[2];
65         size_t len;
66         int cpu_val;
67 
68         mib[0] = CTL_HW;
69         mib[1] = HW_NCPU;
70         len = sizeof(cpu_val);
71         if (sysctl(mib, 2, &cpu_val, &len, NULL, 0) == -1 || cpu_val < 1) {
72             cpu_val = 1;
73         }
74 
75         counters.nProcs = cpu_val;
76 #ifdef __FreeBSD__
77         // Initialise JVM
78         counters.jvmTicks.used  = 0;
79         counters.jvmTicks.total = 0;
80 
81         // Initialise CPU
82         counters.cpuTicks.used  = 0;
83         counters.cpuTicks.total = 0;
84 
85         counters.cpus = calloc(cpu_val, sizeof(ticks));
86         if (counters.cpus != NULL) {
87             // Initialise per CPU
88             for (int i = 0; i < counters.nProcs; i++) {
89                 counters.cpus[i].used = 0;
90                 counters.cpus[i].total = 0;
91             }
92         }
93 #endif
94         initialized = 1;
95     }
96 
97     return initialized ? 0 : -1;
98 }
99 
100 JNIEXPORT jdouble JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getSystemCpuLoad0(JNIEnv * env,jobject dummy)101 Java_com_sun_management_internal_OperatingSystemImpl_getSystemCpuLoad0
102 (JNIEnv *env, jobject dummy)
103 {
104 #if defined(__FreeBSD__) || defined(__DragonFly__)
105     if (perfInit() == 0) {
106         /* This is based on the MacOS X implementation */
107 
108         /* Load CPU times */
109         long cp_time[CPUSTATES];
110         size_t len = sizeof(cp_time);
111         if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) == -1) {
112             return -1.;
113         }
114 
115         jlong used  = cp_time[CP_USER] + cp_time[CP_NICE] + cp_time[CP_SYS] + cp_time[CP_INTR];
116         jlong total = used + cp_time[CP_IDLE];
117 
118         if (counters.cpuTicks.used == 0 || counters.cpuTicks.total == 0) {
119             // First call, just set the last values
120             counters.cpuTicks.used = used;
121             counters.cpuTicks.total = total;
122             // return 0 since we have no data, not -1 which indicates error
123             return 0.;
124         }
125 
126         jlong used_delta  = used - counters.cpuTicks.used;
127         jlong total_delta = total - counters.cpuTicks.total;
128 
129         jdouble cpu = (jdouble) used_delta / total_delta;
130 
131         counters.cpuTicks.used = used;
132         counters.cpuTicks.total = total;
133 
134         return cpu;
135     }
136     else {
137 #endif
138     // Not implemented yet
139     return -1.;
140 #if defined(__FreeBSD__) || defined(__DragonFly__)
141     }
142 #endif
143 }
144 
145 
146 #define TIME_VALUE_TO_TIMEVAL(a, r) do {  \
147      (r)->tv_sec = (a)->seconds;          \
148      (r)->tv_usec = (a)->microseconds;    \
149 } while (0)
150 
151 
152 #define TIME_VALUE_TO_MICROSECONDS(TV) \
153      ((TV).tv_sec * 1000 * 1000 + (TV).tv_usec)
154 
155 
156 JNIEXPORT jdouble JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0(JNIEnv * env,jobject dummy)157 Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0
158 (JNIEnv *env, jobject dummy)
159 {
160 #if defined(__FreeBSD__) || defined(__DragonFly__)
161     if (perfInit() == 0) {
162         /* This is based on the MacOS X implementation */
163 
164         struct timeval now;
165         struct kinfo_proc kp;
166         int mib[4];
167         size_t len = sizeof(struct kinfo_proc);
168 
169         mib[0] = CTL_KERN;
170         mib[1] = KERN_PROC;
171         mib[2] = KERN_PROC_PID;
172         mib[3] = getpid();
173 
174         if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) {
175             return -1.;
176         }
177 
178         if (gettimeofday(&now, NULL) == -1) {
179             return -1.;
180         }
181 
182         jint ncpus      = JVM_ActiveProcessorCount();
183         jlong time      = TIME_VALUE_TO_MICROSECONDS(now) * ncpus;
184 #ifdef __DragonFly__
185         jlong task_time = TIME_VALUE_TO_MICROSECONDS(kp.kp_ru.ru_utime) +
186                           TIME_VALUE_TO_MICROSECONDS(kp.kp_ru.ru_stime);
187 #else
188         jlong task_time = TIME_VALUE_TO_MICROSECONDS(kp.ki_rusage.ru_utime) +
189                           TIME_VALUE_TO_MICROSECONDS(kp.ki_rusage.ru_stime);
190 #endif
191 
192         if (counters.jvmTicks.used == 0 || counters.jvmTicks.total == 0) {
193             // First call, just set the last values.
194             counters.jvmTicks.used  = task_time;
195             counters.jvmTicks.total = time;
196             // return 0 since we have no data, not -1 which indicates error
197             return 0.;
198         }
199 
200         jlong task_time_delta = task_time - counters.jvmTicks.used;
201         jlong time_delta      = time - counters.jvmTicks.total;
202         if (time_delta == 0) {
203             return -1.;
204         }
205 
206         jdouble cpu = (jdouble) task_time_delta / time_delta;
207 
208         counters.jvmTicks.used  = task_time;
209         counters.jvmTicks.total = time;
210 
211         return cpu;
212     }
213     else {
214 #endif
215     // Not implemented yet
216     return -1.0;
217 #if defined(__FreeBSD__) || defined(__DragonFly__)
218     }
219 #endif
220 }
221 
222 JNIEXPORT jdouble JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getSingleCpuLoad0(JNIEnv * env,jobject dummy,jint cpu_number)223 Java_com_sun_management_internal_OperatingSystemImpl_getSingleCpuLoad0
224 (JNIEnv *env, jobject dummy, jint cpu_number)
225 {
226 #ifdef __FreeBSD__
227     if (perfInit() == 0 && 0 <= cpu_number && cpu_number < counters.nProcs) {
228         /* Load CPU times */
229         long cp_times[CPUSTATES * counters.nProcs];
230         size_t len = sizeof(cp_times);
231         if (sysctlbyname("kern.cp_times", &cp_times, &len, NULL, 0) == -1) {
232             return -1.;
233         }
234 
235         size_t offset = cpu_number * CPUSTATES;
236         jlong used  = cp_times[offset + CP_USER] + cp_times[offset + CP_NICE] + cp_times[offset + CP_SYS] + cp_times[offset + CP_INTR];
237         jlong total = used + cp_times[offset + CP_IDLE];
238 
239         if (counters.cpus[cpu_number].used == 0 || counters.cpus[cpu_number].total == 0) {
240             // First call, just set the last values
241             counters.cpus[cpu_number].used  = used;
242             counters.cpus[cpu_number].total = total;
243             // return 0 since we have no data, not -1 which indicates error
244             return 0.;
245         }
246 
247         jlong used_delta  = used - counters.cpus[cpu_number].used;
248         jlong total_delta = total - counters.cpus[cpu_number].total;
249 
250         jdouble cpu = (jdouble) used_delta / total_delta;
251 
252         counters.cpus[cpu_number].used  = used;
253         counters.cpus[cpu_number].total = total;
254 
255         return cpu;
256     }
257     else {
258 #endif
259     return -1.0;
260 #ifdef __FreeBSD__
261     }
262 #endif
263 }
264 
265 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostTotalCpuTicks0(JNIEnv * env,jobject mbean)266 Java_com_sun_management_internal_OperatingSystemImpl_getHostTotalCpuTicks0
267 (JNIEnv *env, jobject mbean)
268 {
269     return -1;
270 }
271 
272 JNIEXPORT jint JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0(JNIEnv * env,jobject mbean)273 Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0
274 (JNIEnv *env, jobject mbean)
275 {
276     if (perfInit() == 0) {
277         return counters.nProcs;
278     }
279     else {
280         return -1;
281     }
282 }
283 
284 JNIEXPORT jint JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostOnlineCpuCount0(JNIEnv * env,jobject mbean)285 Java_com_sun_management_internal_OperatingSystemImpl_getHostOnlineCpuCount0
286 (JNIEnv *env, jobject mbean)
287 {
288 #ifdef __FreeBSD__
289     int n = sysconf(_SC_NPROCESSORS_ONLN);
290     if (n <= 0) {
291         n = 1;
292     }
293     return n;
294 #else
295     return -1;
296 #endif
297 }
298