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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.plugins.profiler;
028    
029    
030    
031    import java.util.ArrayList;
032    import org.opends.server.protocols.asn1.ASN1Element;
033    import org.opends.server.protocols.asn1.ASN1OctetString;
034    import org.opends.server.protocols.asn1.ASN1Sequence;
035    
036    import static org.opends.server.loggers.debug.DebugLogger.*;
037    import org.opends.server.loggers.debug.DebugTracer;
038    import org.opends.server.types.DebugLogLevel;
039    
040    
041    /**
042     * This class defines a data structure that may be used to hold information
043     * about a thread stack trace.
044     */
045    public class ProfileStack
046    {
047      /**
048       * The tracer object for the debug logger.
049       */
050      private static final DebugTracer TRACER = getTracer();
051    
052    
053    
054    
055      /**
056       * The line number that will be used for stack frames in which the line number
057       * is unknown but it is not a native method.
058       */
059      public static final int LINE_NUMBER_UNKNOWN = -1;
060    
061    
062    
063      /**
064       * The line number that will be used for stack frames in which the line number
065       * is unknown because it is a native method.
066       */
067      public static final int LINE_NUMBER_NATIVE = -2;
068    
069    
070    
071      // The number of frames in this stack.
072      private int numFrames;
073    
074      // The source file line numbers for each of the frames in this stack.
075      private int[] lineNumbers;
076    
077      // The class names for each of the frames in this stack.
078      private String[] classNames;
079    
080      // The method names for each of the frames in this stack.
081      private String[] methodNames;
082    
083    
084    
085      /**
086       * Creates a new profile stack with the provided information.
087       *
088       * @param  stackElements  The stack trace elements to use to create this
089       *                        profile stack.
090       */
091      public ProfileStack(StackTraceElement[] stackElements)
092      {
093        numFrames   = stackElements.length;
094        classNames  = new String[numFrames];
095        methodNames = new String[numFrames];
096        lineNumbers = new int[numFrames];
097    
098        for (int i=0, j=(numFrames-1); i < numFrames; i++,j--)
099        {
100          classNames[i]  = stackElements[j].getClassName();
101          methodNames[i] = stackElements[j].getMethodName();
102          lineNumbers[i] = stackElements[j].getLineNumber();
103    
104          if (lineNumbers[i] <= 0)
105          {
106            if (stackElements[j].isNativeMethod())
107            {
108              lineNumbers[i] = LINE_NUMBER_NATIVE;
109            }
110            else
111            {
112              lineNumbers[i] = LINE_NUMBER_UNKNOWN;
113            }
114          }
115        }
116      }
117    
118    
119    
120      /**
121       * Creates a new profile stack with the provided information.
122       *
123       * @param  classNames   The class names for the frames in this stack.
124       * @param  methodNames  The method names for the frames in this stack.
125       * @param  lineNumbers  The line numbers for the frames in this stack.
126       */
127      private ProfileStack(String[] classNames, String[] methodNames,
128                           int[] lineNumbers)
129      {
130        this.numFrames   = classNames.length;
131        this.classNames  = classNames;
132        this.methodNames = methodNames;
133        this.lineNumbers = lineNumbers;
134      }
135    
136    
137    
138      /**
139       * Retrieves the number of frames in this stack.
140       *
141       * @return  The number of frames in this stack.
142       */
143      public int getNumFrames()
144      {
145        return numFrames;
146      }
147    
148    
149    
150      /**
151       * Retrieves the class names in this stack.
152       *
153       * @return  The class names in this stack.
154       */
155      public String[] getClassNames()
156      {
157        return classNames;
158      }
159    
160    
161    
162      /**
163       * Retrieves the class name from the specified frame in the stack.
164       *
165       * @param  depth  The depth of the frame to retrieve, with the first frame
166       *                being frame zero.
167       *
168       * @return  The class name from the specified frame in the stack.
169       */
170      public String getClassName(int depth)
171      {
172        return classNames[depth];
173      }
174    
175    
176    
177      /**
178       * Retrieves the method names in this stack.
179       *
180       * @return  The method names in this stack.
181       */
182      public String[] getMethodNames()
183      {
184        return methodNames;
185      }
186    
187    
188    
189      /**
190       * Retrieves the method name from the specified frame in the stack.
191       *
192       * @param  depth  The depth of the frame to retrieve, with the first frame
193       *                being frame zero.
194       *
195       * @return  The method name from the specified frame in the stack.
196       */
197      public String getMethodName(int depth)
198      {
199        return methodNames[depth];
200      }
201    
202    
203    
204      /**
205       * Retrieves the line numbers in this stack.
206       *
207       * @return  The line numbers in this stack.
208       */
209      public int[] getLineNumbers()
210      {
211        return lineNumbers;
212      }
213    
214    
215    
216      /**
217       * Retrieves the line number from the specified frame in the stack.
218       *
219       * @param  depth  The depth of the frame for which to retrieve the line
220       *                number.
221       *
222       * @return  The line number from the specified frame in the stack.
223       */
224      public int getLineNumber(int depth)
225      {
226        return lineNumbers[depth];
227      }
228    
229    
230    
231      /**
232       * Retrieves the hash code for this profile stack.  It will be the sum of the
233       * hash codes for the class and method name and line number for the first
234       * frame.
235       *
236       * @return  The hash code for this profile stack.
237       */
238      public int hashCode()
239      {
240        if (numFrames == 0)
241        {
242          return 0;
243        }
244        else
245        {
246          return (classNames[0].hashCode() + methodNames[0].hashCode() +
247                  lineNumbers[0]);
248        }
249      }
250    
251    
252    
253      /**
254       * Indicates whether to the provided object is equal to this profile stack.
255       *
256       * @param  o  The object for which to make the determination.
257       *
258       * @return  <CODE>true</CODE> if the provided object is a profile stack object
259       *          with the same set of class names, method names, and line numbers
260       *          as this profile stack, or <CODE>false</CODE> if not.
261       */
262      public boolean equals(Object o)
263      {
264        if (o == null)
265        {
266          return false;
267        }
268        else if (this == o)
269        {
270          return true;
271        }
272    
273    
274        try
275        {
276          ProfileStack s = (ProfileStack) o;
277    
278          if (numFrames != s.numFrames)
279          {
280            return false;
281          }
282    
283          for (int i=0; i < numFrames; i++)
284          {
285            if ((lineNumbers[i] != s.lineNumbers[i]) ||
286                (! classNames[i].equals(s.classNames[i])) ||
287                (! methodNames[i].equals(s.methodNames[i])))
288            {
289              return false;
290            }
291          }
292    
293          return true;
294        }
295        catch (Exception e)
296        {
297          if (debugEnabled())
298          {
299            TRACER.debugCaught(DebugLogLevel.ERROR, e);
300          }
301    
302          return false;
303        }
304      }
305    
306    
307    
308      /**
309       * Encodes this profile stack for writing to the capture file.
310       *
311       * @return  The ASN.1 element containing the encoded representation of this
312       *          profile stack.
313       */
314      public ASN1Element encode()
315      {
316        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3*numFrames);
317        for (int i=0; i < numFrames; i++)
318        {
319          elements.add(new ASN1OctetString(classNames[i]));
320          elements.add(new ASN1OctetString(methodNames[i]));
321          elements.add(new ASN1OctetString(String.valueOf(lineNumbers[i])));
322        }
323    
324        return new ASN1Sequence(elements);
325      }
326    
327    
328    
329      /**
330       * Decodes the contents of the provided element as a profile stack.
331       *
332       * @param  stackElement  The ASN.1 element containing the encoded profile
333       *                       stack information.
334       *
335       * @return  The decoded profile stack, or <CODE>null</CODE> if the element
336       *          could not be decoded for some reason.
337       */
338      public static ProfileStack decode(ASN1Element stackElement)
339      {
340        try
341        {
342          ArrayList<ASN1Element> elements =
343               stackElement.decodeAsSequence().elements();
344    
345          int      numFrames   = (elements.size() / 3);
346          String[] classNames  = new String[numFrames];
347          String[] methodNames = new String[numFrames];
348          int[]    lineNumbers = new int[numFrames];
349    
350          for (int i=0,j=0; i < numFrames; i++, j+=3)
351          {
352            classNames[i]  = elements.get(j).decodeAsOctetString().stringValue();
353            methodNames[i] = elements.get(j+1).decodeAsOctetString().stringValue();
354            lineNumbers[i] =
355                 Integer.parseInt(
356                      elements.get(j+2).decodeAsOctetString().stringValue());
357          }
358    
359          return new ProfileStack(classNames, methodNames, lineNumbers);
360        }
361        catch (Exception e)
362        {
363          if (debugEnabled())
364          {
365            TRACER.debugCaught(DebugLogLevel.ERROR, e);
366          }
367    
368          return null;
369        }
370      }
371    }
372