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 }