# ThreadsCpuCounter.java

    /*
    ** Module   : ThreadsCpuCounter.java
    ** Abstract : Counter for measuring CPU usage by threads
    **
    ** Copyright (c) 2021, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
    ** 001 SBI 20211104 Implemented the jmx bean for measuring CPU utilization by threads.
    */

    /*
    ** 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.io.*;
    import java.lang.management.*;
    import java.util.*;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.*;
    import java.util.function.*;

    /** Represents jmx bean for measuring CPU utilization by threads */
    public class ThreadsCpuCounter
    extends NanoCounter
    implements ThreadsCpuMBean
    {
       /** Creates long counters */
       private static final Function<Long, AtomicLong> COUNTER_CREATOR = k -> new AtomicLong(0);

       /** The management interface for the JVM thread system */
       private final ThreadMXBean jmxBean = ManagementFactory.getThreadMXBean();

       /** Total CPU times */
       private final Map<Long, AtomicLong> cpuTotals = new ConcurrentHashMap<>();

       /** Total CPU times in a user mode */
       private final Map<Long, AtomicLong> cpuUserTotals = new ConcurrentHashMap<>();

       /** Hold the current thread id for which this measurement is planned */
       private long currentThreadId;

       /** Reset counters */
       @Override
       public synchronized void reset()
       {
          currentThreadId = 0;
          cpuTotals.clear();
          cpuUserTotals.clear();
          super.reset();
       }

       /**
        * Converts nanoseconds to milliseconds
        *
        * @param    ns
        *           The given time interval in nanoseconds
        *
        * @return   The time in ms
        */
       private static long ms(long ns)
       {
          return TimeUnit.NANOSECONDS.toMillis(ns);
       }

       private StringBuilder addTheadInfo(long tid, StringBuilder buffer)
       {
          if (buffer == null)
          {
             buffer = new StringBuilder();
          }
          ThreadInfo threadInfo = jmxBean.getThreadInfo(tid);
          if (threadInfo != null)
          {
             buffer.append(threadInfo.toString());
          }
          buffer.append("Thread #").append(tid).append(":").append(" ");
          long count = getCount();
          buffer.append("CPU=").append(ms(getThreadElapsedCpuTime(tid))).append("/").append(count).append(" ");
          buffer.append("USER=").append(ms(getThreadElapsedUserTime(tid))).append("/").append(count);
          buffer.append("\n\r");

          return buffer;
       }
       /**
        * Return the string representation of the counter
        *
        * @param    ts
        *           The measurement time stamp
        * @param    comment
        *           The comment
        *
        * @return   The string representation of the counter
        *
        * @throws   IOException
        *           If IO exception occurs
        */
       public String toString(String ts, String comment)  throws IOException
       {
          StringBuilder buffer = new StringBuilder();
          buffer.append(super.toString(ts, comment));
          buffer.append("\n\r");

          if (isEnabled())
          {
             addTheadInfo(currentThreadId, buffer);

             for (Long tid : cpuTotals.keySet())
             {
                if (tid != currentThreadId)
                {
                   addTheadInfo(tid, buffer);
                }
             }
          }

          return buffer.toString();
       }

       /**
        * Gets the elapsed CPU time for the given thread.
        *
        * @param    tid
        *           The given thread id
        *
        * @return   The total CPU time in nanoseconds spend by the given thread.
        */
       @Override
       public long getThreadElapsedCpuTime(long tid)
       {
          return cpuTotals.computeIfAbsent(tid, COUNTER_CREATOR).get();
       }

       /**
        * Gets the elapsed CPU time for the given thread in a user mode.
        *
        * @param    tid
        *           The given thread id
        *
        * @return   The CPU time in nanoseconds spent by the given thread in a user mode.
        */
       @Override
       public long getThreadElapsedUserTime(long tid)
       {
          return cpuUserTotals.computeIfAbsent(tid, COUNTER_CREATOR).get();
       }

       /**
        * Updates the elapsed CPU time spent the given thread by adding the given value.
        *
        * @param    tid
        *           The given thread id
        * @param    value
        *           The given value
        */
       @Override
       public void updateThreadElapsedCpuTime(long tid, long value)
       {
          cpuTotals.computeIfAbsent(tid, COUNTER_CREATOR).addAndGet(value);
       }

       /**
        * Updates the elapsed CPU time spent the given thread in a user mode by adding the given value.
        *
        * @param    tid
        *           The given thread id
        * @param    value
        *           The given value
        */
       @Override
       public void updateThreadElapsedUserTime(long tid, long value)
       {
          cpuUserTotals.computeIfAbsent(tid, COUNTER_CREATOR).addAndGet(value);
       }

       /**
        * Sets the execution thread.
        *
        * @param    tid
        *           The thread id
        */
       @Override
       public void setCurrentThread(long tid)
       {
          currentThreadId = tid;
       }

       /**
        * Gets the elapsed CPU time for the execution thread.
        *
        * @return   The total CPU time in nanoseconds spend by the execution thread.
        */
       @Override
       public long getThreadElapsedCpuTime()
       {
          if (currentThreadId != 0)
          {
             return getThreadElapsedCpuTime(currentThreadId);
          }

          return 0;
       }

       /**
        * Gets the elapsed CPU time for the execution thread in a user mode.
        *
        * @return   The CPU time in nanoseconds spent by the execution thread in a user mode.
        */
       @Override
       public long getThreadElapsedUserTime()
       {
          if (currentThreadId != 0)
          {
             return getThreadElapsedUserTime(currentThreadId);
          }

          return 0;
       }

    }