1 // Copyright 2022 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.code_intelligence.jazzer.runtime; 16 17 import java.io.UnsupportedEncodingException; 18 import java.util.Arrays; 19 import java.util.Random; 20 import java.util.concurrent.ThreadLocalRandom; 21 import java.util.concurrent.TimeUnit; 22 import org.openjdk.jmh.annotations.Benchmark; 23 import org.openjdk.jmh.annotations.BenchmarkMode; 24 import org.openjdk.jmh.annotations.Fork; 25 import org.openjdk.jmh.annotations.Measurement; 26 import org.openjdk.jmh.annotations.Mode; 27 import org.openjdk.jmh.annotations.OutputTimeUnit; 28 import org.openjdk.jmh.annotations.Param; 29 import org.openjdk.jmh.annotations.Scope; 30 import org.openjdk.jmh.annotations.Setup; 31 import org.openjdk.jmh.annotations.State; 32 import org.openjdk.jmh.annotations.Warmup; 33 34 @Warmup(iterations = 5, time = 1) 35 @Measurement(iterations = 5, time = 1) 36 @Fork(value = 3) 37 @OutputTimeUnit(TimeUnit.NANOSECONDS) 38 @BenchmarkMode(Mode.AverageTime) 39 public class FuzzerCallbacksBenchmark { 40 @State(Scope.Benchmark) 41 public static class TraceCmpIntState { 42 int arg1 = 0xCAFECAFE; 43 int arg2 = 0xFEEDFEED; 44 int pc = 0x12345678; 45 } 46 47 @Benchmark traceCmpInt(TraceCmpIntState state)48 public void traceCmpInt(TraceCmpIntState state) { 49 FuzzerCallbacks.traceCmpInt(state.arg1, state.arg2, state.pc); 50 } 51 52 @Benchmark traceCmpIntWithPc(TraceCmpIntState state)53 public void traceCmpIntWithPc(TraceCmpIntState state) { 54 FuzzerCallbacksWithPc.traceCmpInt(state.arg1, state.arg2, state.pc); 55 } 56 57 @Benchmark 58 @Fork(jvmArgsAppend = {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+CriticalJNINatives"}) traceCmpIntOptimizedCritical(TraceCmpIntState state)59 public void traceCmpIntOptimizedCritical(TraceCmpIntState state) { 60 FuzzerCallbacksOptimizedCritical.traceCmpInt(state.arg1, state.arg2, state.pc); 61 } 62 63 // Uncomment to benchmark Project Panama-backed implementation (requires JDK 16+). 64 // @Benchmark 65 // @Fork(jvmArgsAppend = {"--enable-native-access=ALL-UNNAMED", "--add-modules", 66 // "jdk.incubator.foreign"}) 67 // public void 68 // traceCmpIntPanama(TraceCmpIntState state) throws Throwable { 69 // FuzzerCallbacksPanama.traceCmpInt(state.arg1, state.arg2, state.pc); 70 // } 71 72 @State(Scope.Benchmark) 73 public static class TraceSwitchState { 74 @Param({"5", "10"}) int numCases; 75 76 long val; 77 long[] cases; 78 int pc = 0x12345678; 79 80 @Setup setup()81 public void setup() { 82 cases = new long[2 + numCases]; 83 Random random = ThreadLocalRandom.current(); 84 Arrays.setAll(cases, i -> { 85 switch (i) { 86 case 0: 87 return numCases; 88 case 1: 89 return 32; 90 default: 91 return random.nextInt(); 92 } 93 }); 94 Arrays.sort(cases, 2, cases.length); 95 val = random.nextInt(); 96 } 97 } 98 99 @Benchmark traceSwitch(TraceSwitchState state)100 public void traceSwitch(TraceSwitchState state) { 101 FuzzerCallbacks.traceSwitch(state.val, state.cases, state.pc); 102 } 103 104 @Benchmark traceSwitchWithPc(TraceSwitchState state)105 public void traceSwitchWithPc(TraceSwitchState state) { 106 FuzzerCallbacksWithPc.traceSwitch(state.val, state.cases, state.pc); 107 } 108 109 @Benchmark 110 @Fork(jvmArgsAppend = {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+CriticalJNINatives"}) traceSwitchOptimizedCritical(TraceSwitchState state)111 public void traceSwitchOptimizedCritical(TraceSwitchState state) { 112 FuzzerCallbacksOptimizedCritical.traceSwitch(state.val, state.cases, state.pc); 113 } 114 115 @Benchmark traceSwitchOptimizedNonCritical(TraceSwitchState state)116 public void traceSwitchOptimizedNonCritical(TraceSwitchState state) { 117 FuzzerCallbacksOptimizedNonCritical.traceSwitch(state.val, state.cases, state.pc); 118 } 119 120 // Uncomment to benchmark Project Panama-backed implementation (requires JDK 16+). 121 // @Benchmark 122 // @Fork(jvmArgsAppend = {"--enable-native-access=ALL-UNNAMED", "--add-modules", 123 // "jdk.incubator.foreign"}) 124 // public void 125 // traceCmpSwitchPanama(TraceSwitchState state) throws Throwable { 126 // FuzzerCallbacksPanama.traceCmpSwitch(state.val, state.cases, state.pc); 127 // } 128 129 @State(Scope.Benchmark) 130 public static class TraceMemcmpState { 131 @Param({"10", "100", "1000"}) int length; 132 133 byte[] array1; 134 byte[] array2; 135 int pc = 0x12345678; 136 137 @Setup setup()138 public void setup() { 139 array1 = new byte[length]; 140 array2 = new byte[length]; 141 142 Random random = ThreadLocalRandom.current(); 143 random.nextBytes(array1); 144 random.nextBytes(array2); 145 // Make the arrays agree unil the midpoint to benchmark the "average" 146 // case of an interesting memcmp. 147 System.arraycopy(array1, 0, array2, 0, length / 2); 148 } 149 } 150 151 @Benchmark traceMemcmp(TraceMemcmpState state)152 public void traceMemcmp(TraceMemcmpState state) { 153 FuzzerCallbacks.traceMemcmp(state.array1, state.array2, 1, state.pc); 154 } 155 156 @Benchmark 157 @Fork(jvmArgsAppend = {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+CriticalJNINatives"}) traceMemcmpOptimizedCritical(TraceMemcmpState state)158 public void traceMemcmpOptimizedCritical(TraceMemcmpState state) { 159 FuzzerCallbacksOptimizedCritical.traceMemcmp(state.array1, state.array2, 1, state.pc); 160 } 161 162 @Benchmark traceMemcmpOptimizedNonCritical(TraceMemcmpState state)163 public void traceMemcmpOptimizedNonCritical(TraceMemcmpState state) { 164 FuzzerCallbacksOptimizedNonCritical.traceMemcmp(state.array1, state.array2, 1, state.pc); 165 } 166 167 @State(Scope.Benchmark) 168 public static class TraceStrstrState { 169 @Param({"10", "100", "1000"}) int length; 170 @Param({"true", "false"}) boolean asciiOnly; 171 172 String haystack; 173 String needle; 174 int pc = 0x12345678; 175 176 @Setup setup()177 public void setup() { 178 haystack = randomString(length, asciiOnly); 179 needle = randomString(length, asciiOnly); 180 } 181 randomString(int length, boolean asciiOnly)182 private String randomString(int length, boolean asciiOnly) { 183 String asciiString = 184 ThreadLocalRandom.current() 185 .ints('a', 'z' + 1) 186 .limit(length) 187 .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) 188 .toString(); 189 if (asciiOnly) { 190 return asciiString; 191 } 192 // Force String to be non-Latin-1 to preclude compact string optimization. 193 return "\uFFFD" + asciiString.substring(1); 194 } 195 } 196 197 @Benchmark traceStrstr(TraceStrstrState state)198 public void traceStrstr(TraceStrstrState state) { 199 FuzzerCallbacks.traceStrstr(state.haystack, state.needle, state.pc); 200 } 201 202 @Benchmark traceStrstrOptimizedNonCritical(TraceStrstrState state)203 public void traceStrstrOptimizedNonCritical(TraceStrstrState state) { 204 FuzzerCallbacksOptimizedNonCritical.traceStrstr(state.haystack, state.needle, state.pc); 205 } 206 207 @Benchmark 208 @Fork(jvmArgsAppend = {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+CriticalJNINatives"}) traceStrstrOptimizedJavaCritical(TraceStrstrState state)209 public void traceStrstrOptimizedJavaCritical(TraceStrstrState state) 210 throws UnsupportedEncodingException { 211 FuzzerCallbacksOptimizedCritical.traceStrstrJava(state.haystack, state.needle, state.pc); 212 } 213 214 @Benchmark traceStrstrOptimizedJavaNonCritical(TraceStrstrState state)215 public void traceStrstrOptimizedJavaNonCritical(TraceStrstrState state) 216 throws UnsupportedEncodingException { 217 FuzzerCallbacksOptimizedNonCritical.traceStrstrJava(state.haystack, state.needle, state.pc); 218 } 219 } 220