001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.utils;
031:
032: import java.awt.*;
033: import java.io.*;
034: import java.text.SimpleDateFormat;
035: import java.util.*;
036: import java.util.List;
037:
038: import javax.swing.UIDefaults;
039: import javax.swing.UIManager;
040:
041: import org.jvnet.lafwidget.utils.TrackableThread;
042: import org.jvnet.substance.*;
043: import org.jvnet.substance.painter.highlight.ClassicHighlightPainter;
044:
045: /**
046: * Tracer for memory usage patterns of <b>Substance</b> look-and-feel. The
047: * tracer is started when VM has <code>-Dsubstancelaf.traceFile</code> flag.
048: * The value of this flag specifies the location of trace log file. When
049: * activated, the tracer runs a thread that collects information on memory usage
050: * and appends it to the trace log file every <code>X</code> seconds. The
051: * <code>X</code> (delay) is specified in the constructor. This class is
052: * <b>for internal use only</b>.
053: *
054: * @author Kirill Grouchnikov
055: */
056: public class MemoryAnalyzer extends TrackableThread {
057: /**
058: * Sleep delay between trace log iterations.
059: */
060: private long delay;
061:
062: /**
063: * Trace logfile name.
064: */
065: private String filename;
066:
067: /**
068: * Singleton instance.
069: */
070: private static MemoryAnalyzer instance;
071:
072: /**
073: * If <code>true</code>, <code>this</code> tracer has received a
074: * request to stop.
075: */
076: private static boolean isStopRequest = false;
077:
078: /**
079: * Usage strings collected during the sleep time.
080: */
081: private static ArrayList<String> usages;
082:
083: /**
084: * Formatting object.
085: */
086: private static SimpleDateFormat sdf;
087:
088: /**
089: * Simple constructor.
090: *
091: * @param delay
092: * Sleep delay between trace log iterations.
093: * @param filename
094: * Trace logfile name.
095: */
096: private MemoryAnalyzer(long delay, String filename) {
097: super ();
098: this .delay = delay;
099: this .filename = filename;
100: this .setName("Substance memory analyzer");
101: }
102:
103: /**
104: * Starts the memory tracing.
105: *
106: * @param delay
107: * Sleep delay between trace log iterations.
108: * @param filename
109: * Trace logfile name.
110: */
111: public static synchronized void commence(long delay, String filename) {
112: if (instance == null) {
113: instance = new MemoryAnalyzer(delay, filename);
114: usages = new ArrayList<String>();
115: // yeah, yeah, it's not multi-thread safe.
116: sdf = new SimpleDateFormat("HH:mm:ss.SSS");
117: instance.start();
118: }
119: }
120:
121: /**
122: * Issues request to stop tracing.
123: */
124: @Override
125: public synchronized void requestStop() {
126: isStopRequest = true;
127: }
128:
129: /**
130: * Checks whether a request to stop tracing has been issued.
131: *
132: * @return <code>true</code> if a request to stop tracing has been issued,
133: * <code>false</code> otherwise.
134: */
135: private static synchronized boolean hasStopRequest() {
136: return isStopRequest;
137: }
138:
139: /**
140: * Checks whether tracer is running.
141: *
142: * @return <code>true</code> if tracer is running, <code>false</code>
143: * otherwise.
144: */
145: public static boolean isRunning() {
146: return (instance != null);
147: }
148:
149: /**
150: * Adds usage string.
151: *
152: * @param usage
153: * Usage string. Will be output to the trace file at next
154: * iteration of the tracer.
155: */
156: public static synchronized void enqueueUsage(String usage) {
157: if (instance != null) {
158: usages.add(sdf.format(new Date()) + ": " + usage);
159: }
160: }
161:
162: /**
163: * Returns all queued usages.
164: *
165: * @return All queued usages.
166: */
167: public static synchronized ArrayList<String> getUsages() {
168: ArrayList<String> copy = new ArrayList<String>();
169: for (String usage : usages)
170: copy.add(usage);
171: usages.clear();
172: return copy;
173: }
174:
175: /*
176: * (non-Javadoc)
177: *
178: * @see java.lang.Runnable#run()
179: */
180: @Override
181: public void run() {
182: // output all settings from UIManager
183: BufferedWriter bw = null;
184: try {
185: bw = new BufferedWriter(new FileWriter(new File(
186: this .filename), true));
187: bw.write(sdf.format(new Date()) + "\n");
188:
189: UIDefaults uidefs = UIManager.getLookAndFeel()
190: .getDefaults();
191: // Retrieve the keys. Can't use an iterator since the map
192: // may be modified during the iteration. So retrieve all at
193: // once.
194: Set<Object> keySet = uidefs.keySet();
195: List<String> keyList = new LinkedList<String>();
196: for (Object key : keySet) {
197: keyList.add((String) key);
198: }
199: Collections.sort(keyList);
200:
201: for (String key : keyList) {
202: Object v = uidefs.get(key);
203:
204: if (v instanceof Integer) {
205: int intVal = uidefs.getInt(key);
206: bw.write(key + " (int) : " + intVal);
207: } else if (v instanceof Boolean) {
208: boolean boolVal = uidefs.getBoolean(key);
209: bw.write(key + " (bool) : " + boolVal);
210: } else if (v instanceof String) {
211: String strVal = uidefs.getString(key);
212: bw.write(key + " (string) : " + strVal);
213: } else if (v instanceof Dimension) {
214: Dimension dimVal = uidefs.getDimension(key);
215: bw.write(key + " (Dimension) : " + dimVal.width
216: + "*" + dimVal.height);
217: } else if (v instanceof Insets) {
218: Insets insetsVal = uidefs.getInsets(key);
219: bw.write(key + " (Insets) : " + insetsVal.top + "*"
220: + insetsVal.left + "*" + insetsVal.bottom
221: + "*" + insetsVal.right);
222: } else if (v instanceof Color) {
223: Color colorVal = uidefs.getColor(key);
224: bw.write(key + " (int) : " + colorVal.getRed()
225: + "," + colorVal.getGreen() + ","
226: + colorVal.getBlue());
227: } else if (v instanceof Font) {
228: Font fontVal = uidefs.getFont(key);
229: bw.write(key + " (Font) : " + fontVal.getFontName()
230: + "*" + fontVal.getSize());
231: } else {
232: bw.write(key + " (Object) : " + uidefs.get(key));
233: }
234: bw.write("\n");
235: }
236: } catch (IOException ioe) {
237: this .requestStop();
238: } catch (Throwable t) {
239: } finally {
240: if (bw != null) {
241: try {
242: bw.close();
243: } catch (Exception exc) {
244: }
245: }
246: }
247: while (!hasStopRequest()) {
248: // gather statistics and print them to file
249: bw = null;
250: try {
251: bw = new BufferedWriter(new FileWriter(new File(
252: this .filename), true));
253: bw.write(sdf.format(new Date()) + "\n");
254: bw.write(PulseTracker.getMemoryUsage() + "\n");
255: // bw.write(RolloverButtonListener.getMemoryUsage() + "\n");
256: // bw.write(RolloverScrollBarButtonListener.getMemoryUsage()
257: // + "\n");
258: bw.write(ButtonBackgroundDelegate.getMemoryUsage()
259: + "\n");
260: bw.write(SubstanceCheckBoxUI.getMemoryUsage() + "\n");
261: // bw.write(SubstanceComboBoxUI.getMemoryUsage() + "\n");
262: bw
263: .write(SubstanceProgressBarUI.getMemoryUsage()
264: + "\n");
265: bw
266: .write(SubstanceRadioButtonUI.getMemoryUsage()
267: + "\n");
268: // bw.write(SubstanceRootPaneUI.getMemoryUsage() + "\n");
269: bw.write(SubstanceScrollBarUI.getMemoryUsage() + "\n");
270: bw.write(SubstanceTabbedPaneUI.getMemoryUsage() + "\n");
271: bw.write(ClassicHighlightPainter.getMemoryUsage()
272: + "\n");
273: ArrayList<String> usages = getUsages();
274: for (String usage : usages) {
275: bw.write(usage + "\n");
276: }
277: bw
278: .write("UIManager has "
279: + UIManager.getDefaults().size()
280: + " entries\n");
281: long heapSize = Runtime.getRuntime().totalMemory();
282: long heapFreeSize = Runtime.getRuntime().freeMemory();
283:
284: int heapSizeKB = (int) (heapSize / 1024);
285: int takenHeapSizeKB = (int) ((heapSize - heapFreeSize) / 1024);
286: bw.write("Heap : " + takenHeapSizeKB + " / "
287: + heapSizeKB);
288: bw.write("\n");
289: } catch (IOException ioe) {
290: this .requestStop();
291: } finally {
292: if (bw != null) {
293: try {
294: bw.close();
295: } catch (Exception exc) {
296: }
297: }
298: }
299:
300: // sleep
301: try {
302: sleep(this .delay);
303: } catch (InterruptedException ie) {
304: }
305: }
306: }
307: }
|