1 /*
   2  * Copyright (c) 2011, 2015, 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 "com_sun_management_internal_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 
  63 static void next_line(FILE *f) {
  64     while (fgetc(f) != '\n');
  65 }
  66 
  67 /**
  68  * Return the total number of ticks since the system was booted.
  69  * If the usedTicks parameter is not NULL, it will be filled with
  70  * the number of ticks spent on actual processes (user, system or
  71  * nice processes) since system boot. Note that this is the total number
  72  * of "executed" ticks on _all_ CPU:s, that is on a n-way system it is
  73  * n times the number of ticks that has passed in clock time.
  74  *
  75  * Returns a negative value if the reading of the ticks failed.
  76  */
  77 static int get_totalticks(int which, ticks *pticks) {
  78     FILE         *fh;
  79     uint64_t        userTicks, niceTicks, systemTicks, idleTicks;
  80     int             n;
  81 
  82     if((fh = fopen("/proc/stat", "r")) == NULL) {
  83         return -1;
  84     }
  85 
  86     n = fscanf(fh, "cpu " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64,
  87            &userTicks, &niceTicks, &systemTicks, &idleTicks);
  88 
  89     // Move to next line
  90     next_line(fh);
  91 
  92     //find the line for requested cpu faster to just iterate linefeeds?
  93     if (which != -1) {
  94         int i;
  95         for (i = 0; i < which; i++) {
  96             if (fscanf(fh, "cpu%*d " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64, &userTicks, &niceTicks, &systemTicks, &idleTicks) != 4) {
  97                 fclose(fh);
  98                 return -2;
  99             }
 100             next_line(fh);
 101         }
 102         n = fscanf(fh, "cpu%*d " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64 "\n",
 103            &userTicks, &niceTicks, &systemTicks, &idleTicks);
 104     }
 105 
 106     fclose(fh);
 107     if (n != 4) {
 108         return -2;
 109     }
 110 
 111     pticks->used       = userTicks + niceTicks;
 112     pticks->usedKernel = systemTicks;
 113     pticks->total      = userTicks + niceTicks + systemTicks + idleTicks;
 114 
 115     return 0;
 116 }
 117 
 118 static int vread_statdata(const char *procfile, const char *fmt, va_list args) {
 119     FILE    *f;
 120     int     n;
 121     char     buf[2048];
 122 
 123     if ((f = fopen(procfile, "r")) == NULL) {
 124         return -1;
 125     }
 126 
 127     if ((n = fread(buf, 1, sizeof(buf), f)) != -1) {
 128     char *tmp;
 129 
 130     buf[n-1] = '\0';
 131     /** skip through pid and exec name. the exec name _could be wacky_ (renamed) and
 132      *  make scanf go mupp.
 133      */
 134     if ((tmp = strrchr(buf, ')')) != NULL) {
 135         // skip the ')' and the following space but check that the buffer is long enough
 136         tmp += 2;
 137         if (tmp < buf + n) {
 138         n = vsscanf(tmp, fmt, args);
 139         }
 140     }
 141     }
 142 
 143     fclose(f);
 144 
 145     return n;
 146 }
 147 
 148 static int read_statdata(const char *procfile, const char *fmt, ...) {
 149     int       n;
 150     va_list args;
 151 
 152     va_start(args, fmt);
 153     n = vread_statdata(procfile, fmt, args);
 154     va_end(args);
 155     return n;
 156 }
 157 
 158 /** read user and system ticks from a named procfile, assumed to be in 'stat' format then. */
 159 static int read_ticks(const char *procfile, uint64_t *userTicks, uint64_t *systemTicks) {
 160     return read_statdata(procfile, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u "DEC_64" "DEC_64,
 161              userTicks, systemTicks
 162              );
 163 }
 164 
 165 /**
 166  * Return the number of ticks spent in any of the processes belonging
 167  * to the JVM on any CPU.
 168  */
 169 static int get_jvmticks(ticks *pticks) {
 170     uint64_t userTicks;
 171     uint64_t systemTicks;
 172 
 173     if (read_ticks("/proc/self/stat", &userTicks, &systemTicks) < 0) {
 174         return -1;
 175     }
 176 
 177     // get the total
 178     if (get_totalticks(-1, pticks) < 0) {
 179         return -1;
 180     }
 181 
 182     pticks->used       = userTicks;
 183     pticks->usedKernel = systemTicks;
 184 
 185     return 0;
 186 }
 187 
 188 /**
 189  * This method must be called first, before any data can be gathererd.
 190  */
 191 int perfInit() {
 192     static int initialized=1;
 193 
 194     if (!initialized) {
 195         int  i;
 196 
 197         int n = sysconf(_SC_NPROCESSORS_ONLN);
 198         if (n <= 0) {
 199             n = 1;
 200         }
 201 
 202         counters.cpus = calloc(n,sizeof(ticks));
 203         if (counters.cpus != NULL)  {
 204             // For the CPU load
 205             get_totalticks(-1, &counters.cpuTicks);
 206 
 207             for (i = 0; i < n; i++) {
 208                 get_totalticks(i, &counters.cpus[i]);
 209             }
 210             // For JVM load
 211             get_jvmticks(&counters.jvmTicks);
 212             initialized = 1;
 213         }
 214     }
 215 
 216     return initialized ? 0 : -1;
 217 }
 218 
 219 #define MAX(a,b) (a>b?a:b)
 220 #define MIN(a,b) (a<b?a:b)
 221 
 222 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
 223 
 224 /**
 225  * Return the load of the CPU as a double. 1.0 means the CPU process uses all
 226  * available time for user or system processes, 0.0 means the CPU uses all time
 227  * being idle.
 228  *
 229  * Returns a negative value if there is a problem in determining the CPU load.
 230  */
 231 
 232 static double get_cpuload_internal(int which, double *pkernelLoad, CpuLoadTarget target) {
 233     uint64_t udiff, kdiff, tdiff;
 234     ticks *pticks, tmp;
 235     double user_load = -1.0;
 236     int failed = 0;
 237 
 238     *pkernelLoad = 0.0;
 239 
 240     pthread_mutex_lock(&lock);
 241 
 242     if(perfInit() == 0) {
 243 
 244         if (target == CPU_LOAD_VM_ONLY) {
 245             pticks = &counters.jvmTicks;
 246         } else if (which == -1) {
 247             pticks = &counters.cpuTicks;
 248         } else {
 249             pticks = &counters.cpus[which];
 250         }
 251 
 252         tmp = *pticks;
 253 
 254         if (target == CPU_LOAD_VM_ONLY) {
 255             if (get_jvmticks(pticks) != 0) {
 256                 failed = 1;
 257             }
 258         } else if (get_totalticks(which, pticks) < 0) {
 259             failed = 1;
 260         }
 261 
 262         if(!failed) {
 263             // seems like we sometimes end up with less kernel ticks when
 264             // reading /proc/self/stat a second time, timing issue between cpus?
 265             if (pticks->usedKernel < tmp.usedKernel) {
 266                 kdiff = 0;
 267             } else {
 268                 kdiff = pticks->usedKernel - tmp.usedKernel;
 269             }
 270             tdiff = pticks->total - tmp.total;
 271             udiff = pticks->used - tmp.used;
 272 
 273             if (tdiff == 0) {
 274                 user_load = 0;
 275             } else {
 276                 if (tdiff < (udiff + kdiff)) {
 277                     tdiff = udiff + kdiff;
 278                 }
 279                 *pkernelLoad = (kdiff / (double)tdiff);
 280                 // BUG9044876, normalize return values to sane values
 281                 *pkernelLoad = MAX(*pkernelLoad, 0.0);
 282                 *pkernelLoad = MIN(*pkernelLoad, 1.0);
 283 
 284                 user_load = (udiff / (double)tdiff);
 285                 user_load = MAX(user_load, 0.0);
 286                 user_load = MIN(user_load, 1.0);
 287             }
 288         }
 289     }
 290     pthread_mutex_unlock(&lock);
 291     return user_load;
 292 }
 293 
 294 double get_cpu_load(int which) {
 295     double u, s;
 296     u = get_cpuload_internal(which, &s, CPU_LOAD_GLOBAL);
 297     if (u < 0) {
 298         return -1.0;
 299     }
 300     // Cap total systemload to 1.0
 301     return MIN((u + s), 1.0);
 302 }
 303 
 304 double get_process_load() {
 305     double u, s;
 306     u = get_cpuload_internal(-1, &s, CPU_LOAD_VM_ONLY);
 307     if (u < 0) {
 308         return -1.0;
 309     }
 310     return u + s;
 311 }
 312 
 313 JNIEXPORT jdouble JNICALL
 314 Java_com_sun_management_internal_OperatingSystemImpl_getSystemCpuLoad0
 315 (JNIEnv *env, jobject dummy)
 316 {
 317     if(perfInit() == 0) {
 318         return get_cpu_load(-1);
 319     } else {
 320         return -1.0;
 321     }
 322 }
 323 
 324 JNIEXPORT jdouble JNICALL
 325 Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0
 326 (JNIEnv *env, jobject dummy)
 327 {
 328     if(perfInit() == 0) {
 329         return get_process_load();
 330     } else {
 331         return -1.0;
 332     }
 333 }