/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.plugins.profiler; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.opends.server.api.DirectoryThread; import org.opends.server.protocols.asn1.*; import static org.opends.server.loggers.debug.DebugLogger.*; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.DebugLogLevel; /** * This class defines a thread that may be used to actually perform * profiling in the Directory Server. When activated, it will repeatedly * retrieve thread stack traces and store them so that they can be written out * and analyzed with a separate utility. */ public class ProfilerThread extends DirectoryThread { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); // Indicates whether a request has been received to stop profiling. private boolean stopProfiling; // The time at which the capture started. private long captureStartTime; // The time at which the capture stopped. private long captureStopTime; // The number of intervals for which we have captured data. private long numIntervals; // The sampling interval that will be used by this thread. private long sampleInterval; // The set of thread stack traces captured by this profiler thread. private HashMap<ProfileStack,Long> stackTraces; // The thread that is actually performing the capture. private Thread captureThread; /** * Creates a new profiler thread that will obtain stack traces at the * specified interval. * * @param sampleInterval The length of time in milliseconds between polls * for stack trace information. */ public ProfilerThread(long sampleInterval) { super("Directory Server Profiler Thread"); this.sampleInterval = sampleInterval; stackTraces = new HashMap<ProfileStack,Long>(); numIntervals = 0; stopProfiling = false; captureStartTime = -1; captureStopTime = -1; captureThread = null; } /** * Runs in a loop, periodically capturing a list of the stack traces for all * active threads. */ public void run() { captureThread = currentThread(); captureStartTime = System.currentTimeMillis(); while (! stopProfiling) { // Get the current time so we can sleep more accurately. long startTime = System.currentTimeMillis(); // Get a stack trace of all threads that are currently active. Map<Thread,StackTraceElement[]> stacks = getAllStackTraces(); numIntervals++; // Iterate through the threads and process their associated stack traces. for (Thread t : stacks.keySet()) { // We don't want to capture information about the profiler thread. if (t == currentThread()) { continue; } // We'll skip over any stack that doesn't have any information. StackTraceElement[] threadStack = stacks.get(t); if ((threadStack == null) || (threadStack.length == 0)) { continue; } // Create a profile stack for this thread stack trace and get its // current count. Then put the incremented count. ProfileStack profileStack = new ProfileStack(threadStack); Long currentCount = stackTraces.get(profileStack); if (currentCount == null) { // This is a new trace that we haven't seen, so its count will be 1. stackTraces.put(profileStack, 1L); } else { // This is a repeated stack, so increment its count. stackTraces.put(profileStack, 1L+currentCount.intValue()); } } // Determine how long we should sleep and do so. if (! stopProfiling) { long sleepTime = sampleInterval - (System.currentTimeMillis() - startTime); if (sleepTime > 0) { try { Thread.sleep(sleepTime); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } } } } captureStopTime = System.currentTimeMillis(); captureThread = null; } /** * Causes the profiler thread to stop capturing stack traces. This method * will not return until the thread has stopped. */ public void stopProfiling() { stopProfiling = true; try { if (captureThread != null) { captureThread.join(); } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } } /** * Writes the information captured by this profiler thread to the specified * file. This should only be called after * * @param filename The path and name of the file to write. * * @throws IOException If a problem occurs while trying to write the * capture data. */ public void writeCaptureData(String filename) throws IOException { // Open the capture file for writing. We'll use an ASN.1 writer to write // the data. ASN1Writer writer = ASN1.getWriter(new FileOutputStream(filename)); try { if (captureStartTime < 0) { captureStartTime = System.currentTimeMillis(); captureStopTime = captureStartTime; } else if (captureStopTime < 0) { captureStopTime = System.currentTimeMillis(); } // Write a header to the file containing the number of samples and the // start and stop times. writer.writeStartSequence(); writer.writeInteger(numIntervals); writer.writeInteger(captureStartTime); writer.writeInteger(captureStopTime); writer.writeEndSequence(); // For each unique stack captured, write it to the file followed by the // number of occurrences. for (ProfileStack s : stackTraces.keySet()) { s.write(writer); writer.writeInteger(stackTraces.get(s)); } } finally { // Make sure to close the file when we're done. writer.close(); } } }