1 /*
   2  * Copyright (c) 2015, 2019 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.openjdk.bench.vm.lang;
  24 
  25 import java.util.concurrent.TimeUnit;
  26 import java.util.concurrent.atomic.AtomicInteger;
  27 import java.util.logging.LogRecord;
  28 import org.openjdk.jmh.annotations.Benchmark;
  29 import org.openjdk.jmh.annotations.BenchmarkMode;
  30 import org.openjdk.jmh.annotations.Mode;
  31 import org.openjdk.jmh.annotations.OutputTimeUnit;
  32 import org.openjdk.jmh.annotations.Param;
  33 import org.openjdk.jmh.annotations.Scope;
  34 import org.openjdk.jmh.annotations.State;
  35 import org.openjdk.jmh.infra.Blackhole;
  36 
  37 @State(value = Scope.Benchmark)
  38 @BenchmarkMode(Mode.AverageTime)
  39 @OutputTimeUnit(TimeUnit.NANOSECONDS)
  40 public class ThrowableRuntimeMicros {
  41 
  42     // TestStack will add this number of calls to the call stack
  43     @Param({"4", "10", "100", "256", "1000"})
  44     public int depth;
  45 
  46     /** Logging handler for testing logging calls. */
  47     @State(value = Scope.Thread) // create a separate one for each worker thread
  48     public static class TestHandler extends java.util.logging.Handler {
  49         private final static AtomicInteger serialNum = new AtomicInteger(0);
  50         
  51         private final java.util.logging.Logger logger;
  52         private volatile LogRecord record;
  53         
  54         public TestHandler() {
  55             // Each instance uses its own logger
  56             logger = java.util.logging.Logger.getLogger("StackWalkBench" + serialNum.incrementAndGet());
  57             logger.setUseParentHandlers(false);
  58             logger.addHandler(this);
  59         }
  60         
  61         @Override
  62         public void publish(LogRecord record) {
  63             record.getSourceMethodName();
  64             this.record = record;
  65         }
  66 
  67         private LogRecord reset() {
  68             LogRecord record = this.record;
  69             this.record = null;
  70             return record;
  71         }
  72         
  73         public final LogRecord testInferCaller(String msg) {
  74             logger.info(msg);
  75             LogRecord rec = this.reset();
  76             if (!"testInferCaller".equals(rec.getSourceMethodName())) {
  77                 throw new RuntimeException("bad caller: "
  78                         + rec.getSourceClassName() + "."
  79                         + rec.getSourceMethodName());
  80             }
  81             return rec;
  82         }
  83 
  84         public final LogRecord testLogp(String msg) {
  85             logger.logp(java.util.logging.Level.INFO, "foo", "bar", msg);
  86             LogRecord rec = this.reset();
  87             if (!"bar".equals(rec.getSourceMethodName())) {
  88                 throw new RuntimeException("bad caller: "
  89                         + rec.getSourceClassName() + "."
  90                         + rec.getSourceMethodName());
  91             }
  92             return rec;
  93         }
  94         @Override public void flush() {}        
  95         @Override public void close() throws SecurityException {}
  96     }    
  97     
  98     /** Build a call stack of a given size, then run trigger code in it.
  99       * (Does not account for existing frames higher up in the JMH machinery).
 100       */
 101     static class TestStack {
 102         final long fence;
 103         long current;
 104         final Runnable trigger;
 105 
 106         TestStack(long max, Runnable trigger) {
 107             this.fence = max;
 108             this.current = 0;
 109             this.trigger = trigger;
 110         }
 111 
 112         public void start() {
 113             one();
 114         }
 115 
 116         public void one() {
 117             if (check()) {
 118                 two();
 119             }
 120         }
 121 
 122         void two() {
 123             if (check()) {
 124                 three();
 125             }
 126         }
 127 
 128         private void three() {
 129             if (check()) {
 130                 one();
 131             }
 132         }
 133 
 134         boolean check() {
 135             if (++current == fence) {
 136                 trigger.run();
 137                 return false;
 138             } else {
 139                 return true;
 140             }
 141         }
 142     }
 143 
 144     @Benchmark
 145     public void testLoggingInferCaller(TestHandler handler, Blackhole bh) {
 146         final Blackhole localBH = bh;
 147         final boolean[] done = {false};
 148         new TestStack(depth, new Runnable() {
 149             public void run() {
 150                 localBH.consume(handler.testInferCaller("test"));
 151                 done[0] = true;
 152             }
 153         }).start();
 154         if (!done[0]) {
 155             throw new RuntimeException();
 156         }
 157     }
 158 
 159     @Benchmark
 160     public void testLoggingLogp(TestHandler handler, Blackhole bh) {
 161         final Blackhole localBH = bh;
 162         final boolean[] done = {false};
 163         new TestStack(depth, new Runnable() {
 164             public void run() {
 165                 localBH.consume(handler.testLogp("test"));
 166                 done[0] = true;
 167             }
 168         }).start();
 169         if (!done[0]) {
 170             throw new RuntimeException();
 171         }
 172     }
 173 
 174     @Benchmark
 175     public void testThrowableInit(Blackhole bh) {
 176         final Blackhole localBH = bh;
 177         final boolean[] done = {false};
 178         new TestStack(depth, new Runnable() {
 179             public void run() {
 180                 localBH.consume(new Throwable());
 181                 done[0] = true;
 182             }
 183         }).start();
 184         if (!done[0]) {
 185             throw new RuntimeException();
 186         }
 187     }
 188 
 189     @Benchmark
 190     public void testThrowableGetStackTrace(Blackhole bh) {
 191         final Blackhole localBH = bh;
 192         final boolean[] done = {false};
 193         new TestStack(depth, new Runnable() {
 194             public void run() {
 195                 localBH.consume(new Throwable().getStackTrace());
 196                 done[0] = true;
 197             }
 198         }).start();
 199         if (!done[0]) {
 200             throw new RuntimeException();
 201         }
 202     }
 203     
 204     @Benchmark
 205     public void testThrowableSTEtoString(Blackhole bh) {
 206         final Blackhole localBH = bh;
 207         final boolean[] done = {false};
 208         new TestStack(depth, new Runnable() {
 209             public void run() {
 210                 Throwable t = new Throwable();
 211                 for (StackTraceElement ste : t.getStackTrace()) {
 212                     localBH.consume(ste.toString());
 213                 }
 214                 done[0] = true;
 215             }
 216         }).start();
 217         if (!done[0]) {
 218             throw new RuntimeException();
 219         }
 220     }    
 221 
 222 // These need replacements following removal of Reflection.getCallerClass()
 223 //    
 224 //    // Max value for calling Reflection.getCallserClass in a loop
 225 //    // Might consider making this a Param, too
 226 //    private static int MAX_GETCALLERCLASS_DEPTH = 6;
 227 //
 228 //    @Benchmark
 229 //    public void testReflectionGetCallerClass(Blackhole bh) {
 230 //        final Blackhole localBH = bh;
 231 //        final boolean[] done = {false};
 232 //
 233 //        new TestStack(depth, new Runnable() {
 234 //            public void run() {
 235 //                localBH.consume(Reflection.getCallerClass(2));
 236 //                done[0] = true;
 237 //            }
 238 //        }).start();
 239 //        if (!done[0]) {
 240 //            throw new RuntimeException();
 241 //        }
 242 //    }
 243 //
 244 //    @Benchmark
 245 //    public void testReflectionGetCallerClassLoop(Blackhole bh) {
 246 //        final Blackhole localBH = bh;
 247 //        final boolean[] done = {false};
 248 //
 249 //        new TestStack(depth, new Runnable() {
 250 //            public void run() {
 251 //                for (int i = 2; i <= MAX_GETCALLERCLASS_DEPTH; i++) {
 252 //                    localBH.consume(Reflection.getCallerClass(i));
 253 //                }
 254 //                done[0] = true;
 255 //            }
 256 //        }).start();
 257 //        if (!done[0]) {
 258 //            throw new RuntimeException();
 259 //        }
 260 //    }
 261 
 262 //    // Meant to measure the overhead of testing machinery
 263 //    // @Benchmark
 264 //    public void testGoThere(Blackhole bh) {
 265 //        final Blackhole localBH = bh;
 266 //        final boolean[] done = {false};
 267 //        new TestStack(depth, new Runnable() {
 268 //            public void run() {
 269 //                Object t = new Object();
 270 //                localBH.consume(t);
 271 //                done[0] = true;
 272 //            }
 273 //        }).start();
 274 //        if (!done[0]) {
 275 //            throw new RuntimeException();
 276 //        }
 277 //    }
 278 }