1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package compiler.testlibrary.rtm;
  26 
  27 import java.util.EnumMap;
  28 import java.util.LinkedList;
  29 import java.util.List;
  30 import java.util.Map;
  31 import java.util.regex.Matcher;
  32 import java.util.regex.Pattern;
  33 
  34 /**
  35  * Wrapper for +UsePreciseRTMLockingStatistics output.
  36  *
  37  * Example of locking statistics:
  38  *
  39  * java/lang/ClassLoader.loadClass@7
  40  * # rtm locks total (estimated): 0
  41  * # rtm lock aborts  : 13
  42  * # rtm lock aborts 0: 12
  43  * # rtm lock aborts 1: 0
  44  * # rtm lock aborts 2: 0
  45  * # rtm lock aborts 3: 0
  46  * # rtm lock aborts 4: 0
  47  * # rtm lock aborts 5: 0
  48  */
  49 public class RTMLockingStatistics {
  50     /**
  51      * Pattern for aborts per abort type entries.
  52      */
  53     private static final Pattern ABORT_PATTERN;
  54 
  55     /**
  56      * Pattern for whole statistics.
  57      */
  58     private static final Pattern RTM_LOCKING_STATISTICS_PATTERN;
  59 
  60     static {
  61         String abortRe
  62                 = "# rtm lock aborts\\s+(?<type>[0-9]+):\\s(?<count>[0-9]+)";
  63 
  64         ABORT_PATTERN = Pattern.compile(abortRe);
  65         RTM_LOCKING_STATISTICS_PATTERN = Pattern.compile(
  66                 "(?<className>[^.\n]+)\\." +
  67                 "(?<methodName>[^@\n]+)@(?<bci>[0-9]+)\n" +
  68                 "# rtm locks total \\(estimated\\):\\s*" +
  69                 "(?<totalLocks>[0-9]+)\n" +
  70                 "# rtm lock aborts\\s+:\\s*(?<totalAborts>[0-9]+)\n" +
  71                 "(?<abortStats>(" + abortRe + "\n)+)");
  72     }
  73 
  74     private final long totalLocks;
  75     private final long totalAborts;
  76     private final String className;
  77     private final String methodName;
  78     private final int bci;
  79     private final Map<AbortType, Long> aborts = new EnumMap<>(AbortType.class);
  80 
  81     /**
  82      * Constructs RTMLockingStatistics from matcher captured statistics entry.
  83      * @param matcher Matcher captured statistics entry.
  84      */
  85     private RTMLockingStatistics(Matcher matcher) {
  86         className = matcher.group("className");
  87         methodName = matcher.group("methodName");
  88         bci = Integer.valueOf(matcher.group("bci"));
  89         totalLocks = Long.valueOf(matcher.group("totalLocks"));
  90         totalAborts = Long.valueOf(matcher.group("totalAborts"));
  91 
  92         Matcher abortMatcher = ABORT_PATTERN.matcher(matcher.
  93                 group("abortStats"));
  94 
  95         while (abortMatcher.find()) {
  96             int type = Integer.valueOf(abortMatcher.group("type"));
  97             long count = Long.valueOf(abortMatcher.group("count"));
  98             setAborts(AbortType.lookup(type), count);
  99         }
 100     }
 101 
 102 
 103     /**
 104      * Parses string and return all founded RTM locking statistics entries.
 105      *
 106      * @param str the string to be parsed.
 107      * @return list with all founded RTM locking statistics entries or
 108      *         empty list if nothing was found.
 109      */
 110     public static List<RTMLockingStatistics> fromString(String str) {
 111         List<RTMLockingStatistics> statistics = new LinkedList<>();
 112         Matcher matcher = RTM_LOCKING_STATISTICS_PATTERN.matcher(str);
 113 
 114         while (matcher.find()) {
 115             RTMLockingStatistics lock = new RTMLockingStatistics(matcher);
 116             statistics.add(lock);
 117         }
 118 
 119         return statistics;
 120     }
 121 
 122     /**
 123      * Parses string and return all founded RTM locking statistics entries
 124      * for locks in method {@code methodName}.
 125      *
 126      * @param methodName a name of the method for locks from which statistics
 127      *                   should be gathered.
 128      * @param str the string to be parsed.
 129      * @return list with all founded RTM locking statistics entries or
 130      *         empty list if nothing was found.
 131      */
 132     public static List<RTMLockingStatistics> fromString(String methodName,
 133             String str) {
 134         String formattedMethodName = formatMethodName(methodName);
 135 
 136         List<RTMLockingStatistics> statisticsForMethod = new LinkedList<>();
 137         for (RTMLockingStatistics statistics : fromString(str)) {
 138             if (statistics.getLockName().startsWith(formattedMethodName)) {
 139                 statisticsForMethod.add(statistics);
 140             }
 141         }
 142         return statisticsForMethod;
 143     }
 144 
 145     /**
 146      * Formats method's name so it will have the same format as
 147      * in rtm locking statistics.
 148      *
 149      * <pre>
 150      * Example:
 151      * com/example/Klass::method =&gt; com/example/Klass.method
 152      * com/example/Klass.method  =&gt; com/example/Klass.method
 153      * com.example.Klass::method =&gt; com/example/Klass.method
 154      * com.example.Klass.method  =&gt; com/example/Klass.method
 155      * </pre>
 156      *
 157      * @param methodName method's name that should be formatted.
 158      * @return formatted method's name.
 159      */
 160     private static String formatMethodName(String methodName) {
 161         String m[];
 162         if (methodName.contains("::")) {
 163             m = methodName.split("::");
 164         } else {
 165             int splitAt = methodName.lastIndexOf('.');
 166             m = new String[2];
 167             m[0] = methodName.substring(0, splitAt);
 168             m[1] = methodName.substring(splitAt + 1);
 169         }
 170         return String.format("%s.%s", m[0].replaceAll("\\.", "/"), m[1]);
 171     }
 172 
 173     /**
 174      * Returns name of lock for which this statistics was collected.
 175      * Lock name has following format:
 176      * &lt;class name&gt;.&lt;method name&gt;@&lt;bci&gt;
 177      *
 178      * @return name of lock.
 179      */
 180     public String getLockName() {
 181         return String.format("%s.%s@%d", className, methodName, bci);
 182     }
 183 
 184     /**
 185      * Returns aborts count for specified abort type.
 186      *
 187      * @param type an abort type.
 188      * @return count of aborts.
 189      */
 190     public long getAborts(AbortType type) {
 191         return aborts.getOrDefault(type, 0L);
 192     }
 193 
 194     /**
 195      * Sets aborts count for specified abort type.
 196      *
 197      * @param type an abort type.
 198      * @param count count of aborts.
 199      */
 200     public void setAborts(AbortType type, long count) {
 201         aborts.put(type, count);
 202     }
 203 
 204     public long getTotalLocks() {
 205         return totalLocks;
 206     }
 207 
 208     public long getTotalAborts() {
 209         return totalAborts;
 210     }
 211 
 212     @Override
 213     public String toString() {
 214         StringBuilder builder = new StringBuilder();
 215         builder.append(getLockName()).append('\n');
 216         builder.append(String.format("# rtm locks total (estimated): %d\n",
 217                 getTotalLocks()));
 218         builder.append(String.format("# rtm lock aborts: %d\n",
 219                 getTotalLocks()));
 220 
 221         for (AbortType type : AbortType.values()) {
 222             builder.append(String.format("# rtm lock aborts %s %d\n",
 223                     type.toString(), getAborts(type)));
 224         }
 225         return builder.toString();
 226     }
 227 }