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