001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.plugins.profiler;
018
019import java.io.IOException;
020
021import org.forgerock.i18n.slf4j.LocalizedLogger;
022import org.forgerock.opendj.io.ASN1Reader;
023import org.forgerock.opendj.io.ASN1Writer;
024
025/**
026 * This class defines a data structure that may be used to hold information
027 * about a thread stack trace.
028 */
029public class ProfileStack
030{
031  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
032
033
034
035
036  /**
037   * The line number that will be used for stack frames in which the line number
038   * is unknown but it is not a native method.
039   */
040  public static final int LINE_NUMBER_UNKNOWN = -1;
041
042
043
044  /**
045   * The line number that will be used for stack frames in which the line number
046   * is unknown because it is a native method.
047   */
048  public static final int LINE_NUMBER_NATIVE = -2;
049
050
051
052  /** The number of frames in this stack. */
053  private int numFrames;
054
055  /** The source file line numbers for each of the frames in this stack. */
056  private int[] lineNumbers;
057
058  /** The class names for each of the frames in this stack. */
059  private String[] classNames;
060
061  /** The method names for each of the frames in this stack. */
062  private String[] methodNames;
063
064
065
066  /**
067   * Creates a new profile stack with the provided information.
068   *
069   * @param  stackElements  The stack trace elements to use to create this
070   *                        profile stack.
071   */
072  public ProfileStack(StackTraceElement[] stackElements)
073  {
074    numFrames   = stackElements.length;
075    classNames  = new String[numFrames];
076    methodNames = new String[numFrames];
077    lineNumbers = new int[numFrames];
078
079    for (int i=0, j=numFrames-1; i < numFrames; i++,j--)
080    {
081      classNames[i]  = stackElements[j].getClassName();
082      methodNames[i] = stackElements[j].getMethodName();
083      lineNumbers[i] = stackElements[j].getLineNumber();
084
085      if (lineNumbers[i] <= 0)
086      {
087        if (stackElements[j].isNativeMethod())
088        {
089          lineNumbers[i] = LINE_NUMBER_NATIVE;
090        }
091        else
092        {
093          lineNumbers[i] = LINE_NUMBER_UNKNOWN;
094        }
095      }
096    }
097  }
098
099
100
101  /**
102   * Creates a new profile stack with the provided information.
103   *
104   * @param  classNames   The class names for the frames in this stack.
105   * @param  methodNames  The method names for the frames in this stack.
106   * @param  lineNumbers  The line numbers for the frames in this stack.
107   */
108  private ProfileStack(String[] classNames, String[] methodNames,
109                       int[] lineNumbers)
110  {
111    this.numFrames   = classNames.length;
112    this.classNames  = classNames;
113    this.methodNames = methodNames;
114    this.lineNumbers = lineNumbers;
115  }
116
117
118
119  /**
120   * Retrieves the number of frames in this stack.
121   *
122   * @return  The number of frames in this stack.
123   */
124  public int getNumFrames()
125  {
126    return numFrames;
127  }
128
129
130
131  /**
132   * Retrieves the class names in this stack.
133   *
134   * @return  The class names in this stack.
135   */
136  public String[] getClassNames()
137  {
138    return classNames;
139  }
140
141
142
143  /**
144   * Retrieves the class name from the specified frame in the stack.
145   *
146   * @param  depth  The depth of the frame to retrieve, with the first frame
147   *                being frame zero.
148   *
149   * @return  The class name from the specified frame in the stack.
150   */
151  public String getClassName(int depth)
152  {
153    return classNames[depth];
154  }
155
156
157
158  /**
159   * Retrieves the method names in this stack.
160   *
161   * @return  The method names in this stack.
162   */
163  public String[] getMethodNames()
164  {
165    return methodNames;
166  }
167
168
169
170  /**
171   * Retrieves the method name from the specified frame in the stack.
172   *
173   * @param  depth  The depth of the frame to retrieve, with the first frame
174   *                being frame zero.
175   *
176   * @return  The method name from the specified frame in the stack.
177   */
178  public String getMethodName(int depth)
179  {
180    return methodNames[depth];
181  }
182
183
184
185  /**
186   * Retrieves the line numbers in this stack.
187   *
188   * @return  The line numbers in this stack.
189   */
190  public int[] getLineNumbers()
191  {
192    return lineNumbers;
193  }
194
195
196
197  /**
198   * Retrieves the line number from the specified frame in the stack.
199   *
200   * @param  depth  The depth of the frame for which to retrieve the line
201   *                number.
202   *
203   * @return  The line number from the specified frame in the stack.
204   */
205  public int getLineNumber(int depth)
206  {
207    return lineNumbers[depth];
208  }
209
210
211
212  /**
213   * Retrieves the hash code for this profile stack.  It will be the sum of the
214   * hash codes for the class and method name and line number for the first
215   * frame.
216   *
217   * @return  The hash code for this profile stack.
218   */
219  @Override
220  public int hashCode()
221  {
222    if (numFrames != 0)
223    {
224      return classNames[0].hashCode() + methodNames[0].hashCode() + lineNumbers[0];
225    }
226    return 0;
227  }
228
229
230
231  /**
232   * Indicates whether to the provided object is equal to this profile stack.
233   *
234   * @param  o  The object for which to make the determination.
235   *
236   * @return  <CODE>true</CODE> if the provided object is a profile stack object
237   *          with the same set of class names, method names, and line numbers
238   *          as this profile stack, or <CODE>false</CODE> if not.
239   */
240  @Override
241  public boolean equals(Object o)
242  {
243    if (o == null)
244    {
245      return false;
246    }
247    else if (this == o)
248    {
249      return true;
250    }
251
252
253    try
254    {
255      ProfileStack s = (ProfileStack) o;
256
257      if (numFrames != s.numFrames)
258      {
259        return false;
260      }
261
262      for (int i=0; i < numFrames; i++)
263      {
264        if (lineNumbers[i] != s.lineNumbers[i] ||
265            !classNames[i].equals(s.classNames[i]) ||
266            !methodNames[i].equals(s.methodNames[i]))
267        {
268          return false;
269        }
270      }
271
272      return true;
273    }
274    catch (Exception e)
275    {
276      logger.traceException(e);
277
278      return false;
279    }
280  }
281
282
283
284  /**
285   * Encodes and writes this profile stack to the capture file.
286   *
287   * @param  writer The writer to use.
288   * @throws IOException if an error occurs while writing.
289   */
290  public void write(ASN1Writer writer) throws IOException
291  {
292    writer.writeStartSequence();
293    writer.writeInteger(numFrames);
294    for (int i=0; i < numFrames; i++)
295    {
296      writer.writeOctetString(classNames[i]);
297      writer.writeOctetString(methodNames[i]);
298      writer.writeInteger(lineNumbers[i]);
299    }
300    writer.writeEndSequence();
301  }
302
303
304
305  /**
306   * Decodes the contents of the provided element as a profile stack.
307   *
308   * @param  reader  The ASN.1 reader to read the encoded profile stack
309   *                 information from.
310   *
311   * @return  The decoded profile stack.
312   * @throws IOException If the element could not be decoded for some reason.
313   */
314  public static ProfileStack decode(ASN1Reader reader) throws IOException
315  {
316    reader.readStartSequence();
317
318    int      numFrames   = (int)reader.readInteger();
319    String[] classNames  = new String[numFrames];
320    String[] methodNames = new String[numFrames];
321    int[]    lineNumbers = new int[numFrames];
322
323    int i = 0;
324    while(reader.hasNextElement())
325    {
326      classNames[i]  = reader.readOctetStringAsString();
327      methodNames[i] = reader.readOctetStringAsString();
328      lineNumbers[i] = (int)reader.readInteger();
329      i++;
330    }
331
332    reader.readEndSequence();
333
334    return new ProfileStack(classNames, methodNames, lineNumbers);
335  }
336}
337