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