001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.system.profile;
020
021import icy.system.SystemUtil;
022
023import java.lang.management.ManagementFactory;
024import java.lang.management.OperatingSystemMXBean;
025import java.lang.management.ThreadMXBean;
026import java.util.HashMap;
027import java.util.Map;
028
029/**
030 * CPU monitor class.<br>
031 * Use for profiling.
032 * 
033 * @author Nicolas HERVE
034 */
035public class CPUMonitor
036{
037    private class CPUTime
038    {
039        private long startTime;
040        private long stopTime;
041        private long startUserTime;
042        private long startCPUTime;
043        private long stopUserTime;
044        private long stopCPUTime;
045
046        public CPUTime()
047        {
048            super();
049
050            startUserTime = 0;
051            startCPUTime = 0;
052            stopUserTime = 0;
053            stopCPUTime = 0;
054            startTime = 0;
055            stopTime = 0;
056        }
057
058        public void setStartTime(long startTime)
059        {
060            this.startTime = startTime;
061            setStopTime(startTime);
062        }
063
064        public void setStopTime(long stopTime)
065        {
066            this.stopTime = stopTime;
067        }
068
069        public long getStartUserTime()
070        {
071            return startUserTime;
072        }
073
074        public void setStartUserTime(long startUserTime)
075        {
076            this.startUserTime = startUserTime;
077            setStopUserTime(startUserTime);
078        }
079
080        public void setStartCPUTime(long startCPUTime)
081        {
082            this.startCPUTime = startCPUTime;
083            setStopCPUTime(startCPUTime);
084        }
085
086        public long getStopUserTime()
087        {
088            return stopUserTime;
089        }
090
091        public void setStopUserTime(long stopUserTime)
092        {
093            this.stopUserTime = stopUserTime;
094        }
095
096        public void setStopCPUTime(long stopCPUTime)
097        {
098            this.stopCPUTime = stopCPUTime;
099        }
100
101        public long getCPUElapsedTimeNano()
102        {
103            return stopCPUTime - startCPUTime;
104        }
105
106        public long getUserElapsedTimeNano()
107        {
108            return stopUserTime - startUserTime;
109        }
110
111        public long getElapsedTimeMilli()
112        {
113            return stopTime - startTime;
114        }
115    }
116
117    public final static int MONITOR_CURRENT_THREAD = 0;
118    public final static int MONITOR_ALL_THREAD_ROUGHLY = 1;
119    public final static int MONITOR_ALL_THREAD_FINELY = 2;
120
121    private static final double NANO_TO_MILLI = 1d / 1000000d;
122    private static final double MILLI_TO_SEC = 1d / 1000d;
123    private static final double NANO_TO_SEC = NANO_TO_MILLI * MILLI_TO_SEC;
124
125    private CPUTime time;
126    private Map<Long, CPUTime> threadTimes;
127
128    private ThreadMXBean bean;
129    private OperatingSystemMXBean osBean;
130
131    private int monitorType;
132
133    public CPUMonitor()
134    {
135        this(MONITOR_CURRENT_THREAD);
136    }
137
138    public CPUMonitor(int type)
139    {
140        super();
141
142        this.monitorType = type;
143
144        time = new CPUTime();
145
146        bean = ManagementFactory.getThreadMXBean();
147        osBean = ManagementFactory.getOperatingSystemMXBean();
148    }
149
150    public void start() throws IllegalAccessError
151    {
152        if (!bean.isCurrentThreadCpuTimeSupported())
153        {
154            throw new IllegalAccessError("This JVM does not support time benchmarking");
155        }
156
157        switch (monitorType)
158        {
159            case MONITOR_CURRENT_THREAD:
160                time.setStartUserTime(bean.getCurrentThreadUserTime());
161                time.setStartCPUTime(bean.getCurrentThreadCpuTime());
162                break;
163            case MONITOR_ALL_THREAD_ROUGHLY:
164                if (!(osBean instanceof com.sun.management.OperatingSystemMXBean))
165                {
166                    throw new IllegalAccessError(
167                            "This JVM does not support this version of multiple threads time benchmarking");
168                }
169
170                time.setStartUserTime(((com.sun.management.OperatingSystemMXBean) osBean).getProcessCpuTime());
171                time.setStartCPUTime(time.getStartUserTime());
172                break;
173            case MONITOR_ALL_THREAD_FINELY:
174                threadTimes = new HashMap<Long, CPUTime>();
175                time.setStartUserTime(0);
176                time.setStartCPUTime(0);
177                long[] tids = bean.getAllThreadIds();
178                for (long id : tids)
179                {
180                    CPUTime cput = new CPUTime();
181                    cput.setStartCPUTime(bean.getThreadCpuTime(id));
182                    cput.setStartUserTime(bean.getThreadUserTime(id));
183                    threadTimes.put(id, cput);
184                }
185
186                break;
187        }
188
189        time.setStartTime(System.currentTimeMillis());
190    }
191
192    public void stop()
193    {
194        switch (monitorType)
195        {
196            case MONITOR_CURRENT_THREAD:
197                time.setStopUserTime(bean.getCurrentThreadUserTime());
198                time.setStopCPUTime(bean.getCurrentThreadCpuTime());
199                break;
200            case MONITOR_ALL_THREAD_ROUGHLY:
201                time.setStopUserTime(((com.sun.management.OperatingSystemMXBean) osBean).getProcessCpuTime());
202                time.setStopCPUTime(time.getStopUserTime());
203                break;
204            case MONITOR_ALL_THREAD_FINELY:
205                // Ignores threads that died during the monitoring
206                long[] tids = bean.getAllThreadIds();
207                long c = 0;
208                long u = 0;
209                for (long id : tids)
210                {
211                    CPUTime cput = threadTimes.get(id);
212                    if (cput == null)
213                    {
214                        cput = new CPUTime();
215                    }
216                    cput.setStopCPUTime(bean.getThreadCpuTime(id));
217                    cput.setStopUserTime(bean.getThreadUserTime(id));
218
219                    c += cput.getCPUElapsedTimeNano();
220                    u += cput.getUserElapsedTimeNano();
221                }
222                time.setStopCPUTime(c);
223                time.setStopUserTime(u);
224                break;
225        }
226        time.setStopTime(System.currentTimeMillis());
227    }
228
229    private long nanoToMilli(long nano)
230    {
231        return Math.round(nano * NANO_TO_MILLI);
232    }
233
234    private double nanoToSec(long nano)
235    {
236        return nano * NANO_TO_SEC;
237    }
238
239    private double milliToSec(long milli)
240    {
241        return milli * MILLI_TO_SEC;
242    }
243
244    public long getCPUElapsedTimeMilli()
245    {
246        return nanoToMilli(time.getCPUElapsedTimeNano());
247    }
248
249    public long getUserElapsedTimeMilli()
250    {
251        return nanoToMilli(time.getUserElapsedTimeNano());
252    }
253
254    public double getCPUElapsedTimeSec()
255    {
256        return nanoToSec(time.getCPUElapsedTimeNano());
257    }
258
259    public double getUserElapsedTimeSec()
260    {
261        return nanoToSec(time.getUserElapsedTimeNano());
262    }
263
264    public double getElapsedTimeSec()
265    {
266        return milliToSec(time.getElapsedTimeMilli());
267    }
268
269    public int getThreadCount()
270    {
271        return bean.getThreadCount();
272    }
273
274    /**
275     * Uses SystemUtil.getAvailableProcessors() instead.
276     * 
277     * @deprecated
278     */
279    @Deprecated
280    public static int getAvailableProcessors()
281    {
282        return SystemUtil.getNumberOfCPUs();
283    }
284
285    public long getElapsedTimeMilli()
286    {
287        return time.getElapsedTimeMilli();
288    }
289}