1 /* 2 * Copyright (c) 2015, 2016, 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 package org.graalvm.compiler.core.test.debug; 24 25 import java.io.PrintStream; 26 import java.lang.reflect.Field; 27 import java.lang.reflect.Method; 28 import java.util.ArrayList; 29 import java.util.HashMap; 30 import java.util.List; 31 import java.util.ListIterator; 32 import java.util.Map; 33 import java.util.stream.Collectors; 34 35 import org.junit.After; 36 import org.junit.Assert; 37 import org.junit.Before; 38 import org.junit.Test; 39 40 import org.graalvm.compiler.core.test.GraalCompilerTest; 41 import org.graalvm.compiler.debug.Debug; 42 import org.graalvm.compiler.debug.DebugCloseable; 43 import org.graalvm.compiler.debug.DebugConfig; 44 import org.graalvm.compiler.debug.DebugConfigScope; 45 import org.graalvm.compiler.debug.DebugCounter; 46 import org.graalvm.compiler.debug.DebugDumpHandler; 47 import org.graalvm.compiler.debug.DebugMethodMetrics; 48 import org.graalvm.compiler.debug.DebugTimer; 49 import org.graalvm.compiler.debug.DebugVerifyHandler; 50 import org.graalvm.compiler.debug.DelegatingDebugConfig; 51 import org.graalvm.compiler.debug.DelegatingDebugConfig.Feature; 52 import org.graalvm.compiler.debug.GraalDebugConfig; 53 import org.graalvm.compiler.debug.internal.DebugScope; 54 import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl; 55 import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl.CompilationData; 56 import org.graalvm.compiler.debug.internal.method.MethodMetricsInlineeScopeInfo; 57 import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter; 58 import org.graalvm.compiler.nodes.InvokeNode; 59 import org.graalvm.compiler.nodes.StructuredGraph; 60 import org.graalvm.compiler.nodes.calc.BinaryNode; 61 import org.graalvm.compiler.nodes.calc.FixedBinaryNode; 62 import org.graalvm.compiler.nodes.calc.MulNode; 63 import org.graalvm.compiler.nodes.calc.ShiftNode; 64 import org.graalvm.compiler.nodes.calc.SignedDivNode; 65 import org.graalvm.compiler.nodes.calc.SubNode; 66 import org.graalvm.compiler.options.OptionValue; 67 import org.graalvm.compiler.options.OptionValue.OverrideScope; 68 import org.graalvm.compiler.phases.BasePhase; 69 import org.graalvm.compiler.phases.Phase; 70 import org.graalvm.compiler.phases.PhaseSuite; 71 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 72 import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase; 73 import org.graalvm.compiler.phases.schedule.SchedulePhase; 74 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy; 75 import org.graalvm.compiler.phases.tiers.HighTierContext; 76 import org.graalvm.compiler.phases.tiers.Suites; 77 78 import jdk.vm.ci.meta.ResolvedJavaMethod; 79 80 public abstract class MethodMetricsTest extends GraalCompilerTest { 81 static class TestApplication { 82 public static int m01(int x, int y) { 83 return x + y; 84 } 85 86 public static int m02(int x, int y) { 87 return x * y; 88 } 89 90 public static int m03(int x, int y) { 91 return x ^ y; 92 } 93 94 public static int m04(int x, int y) { 95 return x >> y; 96 } 97 98 public static int m05(int x, int y) { 99 return x >>> y; 100 } 101 102 public static int m06(int x, int y) { 103 return x << y; 104 } 105 106 public static int m07(int x, int y) { 107 return x > y ? 0 : 1; 108 } 109 110 public static int m08(int x, int y) { 111 return x % y; 112 } 113 114 public static int m09(int x, int y) { 115 return x / y; 116 } 117 118 public static int m10(int x, int y) { 119 return x - y; 120 } 121 122 } 123 124 public static final Class<?>[] testSignature = new Class<?>[]{int.class, int.class}; 125 public static final Object[] testArgs = new Object[]{10, 10}; 126 127 static class MethodMetricPhases { 128 static class CountingAddPhase extends Phase { 129 130 // typically those global metrics would be static final, but we need new timers every 131 // invocation if we override the debugvaluefactory 132 private final DebugCounter globalCounter = Debug.counter("GlobalMetric"); 133 private final DebugTimer globalTimer = Debug.timer("GlobalTimer"); 134 135 @Override 136 @SuppressWarnings("try") 137 protected void run(StructuredGraph graph) { 138 try (DebugCloseable d = globalTimer.start()) { 139 ResolvedJavaMethod method = graph.method(); 140 DebugMethodMetrics mm = Debug.methodMetrics(method); 141 mm.addToMetric(graph.getNodes().filter(InvokeNode.class).count(), "Invokes"); 142 mm.incrementMetric("PhaseRunsOnMethod"); 143 globalCounter.increment(); 144 } 145 } 146 } 147 148 static class CountingShiftPhase extends Phase { 149 @Override 150 protected void run(StructuredGraph graph) { 151 Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(ShiftNode.class).count(), "Shifts"); 152 } 153 } 154 155 static class CountingMulPhase extends Phase { 156 @Override 157 protected void run(StructuredGraph graph) { 158 Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(MulNode.class).count(), "Muls"); 159 } 160 } 161 162 static class CountingSubPhase extends Phase { 163 @Override 164 protected void run(StructuredGraph graph) { 165 Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(SubNode.class).count(), "Subs"); 166 } 167 } 168 169 static class CountingDivPhase extends Phase { 170 @Override 171 protected void run(StructuredGraph graph) { 172 Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(SignedDivNode.class).count(), "Divs"); 173 } 174 } 175 176 static class CountingBinOpPhase extends Phase { 177 @Override 178 protected void run(StructuredGraph graph) { 179 Debug.methodMetrics(graph.method()).addToMetric(graph.getNodes().filter(x -> x instanceof BinaryNode || x instanceof FixedBinaryNode).count(), "BinOps"); 180 } 181 } 182 183 static class ScopeTestPhase extends Phase { 184 // typically those global metrics would be static final, but we need new timers every 185 // invocation if we override the debugvaluefactory 186 private final DebugTimer timer = Debug.timer("GlobalTimer1"); 187 private final DebugTimer scopedTimer = Debug.timer("GlobalTimer2"); 188 private final DebugTimer scopedScopedTimer = Debug.timer("GlobalTimer3"); 189 private final DebugTimer scopedScopedScopeTimer = Debug.timer("GlobalTimer4"); 190 191 private final DebugTimer timer1 = Debug.timer("GlobalTimer1_WithoutInlineEnhancement"); 192 private final DebugTimer scopedTimer1 = Debug.timer("GlobalTimer2_WithoutInlineEnhancement"); 193 private final DebugTimer scopedScopedTimer1 = Debug.timer("GlobalTimer3_WithoutInlineEnhancement"); 194 private final DebugTimer scopedScopedScopeTimer1 = Debug.timer("GlobalTimer4_WithoutInlineEnhancement"); 195 196 @Override 197 @SuppressWarnings("try") 198 protected void run(StructuredGraph graph) { 199 // we are in an enhanced debug scope from graal compiler 200 // now we open multiple inlining scopes, record their time 201 try (DebugCloseable c1 = timer.start()) { 202 try (DebugCloseable c2 = scopedTimer.start()) { 203 try (DebugCloseable c3 = scopedScopedTimer.start()) { 204 // do sth unnecessary long allocating many inlinee scopes 205 for (int i = 0; i < 50; i++) { 206 try (Debug.Scope s1 = Debug.methodMetricsScope("InlineEnhancement1", MethodMetricsInlineeScopeInfo.create(graph.method()), false)) { 207 try (DebugCloseable c4 = scopedScopedScopeTimer.start()) { 208 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph); 209 // double scoped inlinee scopes should not make problems 210 // with the data 211 try (Debug.Scope s2 = Debug.methodMetricsScope("InlineEnhancement2", MethodMetricsInlineeScopeInfo.create(graph.method()), 212 false)) { 213 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph); 214 } 215 } 216 } 217 } 218 } 219 } 220 } 221 222 // now lets try different counters without the inline enhancement 223 try (DebugCloseable c1 = timer1.start()) { 224 try (DebugCloseable c2 = scopedTimer1.start()) { 225 try (DebugCloseable c3 = scopedScopedTimer1.start()) { 226 // do sth unnecessary long allocating many inlinee scopes 227 for (int i = 0; i < 50; i++) { 228 try (DebugCloseable c4 = scopedScopedScopeTimer1.start()) { 229 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph); 230 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS).apply(graph); 231 } 232 } 233 } 234 } 235 } 236 } 237 238 } 239 } 240 241 static DebugConfig overrideGraalDebugConfig(PrintStream log, String methodFilter, String methodMeter) { 242 List<DebugDumpHandler> dumpHandlers = new ArrayList<>(); 243 List<DebugVerifyHandler> verifyHandlers = new ArrayList<>(); 244 GraalDebugConfig debugConfig = new GraalDebugConfig( 245 GraalDebugConfig.Options.Log.getValue(), 246 GraalDebugConfig.Options.Count.getValue(), 247 GraalDebugConfig.Options.TrackMemUse.getValue(), 248 GraalDebugConfig.Options.Time.getValue(), 249 GraalDebugConfig.Options.Dump.getValue(), 250 GraalDebugConfig.Options.Verify.getValue(), 251 methodFilter, 252 methodMeter, 253 log, dumpHandlers, verifyHandlers); 254 return debugConfig; 255 } 256 257 private static OverrideScope overrideMetricPrinterConfig() { 258 Map<OptionValue<?>, Object> mapping = new HashMap<>(); 259 mapping.put(MethodMetricsPrinter.Options.MethodMeterPrintAscii, true); 260 return OptionValue.override(mapping); 261 } 262 263 abstract Phase additionalPhase(); 264 265 @Override 266 protected Suites createSuites() { 267 Suites ret = super.createSuites(); 268 ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(ConvertDeoptimizeToGuardPhase.class, true); 269 PhaseSuite.findNextPhase(iter, CanonicalizerPhase.class); 270 iter.add(additionalPhase()); 271 return ret; 272 } 273 274 @Test 275 @SuppressWarnings("try") 276 public void test() throws Throwable { 277 try (DebugConfigScope s = Debug.setConfig(getConfig()); OverrideScope o = getOScope();) { 278 executeMethod(TestApplication.class.getMethod("m01", testSignature), null, testArgs); 279 executeMethod(TestApplication.class.getMethod("m02", testSignature), null, testArgs); 280 executeMethod(TestApplication.class.getMethod("m03", testSignature), null, testArgs); 281 executeMethod(TestApplication.class.getMethod("m04", testSignature), null, testArgs); 282 executeMethod(TestApplication.class.getMethod("m05", testSignature), null, testArgs); 283 executeMethod(TestApplication.class.getMethod("m06", testSignature), null, testArgs); 284 executeMethod(TestApplication.class.getMethod("m07", testSignature), null, testArgs); 285 executeMethod(TestApplication.class.getMethod("m08", testSignature), null, testArgs); 286 executeMethod(TestApplication.class.getMethod("m09", testSignature), null, testArgs); 287 executeMethod(TestApplication.class.getMethod("m10", testSignature), null, testArgs); 288 assertValues(); 289 } 290 } 291 292 @Before 293 public void rememberScopeId() { 294 scopeIdBeforeAccess = DebugScope.getCurrentGlobalScopeId(); 295 } 296 297 @After 298 public void clearMMCache() { 299 MethodMetricsImpl.clearMM(); 300 } 301 302 abstract DebugConfig getConfig(); 303 304 OverrideScope getOScope() { 305 return overrideMetricPrinterConfig(); 306 } 307 308 abstract void assertValues() throws Throwable; 309 310 @SuppressWarnings("unchecked") 311 private static Map<ResolvedJavaMethod, CompilationData> readMethodMetricsImplData() { 312 Map<ResolvedJavaMethod, CompilationData> threadLocalMap = null; 313 for (Field f : MethodMetricsImpl.class.getDeclaredFields()) { 314 if (f.getName().equals("threadEntries")) { 315 f.setAccessible(true); 316 Object map; 317 try { 318 map = ((ThreadLocal<?>) f.get(null)).get(); 319 } catch (Throwable t) { 320 throw new RuntimeException(t); 321 } 322 threadLocalMap = (Map<ResolvedJavaMethod, CompilationData>) map; 323 break; 324 } 325 } 326 return threadLocalMap; 327 } 328 329 private long scopeIdBeforeAccess; 330 private long scopeIdAfterAccess; 331 332 protected long readValFromCurrThread(ResolvedJavaMethod method, String metricName) { 333 334 Map<ResolvedJavaMethod, CompilationData> threadLocalMap = readMethodMetricsImplData(); 335 assert threadLocalMap != null; 336 CompilationData compilationData = threadLocalMap.get(method); 337 assert compilationData != null; 338 Map<Long, Map<String, Long>> compilations = compilationData.getCompilations(); 339 List<Map<String, Long>> compilationEntries = new ArrayList<>(); 340 compilations.forEach((x, y) -> { 341 if (x >= scopeIdBeforeAccess && x <= scopeIdAfterAccess) { 342 compilationEntries.add(y); 343 } 344 }); 345 List<Map<String, Long>> listView = compilationEntries.stream().filter(x -> x.size() > 0).collect(Collectors.toList()); 346 assert listView.size() <= 1 : "There must be at most one none empty compilation data point present:" + listView.size(); 347 /* 348 * NOTE: Using the pre-generation of compilation entries for a method has the disadvantage 349 * that during testing we have different points in time when we request the metric. First, 350 * properly, when we use it and then when we want to know the result, but when we check the 351 * result the debug context no longer holds a correct scope with the unique id, so we return 352 * the first compilation entry that is not empty. 353 */ 354 Map<String, Long> entries = listView.size() > 0 ? listView.get(0) : null; 355 Long res = entries != null ? entries.get(metricName) : null; 356 return res != null ? res : 0; 357 } 358 359 @SuppressWarnings("try") 360 void assertValues(String metricName, long[] vals) { 361 scopeIdAfterAccess = DebugScope.getCurrentGlobalScopeId(); 362 try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().enable(Feature.METHOD_METRICS))) { 363 Assert.assertEquals(vals[0], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m01", testSignature)), metricName)); 364 Assert.assertEquals(vals[1], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m02", testSignature)), metricName)); 365 Assert.assertEquals(vals[2], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m03", testSignature)), metricName)); 366 Assert.assertEquals(vals[3], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m04", testSignature)), metricName)); 367 Assert.assertEquals(vals[4], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m05", testSignature)), metricName)); 368 Assert.assertEquals(vals[5], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m06", testSignature)), metricName)); 369 Assert.assertEquals(vals[6], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m07", testSignature)), metricName)); 370 Assert.assertEquals(vals[7], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m08", testSignature)), metricName)); 371 Assert.assertEquals(vals[8], readValFromCurrThread(asResolvedJavaMethod(TestApplication.class.getMethod("m09", testSignature)), metricName)); 372 } catch (Throwable t) { 373 throw new RuntimeException(t); 374 } 375 } 376 377 void executeMethod(Method m, Object receiver, Object... args) { 378 test(asResolvedJavaMethod(m), receiver, args); 379 } 380 381 }