1 /*
2  * Copyright (c) 2003, 2018, 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 "jni.h"
27 #include "jni_util.h"
28 #include "jlong.h"
29 #include "jvm.h"
30 #include "management_ext.h"
31 #include "com_sun_management_internal_OperatingSystemImpl.h"
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #if defined(_ALLBSD_SOURCE)
36 #include <sys/sysctl.h>
37 #ifdef __APPLE__
38 #include <sys/param.h>
39 #include <sys/mount.h>
40 #include <mach/mach.h>
41 #include <sys/proc_info.h>
42 #include <libproc.h>
43 #endif
44 #elif !defined(_AIX)
45 #include <sys/swap.h>
46 #endif
47 #include <sys/resource.h>
48 #include <sys/times.h>
49 #ifndef _ALLBSD_SOURCE
50 #include <sys/sysinfo.h>
51 #endif
52 #include <ctype.h>
53 #include <dirent.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <limits.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 
60 #if defined(__FreeBSD__) || defined(__DragonFly__)
61 #include <vm/vm_param.h>
62 #endif
63 
64 #if defined(_AIX)
65 #include <libperfstat.h>
66 #endif
67 
68 static jlong page_size = 0;
69 
70 #if defined(_ALLBSD_SOURCE) || defined(_AIX)
71 #define MB      (1024UL * 1024UL)
72 #else
73 
74 /* This gets us the new structured proc interfaces of 5.6 & later */
75 /* - see comment in <sys/procfs.h> */
76 #define _STRUCTURED_PROC 1
77 #include <sys/procfs.h>
78 
79 #endif /* _ALLBSD_SOURCE */
80 
81 #if defined(_ALLBSD_SOURCE)
82   #define dirent64 dirent
83   #define readdir64 readdir
84 #endif
85 
86 // true = get available swap in bytes
87 // false = get total swap in bytes
get_total_or_available_swap_space_size(JNIEnv * env,jboolean available)88 static jlong get_total_or_available_swap_space_size(JNIEnv* env, jboolean available) {
89 #ifdef __solaris__
90     long total, avail;
91     int nswap, i, count;
92     swaptbl_t *stbl;
93     char *strtab;
94 
95     // First get the number of swap resource entries
96     if ((nswap = swapctl(SC_GETNSWP, NULL)) == -1) {
97         throw_internal_error(env, "swapctl failed to get nswap");
98         return -1;
99     }
100     if (nswap == 0) {
101         return 0;
102     }
103 
104     // Allocate storage for resource entries
105     stbl = (swaptbl_t*) malloc(nswap * sizeof(swapent_t) +
106                                sizeof(struct swaptable));
107     if (stbl == NULL) {
108         JNU_ThrowOutOfMemoryError(env, 0);
109         return -1;
110     }
111 
112     // Allocate storage for the table
113     strtab = (char*) malloc((nswap + 1) * MAXPATHLEN);
114     if (strtab == NULL) {
115         free(stbl);
116         JNU_ThrowOutOfMemoryError(env, 0);
117         return -1;
118     }
119 
120     for (i = 0; i < (nswap + 1); i++) {
121       stbl->swt_ent[i].ste_path = strtab + (i * MAXPATHLEN);
122     }
123     stbl->swt_n = nswap + 1;
124 
125     // Get the entries
126     if ((count = swapctl(SC_LIST, stbl)) < 0) {
127         free(stbl);
128         free(strtab);
129         throw_internal_error(env, "swapctl failed to get swap list");
130         return -1;
131     }
132 
133     // Sum the entries to get total and free swap
134     total = 0;
135     avail = 0;
136     for (i = 0; i < count; i++) {
137       total += stbl->swt_ent[i].ste_pages;
138       avail += stbl->swt_ent[i].ste_free;
139     }
140 
141     free(stbl);
142     free(strtab);
143     return available ? ((jlong)avail * page_size) :
144                        ((jlong)total * page_size);
145 #elif defined(__linux__)
146     int ret;
147     FILE *fp;
148     jlong total = 0, avail = 0;
149 
150     struct sysinfo si;
151     ret = sysinfo(&si);
152     if (ret != 0) {
153         throw_internal_error(env, "sysinfo failed to get swap size");
154     }
155     total = (jlong)si.totalswap * si.mem_unit;
156     avail = (jlong)si.freeswap * si.mem_unit;
157 
158     return available ? avail : total;
159 #elif defined(__APPLE__)
160     struct xsw_usage vmusage;
161     size_t size = sizeof(vmusage);
162     if (sysctlbyname("vm.swapusage", &vmusage, &size, NULL, 0) != 0) {
163         throw_internal_error(env, "sysctlbyname failed");
164     }
165     return available ? (jlong)vmusage.xsu_avail : (jlong)vmusage.xsu_total;
166 #elif defined(__FreeBSD__)
167     struct xswdev xsw;
168     size_t mibsize, size;
169     jlong npages;
170     int mib[16], n;
171 
172     mibsize = sizeof(mib) / sizeof(mib[0]);
173     if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
174        return (0);
175     for (n = 0, npages = 0; ; n++) {
176        mib[mibsize] = n;
177        size = sizeof(xsw);
178        if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1)
179            break;
180        npages += xsw.xsw_nblks;
181        if (available)
182            npages -= xsw.xsw_used;
183     }
184     return (npages * page_size);
185 #else /* _ALLBSD_SOURCE */
186     /*
187      * XXXBSD: there's no way available to get swap info in
188      *         FreeBSD.  Usage of libkvm is not an option here
189      */
190     // throw_internal_error(env, "Unimplemented in FreeBSD");
191     return (0);
192 #endif
193 }
194 
195 JNIEXPORT void JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_initialize0(JNIEnv * env,jclass cls)196 Java_com_sun_management_internal_OperatingSystemImpl_initialize0
197   (JNIEnv *env, jclass cls)
198 {
199     page_size = sysconf(_SC_PAGESIZE);
200 }
201 
202 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getCommittedVirtualMemorySize0(JNIEnv * env,jobject mbean)203 Java_com_sun_management_internal_OperatingSystemImpl_getCommittedVirtualMemorySize0
204   (JNIEnv *env, jobject mbean)
205 {
206 #ifdef __solaris__
207     psinfo_t psinfo;
208     ssize_t result;
209     size_t remaining;
210     char* addr;
211     int fd;
212 
213     fd = open64("/proc/self/psinfo", O_RDONLY, 0);
214     if (fd < 0) {
215         throw_internal_error(env, "Unable to open /proc/self/psinfo");
216         return -1;
217     }
218 
219     addr = (char *)&psinfo;
220     for (remaining = sizeof(psinfo_t); remaining > 0;) {
221         result = read(fd, addr, remaining);
222         if (result < 0) {
223             if (errno != EINTR) {
224                 close(fd);
225                 throw_internal_error(env, "Unable to read /proc/self/psinfo");
226                 return -1;
227             }
228         } else {
229             remaining -= result;
230             addr += result;
231         }
232     }
233 
234     close(fd);
235     return (jlong) psinfo.pr_size * 1024;
236 #elif defined(__linux__)
237     FILE *fp;
238     unsigned long vsize = 0;
239 
240     if ((fp = fopen("/proc/self/stat", "r")) == NULL) {
241         throw_internal_error(env, "Unable to open /proc/self/stat");
242         return -1;
243     }
244 
245     // Ignore everything except the vsize entry
246     if (fscanf(fp, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u %*u %*d %lu %*[^\n]\n", &vsize) == EOF) {
247         throw_internal_error(env, "Unable to get virtual memory usage");
248         fclose(fp);
249         return -1;
250     }
251 
252     fclose(fp);
253     return (jlong)vsize;
254 #elif defined(__APPLE__)
255     struct task_basic_info t_info;
256     mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
257 
258     kern_return_t res = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
259     if (res != KERN_SUCCESS) {
260         throw_internal_error(env, "task_info failed");
261     }
262     return t_info.virtual_size;
263 #elif defined(__FreeBSD__) || defined(__DragonFly__)
264     FILE *fp;
265     unsigned long end, start;
266     jlong total = 0;
267 
268     if ((fp = fopen("/proc/curproc/map", "r")) == NULL) {
269         throw_internal_error(env, "Unable to open /proc/curproc/map");
270         return -1;
271     }
272 
273     for (;;) {
274        // Ignore everything except start and end entries
275        if (fscanf(fp, "0x%lx 0x%lx %*[^\n]\n", &start, &end) != 2 || start > end)
276            break;
277        total += end - start;
278     }
279 
280     fclose(fp);
281     return total;
282 #else /* _ALLBSD_SOURCE */
283     /*
284      * XXXBSD: there's no way available to do it in FreeBSD, AFAIK.
285      */
286     // throw_internal_error(env, "Unimplemented in FreeBSD");
287     return (64 * MB);
288 #endif
289 }
290 
291 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getTotalSwapSpaceSize0(JNIEnv * env,jobject mbean)292 Java_com_sun_management_internal_OperatingSystemImpl_getTotalSwapSpaceSize0
293   (JNIEnv *env, jobject mbean)
294 {
295     return get_total_or_available_swap_space_size(env, JNI_FALSE);
296 }
297 
298 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getFreeSwapSpaceSize0(JNIEnv * env,jobject mbean)299 Java_com_sun_management_internal_OperatingSystemImpl_getFreeSwapSpaceSize0
300   (JNIEnv *env, jobject mbean)
301 {
302     return get_total_or_available_swap_space_size(env, JNI_TRUE);
303 }
304 
305 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuTime0(JNIEnv * env,jobject mbean)306 Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuTime0
307   (JNIEnv *env, jobject mbean)
308 {
309 #ifdef __APPLE__
310     struct rusage usage;
311     if (getrusage(RUSAGE_SELF, &usage) != 0) {
312         throw_internal_error(env, "getrusage failed");
313         return -1;
314     }
315     jlong microsecs =
316         usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
317         usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
318     return microsecs * 1000;
319 #else
320     jlong clk_tck, ns_per_clock_tick;
321     jlong cpu_time_ns;
322     struct tms time;
323 
324     /*
325      * BSDNOTE: FreeBSD implements _SC_CLK_TCK since FreeBSD 5, so
326      *          add a magic to handle it
327      */
328 #if defined(__solaris__) || defined(_SC_CLK_TCK)
329     clk_tck = (jlong) sysconf(_SC_CLK_TCK);
330 #elif defined(__linux__) || defined(_ALLBSD_SOURCE)
331     clk_tck = 100;
332 #endif
333     if (clk_tck == -1) {
334         throw_internal_error(env,
335                              "sysconf failed - not able to get clock tick");
336         return -1;
337     }
338 
339     times(&time);
340     ns_per_clock_tick = (jlong) 1000 * 1000 * 1000 / (jlong) clk_tck;
341     cpu_time_ns = ((jlong)time.tms_utime + (jlong) time.tms_stime) *
342                       ns_per_clock_tick;
343     return cpu_time_ns;
344 #endif
345 }
346 
347 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getFreePhysicalMemorySize0(JNIEnv * env,jobject mbean)348 Java_com_sun_management_internal_OperatingSystemImpl_getFreePhysicalMemorySize0
349   (JNIEnv *env, jobject mbean)
350 {
351 #ifdef __APPLE__
352     mach_msg_type_number_t count;
353     vm_statistics_data_t vm_stats;
354     kern_return_t res;
355 
356     count = HOST_VM_INFO_COUNT;
357     res = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stats, &count);
358     if (res != KERN_SUCCESS) {
359         throw_internal_error(env, "host_statistics failed");
360         return -1;
361     }
362     return (jlong)vm_stats.free_count * page_size;
363 #elif defined(__FreeBSD__) || defined(__DragonFly__)
364     static const char *vm_stats[] = {
365        "vm.stats.vm.v_free_count",
366        "vm.stats.vm.v_cache_count",
367        /* "vm.stats.vm.v_inactive_count", */
368        NULL
369     };
370     size_t size;
371     jlong free_pages;
372 #ifdef __DragonFly__
373     u_long i, npages;
374 #else
375     u_int i, npages;
376 #endif
377     for (i = 0, free_pages = 0, size = sizeof(npages); vm_stats[i] != NULL; i++) {
378        if (sysctlbyname(vm_stats[i], &npages, &size, NULL, 0) == -1)
379            return 0;
380        free_pages += npages;
381     }
382     return (free_pages * page_size);
383 #elif defined(_ALLBSD_SOURCE)
384     /*
385      * XXBSDL no way to do it in FreeBSD
386      */
387     // throw_internal_error(env, "unimplemented in FreeBSD")
388     return (128 * MB);
389 #elif defined(_AIX)
390     perfstat_memory_total_t memory_info;
391     if (-1 != perfstat_memory_total(NULL, &memory_info, sizeof(perfstat_memory_total_t), 1)) {
392         return (jlong)(memory_info.real_free * 4L * 1024L);
393     }
394     return -1;
395 #else // solaris / linux
396     jlong num_avail_physical_pages = sysconf(_SC_AVPHYS_PAGES);
397     return (num_avail_physical_pages * page_size);
398 #endif
399 }
400 
401 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getTotalPhysicalMemorySize0(JNIEnv * env,jobject mbean)402 Java_com_sun_management_internal_OperatingSystemImpl_getTotalPhysicalMemorySize0
403   (JNIEnv *env, jobject mbean)
404 {
405 #ifdef _ALLBSD_SOURCE
406     int mib[2];
407     size_t rlen;
408 #if defined (HW_MEMSIZE) // Apple
409     uint64_t result = 0;
410     #define MEMMIB HW_MEMSIZE;
411 #elif defined(HW_PHYSMEM64) // OpenBSD & NetBSD
412     int64_t result = 0;
413     #define MEMMIB HW_PHYSMEM64;
414 #elif defined(HW_PHYSMEM) // FreeBSD
415     unsigned long result = 0;
416     #define MEMMIB HW_PHYSMEM;
417 #else
418     #error No ways to get physmem
419 #endif
420 
421     mib[0] = CTL_HW;
422     mib[1] = MEMMIB;
423 
424     rlen = sizeof(result);
425     if (sysctl(mib, 2, &result, &rlen, NULL, 0) != 0) {
426         throw_internal_error(env, "sysctl failed");
427         return -1;
428     }
429     return result;
430 #elif defined(_AIX)
431     perfstat_memory_total_t memory_info;
432     if (-1 != perfstat_memory_total(NULL, &memory_info, sizeof(perfstat_memory_total_t), 1)) {
433         return (jlong)(memory_info.real_total * 4L * 1024L);
434     }
435     return -1;
436 #else // solaris / linux
437     jlong num_physical_pages = sysconf(_SC_PHYS_PAGES);
438     return (num_physical_pages * page_size);
439 #endif
440 }
441 
442 
443 
444 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getOpenFileDescriptorCount0(JNIEnv * env,jobject mbean)445 Java_com_sun_management_internal_OperatingSystemImpl_getOpenFileDescriptorCount0
446   (JNIEnv *env, jobject mbean)
447 {
448 #ifdef __APPLE__
449     // This code is influenced by the darwin lsof source
450     pid_t my_pid;
451     struct proc_bsdinfo bsdinfo;
452     struct proc_fdinfo *fds;
453     int nfiles;
454     kern_return_t kres;
455     int res;
456     size_t fds_size;
457 
458     kres = pid_for_task(mach_task_self(), &my_pid);
459     if (kres != KERN_SUCCESS) {
460         throw_internal_error(env, "pid_for_task failed");
461         return -1;
462     }
463 
464     // get the maximum number of file descriptors
465     res = proc_pidinfo(my_pid, PROC_PIDTBSDINFO, 0, &bsdinfo, PROC_PIDTBSDINFO_SIZE);
466     if (res <= 0) {
467         throw_internal_error(env, "proc_pidinfo with PROC_PIDTBSDINFO failed");
468         return -1;
469     }
470 
471     // allocate memory to hold the fd information (we don't acutally use this information
472     // but need it to get the number of open files)
473     fds_size = bsdinfo.pbi_nfiles * sizeof(struct proc_fdinfo);
474     fds = malloc(fds_size);
475     if (fds == NULL) {
476         JNU_ThrowOutOfMemoryError(env, "could not allocate space for file descriptors");
477         return -1;
478     }
479 
480     // get the list of open files - the return value is the number of bytes
481     // proc_pidinfo filled in
482     res = proc_pidinfo(my_pid, PROC_PIDLISTFDS, 0, fds, fds_size);
483     if (res <= 0) {
484         free(fds);
485         throw_internal_error(env, "proc_pidinfo failed for PROC_PIDLISTFDS");
486         return -1;
487     }
488     nfiles = res / sizeof(struct proc_fdinfo);
489     free(fds);
490 
491     return nfiles;
492 #elif defined(__OpenBSD__)
493     return getdtablecount();
494 #else /* solaris/linux */
495     DIR *dirp;
496     struct dirent64* dentp;
497     jlong fds = 0;
498 
499 #if defined(_AIX)
500 /* AIX does not understand '/proc/self' - it requires the real process ID */
501 #define FD_DIR aix_fd_dir
502     char aix_fd_dir[32];     /* the pid has at most 19 digits */
503     snprintf(aix_fd_dir, 32, "/proc/%d/fd", getpid());
504 #elif defined(_ALLBSD_SOURCE)
505 #define FD_DIR "/dev/fd"
506 #else
507 #define FD_DIR "/proc/self/fd"
508 #endif
509 
510     dirp = opendir(FD_DIR);
511     if (dirp == NULL) {
512         throw_internal_error(env, "Unable to open directory /proc/self/fd");
513         return -1;
514     }
515 
516     // iterate through directory entries, skipping '.' and '..'
517     // each entry represents an open file descriptor.
518     while ((dentp = readdir64(dirp)) != NULL) {
519         if (isdigit(dentp->d_name[0])) {
520             fds++;
521         }
522     }
523 
524     closedir(dirp);
525     // subtract by 1 which was the fd open for this implementation
526     return (fds - 1);
527 #endif
528 }
529 
530 JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getMaxFileDescriptorCount0(JNIEnv * env,jobject mbean)531 Java_com_sun_management_internal_OperatingSystemImpl_getMaxFileDescriptorCount0
532   (JNIEnv *env, jobject mbean)
533 {
534     struct rlimit rlp;
535 
536     if (getrlimit(RLIMIT_NOFILE, &rlp) == -1) {
537         throw_internal_error(env, "getrlimit failed");
538         return -1;
539     }
540     return (jlong) rlp.rlim_cur;
541 }
542