KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > runtime > PerformanceStats


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.runtime;
12
13 import java.io.PrintWriter JavaDoc;
14 import java.util.*;
15 import org.eclipse.core.internal.runtime.InternalPlatform;
16 import org.eclipse.core.internal.runtime.PerformanceStatsProcessor;
17
18 /**
19  * PerformanceStats collects and aggregates timing data about events such as
20  * a builder running, an editor opening, etc. This data is collected for the
21  * purpose of performance analysis, and is not intended to be used as
22  * a generic event tracking and notification system.
23  * <p>
24  * Each performance event can have an associated maximum acceptable
25  * duration that is specified in the platform debug options file (.options).
26  * Events that take longer than this maximum are logged as errors. Along
27  * with option file entries for each debug event, there are some global debug
28  * options for enabling or disabling performance event gathering and reporting.
29  * See the "org.eclipse.core.runtime/perf*" debug options in the .options file
30  * for the org.eclipse.core.runtime plugin for more details.
31  * </p><p>
32  * A performance event can optionally have additional context information
33  * ({@link #getContext}). This information is only stored in the case
34  * of a performance failure, and can be used to provide further diagnostic
35  * information that can help track down the cause of the failure.
36  * </p><p>
37  * Performance events and performance failures are batched up and periodically
38  * sent to interested performance event listeners.
39  * </p><p>
40  * This class is not intended to be subclassed or instantiated by clients.
41  * </p>
42  * @since 3.1
43  */

