/******************************************************************************* * Copyright (c) 2005, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.osgi.internal.profile; import java.io.*; import java.util.*; import org.eclipse.osgi.framework.debug.FrameworkDebugOptions; import org.eclipse.osgi.framework.internal.core.FrameworkProperties; public class DefaultProfileLogger implements ProfileLogger { protected static final String DEFAULTPROFILE_PROP = "osgi.defaultprofile."; //$NON-NLS-1$ protected static final String PROP_FILENAME = DEFAULTPROFILE_PROP + "logfilename"; //$NON-NLS-1$ protected static final String PROP_LOGSYNCHRONOUSLY = DEFAULTPROFILE_PROP + "logsynchronously"; //$NON-NLS-1$ protected static final String PROP_BUFFERSIZE = DEFAULTPROFILE_PROP + "buffersize"; //$NON-NLS-1$ protected static final String DEFAULTPROFILE_OPTION = "org.eclipse.osgi/defaultprofile/"; //$NON-NLS-1$ protected static final String OPTION_FILENAME = DEFAULTPROFILE_OPTION + "logfilename"; //$NON-NLS-1$ protected static final String OPTION_LOGSYNCHRONOUSLY = DEFAULTPROFILE_OPTION + "logsynchronously"; //$NON-NLS-1$ protected static final String OPTION_BUFFERSIZE = DEFAULTPROFILE_OPTION + "buffersize"; //$NON-NLS-1$ protected boolean logSynchronously = false; protected long startTime = 0; protected static final int DEFAULT_BUFFER_SIZE = 256; protected TimeEntry[] timeLogEntries = null; protected int timeEntriesIndex = 0; protected StringBuffer timelog = null; protected long launchTime = -1; protected int bufferSize = DEFAULT_BUFFER_SIZE; protected String logFileName = null; protected File logFile = null; private StringBuffer entryReport = new StringBuffer(120); private StringBuffer padsb = new StringBuffer(16); // to prevent creating this over and over protected int indent; protected int timePaddingLength; protected Stack<AccumPerfScope> scopeStack; protected Map<String, AccumPerfData> scopeToAccumPerfDataMap; public DefaultProfileLogger() { initProps(); int size = getBufferSize(); timeLogEntries = new TimeEntry[size]; timelog = new StringBuffer(4096); for (int i = 0; i < size; i++) { timeLogEntries[i] = timeEntryFactory(); } timeEntriesIndex = 0; launchTime = getLaunchTime(); if (launchTime == -1) { startTime = getMainStartTime(); } else { startTime = launchTime; } long freq = getTimerFrequency(); for (timePaddingLength = 3; freq > 9; timePaddingLength++) { freq /= 10; } logInitMessages(); } protected void logInitMessages() { int index = 0; if (launchTime != -1L) { logTime(Profile.FLAG_NONE, "DefaultProfileLogger.init()", "launch time initialized", null); //$NON-NLS-1$//$NON-NLS-2$ timeLogEntries[index++].time = launchTime; } logTime(Profile.FLAG_NONE, "DefaultProfileLogger.init()", "start time initialized", null); //$NON-NLS-1$//$NON-NLS-2$ timeLogEntries[index++].time = getMainStartTime(); } protected long getLaunchTime() { String launchTimeString = FrameworkProperties.getProperty("launch.startMillis"); //$NON-NLS-1$ if (launchTimeString != null) { return Long.parseLong(launchTimeString); } return -1L; } protected long getMainStartTime() { String timeString = FrameworkProperties.getProperty("eclipse.startTime"); //$NON-NLS-1$ if (timeString != null) return Long.parseLong(timeString); return System.currentTimeMillis(); } public void initProps() { String prop; FrameworkDebugOptions dbgOptions = null; // if osgi.debug is not available, don't force DebugOptions // to init as this variable may be set later on where // DebugOptions will succeed. if (FrameworkProperties.getProperty("osgi.debug") != null) { //$NON-NLS-1$ dbgOptions = FrameworkDebugOptions.getDefault(); if (dbgOptions != null) { logFileName = dbgOptions.getOption(OPTION_FILENAME); logSynchronously = dbgOptions.getBooleanOption(OPTION_LOGSYNCHRONOUSLY, false); int size = dbgOptions.getIntegerOption(OPTION_BUFFERSIZE, 0); if (size > 0) bufferSize = size; } } if ((prop = FrameworkProperties.getProperty(PROP_FILENAME)) != null) { logFileName = prop; if (dbgOptions != null) dbgOptions.setOption(OPTION_FILENAME, logFileName); } if ((prop = FrameworkProperties.getProperty(PROP_LOGSYNCHRONOUSLY)) != null) { logSynchronously = Boolean.valueOf(prop).booleanValue(); if (dbgOptions != null) dbgOptions.setOption(OPTION_LOGSYNCHRONOUSLY, new Boolean(logSynchronously).toString()); } if ((prop = FrameworkProperties.getProperty(PROP_BUFFERSIZE)) != null) { try { int value = Integer.parseInt(prop); if (value > 0) { bufferSize = value; if (dbgOptions != null) dbgOptions.setOption(OPTION_BUFFERSIZE, Integer.toString(bufferSize)); } } catch (NumberFormatException e) { // do nothing } } } public synchronized void logTime(int flag, String id, String msg, String description) { if (timeEntriesIndex == timeLogEntries.length) { makeLog(); logTime(Profile.FLAG_NONE, "Profile.logTime()", "log entries rolled", null); //$NON-NLS-1$ //$NON-NLS-2$ } TimeEntry entry = timeLogEntries[timeEntriesIndex++]; entry.time = getTime(); entry.id = id; entry.msg = msg; entry.flag = flag; entry.description = description; if (logSynchronously) { System.out.print(getProfileLog().substring(2)); } } public synchronized String getProfileLog() { String log; log = getProfileLogReport(); writeToProfileLogFile(log); return log; } public synchronized void accumLogEnter(String scope) { // Initialize our data structures if (scopeStack == null) scopeStack = new Stack<AccumPerfScope>(); if (scopeToAccumPerfDataMap == null) scopeToAccumPerfDataMap = new TreeMap<String, AccumPerfData>(); // We want getTime() to evaluate as late as possible scopeStack.push(new AccumPerfScope(scope, getTime())); } public synchronized void accumLogExit(String scope) { // What time is it? long exit = getTime(); // Initialize our data structures if (scopeStack == null) scopeStack = new Stack<AccumPerfScope>(); if (scopeToAccumPerfDataMap == null) scopeToAccumPerfDataMap = new TreeMap<String, AccumPerfData>(); // Do our calculations AccumPerfScope then = scopeStack.pop(); if (then == null) System.err.println("ACCUM PERF ERROR: Scope stack empty: " + scope); //$NON-NLS-1$ else { if (!then.scope.equals(scope)) System.err.println("ACCUM PERF ERROR: Scope mismatch: then='" + then.scope + "', now='" + scope + "'"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ AccumPerfData now = scopeToAccumPerfDataMap.get(scope); if (now == null) { now = new AccumPerfData(scope); scopeToAccumPerfDataMap.put(scope, now); } now.time += exit - then.enter; now.enters++; } } protected long getTime() { return System.currentTimeMillis(); } protected long getTimerFrequency() { return 1000L; // millisecond } protected TimeEntry findCompareEntry(int index, String id, int flag) { if (index > 0) index--; int prev = index; if (flag != Profile.FLAG_ENTER) { while (index >= 0) { TimeEntry entry = timeLogEntries[index]; if (entry.id.equals(id)) { switch (flag) { case Profile.FLAG_NONE : return entry; case Profile.FLAG_EXIT : if (entry.flag == Profile.FLAG_ENTER) return entry; break; } } index--; } } return timeLogEntries[prev]; } protected String entryReport(TimeEntry entry, TimeEntry compareWith) { // indent level: entryReport.setLength(0); if (entry.flag == Profile.FLAG_ENTER) indent++; long zeroTime = getRelativeTime(getStartTime()); entryReport.append('-'); long entryTime = getRelativeTime(entry.time); long diff = entryTime - zeroTime; entryReport.append(pad(Long.toString(diff), timePaddingLength)); entryReport.append(" :"); //$NON-NLS-1$ diff = entry.time - compareWith.time; entryReport.append(pad(Long.toString(diff), timePaddingLength)); entryReport.append(pad("", indent * 2)); // indent before displaying the entry.id //$NON-NLS-1$ if (entry.flag == Profile.FLAG_ENTER) entryReport.append(" >> "); //$NON-NLS-1$ else if (entry.flag == Profile.FLAG_EXIT) entryReport.append(" << "); //$NON-NLS-1$ else if (entry.flag == Profile.FLAG_NONE) entryReport.append(" -- "); //$NON-NLS-1$ entryReport.append(entry.id); entryReport.append(" > "); //$NON-NLS-1$ entryReport.append(entry.msg); if (entry.description != null) { entryReport.append(" :: "); //$NON-NLS-1$ entryReport.append(entry.description); } entryReport.append("\r\n"); //$NON-NLS-1$ if (entry.flag == Profile.FLAG_EXIT) indent -= 1; return entryReport.toString(); } protected String accumEntryReport(AccumPerfData d) { return (" " + d.scope + ":enters=" + d.enters + ";time=" + d.time + ";\r\n"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ } protected void makeLog() { indent = 0; timelog.append("\r\n"); //$NON-NLS-1$ for (int i = 0; i < timeEntriesIndex; i++) { TimeEntry entry = timeLogEntries[i]; TimeEntry cmpEntry = findCompareEntry(i, entry.id, entry.flag); timelog.append(entryReport(entry, cmpEntry)); } timeEntriesIndex = 0; if (scopeToAccumPerfDataMap == null || scopeToAccumPerfDataMap.isEmpty()) return; // No data; nothing to do timelog.append("\r\n"); //$NON-NLS-1$ timelog.append("Cumulative Log:\r\n"); //$NON-NLS-1$ for (AccumPerfData d : scopeToAccumPerfDataMap.values()) { timelog.append(accumEntryReport(d)); } scopeToAccumPerfDataMap.clear(); } protected String pad(String str, int size) { padsb.setLength(0); int len = str.length(); int count = size - len; for (int i = 0; i < count; i++) padsb.append(' '); padsb.append(str); return padsb.toString(); } protected String getProfileLogReport() { if (timelog == null) return ""; //$NON-NLS-1$ makeLog(); String log = timelog.toString(); timelog.setLength(0); return log; } protected void writeToProfileLogFile(String log) { File profileLog = getProfileLogFile(); if (profileLog == null) return; FileWriter fw = null; try { fw = new FileWriter(profileLog.getAbsolutePath(), true); fw.write(log); } catch (IOException e) { e.printStackTrace(); } finally { if (fw != null) try { fw.close(); } catch (IOException e) { // do nothing } } } protected File getProfileLogFile() { if (logFile == null) if ((logFileName != null) && (logFileName.length() > 0)) logFile = new File(logFileName); return logFile; } protected long getStartTime() { return startTime; } protected long getRelativeTime(long absoluteTime) { return absoluteTime; } protected int getBufferSize() { if (bufferSize < 2) return DEFAULT_BUFFER_SIZE; return bufferSize; } protected TimeEntry timeEntryFactory() { return new TimeEntry(); } protected class TimeEntry { public long time; public String id; public String msg; public String description; public int flag; } protected static class AccumPerfData { public AccumPerfData(String scope) { this.scope = scope; } public String scope; public long time; public long enters; } protected static class AccumPerfScope { public AccumPerfScope(String scope, long enter) { this.scope = scope; this.enter = enter; } public String scope; public long enter; } }