All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.btrace.runtime.Preprocessor Maven / Gradle / Ivy

/*
 * Copyright 2008-2010 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package com.sun.btrace.runtime;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.sun.btrace.org.objectweb.asm.Opcodes.*;
import static com.sun.btrace.runtime.Constants.*;
import com.sun.btrace.BTraceRuntime;
import com.sun.btrace.annotations.Export;
import com.sun.btrace.annotations.TLS;
import com.sun.btrace.annotations.Property;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.sun.btrace.org.objectweb.asm.AnnotationVisitor;
import com.sun.btrace.org.objectweb.asm.Attribute;
import com.sun.btrace.org.objectweb.asm.ClassReader;
import com.sun.btrace.org.objectweb.asm.ClassVisitor;
import com.sun.btrace.org.objectweb.asm.ClassWriter;
import com.sun.btrace.org.objectweb.asm.FieldVisitor;
import com.sun.btrace.org.objectweb.asm.Label;
import com.sun.btrace.org.objectweb.asm.MethodVisitor;
import com.sun.btrace.org.objectweb.asm.Opcodes;
import com.sun.btrace.org.objectweb.asm.Type;

/**
 * This class preprocesses a compiled BTrace program.
 * This is done after BTrace safety verification but
 * before instrumenting the probed classes.
 *
 * Transformations done here:
 *
 *    1. add  method, if one not found
 *    2. replace @Export fields by perf counters
 *       and replace put/get by perf counter update/read
 *    3. replace @TLS fields by ThreadLocal fields
 *       and replace put/get by ThreadLocal set/get
 *    4. In  method, add ThreadLocal creation
 *       and perf counter creation calls (for @Export and
 *       @TLS fields respectively)
 *    5. Add a field to store BTraceRuntime object and
 *       initialize the same in  method
 *    6. add prolog and epilog in each BTrace action method
 *       to insert BTraceRuntime.enter/leave and also to call
 *       BTraceRuntime.handleException on exception catch
 *    7. add a field to store client's BTraceRuntime instance
 *
 * 
 * @author A. Sundararajan
 */
public class Preprocessor extends ClassVisitor {
    public static final String JAVA_LANG_THREAD_LOCAL_DESC = "Ljava/lang/ThreadLocal;";
    // btrace specific stuff
    public static final String BTRACE_EXPORT_DESC =
        Type.getDescriptor(Export.class);
    public static final String BTRACE_TLS_DESC =
        Type.getDescriptor(TLS.class);
    public static final String BTRACE_PROPERTY_DESC =
        Type.getDescriptor(Property.class);
    public static final String BTRACE_PROPERTY_NAME = "name";
    public static final String BTRACE_PROPERTY_DESCRIPTION = "description";
    
        
    // stuff from BTraceRuntime class
    public static final String BTRACE_RUNTIME =
        Type.getInternalName(BTraceRuntime.class);
    public static final String BTRACE_RUNTIME_DESC =
        Type.getDescriptor(BTraceRuntime.class);

    public static final String BTRACE_RUNTIME_FIELD_NAME = "runtime";
    public static final String BTRACE_FIELD_PREFIX = "$";

