blob: 60975e598c4521c50747237d3fd4d23e1f0ff0a4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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.core.runtime.internal.stats;
import java.io.*;
import java.util.*;
/**
* Contains information about the classes and the bundles loaded by a given classloader. Typically there is one classloader per plugin so at levels above boot, this equates to information about
* classes and bundles in a plugin.
*/
public class ClassloaderStats {
private String id;
private long loadingTime; // time spent loading classes
private int failureCount = 0; // number of classes requested but that we fail to provide
/**
* classes loaded by the plugin (key: class name, value: ClassStats)
*/
private Map classes = Collections.synchronizedMap(new HashMap(20));
private ArrayList bundles = new ArrayList(2); // bundles loaded
private boolean keepTraces = false; // indicate whether or not the traces of classes loaded are kept
// filters to indicate which classes we want to keep the traces
private static ArrayList packageFilters = new ArrayList(4); // filters on a package basis
private static Set pluginFilters = new HashSet(5); // filters on a plugin basis
private static Hashtable classStacks = new Hashtable(); // represents the classes that are currently being loaded
/**
* a dictionary of the classloaderStats (key: pluginId, value: ClassloaderStats)
*/
private static Map loaders = Collections.synchronizedMap(new HashMap(20));
public static File traceFile;
static {
if (StatsManager.TRACE_CLASSES || StatsManager.TRACE_BUNDLES)
initializeTraceOptions();
}
private static void initializeTraceOptions() {
// create the trace file
String filename = StatsManager.TRACE_FILENAME;
traceFile = new File(filename);
traceFile.delete();
//load the filters
if (!StatsManager.TRACE_CLASSES)
return;
filename = StatsManager.TRACE_FILTERS;
if (filename == null || filename.length() == 0)
return;
try {
File filterFile = new File(filename);
System.out.print("Runtime tracing elements defined in: " + filterFile.getAbsolutePath() + "..."); //$NON-NLS-1$ //$NON-NLS-2$
InputStream input = new FileInputStream(filterFile);
System.out.println(" Loaded."); //$NON-NLS-1$
Properties filters = new Properties() {
private static final long serialVersionUID = 3546359543853365296L;
public Object put(Object key, Object value) {
addFilters((String) key, (String) value);
return null;
}
};
try {
filters.load(input);
} finally {
input.close();
}
} catch (IOException e) {
System.out.println(" No trace filters loaded."); //$NON-NLS-1$
}
}
protected static void addFilters(String key, String value) {
String[] filters = StatsManager.getArrayFromList(value);
if ("plugins".equals(key)) //$NON-NLS-1$
pluginFilters.addAll(Arrays.asList(filters));
if ("packages".equals(key)) //$NON-NLS-1$
packageFilters.addAll(Arrays.asList(filters));
}
public static void startLoadingClass(String id, String className) {
findLoader(id).startLoadClass(className);
}
// get and create if does not exist
private static ClassloaderStats findLoader(String id) {
synchronized (loaders) {
ClassloaderStats result = (ClassloaderStats) loaders.get(id);
if (result == null) {
result = new ClassloaderStats(id);
loaders.put(id, result);
}
return result;
}
}
public static synchronized Stack getClassStack() {
Stack result = (Stack) classStacks.get(Thread.currentThread());
if (result == null) {
result = new Stack();
classStacks.put(Thread.currentThread(), result);
}
return result;
}
public static ClassloaderStats[] getLoaders() {
//the parameter to toArray is of size zero for thread safety, otherwise this
//could return an array with null entries if the map shrinks concurrently
return (ClassloaderStats[]) loaders.values().toArray(new ClassloaderStats[0]);
}
public static void endLoadingClass(String id, String className, boolean success) {
findLoader(id).endLoadClass(className, success);
}
public static void loadedBundle(String id, ResourceBundleStats info) {
findLoader(id).loadedBundle(info);
}
public static ClassloaderStats getLoader(String id) {
return (ClassloaderStats) loaders.get(id);
}
public ClassloaderStats(String id) {
this.id = id;
keepTraces = pluginFilters.contains(id);
}
public void addBaseClasses(String[] baseClasses) {
for (int i = 0; i < baseClasses.length; i++) {
String name = baseClasses[i];
if (classes.get(name) == null) {
ClassStats value = new ClassStats(name, this);
value.toBaseClass();
classes.put(name, value);
}
}
}
private void loadedBundle(ResourceBundleStats bundle) {
bundles.add(bundle);
}
public ArrayList getBundles() {
return bundles;
}
private synchronized void startLoadClass(String name) {
getClassStack().push(findClass(name));
}
// internal method that return the existing classStats or creates one
private ClassStats findClass(String name) {
ClassStats result = (ClassStats) classes.get(name);
return result == null ? new ClassStats(name, this) : result;
}
private synchronized void endLoadClass(String name, boolean success) {
ClassStats current = (ClassStats) getClassStack().pop();
if (!success) {
failureCount++;
return;
}
if (current.getLoadOrder() >= 0)
return;
classes.put(name, current);
current.setLoadOrder(classes.size());
current.loadingDone();
traceLoad(name, current);
// is there something on the load stack. if so, link them together...
Stack classStack = getClassStack();
if (classStack.size() != 0) {
// get the time spent loading cli and subtract its load time from the class that requires loading
ClassStats previous = ((ClassStats) classStack.peek());
previous.addTimeLoadingOthers(current.getTimeLoading());
current.setLoadedBy(previous);
previous.loaded(current);
} else {
loadingTime = loadingTime + current.getTimeLoading();
}
}
private void traceLoad(String name, ClassStats target) {
// Stack trace code
if (!keepTraces) {
boolean found = false;
for (int i = 0; !found && i < packageFilters.size(); i++)
if (name.startsWith((String) packageFilters.get(i)))
found = true;
if (!found)
return;
}
// Write the stack trace. The position in the file are set to the corresponding classStat object
try {
target.setTraceStart(traceFile.length());
PrintWriter output = new PrintWriter(new FileOutputStream(traceFile.getAbsolutePath(), true));
try {
output.println("Loading class: " + name); //$NON-NLS-1$
output.println("Class loading stack:"); //$NON-NLS-1$
output.println("\t" + name); //$NON-NLS-1$
Stack classStack = getClassStack();
for (int i = classStack.size() - 1; i >= 0; i--)
output.println("\t" + ((ClassStats) classStack.get(i)).getClassName()); //$NON-NLS-1$
output.println("Stack trace:"); //$NON-NLS-1$
new Throwable().printStackTrace(output);
} finally {
output.close();
}
target.setTraceEnd(traceFile.length());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public int getClassLoadCount() {
return classes.size();
}
public long getClassLoadTime() {
return loadingTime;
}
public ClassStats[] getClasses() {
//the parameter to toArray is of size zero for thread safety, otherwise this
//could return an array with null entries if the map shrinks concurrently
return (ClassStats[]) classes.values().toArray(new ClassStats[0]);
}
public String getId() {
return id;
}
}