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("<");
133 }
134 else if (c == '>')
135 {
136 buffer.append(">");
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