    public static final String BTRACE_RUNTIME_HANDLE_EXCEPTION;
    public static final String BTRACE_RUNTIME_HANDLE_EXCEPTION_DESC;
    public static final String BTRACE_RUNTIME_ENTER;
    public static final String BTRACE_RUNTIME_ENTER_DESC;
    public static final String BTRACE_RUNTIME_LEAVE;
    public static final String BTRACE_RUNTIME_LEAVE_DESC; 
    public static final String BTRACE_RUNTIME_START;
    public static final String BTRACE_RUNTIME_START_DESC; 
    public static final String BTRACE_RUNTIME_FOR_CLASS;
    public static final String BTRACE_RUNTIME_FOR_CLASS_DESC;
    public static final String BTRACE_RUNTIME_NEW_THREAD_LOCAL;
    public static final String BTRACE_RUNTIME_NEW_THREAD_LOCAL_DESC;
    public static final String BTRACE_RUNTIME_NEW_PERFCOUNTER;
    public static final String BTRACE_RUNTIME_NEW_PERFCOUNTER_DESC;
    public static final String BTRACE_RUNTIME_GET_PERFSTRING;
    public static final String BTRACE_RUNTIME_GET_PERFSTRING_DESC;
    public static final String BTRACE_RUNTIME_GET_PERFINT;
    public static final String BTRACE_RUNTIME_GET_PERFINT_DESC;
    public static final String BTRACE_RUNTIME_GET_PERFLONG;
    public static final String BTRACE_RUNTIME_GET_PERFLONG_DESC;
    public static final String BTRACE_RUNTIME_GET_PERFFLOAT;
    public static final String BTRACE_RUNTIME_GET_PERFFLOAT_DESC;
    public static final String BTRACE_RUNTIME_GET_PERFDOUBLE;
    public static final String BTRACE_RUNTIME_GET_PERFDOUBLE_DESC;
    public static final String BTRACE_RUNTIME_PUT_PERFSTRING;
    public static final String BTRACE_RUNTIME_PUT_PERFSTRING_DESC;
    public static final String BTRACE_RUNTIME_PUT_PERFINT;
    public static final String BTRACE_RUNTIME_PUT_PERFINT_DESC;
    public static final String BTRACE_RUNTIME_PUT_PERFLONG;
    public static final String BTRACE_RUNTIME_PUT_PERFLONG_DESC;
    public static final String BTRACE_RUNTIME_PUT_PERFFLOAT;
    public static final String BTRACE_RUNTIME_PUT_PERFFLOAT_DESC;
    public static final String BTRACE_RUNTIME_PUT_PERFDOUBLE;
    public static final String BTRACE_RUNTIME_PUT_PERFDOUBLE_DESC;

