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