<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.io.IOException,java.util.*" %> <% // tell browser not to cache this page response.setHeader("Expires", "0"); // extract all known parameters String mode = getParameter(request, "mode", "main"); int delay = getParameter(request, "delay", 100); int samples = getParameter(request, "samples", 100); String excludeList = getParameter(request, "excludeList", "java.lang.Object.wait\njava.lang.Thread.sleep\njava.net.PlainSocketImpl.socketAccept\njava.net.SocketInputStream.socketRead0\nsun.nio.ch.WindowsSelectorImpl$SubSelector.poll0"); String restart = getParameter(request, "restart", "false"); /* * In mode "main" the profiler UI is outputted together with all * the JavaScript needed to drive the server side profiler code. */ if (mode.equals("main")) { %>
Open all entries with >=
%
Auto reload interval: s
Delay per Sample: ms
Samples per Request:
Methods to exclude:

Drop Profiler 0.4

Drop profiler is a simple browser based profiler for Java servlet containers. It is intended for finding bottle necks in web applications under load.

Simply drop the JSP file into a web directory belonging to the application you want to profile and you are good to go. No server restart or reconfiguration needed. Requires Java 5 or later.

The profiler does not use sessions because some apps have issues with sessions which do not meet their expectations. State is kept in a static field of the JSP. This may have side effects as well. Particularly, multiple profilers running on the same VM will interfere.

Be advised that there is no abuse protection built in and that this software should only be used in safe environments.

Project Home: http://www.sump.org/projects/profiler/

Copyrights (C) 2009 by Michael Poppitz.

<% /* * In mode "tree" the call tree is returned using nested HTML lists. */ } else if (mode.equals("tree")) { if (tree == null) { out.print("

Please wait until profiler has been initialized.

"); } else if (tree.getChildren() == null) { out.print("

No matching data has been collected yet.

"); } else { out.print("

Total samples: " + tree.hits + " (100%)

"); out.print(""); } /* * In mode "methods" the method dictionary is returned as HTML list. */ } else if (mode.equals("methods")) { if (tree == null) { out.print("

Please wait until profiler has been initialized.

"); } else { int total = tree.hits; out.print("

Total samples: " + total + " (100%)

"); out.print(""); } /* * In mode "threads" a thread list is generated and returned as HTML table. */ } else if (mode.equals("threads")) { try { out.print(""); out.print(""); Thread requestThread = Thread.currentThread(); Map traces = Thread.getAllStackTraces(); List sortedThreads = new ArrayList(traces.keySet()); Collections.sort(sortedThreads, new Comparator() { public int compare(Thread a, Thread b) { return a.getName().compareTo(b.getName()); } }); for (Thread thread : sortedThreads) { StackTraceElement[] stack = traces.get(thread); String state = null; out.print(""); } out.print("
NameStateMethod
" + thread.getName() + (thread.isDaemon()?" (Daemon)":"") + "" + thread.getState() + "" + (stack.length > 0?stack[0].getClassName() + "." + stack[0].getMethodName():"") + "
"); } catch (Exception e) { // too bad } /* * Mode "sample" abuses one of the servlet container processing threads * to periodically sample the stack traces. Each call will capture a fixed * number of samples and then return. This avoids timeouts while only * creating a small number of additional HTTP requests. */ } else if (mode.equals("sample")) { sample(excludeList, delay, samples, restart.equals("true")); } %> <%! public String getParameter(HttpServletRequest request, String name, String defaultValue) { String value = request.getParameter(name); if (value != null) { // convert windows & old mac line breaks into unix line breaks // TODO: everybody with only "\r" gets screwed here value = value.replaceAll("\r", ""); return value; } return defaultValue; } public int getParameter(HttpServletRequest request, String name, int defaultValue) { String value = request.getParameter(name); if (value != null) try { return Integer.parseInt(value); } catch (Exception e) { } return defaultValue; } %> <%! /** * This is the evil line which allows a persistent call tree without using a session. * TODO: Use a session when possible. */ static CallTree tree; /** * Recursively print the call tree. */ public void printTree(JspWriter out, CallTreeElement element, long total) throws IOException { Collection children = element.getChildren(); double percent = Math.round((1000 * element.hits) / total) / 10.0; int bar = (int)percent / 2; int id = element.hashCode(); out.print("
  •  " + percent + "% - " + element.method + ""); if (children != null) { out.print(""); } out.print("
  • "); } /** * Samples thread information a given number of times. */ public void sample(String excludeList, int delay, int samples, boolean restart) { if (tree == null || restart) tree = new CallTree(excludeList); // start probing the VM Thread profilingThread = Thread.currentThread(); for (int i = 0; i < samples; i++) { try { Thread.sleep(delay); Map traces = Thread.getAllStackTraces(); for (Map.Entry trace : traces.entrySet()) { // exclude the profiling thread from monitoring if (trace.getKey() == profilingThread) continue; tree.add(trace.getValue()); } } catch (Exception e) { // too bad } } } /** * A call tree counts all detected method calls in a tree structure. * The tree is created by merging all stack traces ever added to the tree. * A tree wide method/class name mapping is used to reduce memory consumption. */ public class CallTree extends CallTreeElement { public CallTree(String ignoredMethodList) { super("root", 0, new HashMap()); this.ignoredMethods = new HashSet(); if (ignoredMethodList != null) { String[]methods = ignoredMethodList.split("\n"); for (String method : methods) ignoredMethods.add(method); } } public void add(StackTraceElement[] trace) { if (trace.length == 0) return; String topMethod = trace[0].getClassName() + "." + trace[0].getMethodName(); if (ignoredMethods.contains(topMethod)) return; super.add(trace); } public Collection getMethods() { Collection methods = super.methodDictionary.values(); if (methods == null) return null; List tmp = new ArrayList(methods); Collections.sort(tmp); return tmp; } private HashSet ignoredMethods; } /** * A call tree element counts detected calls to a specific method/class in a specific call path. * In case an element is not at the lowest level of the trace, it makes sure to notify the next * child. If this child does not exist, it is automatically generated. */ public class CallTreeElement extends Element { public CallTreeElement(String method, int level, Map methodDictionary) { super(method); this.methodDictionary = methodDictionary; this.level = level; } public List getChildren() { if (children == null) return null; List tmp = new ArrayList(children.values()); Collections.sort(tmp); return tmp; } public void add(StackTraceElement[] trace) { // count the call super.count(); // handle call tree children when bottom of stack has not yet been reached if (trace.length > level) { // first make sure a children container exists if (children == null) children = new HashMap(); // next ensure the child method is in the method dictionary int pos = trace.length - level - 1; String method = trace[pos].getClassName() + "." + trace[pos].getMethodName(); Element element = methodDictionary.get(method); if (element == null) { element = new Element(method); methodDictionary.put(method, element); } element.count(); // finally get or create the child and call its add method CallTreeElement child = children.get(element.method); if (child == null) { child = new CallTreeElement(method, level + 1, methodDictionary); children.put(element.method, child); } child.add(trace); } } private Map methodDictionary; private Map children; private int level; } /** * Element for method list and call tree. * Stores method name and counts encounters. */ public class Element implements Comparable { public Element(String method) { this.hits = 0; this.method = method; } public void count() { hits++; } public int compareTo(Element o) { return o.hits - hits; } public String method; public int hits; } %>