001    /*--------------------------------------------------------------------------+
002    $Id: PerformanceMonitor.java 26283 2010-02-18 11:18:57Z juergens $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.system;
019    
020    /**
021     * Combines a timer and a memory monitor in a simple utility class. Measures
022     * both total memory and the delta between {@link PerformanceMonitor#start()}
023     * and {@link PerformanceMonitor#stop()}.
024     * <p>
025     * In order to avoid programming mistakes, the calls to the methods
026     * {@link PerformanceMonitor#start()} and {@link PerformanceMonitor#stop()} must
027     * adhere to a simple protocol:<br>
028     * The {@link PerformanceMonitor} can be in on of the three states NOT_RUN,
029     * RUNNING, FINISHED.
030     * <p>
031     * {@link PerformanceMonitor#start()} May only be called in state NOT_RUN and
032     * {@link PerformanceMonitor#stop()} may only be called in state RUNNING.
033     * <p>
034     * All other calls to {@link PerformanceMonitor#start()} and
035     * {@link PerformanceMonitor#stop()} result in {@link IllegalArgumentException}s
036     * 
037     * @author Elmar Juergens
038     * @author $Author: juergens $
039     * 
040     * @version $Revision: 26283 $
041     * @levd.rating GREEN Hash: 11F99E9698215A2906756FE003D9D30A
042     */
043    public class PerformanceMonitor {
044    
045            /** The state the {@link PerformanceMonitor} is currently in */
046            private EPerformanceMonitorState state = EPerformanceMonitorState.NOT_RUN;
047    
048            /**
049             * Timestamp the call to the {@link PerformanceMonitor#start()} method
050             * occurred
051             */
052            private long startTimeInMillis;
053    
054            /**
055             * Timestamp the call to the {@link PerformanceMonitor#stop()} method
056             * occurred
057             */
058            private long stopTimeInMillis;
059    
060            /** {@link MemoryMonitor} used to measure total memory consumption */
061            private MemoryMonitor memMonitor;
062    
063            /**
064             * Flag that determines whether a memory monitor (that uses its own thread)
065             * should be used to get a more exact measurement of maximum memory
066             * consumption.
067             */
068            private final boolean useMemoryMonitor;
069    
070            /**
071             * Memory consumption when the call to {@link PerformanceMonitor#start()}
072             * occurred.
073             */
074            private long startMemoryInBytes;
075    
076            /**
077             * Memory consumption when the call to {@link PerformanceMonitor#stop()}
078             * occurred.
079             */
080            private long stopMemoryInBytes;
081    
082            /**
083             * Convenience factory method: Creates a new {@link PerformanceMonitor} and
084             * starts it.
085             * 
086             * @param useMemoryMonitor
087             *            detemines whether the PerformanceMonitor internally uses a
088             *            {@link MemoryMonitor} to measure memory consumption. This
089             *            requires more resources, since the {@link MemoryMonitor} runs
090             *            its own thread, but promises more precise measurement of
091             *            maximum memory consumption.
092             */
093            public static PerformanceMonitor create(boolean useMemoryMonitor) {
094                    PerformanceMonitor monitor = new PerformanceMonitor(useMemoryMonitor);
095                    monitor.start();
096                    return monitor;
097            }
098    
099            /**
100             * Convenience factory method: Creates a new {@link PerformanceMonitor} and
101             * starts it. PerformanceMonitor does not use a MemoryMonitor internally
102             */
103            public static PerformanceMonitor create() {
104                    return create(false);
105            }
106    
107            /**
108             * Constructor has package level to allow tests to access it, yet enforce
109             * use of factory methods for public use.
110             */
111            /* package */PerformanceMonitor(boolean useMemoryMonitor) {
112                    this.useMemoryMonitor = useMemoryMonitor;
113            }
114    
115            /**
116             * Starts the {@link PerformanceMonitor}. It will measure time and maximal
117             * memory consumption until the method {@link PerformanceMonitor#stop()} is
118             * called.
119             * <p>
120             * This method may only be called, if the {@link PerformanceMonitor} is in
121             * state NOT_RUN. (i.e., after it has been created).
122             * <p>
123             * All subsequent calls to this method will result in a
124             * {@link IllegalStateException}
125             */
126            public void start() {
127                    if (state != EPerformanceMonitorState.NOT_RUN) {
128                            throw new IllegalStateException(
129                                            "PerformanceMonitor is already running and cannot be restarted");
130                    }
131    
132                    state = EPerformanceMonitorState.RUNNING;
133                    if (useMemoryMonitor) {
134                            memMonitor = new MemoryMonitor();
135                            memMonitor.start();
136                    }
137                    startMemoryInBytes = Runtime.getRuntime().totalMemory();
138                    startTimeInMillis = System.currentTimeMillis();
139            }
140    
141            /**
142             * Stops the {@link PerformanceMonitor}.
143             * <p>
144             * This method may only be called, if the {@link PerformanceMonitor} is in
145             * state RUNNING. (i.e., after a call to {@link PerformanceMonitor#start()}).
146             * <p>
147             * If the {@link PerformanceMonitor} is in any other state, a call to this
148             * method results in an {@link IllegalStateException}
149             */
150            public void stop() {
151                    if (state != EPerformanceMonitorState.RUNNING) {
152                            throw new IllegalStateException(
153                                            "PerformanceMonitor can only be stopped if it is running");
154                    }
155    
156                    stopTimeInMillis = System.currentTimeMillis();
157                    if (useMemoryMonitor) {
158                            memMonitor.stop();
159                    }
160                    stopMemoryInBytes = Runtime.getRuntime().totalMemory();
161                    state = EPerformanceMonitorState.FINISHED;
162            }
163    
164            /** Gets the measured time in seconds. (Fractions of seconds are discarded) */
165            public long getSeconds() {
166                    return getMilliseconds() / 1000;
167            }
168    
169            /** Gets the measured time in milliseconds */
170            public long getMilliseconds() {
171                    return stopTimeInMillis - startTimeInMillis;
172            }
173    
174            /** Gets the maximal memory consumption in bytes */
175            public long getMaxMemUsageInBytes() {
176                    if (useMemoryMonitor) {
177                            return memMonitor.getMaximumMemoryUsage();
178                    }
179                    return Math.max(stopMemoryInBytes, startMemoryInBytes);
180            }
181    
182            /** Gets the maximal memory consumption in kilobytes */
183            public long getMaxMemUsageInKBs() {
184                    return getMaxMemUsageInBytes() / 1024;
185            }
186    
187            /** Gets the delta in memory consumption in bytes */
188            public long getDeltaMemUsageInBytes() {
189                    return getMaxMemUsageInBytes() - startMemoryInBytes;
190            }
191    
192            /** Gets the delta in memory consumption in kilobytes */
193            public long getDeltaMemUsageInKBs() {
194                    return getDeltaMemUsageInBytes() / 1024;
195            }
196    
197    }