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
24package compiler.testlibrary.rtm;
25
26import java.util.EnumMap;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30import java.util.regex.Matcher;
31import java.util.regex.Pattern;
32
33/**
34 * Wrapper for +UsePreciseRTMLockingStatistics output.
35 *
36 * Example of locking statistics:
37 *
38 * java/lang/ClassLoader.loadClass@7
39 * # rtm locks total (estimated): 0
40 * # rtm lock aborts  : 13
41 * # rtm lock aborts 0: 12
42 * # rtm lock aborts 1: 0
43 * # rtm lock aborts 2: 0
44 * # rtm lock aborts 3: 0
45 * # rtm lock aborts 4: 0
46 * # rtm lock aborts 5: 0
47 */
48public class RTMLockingStatistics {
49    /**
50     * Pattern for aborts per abort type entries.
51     */
52    private static final Pattern ABORT_PATTERN;
53
54    /**
55     * Pattern for whole statistics.
56     */
57    private static final Pattern RTM_LOCKING_STATISTICS_PATTERN;
58
59    static {
60        String abortRe
61                = "# rtm lock aborts\\s+(?<type>[0-9]+):\\s(?<count>[0-9]+)";
62
63        ABORT_PATTERN = Pattern.compile(abortRe);
64        RTM_LOCKING_STATISTICS_PATTERN = Pattern.compile(
65                "(?<className>[^.\n]+)\\." +
66                "(?<methodName>[^@\n]+)@(?<bci>[0-9]+)\n" +
67                "# rtm locks total \\(estimated\\):\\s*" +
68                "(?<totalLocks>[0-9]+)\n" +
69                "# rtm lock aborts\\s+:\\s*(?<totalAborts>[0-9]+)\n" +
70                "(?<abortStats>(" + abortRe + "\n)+)");
71    }
72
73    private final long totalLocks;
74    private final long totalAborts;
75    private final String className;
76    private final String methodName;
77    private final int bci;
78    private final Map<AbortType, Long> aborts = new EnumMap<>(AbortType.class);
79
80    /**
81     * Constructs RTMLockingStatistics from matcher captured statistics entry.
82     * @param matcher Matcher captured statistics entry.
83     */
84    private RTMLockingStatistics(Matcher matcher) {
85        className = matcher.group("className");
86        methodName = matcher.group("methodName");
87        bci = Integer.valueOf(matcher.group("bci"));
88        totalLocks = Long.valueOf(matcher.group("totalLocks"));
89        totalAborts = Long.valueOf(matcher.group("totalAborts"));
90
91        Matcher abortMatcher = ABORT_PATTERN.matcher(matcher.
92                group("abortStats"));
93
94        while (abortMatcher.find()) {
95            int type = Integer.valueOf(abortMatcher.group("type"));
96            long count = Long.valueOf(abortMatcher.group("count"));
97            setAborts(AbortType.lookup(type), count);
98        }
99    }
100
101
102    /**
103     * Parses string and return all founded RTM locking statistics entries.
104     *
105     * @param str the string to be parsed.
106     * @return list with all founded RTM locking statistics entries or
107     *         empty list if nothing was found.
108     */
109    public static List<RTMLockingStatistics> fromString(String str) {
110        List<RTMLockingStatistics> statistics = new LinkedList<>();
111        Matcher matcher = RTM_LOCKING_STATISTICS_PATTERN.matcher(str);
112
113        while (matcher.find()) {
114            RTMLockingStatistics lock = new RTMLockingStatistics(matcher);
115            statistics.add(lock);
116        }
117
118        return statistics;
119    }
120
121    /**
122     * Parses string and return all founded RTM locking statistics entries
123     * for locks in method {@code methodName}.
124     *
125     * @param methodName a name of the method for locks from which statistics
126     *                   should be gathered.
127     * @param str the string to be parsed.
128     * @return list with all founded RTM locking statistics entries or
129     *         empty list if nothing was found.
130     */
131    public static List<RTMLockingStatistics> fromString(String methodName,
132            String str) {
133        String formattedMethodName = formatMethodName(methodName);
134
135        List<RTMLockingStatistics> statisticsForMethod = new LinkedList<>();
136        for (RTMLockingStatistics statistics : fromString(str)) {
137            if (statistics.getLockName().startsWith(formattedMethodName)) {
138                statisticsForMethod.add(statistics);
139            }
140        }
141        return statisticsForMethod;
142    }
143
144    /**
145     * Formats method's name so it will have the same format as
146     * in rtm locking statistics.
147     *
148     * <pre>
149     * Example:
150     * com/example/Klass::method =&gt; com/example/Klass.method
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     * </pre>
155     *
156     * @param methodName method's name that should be formatted.
157     * @return formatted method's name.
158     */
159    private static String formatMethodName(String methodName) {
160        String m[];
161        if (methodName.contains("::")) {
162            m = methodName.split("::");
163        } else {
164            int splitAt = methodName.lastIndexOf('.');
165            m = new String[2];
166            m[0] = methodName.substring(0, splitAt);
167            m[1] = methodName.substring(splitAt + 1);
168        }
169        return String.format("%s.%s", m[0].replaceAll("\\.", "/"), m[1]);
170    }
171
172    /**
173     * Returns name of lock for which this statistics was collected.
174     * Lock name has following format:
175     * &lt;class name&gt;.&lt;method name&gt;@&lt;bci&gt;
176     *
177     * @return name of lock.
178     */
179    public String getLockName() {
180        return String.format("%s.%s@%d", className, methodName, bci);
181    }
182
183    /**
184     * Returns aborts count for specified abort type.
185     *
186     * @param type an abort type.
187     * @return count of aborts.
188     */
189    public long getAborts(AbortType type) {
190        return aborts.getOrDefault(type, 0L);
191    }
192
193    /**
194     * Sets aborts count for specified abort type.
195     *
196     * @param type an abort type.
197     * @param count count of aborts.
198     */
199    public void setAborts(AbortType type, long count) {
200        aborts.put(type, count);
201    }
202
203    public long getTotalLocks() {
204        return totalLocks;
205    }
206
207    public long getTotalAborts() {
208        return totalAborts;
209    }
210
211    @Override
212    public String toString() {
213        StringBuilder builder = new StringBuilder();
214        builder.append(getLockName()).append('\n');
215        builder.append(String.format("# rtm locks total (estimated): %d\n",
216                getTotalLocks()));
217        builder.append(String.format("# rtm lock aborts: %d\n",
218                getTotalLocks()));
219
220        for (AbortType type : AbortType.values()) {
221            builder.append(String.format("# rtm lock aborts %s %d\n",
222                    type.toString(), getAborts(type)));
223        }
224        return builder.toString();
225    }
226}
227