001: /*
002: * Copyright (c) 1998-2005 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Sam
027: */
028:
029: package com.caucho.tools.profiler;
030:
031: import com.caucho.util.CharBuffer;
032: import com.caucho.util.L10N;
033: import com.caucho.util.Sprintf;
034: import com.caucho.vfs.XmlWriter;
035:
036: import javax.servlet.ServletException;
037: import javax.servlet.http.HttpServlet;
038: import javax.servlet.http.HttpServletRequest;
039: import javax.servlet.http.HttpServletResponse;
040: import java.io.IOException;
041: import java.util.*;
042:
043: /**
044: * Html interface to profiling information.
045: */
046: public class ProfilerServlet extends HttpServlet {
047: private static final L10N L = new L10N(ProfilerServlet.class);
048:
049: // can do this because an instance of Filter is created for each environment
050: private final ProfilerManager _profilerManager = ProfilerManager
051: .getLocal();
052:
053: public ProfilerManager createProfiler() {
054: return _profilerManager;
055: }
056:
057: public void init() {
058: }
059:
060: protected void doGet(HttpServletRequest req, HttpServletResponse res)
061: throws ServletException, IOException {
062: handleRequest(req, res);
063: handleResponse(req, res);
064: }
065:
066: protected void doPost(HttpServletRequest req,
067: HttpServletResponse res) throws ServletException,
068: IOException {
069: handleRequest(req, res);
070: handleResponse(req, res);
071: }
072:
073: protected void handleRequest(HttpServletRequest request,
074: HttpServletResponse response) throws ServletException,
075: IOException {
076: }
077:
078: protected void handleResponse(HttpServletRequest request,
079: HttpServletResponse response) throws ServletException,
080: IOException {
081: String format = request.getParameter("format");
082: boolean isXml = "xml".equals(format);
083:
084: response.setContentType("text/html");
085:
086: response.setHeader("Cache-Control",
087: "no-cache, post-check=0, pre-check=0");
088: response.setHeader("Pragma", "no-cache");
089: response.setHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
090:
091: if (isXml)
092: writeXml(request, response);
093: else
094: writeHtml(request, response);
095: }
096:
097: protected void writeHtml(HttpServletRequest request,
098: HttpServletResponse response) throws ServletException,
099: IOException {
100: response.setContentType("text/html");
101:
102: String sort = request.getParameter("sort");
103:
104: ProfilerNodeComparator comparator;
105:
106: if ("count".equals(sort))
107: comparator = new CountComparator();
108: else
109: comparator = new TimeComparator();
110:
111: comparator.setDescending(true);
112:
113: XmlWriter out = new XmlWriter(response.getWriter());
114:
115: out.setStrategy(XmlWriter.HTML);
116: out.setIndenting(false);
117:
118: out
119: .println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
120:
121: String contextPath = request.getContextPath();
122:
123: if (contextPath == null || contextPath.length() == 0)
124: contextPath = "/";
125:
126: String title = L.l("Profiling Results for {0}", contextPath);
127:
128: out.startElement("html");
129:
130: out.startElement("head");
131: out.writeElement("title", title);
132:
133: out.startElement("style");
134: out.writeAttribute("type", "text/css");
135:
136: out
137: .println("h1 { background: #ccddff; margin : 0 -0.5em 0.25em -0.25em; padding: 0.25em 0.25em; }");
138: out
139: .println("h2 { background: #ccddff; padding: 0.25em 0.5em; margin : 0.5em -0.5em; }");
140: out.println("table { border-collapse : collapse; }");
141: out
142: .println("th { background : #c78ae6; border-left : 1px; border-right : 1px}");
143: out.println("tr { border-bottom : 1px dotted; }");
144: out.println(".number { text-align : right; }");
145: out.println("table table tr { border-bottom : none; }");
146:
147: out.endElement("style");
148:
149: out.endElement("head");
150:
151: out.startElement("body");
152: out.writeElement("h1", title);
153:
154: out.startElement("table");
155: out.writeAttribute("border", 0);
156:
157: out.startElement("tr");
158:
159: out.writeLineElement("th", L.l("Name"));
160:
161: out.writeLineElement("th", L.l("Average Time"));
162: out.writeLineElement("th", L.l("Min Time"));
163: out.writeLineElement("th", L.l("Max Time"));
164: out.writeLineElement("th", L.l("Total Time"));
165:
166: out.writeLineElement("th", L.l("Invocation Count"));
167:
168: out.endElement("tr");
169:
170: ProfilerPoint root = _profilerManager.getRoot();
171:
172: List<ProfilerPoint> children = root.getChildren();
173: Collections.sort(children, comparator);
174: for (ProfilerPoint child : children)
175: display(child, comparator, out, 0);
176:
177: out.endElement("table");
178:
179: out.endElement("body");
180:
181: out.endElement("html");
182: }
183:
184: private void display(ProfilerPoint node,
185: ProfilerNodeComparator comparator, XmlWriter out, int depth) {
186: if (node == null)
187: return;
188:
189: List<ProfilerPoint> children = node.getChildren();
190: Collections.sort(children, comparator);
191:
192: long this Time = node.getTime();
193: long minTime = node.getMinTime();
194: long maxTime = node.getMaxTime();
195:
196: long childrenTime = 0;
197:
198: for (ProfilerPoint child : children) {
199: childrenTime += child.getTime();
200: }
201:
202: long totalTime = childrenTime + this Time;
203:
204: long invocationCount = node.getInvocationCount();
205: long averageThisTime;
206: long averageTotalTime;
207: long averageChildrenTime;
208:
209: if (invocationCount <= 0) {
210: averageThisTime = -1;
211: averageTotalTime = -1;
212: averageChildrenTime = -1;
213: } else {
214: averageThisTime = this Time / invocationCount;
215: averageTotalTime = totalTime / invocationCount;
216: averageChildrenTime = childrenTime / invocationCount;
217: }
218:
219: out.startElement("tr");
220:
221: out.writeAttribute("class", "level" + depth);
222:
223: // Name
224:
225: out.startLineElement("td");
226:
227: out.startElement("table");
228: out.startElement("tr");
229:
230: out.startLineElement("td");
231:
232: if (depth > 0) {
233: for (int i = depth; i > 0; i--) {
234: out.write(" ");
235: out.write(" ");
236: }
237:
238: out.write("→");
239: }
240: out.endLineElement("td");
241:
242: out.startLineElement("td");
243: out.writeAttribute("class", "text");
244: out.writeText(node.getName());
245: out.endLineElement("td");
246:
247: out.endElement("tr");
248: out.endElement("table");
249:
250: out.endLineElement("td");
251:
252: out.startLineElement("td");
253: out.writeAttribute("class", "number");
254: if (averageThisTime < 0)
255: out.write(" ");
256: else {
257: String averageTimeString = createTimeString(
258: averageTotalTime, averageThisTime,
259: averageChildrenTime);
260: out.writeAttribute("title", averageTimeString);
261: printTime(out, averageTotalTime);
262: }
263:
264: out.endLineElement("td");
265:
266: out.startLineElement("td");
267: out.writeAttribute("class", "number");
268: if (minTime < Long.MAX_VALUE)
269: printTime(out, minTime);
270: else
271: out.print(" ");
272: out.endLineElement("td");
273:
274: out.startLineElement("td");
275: out.writeAttribute("class", "number");
276: if (Long.MIN_VALUE < maxTime)
277: printTime(out, maxTime);
278: else
279: out.print(" ");
280: out.endLineElement("td");
281:
282: out.startLineElement("td");
283: out.writeAttribute("class", "number");
284: String timeString = createTimeString(totalTime, this Time,
285: childrenTime);
286: out.writeAttribute("title", timeString);
287: printTime(out, totalTime);
288: out.endLineElement("td");
289:
290: out.startLineElement("td");
291: out.writeAttribute("class", "number");
292: out.print(invocationCount);
293: out.endLineElement("td");
294:
295: out.endElement("tr");
296:
297: // All children
298:
299: for (ProfilerPoint child : children)
300: display(child, comparator, out, depth + 1);
301: }
302:
303: protected void writeXml(HttpServletRequest request,
304: HttpServletResponse response) throws ServletException,
305: IOException {
306: ProfilerNodeComparator comparator = new TimeComparator();
307: comparator.setDescending(true);
308:
309: XmlWriter out = new XmlWriter(response.getWriter());
310:
311: out.setStrategy(XmlWriter.XML);
312: out.setIndenting(false);
313:
314: String contextPath = request.getContextPath();
315:
316: if (contextPath == null || contextPath.length() == 0)
317: contextPath = "/";
318:
319: out.startElement("profile");
320: out.writeLineElement("name", contextPath);
321:
322: List<ProfilerPoint> children = _profilerManager.getRoot()
323: .getChildren();
324:
325: Collections.sort(children, comparator);
326: for (ProfilerPoint child : children)
327: displayXml(child, comparator, out);
328:
329: out.endElement("profile");
330: }
331:
332: private void displayXml(ProfilerPoint node,
333: ProfilerNodeComparator comparator, XmlWriter out) {
334: List<ProfilerPoint> children = node.getChildren();
335: Collections.sort(children, comparator);
336:
337: long this Time = node.getTime();
338: long minTime = node.getMinTime();
339: long maxTime = node.getMaxTime();
340:
341: long childrenTime = 0;
342:
343: for (ProfilerPoint child : children) {
344: childrenTime += child.getTime();
345: }
346:
347: long totalTime = childrenTime + this Time;
348:
349: long invocationCount = node.getInvocationCount();
350:
351: out.startBlockElement("node");
352: out.writeLineElement("name", node.getName());
353:
354: if (minTime < Long.MAX_VALUE)
355: out.writeLineElement("min-time", String.valueOf(minTime));
356: else
357: out.writeLineElement("min-time", "0");
358: if (maxTime >= 0)
359: out.writeLineElement("max-time", String.valueOf(maxTime));
360: else
361: out.writeLineElement("max-time", "0");
362: out.writeLineElement("time", String.valueOf(this Time));
363: out.writeLineElement("total-time", String.valueOf(totalTime));
364: out.writeLineElement("children-time", String
365: .valueOf(childrenTime));
366: out.writeLineElement("count", String.valueOf(invocationCount));
367:
368: for (ProfilerPoint child : children)
369: displayXml(child, comparator, out);
370:
371: out.endBlockElement("node");
372: }
373:
374: private String createTimeString(long totalTime, long this Time,
375: long childrenTime) {
376: CharBuffer cb = new CharBuffer();
377:
378: cb.append("totalTime=");
379: formatTime(cb, totalTime);
380:
381: cb.append(" thisTime=");
382: formatTime(cb, this Time);
383:
384: cb.append(" childrenTime=");
385: formatTime(cb, childrenTime);
386:
387: return cb.toString();
388: }
389:
390: private void printTime(XmlWriter out, long time) {
391: CharBuffer cb = new CharBuffer();
392: formatTime(cb, time);
393: out.writeText(cb.toString());
394: }
395:
396: private void formatTime(CharBuffer cb, long nanoseconds) {
397: long milliseconds = nanoseconds / 1000000;
398:
399: long minutes = milliseconds / 1000 / 60;
400:
401: if (minutes > 0) {
402: Sprintf.sprintf(cb, "%d:", minutes);
403: milliseconds -= minutes * 60 * 1000;
404: }
405:
406: long seconds = milliseconds / 1000;
407:
408: if (minutes > 0)
409: Sprintf.sprintf(cb, "%02d.", seconds);
410: else
411: Sprintf.sprintf(cb, "%d.", seconds);
412:
413: milliseconds -= seconds * 1000;
414:
415: Sprintf.sprintf(cb, "%03d", milliseconds);
416: }
417: }
|