All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.jimr.gae.profiler.MiniProfiler Maven / Gradle / Ivy

/**
 * Copyright (C) 2011 by Jim Riecken
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package ca.jimr.gae.profiler;

import java.io.Closeable;
import java.io.Serializable;
import java.util.*;

/**
 * Simple step instrumentation that can be used to profile Java code in the
 * context of the {@link MiniProfilerFilter}.
 * 

* To start a step, use the {@link #step(String)} method. To finish a step, call * the {@code close} method on the {@link Step} object returned. *

* Typically you will do this in a {@code try/finally} block like: * *

 * Step s = MiniProfiler.step("My Step");
 * try
 * {
 *   // Do Stuff
 * } finally
 * {
 *   s.close();
 * }
 * 
* * Steps can be nested (e.g. starting a step while inside another step) *

* Profiling data is stored in a {@link ThreadLocal}. */ public class MiniProfiler { /** * Contains information about a profiling step */ protected static class Profile implements Serializable { private static final long serialVersionUID = 6373761607106996570L; /** * The id of the step. This is used in the UI to uniquely identify steps * in-order. */ private int id; /** * The depth of the step in the profiling tree. This is used in the UI to * determine indentation. */ private int depth; /** The name of the step. */ private String name; /** When the step started (nanoseconds) */ private long start; /** How long the step was (in nanoseconds) */ private long duration; /** How far from the start of profiling did this step start (in nanoseconds) */ private long offset; /** The child steps of this step */ private List children = new ArrayList(); public Profile(int id, String name) { this.id = id; this.name = name; } /** * The id of the step. This is used in the UI to uniquely identify steps * in-order. * * @return The id of the step. */ public long getId() { return id; } /** * Get the depth of the step in the profiling tree. This is used in the UI * to determine indentation. * * @return The depth. */ public int getDepth() { return depth; } /** * Set the depth of the step in the profiling tree. * * @param depth * The depth. */ public void setDepth(int depth) { this.depth = depth; } /** * Get the name of the step. * * @return The name. */ public String getName() { return name; } /** * Get when the step started (nanoseconds). * * @return When the step started. */ public long getStart() { return start; } /** * Set when the step started (nanoseconds). * * @param start * When the step started. */ public void setStart(long start) { this.start = start; } /** * Set how long the step took (nanoseconds). * * @param duration * The duration. */ public void setDuration(long duration) { this.duration = duration; } /** * Get how long the step tool (nanoseconds). * * @return The duration. */ public long getDuration() { return duration; } /** * Set the step's offset from the start of profiling (nanoseconds). * * @param offset * The offset. */ public void setOffset(long offset) { this.offset = offset; } /** * Get the step's offset from the start of profling (nanoseconds). * * @return The offset. */ public long getOffset() { return offset; } /** * Calculate the duration of this step, minus the duration of all the child * steps. *

* In effect, returns the time spent only in this step. * * @return The time spent just in this step (none of the children) */ public long getSelf() { long result = duration; for (Profile p : children) { result -= p.duration; } return result; } /** * Get the child steps of this step. * * @return The child steps. */ public List getChildren() { return children; } /** * Add a child step to this step. * * @param child * The child to add. */ public void addChild(Profile child) { children.add(child); } } /** * The root of the profiling data. *

* This is what's stored in the {@code ThreadLocal}. */ private static class Root implements Serializable { private static final long serialVersionUID = -7244418353632893875L; /** Counter used for generating ids */ private int count = 0; /** The root step of the profile */ private Profile root = new Profile(count++, "Request"); /** * The stack of steps (the top of the stack will always be the current step) */ private Stack stack = new Stack(); /** * Create the root of the profile - records the start time. */ public Root() { root.setStart(System.nanoTime()); stack.push(root); } /** * Generate the id for the next step. * * @return The next id. */ public int nextId() { return count++; } /** * Add a profile step to the stack. * * @param d * The profile step to add */ public void pushData(Profile d) { long now = System.nanoTime(); d.setDepth(stack.size()); d.setStart(now); d.setOffset(now - root.getStart()); stack.peek().addChild(d); stack.push(d); } /** * Remove the top profile step from the stack. * * @return The top profile step. */ public Profile popData() { long now = System.nanoTime(); Profile d = stack.pop(); d.setDuration(now - d.getStart()); return d; } } /** * Used to control the starting and stopping of profiling steps. *

* Implements {@code Closeable}, so can theoretically be used in a Java 7 * {@code try-with-resources} statement for less code. */ public static class Step implements Closeable { private Root root; /** * Create a step object. * * @param root * The profile root. * @param data * The current step data. */ public Step(Root root, Profile data) { this.root = root; if (root != null) { root.pushData(data); } } /** * Stop the profiling step. */ @Override public void close() { if (root != null) { root.popData(); } } } /** Thread local that contains the profiling data for the current thread */ private static final ThreadLocal PROFILER_STEPS = new ThreadLocal(); /** * Start the profiler. */ protected static void start() { PROFILER_STEPS.set(new Root()); } /** * Stop the profiler. *

* This should be called in a {@code finally} block to avoid leaving data on * the thread. * * @return The profiling data. */ protected static Profile stop() { Root result = PROFILER_STEPS.get(); PROFILER_STEPS.remove(); return result != null ? result.popData() : null; } /** * Start a profiling step. * * @param stepName * The name of the step. * @return A {@code Step} object whose {@link Step#close()} method should be * called to finish the step. */ public static Step step(String stepName) { Root root = PROFILER_STEPS.get(); if (root != null) { Profile data = new Profile(root.nextId(), stepName); return new Step(root, data); } else { return new Step(null, null); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy