1 /*
   2  * Copyright (c) 2012, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
  27 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
  28 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
  29 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
  30 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
  31 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
  32 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
  33 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
  34 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
  35 import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
  36 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
  37 import static jdk.internal.org.objectweb.asm.Opcodes.V1_6;
  38 
  39 import java.lang.instrument.ClassDefinition;
  40 import java.lang.instrument.Instrumentation;
  41 import java.lang.reflect.Method;
  42 import java.lang.reflect.Modifier;
  43 import java.net.InetAddress;
  44 
  45 import jdk.internal.org.objectweb.asm.ClassWriter;
  46 import jdk.internal.org.objectweb.asm.MethodVisitor;
  47 import jdk.internal.org.objectweb.asm.Type;
  48 import sun.misc.IoTrace;
  49 import sun.misc.IoTraceContext;
  50 
  51 public class IoTraceAgent {
  52 
  53     private static IoTraceListener listener;
  54 
  55     public static void setListener(IoTraceListener l) {
  56         listener = l;
  57     }
  58 
  59     public static IoTraceContext socketReadBegin(InetAddress address, int port,
  60             int timeout) {
  61         IoTraceListener l = listener;
  62         if (l != null) {
  63             return l.socketReadBegin(address, port, timeout);
  64         }
  65         return null;
  66     }
  67 
  68     public static void socketReadEnd(IoTraceContext context, long bytesRead) {
  69         IoTraceListener l = listener;
  70         if (l != null) {
  71             l.socketReadEnd(context, bytesRead);
  72         }
  73     }
  74 
  75     public static IoTraceContext socketWriteBegin(InetAddress address, int port) {
  76         IoTraceListener l = listener;
  77         if (l != null) {
  78             return l.socketWriteBegin(address, port);
  79         }
  80         return null;
  81     }
  82 
  83     public static void socketWriteEnd(IoTraceContext context, long bytesWritten) {
  84         IoTraceListener l = listener;
  85         if (l != null) {
  86             l.socketWriteEnd(context, bytesWritten);
  87         }
  88     }
  89 
  90     public static IoTraceContext fileReadBegin(String path) {
  91         IoTraceListener l = listener;
  92         if (l != null) {
  93             return l.fileReadBegin(path);
  94         }
  95         return null;
  96     }
  97 
  98     public static void fileReadEnd(IoTraceContext context, long bytesRead) {
  99         IoTraceListener l = listener;
 100         if (l != null) {
 101             l.fileReadEnd(context, bytesRead);
 102         }
 103     }
 104 
 105     public static IoTraceContext fileWriteBegin(String path) {
 106         IoTraceListener l = listener;
 107         if (l != null) {
 108             return l.fileWriteBegin(path);
 109         }
 110         return null;
 111     }
 112 
 113     public static void fileWriteEnd(IoTraceContext context, long bytesWritten) {
 114         IoTraceListener l = listener;
 115         if (l != null) {
 116             l.fileWriteEnd(context, bytesWritten);
 117         }
 118     }
 119 
 120     public static void premain(String agentArgs, Instrumentation inst)
 121             throws Exception {
 122         ClassDefinition cd = new ClassDefinition(IoTrace.class,
 123                 generateClassAsm());
 124         inst.redefineClasses(cd);
 125     }
 126 
 127     private static byte[] generateClassAsm() {
 128         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
 129                 | ClassWriter.COMPUTE_MAXS);
 130         cw.visit(V1_6, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, "sun/misc/IoTrace",
 131                 null, "java/lang/Object", null);
 132 
 133         // for all methods in the existing IoTrace class
 134         // we want to create a method in the new version of it which call
 135         // IoTraceAgent
 136         //
 137         // 0: aload_0
 138         // 1: iload_1
 139         // 2: iload_2
 140         // 3: invokestatic #16 // Method
 141         // IoTraceAgent.socketReadBegin:(Ljava/net/InetAddress;II)Lsun/misc/IoTraceContext;
 142         // 6: areturn
 143 
 144         for (Method om : IoTrace.class.getDeclaredMethods()) {
 145             if (!Modifier.isStatic(om.getModifiers())) {
 146                 continue;
 147             }
 148 
 149             // create a method with the same signature as the
 150             // original method
 151             MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
 152                     om.getName(), Type.getMethodDescriptor(om), null, null);
 153             mv.visitCode();
 154 
 155             // get the return type and argument list types
 156             Type[] argTypes = Type.getArgumentTypes(om);
 157             Type retType = Type.getReturnType(om);
 158 
 159             // load all the arguments
 160             int i = 0;
 161             for (Type t : argTypes) {
 162                 int instr;
 163                 switch (t.getSort()) {
 164                 case Type.INT:
 165                     instr = ILOAD;
 166                     break;
 167                 case Type.LONG:
 168                     instr = LLOAD;
 169                     break;
 170                 case Type.OBJECT:
 171                     instr = ALOAD;
 172                     break;
 173                 default:
 174                     throw new IllegalArgumentException(
 175                             "Unhandled argument type when generating code: "
 176                                     + t);
 177                 }
 178                 mv.visitVarInsn(instr, i++);
 179             }
 180 
 181             // call a method with the same signature (but in a different class)
 182             // with all the arguments
 183             mv.visitMethodInsn(INVOKESTATIC, "IoTraceAgent", om.getName(),
 184                     Type.getMethodDescriptor(om));
 185 
 186             // return the value from the called method
 187             if (retType.getSort() == Type.VOID) {
 188                 mv.visitInsn(RETURN);
 189             } else if (retType.getSort() == Type.OBJECT) {
 190                 mv.visitInsn(ARETURN);
 191             } else {
 192                 throw new IllegalArgumentException(
 193                         "Unhandled return type when generating code: "
 194                                 + retType);
 195             }
 196             mv.visitEnd();
 197         }
 198 
 199         // empty private constructor
 200         MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "<init>", "()V", null,
 201                 null);
 202         mv.visitCode();
 203         mv.visitInsn(RETURN);
 204         mv.visitEnd();
 205 
 206         cw.visitEnd();
 207         return cw.toByteArray();
 208     }
 209 }