1 /*
   2  * Copyright (c) 2013, 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 org.graalvm.compiler.nodes.debug;
  26 
  27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
  28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
  29 
  30 import org.graalvm.compiler.core.common.type.StampFactory;
  31 import org.graalvm.compiler.debug.GraalError;
  32 import org.graalvm.compiler.graph.NodeClass;
  33 import org.graalvm.compiler.lir.LIRInstruction;
  34 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  35 import org.graalvm.compiler.nodeinfo.NodeInfo;
  36 import org.graalvm.compiler.nodes.ConstantNode;
  37 import org.graalvm.compiler.nodes.FixedNode;
  38 import org.graalvm.compiler.nodes.FixedWithNextNode;
  39 import org.graalvm.compiler.nodes.StructuredGraph;
  40 import org.graalvm.compiler.nodes.ValueNode;
  41 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  42 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  43 
  44 /**
  45  * This node can be used to add a counter to the code that will estimate the dynamic number of calls
  46  * by adding an increment to the compiled code. This should of course only be used for
  47  * debugging/testing purposes.
  48  *
  49  * A unique counter will be created for each unique name passed to the constructor. Depending on the
  50  * value of withContext, the name of the root method is added to the counter's name.
  51  */
  52 //@formatter:off
  53 @NodeInfo(size = SIZE_IGNORED,
  54           sizeRationale = "Node is a debugging node that should not be used in production.",
  55           cycles = CYCLES_IGNORED,
  56           cyclesRationale = "Node is a debugging node that should not be used in production.")
  57 //@formatter:on
  58 public class DynamicCounterNode extends FixedWithNextNode implements LIRLowerable {
  59 
  60     public static final NodeClass<DynamicCounterNode> TYPE = NodeClass.create(DynamicCounterNode.class);
  61     @Input ValueNode increment;
  62 
  63     protected final String group;
  64     protected final String name;
  65     protected final boolean withContext;
  66 
  67     public DynamicCounterNode(String group, String name, ValueNode increment, boolean withContext) {
  68         this(TYPE, group, name, increment, withContext);
  69     }
  70 
  71     public static final long MIN_INCREMENT = 0;
  72     public static final long MAX_INCREMENT = 10_000;
  73 
  74     /**
  75      * Clamps {@code value} to a value between {@link #MIN_INCREMENT} and {@link #MAX_INCREMENT}.
  76      * This mitigates the possibility of overflowing benchmark counters.
  77      */
  78     public static long clampIncrement(long value) {
  79         return Math.min(Math.max(value, MIN_INCREMENT), MAX_INCREMENT);
  80     }
  81 
  82     private boolean checkIncrement() {
  83         if (increment.isJavaConstant()) {
  84             long incValue = increment.asJavaConstant().asLong();
  85             if (incValue < MIN_INCREMENT || incValue > MAX_INCREMENT) {
  86                 String message = String.format("Benchmark counter %s:%s has increment out of range [%d .. %d]: %d", group, getNameWithContext(), MIN_INCREMENT, MAX_INCREMENT, incValue);
  87                 assert false : message;
  88             }
  89         }
  90         return true;
  91     }
  92 
  93     protected DynamicCounterNode(NodeClass<? extends DynamicCounterNode> c, String group, String name, ValueNode increment, boolean withContext) {
  94         super(c, StampFactory.forVoid());
  95         this.group = group;
  96         this.name = name;
  97         this.increment = increment;
  98         this.withContext = withContext;
  99         assert checkIncrement();
 100     }
 101 
 102     public ValueNode getIncrement() {
 103         return increment;
 104     }
 105 
 106     public String getName() {
 107         return name;
 108     }
 109 
 110     public String getGroup() {
 111         return group;
 112     }
 113 
 114     public boolean isWithContext() {
 115         return withContext;
 116     }
 117 
 118     public static void addCounterBefore(String group, String name, long increment, boolean withContext, FixedNode position) {
 119         StructuredGraph graph = position.graph();
 120         graph.addBeforeFixed(position, position.graph().add(new DynamicCounterNode(group, name, ConstantNode.forLong(increment, position.graph()), withContext)));
 121     }
 122 
 123     @NodeIntrinsic
 124     public static native void counter(@ConstantNodeParameter String group, @ConstantNodeParameter String name, long increment, @ConstantNodeParameter boolean addContext);
 125 
 126     @Override
 127     public void generate(NodeLIRBuilderTool generator) {
 128         LIRGeneratorTool lirGen = generator.getLIRGeneratorTool();
 129         String nameWithContext = getNameWithContext();
 130         LIRInstruction counterOp = lirGen.createBenchmarkCounter(nameWithContext, getGroup(), generator.operand(increment));
 131         if (counterOp != null) {
 132             lirGen.append(counterOp);
 133         } else {
 134             throw GraalError.unimplemented("Benchmark counters not enabled or not implemented by the back end.");
 135         }
 136     }
 137 
 138     private String getNameWithContext() {
 139         String nameWithContext;
 140         if (isWithContext()) {
 141             nameWithContext = getName() + " @ ";
 142             if (graph().method() != null) {
 143                 StackTraceElement stackTraceElement = graph().method().asStackTraceElement(0);
 144                 if (stackTraceElement != null) {
 145                     nameWithContext += " " + stackTraceElement.toString();
 146                 } else {
 147                     nameWithContext += graph().method().format("%h.%n");
 148                 }
 149             }
 150             if (graph().name != null) {
 151                 nameWithContext += " (" + graph().name + ")";
 152             }
 153 
 154         } else {
 155             nameWithContext = getName();
 156         }
 157         return nameWithContext;
 158     }
 159 }