44 public class PerformanceStats {
45     /**
46      * A performance listener is periodically notified after performance events occur
47      * or after events fail.
48      * <p>
49      * This class is intended to be subclassed.
50      * </p>
51      *
52      * @see PerformanceStats#addListener(PerformanceStats.PerformanceListener)
53      */

54     public static abstract class PerformanceListener {
55         /**
56          * Creates a new listener.
57          */

58         protected PerformanceListener() {
59             super();
60         }
61
62         /**
63          * Notifies than an event exceeded the maximum duration for that event type.
64          * <p>
65          * This default implementation does nothing. Subclasses may override.
66          * </p>
67          *
68          * @param event The event that failed
69          * @param duration The duration of the failed event, in milliseconds
70          */

71         public void eventFailed(PerformanceStats event, long duration) {
72             //default implementation does nothing
73
}
74
75         /**
76          * Notifies that an event occurred. Notification might not occur
77          * in the same thread or near the time of the actual event.
78          * <p>
79          * This default implementation does nothing. Subclasses may override.
80          * </p>
81          *
82          * @param event The event that occurred
83          */

84         public void eventsOccurred(PerformanceStats[] event) {
85             //default implementation does nothing
86
}
87     }
88
89     /**
90      * An empty stats object that is returned when tracing is turned off
91      */

92     private static final PerformanceStats EMPTY_STATS = new PerformanceStats("", ""); //$NON-NLS-1$ //$NON-NLS-2$
93

94     /**
95      * Constant indicating whether or not tracing is enabled
96      */

97     public static final boolean ENABLED;
98
99     /**
100      * A constant indicating that the timer has not been started.
101      */

102     private static final long NOT_STARTED = -1;
103
104     /**
105      * All known event statistics.
106      */

107     private final static Map statMap = Collections.synchronizedMap(new HashMap());
108
109     /**
110      * Maximum allowed durations for each event.
111      * Maps String (event name) -> Long (threshold)
112      */

113     private final static Map thresholdMap = Collections.synchronizedMap(new HashMap());
114
115     /**
116      * Whether non-failure statistics should be retained.
117      */

118     private static final boolean TRACE_SUCCESS;
119
120     /**
121      * An identifier that can be used to figure out who caused the event. This is
122      * typically a string representation of the object whose code was running when
123      * the event occurred or a <code>String</code> describing the event.
124      */

125     private String JavaDoc blame;
126
127     /**
128      * The id of the plugin that defined the blame object for this event, or
129      * <code>null</code> if it could not be determined.
130      */

131     private String JavaDoc blamePluginId;
132
133     /**
134      * An optional context for the event (may be <code>null</code>).
135      * The context can provide extra information about an event, such as the
136      * name of a project being built, or the input of an editor being opened.
137      */

138     private String JavaDoc context;
139
140     /**
141      * The starting time of the current occurrence of this event.
142      */

143     private long currentStart = NOT_STARTED;
144
145     /**
146      * The symbolic name of the event that occurred. This is usually the name of
147      * the debug option for this event.
148      */

149     private String JavaDoc event;
150
151     /**
152      * Whether this is a performance failure event
153      */

154     private boolean isFailure;
155
156     /**
157      * The total number of times this event has occurred.
158      */

159     private int runCount = 0;
160
161     /**
162      * The total time in milliseconds taken by all occurrences of this event.
163      */

164     private long runningTime = 0;
165
166     static {
167         ENABLED = InternalPlatform.getDefault().getBooleanOption(Platform.PI_RUNTIME + "/perf", false);//$NON-NLS-1$
168
//turn these on by default if the global trace flag is turned on
169
TRACE_SUCCESS = InternalPlatform.getDefault().getBooleanOption(Platform.PI_RUNTIME + "/perf/success", ENABLED); //$NON-NLS-1$
170
}
171
172     /**
173      * Adds a listener that is notified when performance events occur. If
174      * an equal listener is already installed, it will be replaced.
175      *
176      * @param listener The listener to be added
177      * @see #removeListener(PerformanceStats.PerformanceListener)
178      */

179     public static void addListener(PerformanceListener listener) {
180         if (ENABLED)
181             PerformanceStatsProcessor.addListener(listener);
182     }
183
184     /**
185      * Discards all known performance event statistics.
186      */

187     public static void clear() {
188         statMap.clear();
189     }
190
191     /**
192      * Returns all performance event statistics.
193      *
194      * @return An array of known performance event statistics. The array
195      * will be empty if there are no recorded statistics.
196      */

197     public static PerformanceStats[] getAllStats() {
198         return (PerformanceStats[]) statMap.values().toArray(new PerformanceStats[statMap.values().size()]);
199     }
200
201     /**
202      * Returns the stats object corresponding to the given parameters.
203      * A stats object is created and added to the global list of events if it did not
204      * already exist.
205      *
206      * @param eventName A symbolic event name. This is usually the name of
207      * the debug option for this event. An example event name from
208      * the org.eclipse.core.resources plugin describing a build event might look like:
209      * <code>"org.eclipse.core.resources/perf/building"</code>"
210      * @param blameObject The blame for the event. This is typically the object
211      * whose code was running when the event occurred. If a blame object cannot
212      * be obtained, a <code>String</code> describing the event should be supplied
213      */

214     public static PerformanceStats getStats(String JavaDoc eventName, Object JavaDoc blameObject) {
215         if (!ENABLED || eventName == null || blameObject == null)
216             return EMPTY_STATS;
217         PerformanceStats newStats = new PerformanceStats(eventName, blameObject);
218         if (!TRACE_SUCCESS)
219             return newStats;
220         //use existing stats object if available
221
PerformanceStats oldStats = (PerformanceStats) statMap.get(newStats);
222         if (oldStats != null)
223             return oldStats;
224         statMap.put(newStats, newStats);
225         return newStats;
226     }
227
228     /**
229      * Returns whether monitoring of a given performance event is enabled.
230      * <p>
231      * For frequent performance events, the result of this method call should
232      * be cached by the caller to minimize overhead when performance monitoring
233      * is turned off. It is not possible for enablement to change during the life
234      * of this invocation of the platform.
235      * </p>
236      *
237      * @param eventName The name of the event to determine enablement for
238      * @return <code>true</code>If the performance event with the given
239      * name is enabled, and <code>false</code> otherwise.
240      */

241     public static boolean isEnabled(String JavaDoc eventName) {
242         if (!ENABLED)
243             return false;
244         String JavaDoc option = Platform.getDebugOption(eventName);
245         return option != null && !option.equalsIgnoreCase("false") && !option.equalsIgnoreCase("-1"); //$NON-NLS-1$ //$NON-NLS-2$
246
}
247
248     /**
249      * Prints all statistics to the standard output.
250      */

251     public static void printStats() {
252         if (!ENABLED)
253             return;
254         PrintWriter JavaDoc writer = new PrintWriter JavaDoc(System.out);
255         PerformanceStatsProcessor.printStats(writer);
256         writer.flush();
257         writer.close();
258     }
259
260     /**
261      * Writes all statistics using the provided writer
262      *
263      * @param out The writer to print stats to.
264      */

265     public static void printStats(PrintWriter JavaDoc out) {
266         if (!ENABLED)
267             return;
268         PerformanceStatsProcessor.printStats(out);
269     }
270
271     /**
272      * Removes an event listener. Has no effect if an equal
273      * listener object is not currently registered.
274      *
275      * @param listener The listener to remove
276      * @see #addListener(PerformanceStats.PerformanceListener)
277      */

278     public static void removeListener(PerformanceListener listener) {
279         if (ENABLED)
280             PerformanceStatsProcessor.removeListener(listener);
281     }
282
283     /**
284      * Removes statistics for a given event and blame
285      *
286      * @param eventName The name of the event to remove
287      * @param blameObject The blame for the event to remove
288      */

289     public static void removeStats(String JavaDoc eventName, Object JavaDoc blameObject) {
290         synchronized (statMap) {
291             for (Iterator it = statMap.keySet().iterator(); it.hasNext();) {
292                 PerformanceStats stats = (PerformanceStats) it.next();
293                 if (stats.getEvent().equals(eventName) && stats.getBlame().equals(blameObject))
294                     it.remove();
295             }
296         }
297     }
298
299     /**
300      * Creates a new PerformanceStats object. Private to prevent client instantiation.
301      */

302     private PerformanceStats(String JavaDoc event, Object JavaDoc blame) {
303         this(event, blame, null);
304     }
305
306     /**
307      * Creates a new PerformanceStats object. Private to prevent client instantiation.
308      */

309     private PerformanceStats(String JavaDoc event, Object JavaDoc blameObject, String JavaDoc context) {
310         this.event = event;
311         this.blame = blameObject instanceof String JavaDoc ? (String JavaDoc) blameObject : blameObject.getClass().getName();
312         this.blamePluginId = InternalPlatform.getDefault().getBundleId(blameObject);
313         this.context = context;
314     }
315
316     /**
317      * Adds an occurrence of this event to the cumulative counters. This method
318      * can be used as an alternative to <code>startRun</code> and <code>endRun</code>
319      * for clients that want to track the context and execution time separately.
320      *
321      * @param elapsed The elapsed time of the new occurrence in milliseconds
322      * @param contextName The context for the event to return, or <code>null</code>.
323      * The context optionally provides extra information about an event, such as the
324      * name of a project being built, or the input of an editor being opened.
325      */

326     public void addRun(long elapsed, String JavaDoc contextName) {
327         if (!ENABLED)
328             return;
329         runCount++;
330         runningTime += elapsed;
331         if (elapsed > getThreshold(event))
332             PerformanceStatsProcessor.failed(createFailureStats(contextName, elapsed), blamePluginId, elapsed);
333         if (TRACE_SUCCESS)
334             PerformanceStatsProcessor.changed(this);
335     }
336
337     /**
338      * Creates a stats object representing a performance failure
339      *
340      * @param contextName The failure context information.
341      * @param elapsed The elapsed time in milliseconds
342      * @return The failure stats
343      */

344     private PerformanceStats createFailureStats(String JavaDoc contextName, long elapsed) {
345         PerformanceStats failedStat = new PerformanceStats(event, blame, contextName);
346         PerformanceStats old = (PerformanceStats) statMap.get(failedStat);
347         if (old == null)
348             statMap.put(failedStat, failedStat);
349         else
350             failedStat = old;
351         failedStat.isFailure = true;
352         failedStat.runCount++;
353         failedStat.runningTime += elapsed;
354         return failedStat;
355     }
356
357     /**
358      * Stops timing the occurrence of this event that was started by the previous
359      * call to <code>startRun</code>. The event is automatically added to
360      * the cumulative counters for this event and listeners are notified.
361      * <p>
362      * Note that this facility guards itself against runs that start but fail to stop,
363      * so it is not necessary to call this method from a finally block. Tracking
364      * performance of failure cases is generally not of interest.
365      * </p>
366      *
367      * @see #startRun()
368      */

369     public void endRun() {
370         if (!ENABLED || currentStart == NOT_STARTED)
371             return;
372         addRun(System.currentTimeMillis() - currentStart, context);
373         currentStart = NOT_STARTED;
374     }
375
376     /* (non-Javadoc)
377      * @see java.lang.Object#equals()
378      */

379     public boolean equals(Object JavaDoc obj) {
380         //count and time are not considered part of equality
381
if (!(obj instanceof PerformanceStats))
382             return false;
383         PerformanceStats that = (PerformanceStats) obj;
384         if (!this.event.equals(that.event))
385             return false;
386         if (!this.getBlameString().equals(that.getBlameString()))
387             return false;
388         return this.context == null ? that.context == null : this.context.equals(that.context);
389     }
390
391     /**
392      * Returns an object that can be used to figure out who caused the event,
393      * or a string describing the cause of the event.
394      *
395      * @return The blame for this event
396      */

397     public Object JavaDoc getBlame() {
398         return blame;
399     }
400
401     /**
402      * Returns a string describing the blame for this event.
403      *
404      * @return A string describing the blame.
405      */

406     public String JavaDoc getBlameString() {
407         return blame;
408     }
409
410     /**
411      * Returns the optional event context, such as the input of an editor, or the target project
412      * of a build event.
413      *
414      * @return The context, or <code>null</code> if there is none
415      */

416     public String JavaDoc getContext() {
417         return context;
418     }
419
420     /**
421      * Returns the symbolic name of the event that occurred.
422      *
423      * @return The name of the event.
424      */

425     public String JavaDoc getEvent() {
426         return event;
427     }
428
429     /**
430      * Returns the total number of times this event has occurred.
431      *
432      * @return The number of occurrences of this event.
433      */

434     public int getRunCount() {
435         return runCount;
436     }
437
438     /**
439      * Returns the total execution time in milliseconds for all occurrences
440      * of this event.
441      *
442      * @return The total running time in milliseconds.
443      */

444     public long getRunningTime() {
445         return runningTime;
446     }
447
448     /**
449      * Returns the performance threshold for this event.
450      */

451     private long getThreshold(String JavaDoc eventName) {
452         Long JavaDoc value = (Long JavaDoc) thresholdMap.get(eventName);
453         if (value == null) {
454             String JavaDoc option = InternalPlatform.getDefault().getOption(eventName);
455             if (option != null) {
456                 try {
457                     value = new Long JavaDoc(option);
458                 } catch (NumberFormatException JavaDoc e) {
459                     //invalid option, just ignore
460
}
461             }
462             if (value == null)
463                 value = new Long JavaDoc(Long.MAX_VALUE);
464             thresholdMap.put(eventName, value);
465         }
466         return value.longValue();
467     }
468
469     public int hashCode() {
470         //count and time are not considered part of equality
471
int hash = event.hashCode() * 37 + getBlameString().hashCode();
472         if (context != null)
473             hash = hash * 37 + context.hashCode();
474         return hash;
475     }
476
477     /**
478      * Returns whether this performance event represents a performance failure.
479      *
480      * @return <code>true</code> if this is a performance failure, and
481      * <code>false</code> otherwise.
482      */

483     public boolean isFailure() {
484         return isFailure;
485     }
486
487     /**
488      * Resets count and running time for this particular stats event.
489      */

490     public void reset() {
491         runningTime = 0;
492         runCount = 0;
493     }
494
495     /**
496      * Starts timing an occurrence of this event. This is a convenience method,
497      * fully equivalent to <code>startRun(null)</code>.
498      */

499     public void startRun() {
500         if (ENABLED)
501             startRun(null);
502     }
503
504     /**
505      * Starts timing an occurrence of this event. The event should be stopped
506      * by a subsequent call to <code>endRun</code>.
507      *
508      * @param contextName The context for the event to return, or <code>null</code>.
509      * The context optionally provides extra information about an event, such as the
510      * name of a project being built, or the input of an editor being opened.
511      * @see #endRun
512      */

513     public void startRun(String JavaDoc contextName) {
514         if (!ENABLED)
515             return;
516         this.context = contextName;
517         this.currentStart = System.currentTimeMillis();
518     }
519
520     /**
521      * For debugging purposes only.
522      */

523     public String JavaDoc toString() {
524         StringBuffer JavaDoc result = new StringBuffer JavaDoc("PerformanceStats("); //$NON-NLS-1$
525
result.append(event);
526         result.append(',');
527         result.append(blame);
528         if (context != null) {
529             result.append(',');
530             result.append(context);
531         }
532         result.append(')');
533         return result.toString();
534     }
535 }
Popular Tags