ExceptionHandlers.java 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package io.opentelemetry.javaagent.tooling.bytebuddy;
  6. import io.opentelemetry.javaagent.bootstrap.ExceptionLogger;
  7. import net.bytebuddy.ClassFileVersion;
  8. import net.bytebuddy.asm.Advice.ExceptionHandler;
  9. import net.bytebuddy.implementation.Implementation;
  10. import net.bytebuddy.implementation.bytecode.StackManipulation;
  11. import org.objectweb.asm.Label;
  12. import org.objectweb.asm.MethodVisitor;
  13. import org.objectweb.asm.Opcodes;
  14. public final class ExceptionHandlers {
  15. // Bootstrap ExceptionLogger.class will always be resolvable, so we'll use it in the log name
  16. private static final String LOGGER_NAME = ExceptionLogger.class.getName().replace('.', '/');
  17. private static final ExceptionHandler EXCEPTION_STACK_HANDLER =
  18. new ExceptionHandler.Simple(
  19. new StackManipulation() {
  20. // Pops one Throwable off the stack. Maxes the stack to at least 2 (throwable, string).
  21. private final StackManipulation.Size size = new StackManipulation.Size(-1, 2);
  22. @Override
  23. public boolean isValid() {
  24. return true;
  25. }
  26. @Override
  27. public StackManipulation.Size apply(MethodVisitor mv, Implementation.Context context) {
  28. String name = context.getInstrumentedType().getName();
  29. // writes the following bytecode:
  30. // try {
  31. // ExceptionLogger.logSuppressedError("exception in instrumentation", t);
  32. // } catch (Throwable t2) {
  33. // }
  34. Label logStart = new Label();
  35. Label logEnd = new Label();
  36. Label eatException = new Label();
  37. Label handlerExit = new Label();
  38. // Frames are only meaningful for class files in version 6 or later.
  39. boolean frames = context.getClassFileVersion().isAtLeast(ClassFileVersion.JAVA_V6);
  40. mv.visitTryCatchBlock(logStart, logEnd, eatException, "java/lang/Throwable");
  41. // stack: (top) throwable
  42. mv.visitLabel(logStart);
  43. mv.visitLdcInsn("Failed to handle exception in instrumentation for " + name);
  44. mv.visitInsn(Opcodes.SWAP); // stack: (top) throwable,string
  45. mv.visitMethodInsn(
  46. Opcodes.INVOKESTATIC,
  47. LOGGER_NAME,
  48. "logSuppressedError",
  49. "(Ljava/lang/String;Ljava/lang/Throwable;)V",
  50. /* isInterface= */ false);
  51. mv.visitLabel(logEnd);
  52. mv.visitJumpInsn(Opcodes.GOTO, handlerExit);
  53. // if the runtime can't reach our ExceptionLogger,
  54. // silently eat the exception
  55. mv.visitLabel(eatException);
  56. if (frames) {
  57. mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
  58. }
  59. mv.visitInsn(Opcodes.POP);
  60. // mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
  61. // "printStackTrace", "()V", false);
  62. mv.visitLabel(handlerExit);
  63. if (frames) {
  64. mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
  65. // there may be at most one frame at given code location, we need to add an extra
  66. // NOP instruction to ensure that there isn't a duplicate frame
  67. mv.visitInsn(Opcodes.NOP);
  68. }
  69. return size;
  70. }
  71. });
  72. public static ExceptionHandler defaultExceptionHandler() {
  73. return EXCEPTION_STACK_HANDLER;
  74. }
  75. private ExceptionHandlers() {}
  76. }