ThreadsCpuTimer.java

/*
** Module   : ThreadsCpuTimer.java
** Abstract : It extends NanoTimer for measuring CPU utilization of threads.
**
** Copyright (c) 2021, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 SBI 20211104 Implemented jmx bean for measuring CPU utilization of threads.
**     RFB 20211117 Minor javadoc error corrected.
*/ 

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.jmx;

import java.lang.management.*;
import java.util.*;
import java.util.function.*;

/** Represents threads CPU timer for measuring CPU utilization of threads. */
public class ThreadsCpuTimer 
extends NanoTimer 
{
   /** The management interface for the JVM thread system */
   private final ThreadMXBean jmxBean = ManagementFactory.getThreadMXBean();
   /** The flag that indicates that this JVM supports threads monitoring capabilities */
   private final boolean threadsMonitoringSupported = jmxBean.isThreadCpuTimeSupported();
   /** Holds the initial points for CPU utilization measurements of threads */ 
   private final Map<Long, Long> cpuStartTimes = new LinkedHashMap<>();
   /** Holds the initial points for CPU utilization measurements of threads in a user mode*/ 
   private final Map<Long, Long> userStartTimes = new LinkedHashMap<>();
   /** Holds the current threads at the time of this measurement */
   private long[] threadIds;
   /** Holds the current thread id used for this measurement */
   private long currentThreadId;
   /** Holds a counter for storing the measurement results */
   private final ThreadsCpuMBean counter;
   
   /**
    * Create and register its instance.
    * 
    * @param    counter
    *           The instance of NanoCounter
    * 
    * @return   Threads CPU timer
    */
   public static ThreadsCpuTimer getInstance(ThreadsCpuMBean counter) 
   {
       ThreadsCpuTimer timer = new ThreadsCpuTimer(counter, counter::update);
       timer.setEnabler(timer::setEnabled); //prevent GC
       counter.register(timer.getEnabler());
       return timer;
   }

   /**
    * Creates this thread CPU timer.
    * 
    * @param    counter
    *           The threads CPU counter jmx interface
    * @param    publish
    *           The consumer of NanoTimer measurement results
    */
   protected ThreadsCpuTimer(ThreadsCpuMBean counter, LongConsumer publish) 
   {  
       super(publish);
       this.counter = counter;
   }
   
   /**
    * Start measurement operation.
    */
   public void start()
   {
       super.start();
       
       if (threadsMonitoringSupported)
       {
          try
          {
             threadIds = jmxBean.getAllThreadIds();
          }
          catch(SecurityException ex)
          {
             
          }
          
          if (threadIds == null)
          {
             return;
          }
          
          currentThreadId = Thread.currentThread().getId();
          counter.setCurrentThread(currentThreadId);
          cpuStartTimes.put(currentThreadId, jmxBean.getThreadCpuTime(currentThreadId));
          userStartTimes.put(currentThreadId, jmxBean.getThreadUserTime(currentThreadId));
          
          for (int i = 0; i < threadIds.length; i++)
          {
             long tid = threadIds[i];
             if (tid != currentThreadId)
             {
                cpuStartTimes.put(tid, jmxBean.getThreadCpuTime(tid));
                userStartTimes.put(tid, jmxBean.getThreadUserTime(tid));
             }
          }
       }
   }
   
   /** Stop operation and update counter */
   public void stop()
   {
       super.stop();
       
       if (threadsMonitoringSupported && (threadIds != null))
       {
          counter.updateThreadElapsedCpuTime( currentThreadId, (jmxBean.getThreadCpuTime(currentThreadId)  - cpuStartTimes.get(currentThreadId)));
          counter.updateThreadElapsedUserTime(currentThreadId, (jmxBean.getThreadUserTime(currentThreadId) - userStartTimes.get(currentThreadId)));
          for (int i = 0; i < threadIds.length; i++)
          {
             long tid = threadIds[i];
             if (tid != currentThreadId)
             {
                counter.updateThreadElapsedCpuTime( tid, (jmxBean.getThreadCpuTime(tid)  - cpuStartTimes.get(tid)));
                counter.updateThreadElapsedUserTime(tid, (jmxBean.getThreadUserTime(tid) - userStartTimes.get(tid)));
             }
          }
       }
   }

   /**
    * Enables or disables thread CPU time measurement.
    * 
    * @param    enable
    *           The boolean value for enabling or disabling thread CPU time measurement.
    */
   @Override
   public void setEnabled(boolean enable)
   {
      super.setEnabled(enable);
      
      if (jmxBean.isThreadCpuTimeSupported())
      {
         jmxBean.setThreadCpuTimeEnabled(enable);
      }
   }
}