/* * 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 Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * */ package com.caucho.admin.action; import java.io.*; import java.text.*; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Logger; import com.caucho.config.ConfigException; import com.caucho.profile.*; import com.caucho.util.*; public class ProfileAction implements AdminAction { private static final Logger log = Logger.getLogger(ProfileAction.class.getName()); private static final L10N L = new L10N(ProfileAction.class); private AtomicLong _cancelledTime = new AtomicLong(-1); public void cancel() { _cancelledTime.compareAndSet(-1, Alarm.getCurrentTime()); synchronized(this) { this.notify(); } Profile profile = Profile.createProfile(); if (profile != null) profile.stop(); } public void start(long samplingRate, int depth) { Profile profile = Profile.createProfile(); profile.stop(); profile.setPeriod(samplingRate); profile.setDepth(depth); profile.start(); } public String execute(long activeTime, long samplingRate, int depth) throws ConfigException { if (activeTime <= 0) { throw new IllegalArgumentException(L.l("Profile activeTime '{0}': must be > 0.", activeTime)); } Profile profile = Profile.createProfile(); if (profile.isActive()) { throw new ConfigException(L.l("Profile is still active")); } long startedAt = Alarm.getCurrentTime(); start(samplingRate, depth); try { synchronized (this) { this.wait(activeTime); } } catch (InterruptedException e) { _cancelledTime.compareAndSet(-1, Alarm.getCurrentTime()); } profile.stop(); StringWriter buffer = new StringWriter(); PrintWriter out = new PrintWriter(buffer); ProfileEntry []entries = profile.getResults(); if (entries == null || entries.length == 0) { out.println("Profile returned no entries."); } else { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); long cancelledTime = _cancelledTime.get(); if (cancelledTime < 0) { out.print(L.l("Profile started at {0}. Active for a total of {1}ms.", dateFormat.format(new Date(startedAt)), activeTime)); } else { long et = cancelledTime - startedAt; out.print(L.l("Profile started at {0}, cancelled at {1}. Active for a total of {2}ms.", dateFormat.format(new Date(startedAt)), dateFormat.format(new Date(cancelledTime)), et)); } out.println(L.l(" Sampling rate {0}ms. Depth {1}.", samplingRate, String.valueOf(depth))); double totalTicks = 0; for (ProfileEntry entry : entries) { totalTicks += entry.getCount(); } final double sampleTicks = profile.getTicks(); double totalPercent = 0d; out.println(" ref# | % time |time self(s)| % sum | Method Call"); for (int i = 0; i < entries.length; i++) { ProfileEntry entry = entries[i]; double timePercent = (double) 100 * (double) entry.getCount() / sampleTicks; double selfPercent = (double) 100 * (double) entry.getCount() / totalTicks; totalPercent += selfPercent; out.println(String.format(" %4d | %10.3f | %10.3f | %10.3f | %s", i, timePercent, (float) entry.getCount() * samplingRate * 0.001, totalPercent, entry.getDescription())); } for (int i = 0; i < entries.length; i++) { ProfileEntry entry = entries[i]; out.print(String.format(" %4d ", i)); out.println(" " + entry.getDescription()); ArrayList<? extends StackEntry> stackEntries = entry.getStackTrace(); for (StackEntry stackEntry : stackEntries) { out.println(" " + stackEntry.getDescription()); } } } out.flush(); return buffer.toString(); } /** * @return */ public String jsonProfile() { Profile profile = Profile.createProfile(); if (profile == null) return null; ProfileEntry []entries = profile.getResults(); if (entries == null || entries.length == 0) { return null; } StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append("\n \"total_time\" : " + profile.getRunTime()); sb.append(",\n \"ticks\" : " + profile.getTicks()); sb.append(",\n \"depth\" : " + profile.getDepth()); sb.append(",\n \"period\" : " + profile.getPeriod()); sb.append(",\n \"end_time\" : " + profile.getEndTime()); sb.append(",\n \"gc_time\" : " + profile.getGcTime()); sb.append(",\n \"profile\" : [\n"); for (int i = 0; i < entries.length; i++) { if (i != 0) sb.append(",\n"); jsonEntry(sb, entries[i]); } /* long gcTicks = (profile.getGcTime() + profile.getPeriod() - 1) / profile.getPeriod(); if (entries.length > 0) sb.append(",\n"); jsonGc(sb, gcTicks); */ sb.append("\n]"); sb.append("}"); return sb.toString(); } private void jsonEntry(StringBuilder sb, ProfileEntry entry) { sb.append("{"); sb.append("\n \"name\" : \""); escapeString(sb, entry.getDescription()); sb.append("\""); sb.append(",\n \"ticks\" : " + entry.getCount()); sb.append(",\n \"state\" : \"" + entry.getState() + "\""); if (entry.getStackTrace() != null && entry.getStackTrace().size() > 0) { jsonStackTrace(sb, entry.getStackTrace()); } sb.append("\n}"); } private void jsonGc(StringBuilder sb, long ticks) { sb.append("{"); sb.append("\n \"name\" : \"HeapMemory.gc\""); sb.append(",\n \"ticks\" : " + ticks); sb.append(",\n \"state\" : \"RUNNABLE\""); sb.append("\n}"); } private void jsonStackTrace(StringBuilder sb, ArrayList<? extends StackEntry> stack) { sb.append(",\n \"stack\" : "); sb.append("[\n"); int size = stack.size(); for (int i = 0; i < size; i++) { StackEntry entry = stack.get(i); if (i != 0) sb.append(",\n"); sb.append(" {"); sb.append("\n \"class\" : \"" + entry.getClassName() + "\""); sb.append(",\n \"method\" : \"" + entry.getMethodName() + "\""); if (entry.getArg() != null && ! "".equals(entry.getArg())) { sb.append(",\n \"arg\" : \""); escapeString(sb, entry.getArg()); sb.append("\""); } sb.append("\n }"); } sb.append("\n ]"); } private void escapeString(StringBuilder sb, String value) { if (value == null) return; int len = value.length(); for (int i = 0; i < len; i++) { char ch = value.charAt(i); switch (ch) { case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; default: sb.append(ch); break; } } } }