/*
* 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 legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* 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 legal-notices/CDDLv1_0.txt.
* 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.
* Portions Copyright 2014-2015 ForgeRock AS
*/
package org.opends.server.plugins.profiler;
import java.io.IOException;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.io.ASN1Reader;
import org.forgerock.opendj.io.ASN1Writer;
/**
* This class defines a data structure that may be used to hold information
* about a thread stack trace.
*/
public class ProfileStack
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/**
* The line number that will be used for stack frames in which the line number
* is unknown but it is not a native method.
*/
public static final int LINE_NUMBER_UNKNOWN = -1;
/**
* The line number that will be used for stack frames in which the line number
* is unknown because it is a native method.
*/
public static final int LINE_NUMBER_NATIVE = -2;
/** The number of frames in this stack. */
private int numFrames;
/** The source file line numbers for each of the frames in this stack. */
private int[] lineNumbers;
/** The class names for each of the frames in this stack. */
private String[] classNames;
/** The method names for each of the frames in this stack. */
private String[] methodNames;
/**
* Creates a new profile stack with the provided information.
*
* @param stackElements The stack trace elements to use to create this
* profile stack.
*/
public ProfileStack(StackTraceElement[] stackElements)
{
numFrames = stackElements.length;
classNames = new String[numFrames];
methodNames = new String[numFrames];
lineNumbers = new int[numFrames];
for (int i=0, j=numFrames-1; i < numFrames; i++,j--)
{
classNames[i] = stackElements[j].getClassName();
methodNames[i] = stackElements[j].getMethodName();
lineNumbers[i] = stackElements[j].getLineNumber();
if (lineNumbers[i] <= 0)
{
if (stackElements[j].isNativeMethod())
{
lineNumbers[i] = LINE_NUMBER_NATIVE;
}
else
{
lineNumbers[i] = LINE_NUMBER_UNKNOWN;
}
}
}
}
/**
* Creates a new profile stack with the provided information.
*
* @param classNames The class names for the frames in this stack.
* @param methodNames The method names for the frames in this stack.
* @param lineNumbers The line numbers for the frames in this stack.
*/
private ProfileStack(String[] classNames, String[] methodNames,
int[] lineNumbers)
{
this.numFrames = classNames.length;
this.classNames = classNames;
this.methodNames = methodNames;
this.lineNumbers = lineNumbers;
}
/**
* Retrieves the number of frames in this stack.
*
* @return The number of frames in this stack.
*/
public int getNumFrames()
{
return numFrames;
}
/**
* Retrieves the class names in this stack.
*
* @return The class names in this stack.
*/
public String[] getClassNames()
{
return classNames;
}
/**
* Retrieves the class name from the specified frame in the stack.
*
* @param depth The depth of the frame to retrieve, with the first frame
* being frame zero.
*
* @return The class name from the specified frame in the stack.
*/
public String getClassName(int depth)
{
return classNames[depth];
}
/**
* Retrieves the method names in this stack.
*
* @return The method names in this stack.
*/
public String[] getMethodNames()
{
return methodNames;
}
/**
* Retrieves the method name from the specified frame in the stack.
*
* @param depth The depth of the frame to retrieve, with the first frame
* being frame zero.
*
* @return The method name from the specified frame in the stack.
*/
public String getMethodName(int depth)
{
return methodNames[depth];
}
/**
* Retrieves the line numbers in this stack.
*
* @return The line numbers in this stack.
*/
public int[] getLineNumbers()
{
return lineNumbers;
}
/**
* Retrieves the line number from the specified frame in the stack.
*
* @param depth The depth of the frame for which to retrieve the line
* number.
*
* @return The line number from the specified frame in the stack.
*/
public int getLineNumber(int depth)
{
return lineNumbers[depth];
}
/**
* Retrieves the hash code for this profile stack. It will be the sum of the
* hash codes for the class and method name and line number for the first
* frame.
*
* @return The hash code for this profile stack.
*/
public int hashCode()
{
if (numFrames != 0)
{
return classNames[0].hashCode() + methodNames[0].hashCode() + lineNumbers[0];
}
return 0;
}
/**
* Indicates whether to the provided object is equal to this profile stack.
*
* @param o The object for which to make the determination.
*
* @return <CODE>true</CODE> if the provided object is a profile stack object
* with the same set of class names, method names, and line numbers
* as this profile stack, or <CODE>false</CODE> if not.
*/
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
else if (this == o)
{
return true;
}
try
{
ProfileStack s = (ProfileStack) o;
if (numFrames != s.numFrames)
{
return false;
}
for (int i=0; i < numFrames; i++)
{
if (lineNumbers[i] != s.lineNumbers[i] ||
!classNames[i].equals(s.classNames[i]) ||
!methodNames[i].equals(s.methodNames[i]))
{
return false;
}
}
return true;
}
catch (Exception e)
{
logger.traceException(e);
return false;
}
}
/**
* Encodes and writes this profile stack to the capture file.
*
* @param writer The writer to use.
* @throws IOException if an error occurs while writing.
*/
public void write(ASN1Writer writer) throws IOException
{
writer.writeStartSequence();
writer.writeInteger(numFrames);
for (int i=0; i < numFrames; i++)
{
writer.writeOctetString(classNames[i]);
writer.writeOctetString(methodNames[i]);
writer.writeInteger(lineNumbers[i]);
}
writer.writeEndSequence();
}
/**
* Decodes the contents of the provided element as a profile stack.
*
* @param reader The ASN.1 reader to read the encoded profile stack
* information from.
*
* @return The decoded profile stack.
* @throws IOException If the element could not be decoded for some reason.
*/
public static ProfileStack decode(ASN1Reader reader) throws IOException
{
reader.readStartSequence();
int numFrames = (int)reader.readInteger();
String[] classNames = new String[numFrames];
String[] methodNames = new String[numFrames];
int[] lineNumbers = new int[numFrames];
int i = 0;
while(reader.hasNextElement())
{
classNames[i] = reader.readOctetStringAsString();
methodNames[i] = reader.readOctetStringAsString();
lineNumbers[i] = (int)reader.readInteger();
i++;
}
reader.readEndSequence();
return new ProfileStack(classNames, methodNames, lineNumbers);
}
}