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.Arrays;
032    import java.util.HashMap;
033    
034    import static org.opends.server.loggers.debug.DebugLogger.*;
035    import org.opends.server.loggers.debug.DebugTracer;
036    import org.opends.server.types.DebugLogLevel;
037    
038    
039    /**
040     * This class defines a data structure for holding information about a stack
041     * frame captured by the Directory Server profiler.  It will contain the class
042     * and method name for this frame, the set of line numbers within that method
043     * that were captured along with the number of times they were seen, as well as
044     * references to subordinate frames that were encountered.
045     */
046    public class ProfileStackFrame
047           implements Comparable
048    {
049      /**
050       * The tracer object for the debug logger.
051       */
052      private static final DebugTracer TRACER = getTracer();
053    
054    
055    
056    
057      // The mapping between the line numbers for this stack frame and the
058      // number of times that they were encountered.
059      private HashMap<Integer,Long> lineNumbers;
060    
061      // The mapping for subordinate frames.  It is mapped to itself because we
062      // use a fuzzy equality comparison and sets do not have a get method that
063      // can be used to retrieve a specified object.
064      private HashMap<ProfileStackFrame,ProfileStackFrame> subordinateFrames;
065    
066      // The class name for this stack frame.
067      private String className;
068    
069      // The method name for this stack frame.
070      private String methodName;
071    
072    
073    
074      /**
075       * Creates a new profile stack frame with the provided information.
076       *
077       * @param  className   The class name for use in this stack frame.
078       * @param  methodName  The method name for use in this stack frame.
079       */
080      public ProfileStackFrame(String className, String methodName)
081      {
082        this.className  = className;
083        this.methodName = methodName;
084    
085        lineNumbers       = new HashMap<Integer,Long>();
086        subordinateFrames = new HashMap<ProfileStackFrame,ProfileStackFrame>();
087      }
088    
089    
090    
091      /**
092       * Retrieves the class name for this stack frame.
093       *
094       * @return  The class name for this stack frame.
095       */
096      public String getClassName()
097      {
098        return className;
099      }
100    
101    
102    
103      /**
104       * Retrieves the method name for this stack frame.
105       *
106       * @return  The method name for this stack frame.
107       */
108      public String getMethodName()
109      {
110        return methodName;
111      }
112    
113    
114    
115      /**
116       * Retrieves the method name for this stack frame in a manner that will be
117       * safe for use in an HTML context.  Currently, this simply replaces angle
118       * brackets with the appropriate HTML equivalent.
119       *
120       * @return  The generated safe name.
121       */
122      public String getHTMLSafeMethodName()
123      {
124        int length = methodName.length();
125        StringBuilder buffer = new StringBuilder(length + 6);
126    
127        for (int i=0; i < length; i++)
128        {
129          char c = methodName.charAt(i);
130          if (c == '<')
131          {
132            buffer.append("&lt;");
133          }
134          else if (c == '>')
135          {
136            buffer.append("&gt;");
137          }
138          else
139          {
140            buffer.append(c);
141          }
142        }
143    
144        return buffer.toString();
145      }
146    
147    
148    
149      /**
150       * Retrieves the mapping between the line numbers associated with this method
151       * and the number of occurrences for each of those line numbers.
152       *
153       * @return  The mapping between the line numbers associated with this method
154       *          and the number of occurrences for each of those line numbers.
155       */
156      public HashMap<Integer,Long> getLineNumbers()
157      {
158        return lineNumbers;
159      }
160    
161    
162    
163      /**
164       * Updates the count for the number of occurrences of a given stack frame
165       * for the specified line number.
166       *
167       * @param  lineNumber      The line number for which to update the count.
168       * @param  numOccurrences  The number of times the specified line was
169       *                         encountered for this stack frame.
170       */
171      public void updateLineNumberCount(int lineNumber, long numOccurrences)
172      {
173        Long existingCount = lineNumbers.get(lineNumber);
174        if (existingCount == null)
175        {
176          lineNumbers.put(lineNumber, numOccurrences);
177        }
178        else
179        {
180          lineNumbers.put(lineNumber, existingCount+numOccurrences);
181        }
182      }
183    
184    
185    
186      /**
187       * Retrieves the total number of times that a frame with this class and
188       * method name was seen by the profiler thread.
189       *
190       * @return  The total number of times that a frame with this class and method
191       *          name was seen by the profiler thread.
192       */
193      public long getTotalCount()
194      {
195        long totalCount = 0;
196    
197        for (Long l : lineNumbers.values())
198        {
199          totalCount += l;
200        }
201    
202        return totalCount;
203      }
204    
205    
206    
207      /**
208       * Retrieves an array containing the subordinate frames that were seen below
209       * this frame in stack traces.  The elements of the array will be sorted in
210       * descending order of the number of occurrences.
211       *
212       * @return  An array containing the subordinate frames that were seen below
213       *          this frame in stack traces.
214       */
215      public ProfileStackFrame[] getSubordinateFrames()
216      {
217        ProfileStackFrame[] subFrames = new ProfileStackFrame[0];
218        subFrames = subordinateFrames.values().toArray(subFrames);
219    
220        Arrays.sort(subFrames);
221    
222        return subFrames;
223      }
224    
225    
226    
227      /**
228       * Indicates whether this stack frame has one or more subordinate frames.
229       *
230       * @return  <CODE>true</CODE> if this stack frame has one or more subordinate
231       *          frames, or <CODE>false</CODE> if not.
232       */
233      public boolean hasSubFrames()
234      {
235        return (! subordinateFrames.isEmpty());
236      }
237    
238    
239    
240      /**
241       * Recursively processes the frames of the provided stack, adding them as
242       * nested subordinate frames of this stack frame.
243       *
244       * @param  stack           The stack trace to use to obtain the frames.
245       * @param  depth           The slot of the next frame to process in the
246       *                         provided array.
247       * @param  count           The number of occurrences for the provided stack.
248       * @param  stacksByMethod  The set of stack traces mapped from method name to
249       *                         their corresponding stack traces.
250       */
251      public void recurseSubFrames(ProfileStack stack, int depth, long count,
252                       HashMap<String,HashMap<ProfileStack,Long>> stacksByMethod)
253      {
254        if (depth < 0)
255        {
256          return;
257        }
258    
259        String cName = stack.getClassName(depth);
260        String mName = stack.getMethodName(depth);
261        ProfileStackFrame f = new ProfileStackFrame(cName, mName);
262    
263        int lineNumber = stack.getLineNumber(depth);
264    
265        ProfileStackFrame subFrame = subordinateFrames.get(f);
266        if (subFrame == null)
267        {
268          subFrame = f;
269          subordinateFrames.put(subFrame, subFrame);
270        }
271    
272        subFrame.updateLineNumberCount(lineNumber, count);
273    
274    
275        String classAndMethod = cName + "." + mName;
276        HashMap<ProfileStack,Long> stackMap = stacksByMethod.get(classAndMethod);
277        if (stackMap == null)
278        {
279          stackMap = new HashMap<ProfileStack,Long>();
280          stacksByMethod.put(classAndMethod, stackMap);
281        }
282        stackMap.put(stack, count);
283    
284        subFrame.recurseSubFrames(stack, (depth-1), count, stacksByMethod);
285      }
286    
287    
288    
289      /**
290       * Retrieves the hash code for this stack frame.  It will be the sum of the
291       * hash codes for the class and method name.
292       *
293       * @return  The hash code for this stack frame.
294       */
295      public int hashCode()
296      {
297        return (className.hashCode() + methodName.hashCode());
298      }
299    
300    
301    
302      /**
303       * Indicates whether the provided object is equal to this stack frame.  It
304       * will be considered equal if it is a profile stack frame with the same class
305       * and method name.
306       *
307       * @param  o  The object for which to make the determination.
308       *
309       * @return  <CODE>true</CODE> if the provided object may be considered equal
310       *          to this stack frame, or <CODE>false</CODE> if not.
311       */
312      public boolean equals(Object o)
313      {
314        if (o == null)
315        {
316          return false;
317        }
318        else if (this == o)
319        {
320          return true;
321        }
322    
323        try
324        {
325          ProfileStackFrame f = (ProfileStackFrame) o;
326          return (className.equals(f.className) && methodName.equals(f.methodName));
327        }
328        catch (Exception e)
329        {
330          if (debugEnabled())
331          {
332            TRACER.debugCaught(DebugLogLevel.ERROR, e);
333          }
334    
335          return false;
336        }
337      }
338    
339    
340    
341      /**
342       * Indicates the order of this profile stack frame relative to the provided
343       * object in a sorted list.  The order will be primarily based on number of
344       * occurrences, with an equivalent number of occurrences falling back on
345       * alphabetical by class and method names.
346       *
347       * @param  o  The objectfor which to make the comparison.
348       *
349       * @return  A negative integer if this stack frame should come before the
350       *          provided object in a sorted list, a positive integer if it should
351       *          come after the provided object, or zero if they should have
352       *          equivalent order.
353       *
354       * @throws  ClassCastException  If the provided object is not a profile stack
355       *                              frame.
356       */
357      public int compareTo(Object o)
358             throws ClassCastException
359      {
360        ProfileStackFrame f;
361        try
362        {
363          f = (ProfileStackFrame) o;
364        }
365        catch (ClassCastException cce)
366        {
367          throw cce;
368        }
369    
370        long thisCount = getTotalCount();
371        long thatCount = f.getTotalCount();
372        if (thisCount > thatCount)
373        {
374          return -1;
375        }
376        else if (thisCount < thatCount)
377        {
378          return 1;
379        }
380    
381        int value = className.compareTo(f.className);
382        if (value == 0)
383        {
384          value = methodName.compareTo(f.methodName);
385        }
386    
387        return value;
388      }
389    
390    
391    
392      /**
393       * Retrieves a string representation of this stack frame.  It will contain the
394       * total number of matching frames, the class name, and the method name.
395       *
396       * @return  A string representation of this stack frame.
397       */
398      public String toString()
399      {
400        StringBuilder buffer = new StringBuilder();
401        buffer.append(getTotalCount());
402        buffer.append("    ");
403        buffer.append(className);
404        buffer.append('.');
405        buffer.append(methodName);
406    
407        return buffer.toString();
408      }
409    }
410