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