    static {     
       try {
           Method handleException = 
                       BTraceRuntime.class.getMethod(
                       "handleException", 
                       new Class[] { Throwable.class });
           BTRACE_RUNTIME_HANDLE_EXCEPTION = 
                       handleException.getName();
           BTRACE_RUNTIME_HANDLE_EXCEPTION_DESC = 
                       Type.getMethodDescriptor(handleException);

           Method enter = BTraceRuntime.class.getMethod(
                       "enter",
                       new Class[] { BTraceRuntime.class });
           BTRACE_RUNTIME_ENTER = enter.getName();
           BTRACE_RUNTIME_ENTER_DESC = 
                       Type.getMethodDescriptor(enter);

           Method leave = BTraceRuntime.class.getMethod(
                       "leave",
                       new Class[0]);
           BTRACE_RUNTIME_LEAVE = leave.getName();
           BTRACE_RUNTIME_LEAVE_DESC = 
                       Type.getMethodDescriptor(leave);

           Method start = BTraceRuntime.class.getMethod(
                       "start",
                       new Class[0]);
           BTRACE_RUNTIME_START = start.getName();
           BTRACE_RUNTIME_START_DESC = 
                       Type.getMethodDescriptor(start);

           Method forClass = BTraceRuntime.class.getMethod(
                       "forClass",
                       new Class[] { Class.class });
           BTRACE_RUNTIME_FOR_CLASS = forClass.getName();
           BTRACE_RUNTIME_FOR_CLASS_DESC = 
                       Type.getMethodDescriptor(forClass);

           Method newThreadLocal = BTraceRuntime.class.getMethod(
                       "newThreadLocal",
                       new Class[] { Object.class });
           BTRACE_RUNTIME_NEW_THREAD_LOCAL = newThreadLocal.getName();
           BTRACE_RUNTIME_NEW_THREAD_LOCAL_DESC = 
                       Type.getMethodDescriptor(newThreadLocal);

           Method newPerfCounter = BTraceRuntime.class.getMethod(
                       "newPerfCounter",
                       new Class[] { String.class, String.class, Object.class });
           BTRACE_RUNTIME_NEW_PERFCOUNTER = newPerfCounter.getName();
           BTRACE_RUNTIME_NEW_PERFCOUNTER_DESC = 
                       Type.getMethodDescriptor(newPerfCounter);

           Method getPerfString = BTraceRuntime.class.getMethod(
                       "getPerfString",
                       new Class[] { String.class });
           BTRACE_RUNTIME_GET_PERFSTRING = getPerfString.getName();
           BTRACE_RUNTIME_GET_PERFSTRING_DESC = 
                       Type.getMethodDescriptor(getPerfString);

           Method getPerfInt = BTraceRuntime.class.getMethod(
                       "getPerfInt",
                       new Class[] { String.class });
           BTRACE_RUNTIME_GET_PERFINT = getPerfInt.getName();
           BTRACE_RUNTIME_GET_PERFINT_DESC = 
                       Type.getMethodDescriptor(getPerfInt);

           Method getPerfLong = BTraceRuntime.class.getMethod(
                       "getPerfLong",
                       new Class[] { String.class });
           BTRACE_RUNTIME_GET_PERFLONG = getPerfLong.getName();
           BTRACE_RUNTIME_GET_PERFLONG_DESC = 
                       Type.getMethodDescriptor(getPerfLong);

           Method getPerfFloat = BTraceRuntime.class.getMethod(
                       "getPerfFloat",
                       new Class[] { String.class });
           BTRACE_RUNTIME_GET_PERFFLOAT = getPerfFloat.getName();
           BTRACE_RUNTIME_GET_PERFFLOAT_DESC = 
                       Type.getMethodDescriptor(getPerfFloat);

           Method getPerfDouble = BTraceRuntime.class.getMethod(
                       "getPerfDouble",
                       new Class[] { String.class });
           BTRACE_RUNTIME_GET_PERFDOUBLE = getPerfDouble.getName();
           BTRACE_RUNTIME_GET_PERFDOUBLE_DESC = 
                       Type.getMethodDescriptor(getPerfDouble);

           Method putPerfString = BTraceRuntime.class.getMethod(
                       "putPerfString",
                       new Class[] { String.class, String.class });
           BTRACE_RUNTIME_PUT_PERFSTRING = putPerfString.getName();
           BTRACE_RUNTIME_PUT_PERFSTRING_DESC = 
                       Type.getMethodDescriptor(putPerfString);

           Method putPerfInt = BTraceRuntime.class.getMethod(
                       "putPerfInt",
                       new Class[] { int.class, String.class });
           BTRACE_RUNTIME_PUT_PERFINT = putPerfInt.getName();
           BTRACE_RUNTIME_PUT_PERFINT_DESC = 
                       Type.getMethodDescriptor(putPerfInt);

           Method putPerfLong = BTraceRuntime.class.getMethod(
                       "putPerfLong",
                       new Class[] { long.class, String.class });
           BTRACE_RUNTIME_PUT_PERFLONG = putPerfLong.getName();
           BTRACE_RUNTIME_PUT_PERFLONG_DESC = 
                       Type.getMethodDescriptor(putPerfLong);

           Method putPerfFloat = BTraceRuntime.class.getMethod(
                       "putPerfFloat",
                       new Class[] { float.class, String.class });
           BTRACE_RUNTIME_PUT_PERFFLOAT = putPerfFloat.getName();
           BTRACE_RUNTIME_PUT_PERFFLOAT_DESC = 
                       Type.getMethodDescriptor(putPerfFloat);

           Method putPerfDouble = BTraceRuntime.class.getMethod(
                       "putPerfDouble",
                       new Class[] { double.class, String.class });
           BTRACE_RUNTIME_PUT_PERFDOUBLE = putPerfDouble.getName();
           BTRACE_RUNTIME_PUT_PERFDOUBLE_DESC = 
                       Type.getMethodDescriptor(putPerfDouble);           
       } catch (RuntimeException re) {
           throw re;
       } catch (Exception exp) {
           throw new RuntimeException(exp);
       }
    }

    // class name as appears in .class file
    private String className;
    private String superName;
    // external class name
    private String externalClassName;
    // Type of the currently visited class
    private Type classType;

    // FieldDescriptor (see below) for all fields
    private List fields;
    private Map threadLocalFields;
    private Map exportFields;

    // flag to tell whether we have seen  or not
    private boolean classInitializerFound;

    public Preprocessor(ClassVisitor cv) {
        super(Opcodes.ASM4, cv);
        fields = new ArrayList();
        threadLocalFields = new HashMap();
        exportFields = new HashMap();
    }

