1 /* 2 * Copyright (c) 2003, 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 "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 64 static 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 87 static 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 175 JNIEXPORT void JNICALL 176 Java_com_sun_management_internal_OperatingSystemImpl_initialize0 177 (JNIEnv *env, jclass cls) 178 { 179 page_size = sysconf(_SC_PAGESIZE); 180 } 181 182 JNIEXPORT jlong JNICALL 183 Java_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 252 JNIEXPORT jlong JNICALL 253 Java_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 259 JNIEXPORT jlong JNICALL 260 Java_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 266 JNIEXPORT jlong JNICALL 267 Java_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 308 JNIEXPORT jlong JNICALL 309 Java_com_sun_management_internal_OperatingSystemImpl_getFreeMemorySize0 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 342 JNIEXPORT jlong JNICALL 343 Java_com_sun_management_internal_OperatingSystemImpl_getTotalMemorySize0 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 373 JNIEXPORT jlong JNICALL 374 Java_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 461 JNIEXPORT jlong JNICALL 462 Java_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 }