# 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);
          }
       }
    }