    public void visit(int version,
                  int access,
                  String name,
                  String signature,
                  String superName,
                  String[] interfaces) {
        className = name;
        this.superName = superName;
        classType = Type.getObjectType(className);
        classInitializerFound = false;
        super.visit(version, access, name,
                    signature, superName, interfaces);
    }
    
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        final AnnotationVisitor zupr = super.visitAnnotation(desc, visible);
        if ("Lcom/sun/btrace/annotations/BTrace;".equals(desc)) {
            return new AnnotationVisitor(Opcodes.ASM4) {

                public void visit(String string, Object o) {
                    if ("name".equals(string)) {
                        externalClassName = (String)o;
                    }
                    zupr.visit(string, o);
                }

                public void visitEnum(String string, String string1, String string2) {
                    zupr.visitEnum(string, string1, string2);
                }

                public AnnotationVisitor visitAnnotation(String string, String string1) {
                    // do nothing
                    return zupr.visitAnnotation(string, string1);
                }

                public AnnotationVisitor visitArray(String string) {
                    // do nothing
                    return zupr.visitArray(string);
                }

                public void visitEnd() {
                    zupr.visitEnd();
                }
            };
        }
        return zupr;
    }
    
    // prefix for BTrace perf counter names.
    private String externalClassName() {
        if (externalClassName == null) {
            externalClassName = className.replace('/', '.');
        }
        return externalClassName;
    }

    // For each @Export field, we create a perf counter
    // with the name "btrace.."
    private static final String BTRACE_COUNTER_PREFIX = "btrace.";
    private String perfCounterName(String fieldName) {
        return BTRACE_COUNTER_PREFIX + externalClassName() + "." + fieldName;
    }

    // save interesting bits of each field
    private static class FieldDescriptor {
        int access;
        String name, desc, signature;
        Object value;
        List attributes;
        boolean isThreadLocal;
        boolean isExport;
        boolean isProperty;
        String propertyName;
        String propertyDescription;
        int var = -1;
        boolean initialized;

        FieldDescriptor(int acc, String n, String d,
                        String sig, Object val, List attrs,
                        boolean tls, boolean isExp, boolean isProp,
                        String propName, String propDescription) {
            access = acc;
            name = n;
            desc = d;
            signature = sig;
            value = val;
            attributes = attrs;
            isThreadLocal = tls;
            isExport = isExp;
            isProperty = isProp;
            propertyName = propName;
            propertyDescription = propDescription;
        }
    }

    public FieldVisitor visitField(final int access, final String name, 
            final String desc, final String signature, final Object value) {        
        final List attrs = new ArrayList();
        return new FieldVisitor(Opcodes.ASM4) {
            boolean isExport;
            boolean isThreadLocal;
            boolean isProperty;
            String propName = "";
            String propDescription = "";
            
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                if (desc.equals(BTRACE_TLS_DESC)) {
                    isThreadLocal = true;
                } else if (desc.equals(BTRACE_EXPORT_DESC)) {
                    isExport = true;                    
                } else if (desc.equals(BTRACE_PROPERTY_DESC)) {
                    isProperty = true;
                    return new AnnotationVisitor(Opcodes.ASM4) {
                        @Override
                        public void visit(String name, Object value) {
                            if (name.equals(BTRACE_PROPERTY_NAME)) {
                                propName = value.toString();
                            } else if (name.equals(BTRACE_PROPERTY_DESCRIPTION)) {
                                propDescription = value.toString();
                            }
                        }
                    };
                }
                return new AnnotationVisitor(Opcodes.ASM4){};
            }

            public void visitAttribute(Attribute attr) {
                attrs.add(attr);
            }

            public void visitEnd() {
                FieldDescriptor fd = new FieldDescriptor(access, name, desc, 
                                    signature, value, attrs,
                                    isThreadLocal, isExport, isProperty,
                                    propName, propDescription);
                fields.add(fd);
                if (isThreadLocal) {         
                    threadLocalFields.put(name, fd);
                } else if (isExport) {
                    exportFields.put(name, fd);
                }
            }
        };
    }
    
    public void visitEnd() {
        if (! classInitializerFound) {
            // add a dummy  method that just returns.
            MethodVisitor clinit = visitMethod(ACC_STATIC|ACC_PUBLIC,
                               CLASS_INITIALIZER, "()V", null, null);
            clinit.visitCode();
            clinit.visitInsn(RETURN);
            clinit.visitMaxs(0, 0);
            clinit.visitEnd();
        }
        addFields();
        super.visitEnd();
    }

    private void addFields() {
        for (FieldDescriptor fd : fields) {
            String fieldName = fd.name;
            if (fd.isExport) {
                // no need to add a field for exported fields
                continue;
            }
            int fieldAccess = fd.access;
            String fieldDesc = fd.desc;
            String fieldSignature = fd.signature;
            Object fieldValue = fd.value;
            if (fd.isThreadLocal) {
                fieldAccess &= ~ACC_FINAL;
                fieldDesc = JAVA_LANG_THREAD_LOCAL_DESC;
                fieldSignature = null;
                fieldValue = null;
            }

            fieldAccess &= ~ACC_PRIVATE;
            fieldAccess &= ~ACC_PROTECTED;
            fieldAccess |= ACC_PUBLIC;

            FieldVisitor fv = super.visitField(fieldAccess,  
                                 BTRACE_FIELD_PREFIX + fieldName,
                                 fieldDesc, fieldSignature, fieldValue);
            if (fd.isProperty) {
                AnnotationVisitor av = fv.visitAnnotation(BTRACE_PROPERTY_DESC, true);
                if (av != null) {
                    av.visit(BTRACE_PROPERTY_NAME, fd.propertyName);
                    av.visit(BTRACE_PROPERTY_DESCRIPTION, fd.propertyDescription);
                }
            }
            for (Attribute attr : fd.attributes) {
                fv.visitAttribute(attr);
            }
            fv.visitEnd();
        }

        // add a special field to store client's BTraceRuntime
        super.visitField(ACC_PUBLIC|ACC_STATIC, BTRACE_RUNTIME_FIELD_NAME,
                   BTRACE_RUNTIME_DESC, null, null);        
    }

    public MethodVisitor visitMethod(int access, String name, 
            String desc, String signature, String[] exceptions) {
        if (name.equals(CONSTRUCTOR)) {
            return super.visitMethod(access, name, desc, signature, exceptions);
        } else {            
            /*
             * For each probe method, we generate epilog and prolog as shown
             * below:
             *
             *        if (! BTraceRuntime.enter()) {
             *            return;
             *        } else {
             *            try {
             *                // user's probe code here with this change:
             *                // bfore every return in user's code, call 
             *                // BTraceRuntime.leave();
             *            } catch (Throwable t) {
             *                BTraceRuntime.handleException(t);
             *            }
             *        }
             *
             */
            final boolean isClassInitializer = name.equals(CLASS_INITIALIZER);
            classInitializerFound = classInitializerFound || isClassInitializer;

            if (!isClassInitializer) {
                // force the method to be public
                if ((access & Opcodes.ACC_PRIVATE) > 0) access ^= Opcodes.ACC_PRIVATE;
                if ((access & Opcodes.ACC_PROTECTED) > 0) access ^= Opcodes.ACC_PROTECTED;
                access |= Opcodes.ACC_PUBLIC;
            }
            
            MethodVisitor adaptee = super.visitMethod(access, name, desc, 
                                                    signature, exceptions);
            
            return new MethodInstrumentor(adaptee, className, superName, access, name, desc) {
                private boolean isBTraceHandler = false;
                private Label start = new Label();
                private Label handler = new Label();
                private int nextVar = 0;

                private void generateExportGet(String name, String desc) {
                    int typeCode = desc.charAt(0);
                    switch (typeCode) {
                        case '[':
                            visitInsn(ACONST_NULL);
                            break;
                        case 'L':
                            if (desc.equals(JAVA_LANG_STRING_DESC)) {
                                visitLdcInsn(name);
                                visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_GET_PERFSTRING,
                                    BTRACE_RUNTIME_GET_PERFSTRING_DESC);
                            } else {
                                visitInsn(ACONST_NULL);
                            }
                            break;
                        case 'Z':
                        case 'C':
                        case 'B':
                        case 'S':
                        case 'I':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_GET_PERFINT,
                                    BTRACE_RUNTIME_GET_PERFINT_DESC);
                            break;
                        case 'J':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_GET_PERFLONG,
                                    BTRACE_RUNTIME_GET_PERFLONG_DESC);
                            break;
                        case 'F':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_GET_PERFFLOAT,
                                    BTRACE_RUNTIME_GET_PERFFLOAT_DESC);
                            break;
                        case 'D':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_GET_PERFDOUBLE,
                                    BTRACE_RUNTIME_GET_PERFDOUBLE_DESC);
                            break;
                    }
                }

                private void generateExportPut(String name, String desc) {
                    int typeCode = desc.charAt(0);
                    switch (typeCode) {
                        case '[':
                            visitInsn(POP);
                            break;
                        case 'L':
                            if (desc.equals(JAVA_LANG_STRING_DESC)) {
                                visitLdcInsn(name);
                                visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_PUT_PERFSTRING, 
                                    BTRACE_RUNTIME_PUT_PERFSTRING_DESC);
                            } else {
                                visitInsn(POP);
                            }
                            break;
                        case 'Z':
                        case 'C':
                        case 'B':
                        case 'S':
                        case 'I':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_PUT_PERFINT,
                                    BTRACE_RUNTIME_PUT_PERFINT_DESC);
                            break;
                        case 'J':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_PUT_PERFLONG,
                                    BTRACE_RUNTIME_PUT_PERFLONG_DESC);
                            break;
                        case 'F':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_PUT_PERFFLOAT,
                                    BTRACE_RUNTIME_PUT_PERFFLOAT_DESC);
                            break;
                        case 'D':
                            visitLdcInsn(name);
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_PUT_PERFDOUBLE,
                                    BTRACE_RUNTIME_PUT_PERFDOUBLE_DESC);
                            break;
                    }
                }

                private void generateThreadLocalGet(FieldDescriptor fd) {
                    if (isClassInitializer) {
                        if (fd.initialized) {
                            super.visitVarInsn(Type.getType(fd.desc).getOpcode(Opcodes.ILOAD), fd.var);
                        } else if (fd.value != null) {
                            visitLdcInsn(fd.value);
                        } else {
                            defaultValue(fd.desc);
                        }
                    } else {
                        String fieldName = BTRACE_FIELD_PREFIX + fd.name;
                        super.visitFieldInsn(GETSTATIC, className, fieldName, JAVA_LANG_THREAD_LOCAL_DESC);
                        visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_THREAD_LOCAL,
                                     JAVA_LANG_THREAD_LOCAL_GET, JAVA_LANG_THREAD_LOCAL_GET_DESC);
                        unbox(fd.desc);
                    }
                }

                private void generateThreadLocalPut(FieldDescriptor fd) {
                    if (isClassInitializer) {
                        super.visitVarInsn(Type.getType(fd.desc).getOpcode(Opcodes.ISTORE), fd.var);
                        fd.initialized = true;
                    } else {
                        String fieldName = BTRACE_FIELD_PREFIX + fd.name;
                        box(fd.desc);
                        super.visitFieldInsn(GETSTATIC, className, fieldName, JAVA_LANG_THREAD_LOCAL_DESC);
                        visitInsn(SWAP);
                        visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_THREAD_LOCAL,
                                    JAVA_LANG_THREAD_LOCAL_SET, JAVA_LANG_THREAD_LOCAL_SET_DESC);
                    }
                }

                @Override
                public AnnotationVisitor visitAnnotation(String name, boolean bln) {
                    if (name.startsWith("Lcom/sun/btrace/annotations/")) {
                        isBTraceHandler = true;
                    } else {
                        isBTraceHandler = false;
                    }
                    return super.visitAnnotation(name, bln);
                }



                public void visitCode() {
                    if (isClassInitializer || isBTraceHandler) {
                        visitTryCatchBlock(start, handler, handler,
                                        JAVA_LANG_THROWABLE);

                        if (isClassInitializer) {
                             visitLdcInsn(classType);
                             visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                        BTRACE_RUNTIME_FOR_CLASS,
                                        BTRACE_RUNTIME_FOR_CLASS_DESC);
                             super.visitFieldInsn(PUTSTATIC, className,
                                       BTRACE_RUNTIME_FIELD_NAME,
                                       BTRACE_RUNTIME_DESC);
                        }
                        visitFieldInsn(GETSTATIC, className,
                                       BTRACE_RUNTIME_FIELD_NAME,
                                       BTRACE_RUNTIME_DESC);
                        visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                        BTRACE_RUNTIME_ENTER,
                                        BTRACE_RUNTIME_ENTER_DESC);
                        if (isClassInitializer) {
                             for (FieldDescriptor fd : threadLocalFields.values()) {
                                 fd.var = nextVar;
                                 nextVar += Type.getType(fd.desc).getSize();
                             }
                             for (FieldDescriptor fd : exportFields.values()) {
                                 visitLdcInsn(perfCounterName(fd.name));
                                 visitLdcInsn(fd.desc);
                                 if (fd.value == null) {
                                     visitInsn(ACONST_NULL);
                                 } else {
                                     visitLdcInsn(fd.value);
                                     box(fd.desc);
                                 }
                                 visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                        BTRACE_RUNTIME_NEW_PERFCOUNTER,
                                        BTRACE_RUNTIME_NEW_PERFCOUNTER_DESC);
                             }
                        }

                        visitJumpInsn(IFNE, start);
                        super.visitInsn(RETURN);
                        visitLabel(start);
                    }
                    super.visitCode();
                }

                public void visitFieldInsn(int opcode, String owner, 
                                               String name, String desc) {
                    String fieldName = name;
                    if (owner.equals(className)) {   
                        if (exportFields.get(name) != null) {
                            if (opcode == GETSTATIC) {
                                generateExportGet(perfCounterName(name), desc);
                            } else {
                                generateExportPut(perfCounterName(name), desc);
                            }
                            return;                       
                        }

                        if (! name.equals(BTRACE_RUNTIME_FIELD_NAME)) {
                            fieldName = BTRACE_FIELD_PREFIX + name; 
                        }

                        FieldDescriptor fd = threadLocalFields.get(name);
                        if (fd != null) {
                            if (opcode == GETSTATIC) {                                
                                generateThreadLocalGet(fd);
                            } else {
                                generateThreadLocalPut(fd);
                            }
                            return;
                        } // else fall through
                    } // else fall through
                    super.visitFieldInsn(opcode, owner, fieldName, desc);
                }

                public void visitVarInsn(int opcode, int var) {
                    super.visitVarInsn(opcode, var + nextVar);
                }

                public void visitInsn(int opcode) {
                    if (opcode == RETURN) {
                        if (isClassInitializer) {
                            for (FieldDescriptor fd : threadLocalFields.values()) {
                                generateThreadLocalGet(fd); // generates var load here
                                box(fd.desc);
                                visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                        BTRACE_RUNTIME_NEW_THREAD_LOCAL,
                                        BTRACE_RUNTIME_NEW_THREAD_LOCAL_DESC);
                                super.visitFieldInsn(PUTSTATIC, className,
                                        BTRACE_FIELD_PREFIX + fd.name,
                                        JAVA_LANG_THREAD_LOCAL_DESC);
                            }
                            visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                BTRACE_RUNTIME_START,
                                BTRACE_RUNTIME_START_DESC);
                        } else {
                            if (isBTraceHandler) {
                                visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                    BTRACE_RUNTIME_LEAVE, 
                                    BTRACE_RUNTIME_LEAVE_DESC);
                            }
                        }
                    }
                    super.visitInsn(opcode);        
                }

                public void visitMaxs(int maxStack, int maxLocals) {
                    visitLabel(handler);
                    if (isBTraceHandler) {
                        visitMethodInsn(INVOKESTATIC, BTRACE_RUNTIME,
                                        BTRACE_RUNTIME_HANDLE_EXCEPTION,
                                        BTRACE_RUNTIME_HANDLE_EXCEPTION_DESC);
                    }
                    super.visitInsn(RETURN);
                    super.visitMaxs(maxStack, maxLocals);
                }
            };
        }
    }

    public static void main(String[] args) throws Exception {
        if (args.length == 0 || args.length > 2) {
            System.err.println("Usage: java com.sun.btrace.runtime.Preprocessor  []");
            System.exit(1);
        }

        boolean renamed = (args.length == 2);
        String className = args[0].replace('.', '/');
        String newName = className;
        if (renamed) {
            newName = args[1].replace('.', '/');
        }
        FileInputStream fis = new FileInputStream(className + ".class");
        ClassReader reader = new ClassReader(new BufferedInputStream(fis));
        FileOutputStream fos = new FileOutputStream(newName + ".class");
        ClassWriter writer = InstrumentUtils.newClassWriter();  
        ClassVisitor cv;
        if (renamed) {
            cv = new ClassRenamer(args[1], new Preprocessor(writer));
        } else {
            cv = new Preprocessor(writer);
        }
        InstrumentUtils.accept(reader, cv);
        fos.write(writer.toByteArray());
    } 
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy