1 /******************************************************************************* 2 * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * Marc R. Hoffmann - initial API and implementation 10 * 11 *******************************************************************************/ 12 package org.jacoco.core.internal.flow; 13 14 import static org.junit.Assert.assertEquals; 15 16 import java.util.Arrays; 17 18 import org.jacoco.core.instr.MethodRecorder; 19 import org.junit.After; 20 import org.junit.Before; 21 import org.junit.Test; 22 import org.objectweb.asm.Label; 23 import org.objectweb.asm.MethodVisitor; 24 import org.objectweb.asm.Opcodes; 25 import org.objectweb.asm.commons.AnalyzerAdapter; 26 import org.objectweb.asm.util.Printer; 27 28 /** 29 * Unit tests for {@link MethodProbesAdapter}. 30 */ 31 public class MethodProbesAdapterTest implements IProbeIdGenerator { 32 33 private Label label; 34 35 private int id; 36 37 private MethodRecorder expected, actual; 38 39 private MethodProbesVisitor expectedVisitor; 40 41 private MethodVisitor adapter; 42 43 private IFrame frame; 44 45 private static class TraceAdapter extends MethodProbesVisitor { 46 47 private final Printer printer; 48 TraceAdapter(MethodRecorder recorder)49 TraceAdapter(MethodRecorder recorder) { 50 super(recorder.getVisitor()); 51 printer = recorder.getPrinter(); 52 } 53 54 @Override visitProbe(int probeId)55 public void visitProbe(int probeId) { 56 rec("visitProbe", Integer.valueOf(probeId)); 57 } 58 59 @Override visitInsnWithProbe(int opcode, int probeId)60 public void visitInsnWithProbe(int opcode, int probeId) { 61 rec("visitInsnWithProbe", Integer.valueOf(opcode), 62 Integer.valueOf(probeId)); 63 } 64 65 @Override visitJumpInsnWithProbe(int opcode, Label label, int probeId, IFrame frame)66 public void visitJumpInsnWithProbe(int opcode, Label label, 67 int probeId, IFrame frame) { 68 rec("visitJumpInsnWithProbe", Integer.valueOf(opcode), label, 69 Integer.valueOf(probeId)); 70 frame.accept(this); 71 } 72 73 @Override visitTableSwitchInsnWithProbes(int min, int max, Label dflt, Label[] labels, IFrame frame)74 public void visitTableSwitchInsnWithProbes(int min, int max, 75 Label dflt, Label[] labels, IFrame frame) { 76 rec("visitTableSwitchInsnWithProbes", Integer.valueOf(min), 77 Integer.valueOf(max), dflt, labels); 78 frame.accept(this); 79 } 80 81 @Override visitLookupSwitchInsnWithProbes(Label dflt, int[] keys, Label[] labels, IFrame frame)82 public void visitLookupSwitchInsnWithProbes(Label dflt, int[] keys, 83 Label[] labels, IFrame frame) { 84 rec("visitLookupSwitchInsnWithProbes", dflt, keys, labels); 85 frame.accept(this); 86 } 87 rec(String name, Object... args)88 private void rec(String name, Object... args) { 89 printer.text.add(name + Arrays.asList(args)); 90 } 91 92 } 93 94 @Before setup()95 public void setup() { 96 label = new Label(); 97 id = 1000; 98 expected = new MethodRecorder(); 99 expectedVisitor = new TraceAdapter(expected); 100 actual = new MethodRecorder(); 101 MethodProbesVisitor actualVisitor = new TraceAdapter(actual); 102 MethodProbesAdapter probesAdapter = new MethodProbesAdapter( 103 actualVisitor, this); 104 final AnalyzerAdapter analyzer = new AnalyzerAdapter("Foo", 0, "doit", 105 "()V", probesAdapter); 106 probesAdapter.setAnalyzer(analyzer); 107 adapter = analyzer; 108 frame = new IFrame() { 109 110 public void accept(MethodVisitor mv) { 111 } 112 }; 113 } 114 115 @After verify()116 public void verify() { 117 assertEquals(expected, actual); 118 } 119 120 @Test testVisitProbe1()121 public void testVisitProbe1() { 122 LabelInfo.setTarget(label); 123 LabelInfo.setSuccessor(label); 124 125 adapter.visitLabel(label); 126 127 expectedVisitor.visitProbe(1000); 128 expectedVisitor.visitLabel(label); 129 } 130 131 @Test testVisitProbe2()132 public void testVisitProbe2() { 133 LabelInfo.setTarget(label); 134 LabelInfo.setTarget(label); 135 136 adapter.visitLabel(label); 137 138 expectedVisitor.visitLabel(label); 139 } 140 141 @Test testVisitProbe3()142 public void testVisitProbe3() { 143 adapter.visitLabel(label); 144 145 expectedVisitor.visitLabel(label); 146 } 147 148 @Test testVisitInsn1()149 public void testVisitInsn1() { 150 adapter.visitInsn(Opcodes.RETURN); 151 152 expectedVisitor.visitInsnWithProbe(Opcodes.RETURN, 1000); 153 } 154 155 @Test testVisitInsn2()156 public void testVisitInsn2() { 157 adapter.visitInsn(Opcodes.ICONST_0); 158 adapter.visitInsn(Opcodes.ICONST_0); 159 adapter.visitInsn(Opcodes.IADD); 160 161 expectedVisitor.visitInsn(Opcodes.ICONST_0); 162 expectedVisitor.visitInsn(Opcodes.ICONST_0); 163 expectedVisitor.visitInsn(Opcodes.IADD); 164 } 165 166 @Test testVisitJumpInsn1()167 public void testVisitJumpInsn1() { 168 LabelInfo.setTarget(label); 169 LabelInfo.setTarget(label); 170 171 adapter.visitJumpInsn(Opcodes.GOTO, label); 172 173 expectedVisitor 174 .visitJumpInsnWithProbe(Opcodes.GOTO, label, 1000, frame); 175 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 176 0, null); 177 } 178 179 @Test testVisitJumpInsn2()180 public void testVisitJumpInsn2() { 181 LabelInfo.setTarget(label); 182 LabelInfo.setTarget(label); 183 184 adapter.visitInsn(Opcodes.ICONST_0); 185 adapter.visitJumpInsn(Opcodes.IFLT, label); 186 187 expectedVisitor.visitInsn(Opcodes.ICONST_0); 188 expectedVisitor 189 .visitJumpInsnWithProbe(Opcodes.IFLT, label, 1000, frame); 190 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 191 0, null); 192 } 193 194 @Test testVisitJumpInsn3()195 public void testVisitJumpInsn3() { 196 adapter.visitInsn(Opcodes.ICONST_0); 197 adapter.visitJumpInsn(Opcodes.IFLT, label); 198 199 expectedVisitor.visitInsn(Opcodes.ICONST_0); 200 expectedVisitor.visitJumpInsn(Opcodes.IFLT, label); 201 } 202 203 @Test testVisitJumpInsn4()204 public void testVisitJumpInsn4() { 205 LabelInfo.setTarget(label); 206 LabelInfo.setTarget(label); 207 208 adapter.visitInsn(Opcodes.ICONST_0); 209 adapter.visitInsn(Opcodes.ICONST_0); 210 adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, label); 211 212 expectedVisitor.visitInsn(Opcodes.ICONST_0); 213 expectedVisitor.visitInsn(Opcodes.ICONST_0); 214 expectedVisitor.visitJumpInsnWithProbe(Opcodes.IF_ICMPEQ, label, 1000, 215 frame); 216 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 217 0, null); 218 } 219 220 @Test testVisitLookupSwitchInsn1()221 public void testVisitLookupSwitchInsn1() { 222 LabelInfo.setTarget(label); 223 LabelInfo.setTarget(label); 224 225 final int[] keys = new int[] { 0, 1 }; 226 final Label[] labels = new Label[] { label, label }; 227 adapter.visitInsn(Opcodes.ICONST_0); 228 adapter.visitLookupSwitchInsn(label, keys, labels); 229 230 expectedVisitor.visitInsn(Opcodes.ICONST_0); 231 expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels, 232 frame); 233 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 234 0, null); 235 assertEquals(1000, LabelInfo.getProbeId(label)); 236 } 237 238 @Test testVisitLookupSwitchInsn2()239 public void testVisitLookupSwitchInsn2() { 240 Label label2 = new Label(); 241 LabelInfo.setTarget(label2); 242 LabelInfo.setTarget(label2); 243 244 final int[] keys = new int[] { 0, 1 }; 245 final Label[] labels = new Label[] { label2, label }; 246 adapter.visitInsn(Opcodes.ICONST_0); 247 adapter.visitLookupSwitchInsn(label, keys, labels); 248 249 expectedVisitor.visitInsn(Opcodes.ICONST_0); 250 expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels, 251 frame); 252 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 253 0, null); 254 assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label)); 255 assertEquals(1000, LabelInfo.getProbeId(label2)); 256 } 257 258 @Test testVisitLookupSwitchInsn3()259 public void testVisitLookupSwitchInsn3() { 260 final int[] keys = new int[] { 0, 1 }; 261 final Label[] labels = new Label[] { label, label }; 262 adapter.visitInsn(Opcodes.ICONST_0); 263 adapter.visitLookupSwitchInsn(label, keys, labels); 264 265 expectedVisitor.visitInsn(Opcodes.ICONST_0); 266 expectedVisitor.visitLookupSwitchInsn(label, keys, labels); 267 } 268 269 @Test testVisitTableSwitchInsn1()270 public void testVisitTableSwitchInsn1() { 271 LabelInfo.setTarget(label); 272 LabelInfo.setTarget(label); 273 274 final Label[] labels = new Label[] { label, label }; 275 adapter.visitInsn(Opcodes.ICONST_0); 276 adapter.visitTableSwitchInsn(0, 1, label, labels); 277 278 expectedVisitor.visitInsn(Opcodes.ICONST_0); 279 expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels, 280 frame); 281 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 282 0, null); 283 assertEquals(1000, LabelInfo.getProbeId(label)); 284 } 285 286 @Test testVisitTableSwitchInsn2()287 public void testVisitTableSwitchInsn2() { 288 Label label2 = new Label(); 289 LabelInfo.setTarget(label2); 290 LabelInfo.setTarget(label2); 291 292 final Label[] labels = new Label[] { label2, label }; 293 adapter.visitInsn(Opcodes.ICONST_0); 294 adapter.visitTableSwitchInsn(0, 1, label, labels); 295 296 expectedVisitor.visitInsn(Opcodes.ICONST_0); 297 expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels, 298 frame); 299 expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 300 0, null); 301 assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label)); 302 assertEquals(1000, LabelInfo.getProbeId(label2)); 303 } 304 305 @Test testVisitTableSwitchInsn3()306 public void testVisitTableSwitchInsn3() { 307 final Label[] labels = new Label[] { label, label }; 308 adapter.visitInsn(Opcodes.ICONST_0); 309 adapter.visitTableSwitchInsn(0, 1, label, labels); 310 311 expectedVisitor.visitInsn(Opcodes.ICONST_0); 312 expectedVisitor.visitTableSwitchInsn(0, 1, label, labels); 313 } 314 315 @Test testVisitTryCatchBlockNoProbe()316 public void testVisitTryCatchBlockNoProbe() { 317 Label start = new Label(); 318 Label end = new Label(); 319 Label handler = new Label(); 320 321 adapter.visitTryCatchBlock(start, end, handler, "java/lang/Exception"); 322 adapter.visitLabel(start); 323 adapter.visitInsn(Opcodes.NOP); 324 adapter.visitLabel(end); 325 326 expectedVisitor.visitTryCatchBlock(start, end, handler, 327 "java/lang/Exception"); 328 expectedVisitor.visitLabel(start); 329 expectedVisitor.visitInsn(Opcodes.NOP); 330 expectedVisitor.visitLabel(end); 331 } 332 333 @Test testVisitTryCatchBlockWithProbeBeforeStart()334 public void testVisitTryCatchBlockWithProbeBeforeStart() { 335 Label start = new Label(); 336 LabelInfo.setSuccessor(start); 337 LabelInfo.setTarget(start); 338 Label end = new Label(); 339 Label handler1 = new Label(); 340 Label handler2 = new Label(); 341 342 adapter.visitTryCatchBlock(start, end, handler1, "java/lang/Exception"); 343 adapter.visitTryCatchBlock(start, end, handler2, "java/lang/Throwable"); 344 adapter.visitLabel(start); 345 adapter.visitInsn(Opcodes.NOP); 346 adapter.visitLabel(end); 347 348 Label probe = new Label(); 349 expectedVisitor.visitTryCatchBlock(probe, end, handler1, 350 "java/lang/Exception"); 351 expectedVisitor.visitTryCatchBlock(probe, end, handler2, 352 "java/lang/Throwable"); 353 expectedVisitor.visitLabel(probe); 354 expectedVisitor.visitProbe(1000); 355 expectedVisitor.visitLabel(start); 356 expectedVisitor.visitInsn(Opcodes.NOP); 357 expectedVisitor.visitLabel(end); 358 } 359 360 @Test testVisitTryCatchBlockWithProbeBeforeEnd()361 public void testVisitTryCatchBlockWithProbeBeforeEnd() { 362 Label start = new Label(); 363 Label end = new Label(); 364 LabelInfo.setSuccessor(end); 365 LabelInfo.setTarget(end); 366 Label handler1 = new Label(); 367 Label handler2 = new Label(); 368 369 adapter.visitTryCatchBlock(start, end, handler1, "java/lang/Exception"); 370 adapter.visitTryCatchBlock(start, end, handler2, "java/lang/Throwable"); 371 adapter.visitLabel(start); 372 adapter.visitInsn(Opcodes.NOP); 373 adapter.visitLabel(end); 374 375 Label probe = new Label(); 376 expectedVisitor.visitTryCatchBlock(start, probe, handler1, 377 "java/lang/Exception"); 378 expectedVisitor.visitTryCatchBlock(start, probe, handler2, 379 "java/lang/Throwable"); 380 expectedVisitor.visitLabel(start); 381 expectedVisitor.visitInsn(Opcodes.NOP); 382 expectedVisitor.visitLabel(probe); 383 expectedVisitor.visitProbe(1000); 384 expectedVisitor.visitLabel(end); 385 } 386 387 /** 388 * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-2.html#jvms-2.11.10 389 */ 390 @Test testStructuredLocking()391 public void testStructuredLocking() { 392 Label start = new Label(); 393 LabelInfo.setSuccessor(start); 394 LabelInfo.setTarget(start); 395 Label end = new Label(); 396 LabelInfo.setSuccessor(end); 397 LabelInfo.setTarget(end); 398 Label handlerStart = new Label(); 399 Label handlerEnd = new Label(); 400 Label after = new Label(); 401 402 adapter.visitTryCatchBlock(start, end, handlerStart, null); 403 adapter.visitTryCatchBlock(handlerStart, handlerEnd, handlerStart, 404 null); 405 adapter.visitVarInsn(Opcodes.ALOAD, 1); 406 adapter.visitInsn(Opcodes.MONITORENTER); 407 adapter.visitLabel(start); 408 adapter.visitInsn(Opcodes.NOP); 409 adapter.visitVarInsn(Opcodes.ALOAD, 1); 410 adapter.visitInsn(Opcodes.MONITOREXIT); 411 adapter.visitLabel(end); 412 adapter.visitJumpInsn(Opcodes.GOTO, after); 413 adapter.visitLabel(handlerStart); 414 adapter.visitVarInsn(Opcodes.ALOAD, 1); 415 adapter.visitInsn(Opcodes.MONITOREXIT); 416 adapter.visitLabel(handlerEnd); 417 adapter.visitInsn(Opcodes.ATHROW); 418 adapter.visitLabel(after); 419 420 Label probe1 = new Label(); 421 Label probe2 = new Label(); 422 expectedVisitor.visitTryCatchBlock(probe1, probe2, handlerStart, null); 423 expectedVisitor.visitTryCatchBlock(handlerStart, handlerEnd, 424 handlerStart, null); 425 expectedVisitor.visitVarInsn(Opcodes.ALOAD, 1); 426 expectedVisitor.visitInsn(Opcodes.MONITORENTER); 427 // next probe must be INSIDE range of instructions covered by handler, 428 // otherwise monitorexit won't be executed 429 // in case if probe causes exception 430 expectedVisitor.visitLabel(probe1); 431 expectedVisitor.visitProbe(1000); 432 expectedVisitor.visitLabel(start); 433 expectedVisitor.visitInsn(Opcodes.NOP); 434 expectedVisitor.visitVarInsn(Opcodes.ALOAD, 1); 435 expectedVisitor.visitInsn(Opcodes.MONITOREXIT); 436 // next probe must be OUTSIDE range of instructions covered by handler, 437 // otherwise monitorexit will be executed second time 438 // in case if probe causes exception 439 expectedVisitor.visitLabel(probe2); 440 expectedVisitor.visitProbe(1001); 441 expectedVisitor.visitLabel(end); 442 expectedVisitor.visitJumpInsn(Opcodes.GOTO, after); 443 expectedVisitor.visitLabel(handlerStart); 444 expectedVisitor.visitVarInsn(Opcodes.ALOAD, 1); 445 expectedVisitor.visitInsn(Opcodes.MONITOREXIT); 446 expectedVisitor.visitLabel(handlerEnd); 447 expectedVisitor.visitInsnWithProbe(Opcodes.ATHROW, 1002); 448 expectedVisitor.visitLabel(after); 449 } 450 451 // === IProbeIdGenerator === 452 nextId()453 public int nextId() { 454 return id++; 455 } 456 457 } 458