/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source 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. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Sam */ package com.caucho.tools.profiler; import com.caucho.util.CharBuffer; import com.caucho.util.L10N; import com.caucho.util.Sprintf; import com.caucho.vfs.XmlWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; /** * Html interface to profiling information. */ public class ProfilerServlet extends HttpServlet { private static final L10N L = new L10N(ProfilerServlet.class); // can do this because an instance of Filter is created for each environment private final ProfilerManager _profilerManager = ProfilerManager.getLocal(); public ProfilerManager createProfiler() { return _profilerManager; } public void init() { } protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { handleRequest(req, res); handleResponse(req, res); } protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { handleRequest(req, res); handleResponse(req, res); } protected void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void handleResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String format = request.getParameter("format"); boolean isXml = "xml".equals(format); response.setContentType("text/html"); response.setHeader("Cache-Control", "no-cache, post-check=0, pre-check=0"); response.setHeader("Pragma", "no-cache"); response.setHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); if (isXml) writeXml(request, response); else writeHtml(request, response); } protected void writeHtml(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); String sort = request.getParameter("sort"); ProfilerNodeComparator comparator; if ("count".equals(sort)) comparator = new CountComparator(); else comparator = new TimeComparator(); comparator.setDescending(true); XmlWriter out = new XmlWriter(response.getWriter()); out.setStrategy(XmlWriter.HTML); out.setIndenting(false); out.println( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"); String contextPath = request.getContextPath(); if (contextPath == null || contextPath.length() == 0) contextPath = "/"; String title = L.l("Profiling Results for {0}", contextPath); out.startElement("html"); out.startElement("head"); out.writeElement("title", title); out.startElement("style"); out.writeAttribute("type", "text/css"); out.println( "h1 { background: #ccddff; margin : 0 -0.5em 0.25em -0.25em; padding: 0.25em 0.25em; }"); out.println( "h2 { background: #ccddff; padding: 0.25em 0.5em; margin : 0.5em -0.5em; }"); out.println("table { border-collapse : collapse; }"); out.println("th { background : #c78ae6; border-left : 1px; border-right : 1px}"); out.println("tr { border-bottom : 1px dotted; }"); out.println(".number { text-align : right; }"); out.println("table table tr { border-bottom : none; }"); out.endElement("style"); out.endElement("head"); out.startElement("body"); out.writeElement("h1", title); out.startElement("table"); out.writeAttribute("border", 0); out.startElement("tr"); out.writeLineElement("th", L.l("Name")); out.writeLineElement("th", L.l("Average Time")); out.writeLineElement("th", L.l("Min Time")); out.writeLineElement("th", L.l("Max Time")); out.writeLineElement("th", L.l("Total Time")); out.writeLineElement("th", L.l("Invocation Count")); out.endElement("tr"); ProfilerPoint root = _profilerManager.getRoot(); List<ProfilerPoint> children = root.getChildren(); Collections.sort(children, comparator); for (ProfilerPoint child : children) display(child, comparator, out, 0); out.endElement("table"); out.endElement("body"); out.endElement("html"); } private void display(ProfilerPoint node, ProfilerNodeComparator comparator, XmlWriter out, int depth) { if (node == null) return; List<ProfilerPoint> children = node.getChildren(); Collections.sort(children, comparator); long thisTime = node.getTime(); long minTime = node.getMinTime(); long maxTime = node.getMaxTime(); long childrenTime = 0; for (ProfilerPoint child : children) { childrenTime += child.getTime(); } long totalTime = childrenTime + thisTime; long invocationCount = node.getInvocationCount(); long averageThisTime; long averageTotalTime; long averageChildrenTime; if (invocationCount <= 0) { averageThisTime = -1; averageTotalTime = -1; averageChildrenTime = -1; } else { averageThisTime = thisTime / invocationCount; averageTotalTime = totalTime / invocationCount; averageChildrenTime = childrenTime / invocationCount; } out.startElement("tr"); out.writeAttribute("class", "level" + depth); // Name out.startLineElement("td"); out.startElement("table"); out.startElement("tr"); out.startLineElement("td"); if (depth > 0) { for (int i = depth; i > 0; i--) { out.write(" "); out.write(" "); } out.write("→"); } out.endLineElement("td"); out.startLineElement("td"); out.writeAttribute("class", "text"); out.writeText(node.getName()); out.endLineElement("td"); out.endElement("tr"); out.endElement("table"); out.endLineElement("td"); out.startLineElement("td"); out.writeAttribute("class", "number"); if (averageThisTime < 0) out.write(" "); else { String averageTimeString = createTimeString(averageTotalTime, averageThisTime, averageChildrenTime); out.writeAttribute("title", averageTimeString); printTime(out, averageTotalTime); } out.endLineElement("td"); out.startLineElement("td"); out.writeAttribute("class", "number"); if (minTime < Long.MAX_VALUE) printTime(out, minTime); else out.print(" "); out.endLineElement("td"); out.startLineElement("td"); out.writeAttribute("class", "number"); if (Long.MIN_VALUE < maxTime) printTime(out, maxTime); else out.print(" "); out.endLineElement("td"); out.startLineElement("td"); out.writeAttribute("class", "number"); String timeString = createTimeString(totalTime, thisTime, childrenTime); out.writeAttribute("title", timeString); printTime(out, totalTime); out.endLineElement("td"); out.startLineElement("td"); out.writeAttribute("class", "number"); out.print(invocationCount); out.endLineElement("td"); out.endElement("tr"); // All children for (ProfilerPoint child : children) display(child, comparator, out, depth + 1); } protected void writeXml(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfilerNodeComparator comparator = new TimeComparator(); comparator.setDescending(true); XmlWriter out = new XmlWriter(response.getWriter()); out.setStrategy(XmlWriter.XML); out.setIndenting(false); String contextPath = request.getContextPath(); if (contextPath == null || contextPath.length() == 0) contextPath = "/"; out.startElement("profile"); out.writeLineElement("name", contextPath); List<ProfilerPoint> children = _profilerManager.getRoot().getChildren(); Collections.sort(children, comparator); for (ProfilerPoint child : children) displayXml(child, comparator, out); out.endElement("profile"); } private void displayXml(ProfilerPoint node, ProfilerNodeComparator comparator, XmlWriter out) { List<ProfilerPoint> children = node.getChildren(); Collections.sort(children, comparator); long thisTime = node.getTime(); long minTime = node.getMinTime(); long maxTime = node.getMaxTime(); long childrenTime = 0; for (ProfilerPoint child : children) { childrenTime += child.getTime(); } long totalTime = childrenTime + thisTime; long invocationCount = node.getInvocationCount(); out.startBlockElement("node"); out.writeLineElement("name", node.getName()); if (minTime < Long.MAX_VALUE) out.writeLineElement("min-time", String.valueOf(minTime)); else out.writeLineElement("min-time", "0"); if (maxTime >= 0) out.writeLineElement("max-time", String.valueOf(maxTime)); else out.writeLineElement("max-time", "0"); out.writeLineElement("time", String.valueOf(thisTime)); out.writeLineElement("total-time", String.valueOf(totalTime)); out.writeLineElement("children-time", String.valueOf(childrenTime)); out.writeLineElement("count", String.valueOf(invocationCount)); for (ProfilerPoint child : children) displayXml(child, comparator, out); out.endBlockElement("node"); } private String createTimeString(long totalTime, long thisTime, long childrenTime) { CharBuffer cb = new CharBuffer(); cb.append("totalTime="); formatTime(cb, totalTime); cb.append(" thisTime="); formatTime(cb, thisTime); cb.append(" childrenTime="); formatTime(cb, childrenTime); return cb.toString(); } private void printTime(XmlWriter out, long time) { CharBuffer cb = new CharBuffer(); formatTime(cb, time); out.writeText(cb.toString()); } private void formatTime(CharBuffer cb, long nanoseconds) { long milliseconds = nanoseconds / 1000000; long minutes = milliseconds / 1000 / 60; if (minutes > 0) { Sprintf.sprintf(cb, "%d:", minutes); milliseconds -= minutes * 60 * 1000; } long seconds = milliseconds / 1000; if (minutes > 0) Sprintf.sprintf(cb, "%02d.", seconds); else Sprintf.sprintf(cb, "%d.", seconds); milliseconds -= seconds * 1000; Sprintf.sprintf(cb, "%03d", milliseconds); } }