/* * Lokomo OneCMDB - An Open Source Software for Configuration * Management of Datacenter Resources * * Copyright (C) 2006 Lokomo Systems AB * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * * Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via * paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33 * Danderyd, Sweden. * */ package org.onecmdb.core.tests.profiler; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.Date; import java.util.Hashtable; import java.util.Stack; import java.util.Vector; /** * This static class is used for profiling of the system. The application * calls the start(String)/stop() on places where profiling is wanted. The * start()/stop() is thread sensative. That means if different threads class * start()/stop() they will not interfere with another. It is also aware of * the caller stack, that means if a application class start() start() stop() * stop() it will match the first stop to to the second start() and when the * stack of start()/stop() is empty a record is written to the profile file. * The output is generated to a file called profiler.log unless other is * specifified in the config file. */ public class Profiler { private static Hashtable entryTable = new Hashtable(); private static boolean profile = false; private static File profileFile = null; private static boolean profileError = false; private static boolean firstTime = true; /** * Check if profiling is configured to be on. */ public synchronized static boolean isOn() { return (profile); } /** * Set on/off on profiler. */ public static void useProfiler(boolean on) { profile = on; } /** * Set profile file... */ public static void setProfileFile(String file) { profileFile = new File(file); } /** * Record a start point. */ public synchronized static void start(String id) { if (!isOn() || profileError) { return; } // Allocate a new ProfileData. String threadName = Thread.currentThread().getName(); Vector v = (Vector) entryTable.get(threadName); if (v == null) { v = new Vector(); v.add(new Stack()); entryTable.put(threadName, v); } ProfileData profileData = new ProfileData(); profileData.name = id; profileData.start = System.currentTimeMillis(); profileData.startMem = Runtime.getRuntime().totalMemory(); push(v, profileData); } /** * Record a stop point. */ public synchronized static void stop() { stop(null); } /** * Record a stop point. */ public synchronized static void stop(String msg) { if (!isOn() || profileError) { return; } String threadName = Thread.currentThread().getName(); Vector v = (Vector) entryTable.get(threadName); if (v == null) { // OOps call a end without a start.... return; } pop(v, msg); } /** * Internal function to handle the stack of the start()/stop() class.. */ private synchronized static void push(Vector v, ProfileData data) { Stack s = (Stack) v.firstElement(); if (!s.isEmpty()) { ProfileData parent = (ProfileData)s.peek(); parent.calls++; } s.push(data); v.add(data); } /** * Internal function to handle the stack of the start()/stop() class.. */ private synchronized static void pop(Vector v, String msg) { Stack s = (Stack) v.firstElement(); ProfileData data = (ProfileData) s.pop(); data.stop = System.currentTimeMillis(); data.stopMem = Runtime.getRuntime().totalMemory(); if (msg != null) { data.name += ":" + msg; } if (s.isEmpty()) { logProfileData(v); // Remove from table.. String threadName = Thread.currentThread().getName(); entryTable.remove(threadName); } } /** * Internal function for writing a profile entry to a file. */ private synchronized static void logProfileData(Vector v) { // Open file file for append. if (profileFile == null) { profileFile = new File("profile.log"); } // Write entry... PrintStream outf = null; try { outf = new PrintStream(new FileOutputStream(profileFile.getPath(), profileFile.exists())); if (firstTime) { outf.println("############### Start profiling " + new Date()); outf.println("##ThreadName;Levels;" + getHeader()); firstTime = false; } outf.print(Thread.currentThread().getName() + ";" + (v.size() - 1) + ";"); for (int i = 1; i < v.size(); i++) { String prefix = ""; for (int j = 1; j < i; j++) { prefix += " "; } outf.println(prefix + (v.get(i)).toString()); } //outf.println(""); outf.close(); } catch (IOException e) { System.err.println( "Can't open profile file... " + profileFile.getPath() + ":" + e); System.err.println("No profiling will be made..."); profileError = true; } finally { // Close file... if (outf != null) { outf.close(); } } } private static String getHeader() { return ("Name;Calls;StartTime;StopTime;DeltaTime;StartMem;StopMem;DeltaMem"); } /** * Internal class to store variables. */ static class ProfileData { public String name; public long start; public long stop; public long startMem; public long stopMem; public int calls = 0; /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String toString() { return (name + ";" + calls + ";" + start + ";" + stop + ";" + (stop - start) + ";" + startMem + ";" + stopMem + ";" + (stopMem - startMem) + ";"); } } }