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

com.sun.btrace.agent.Main 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.agent;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.net.Socket;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import com.sun.btrace.BTraceRuntime;
import com.sun.btrace.comm.ErrorCommand;
import com.sun.btrace.comm.ExitCommand;
import com.sun.btrace.comm.OkayCommand;
import com.sun.btrace.runtime.OnProbe;
import com.sun.btrace.runtime.OnMethod;
import com.sun.btrace.runtime.ProbeDescriptor;
import com.sun.btrace.util.Messages;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * This is the main class for BTrace java.lang.instrument agent.
 *
 * @author A. Sundararajan
 * @authos Joachim Skeie (rolling output)
 */
public final class Main {
    private static volatile Map argMap;
    private static volatile Instrumentation inst;
    private static volatile boolean debugMode;
    private static volatile boolean trackRetransforms;
    private static volatile boolean unsafeMode;
    private static volatile boolean dumpClasses;
    private static volatile String dumpDir;
    private static volatile String probeDescPath;
    private static volatile String scriptOutputFile;
    private static volatile Long fileRollMilliseconds;;

    // #BTRACE-42: Non-daemon thread prevents traced application from exiting
    private static final ThreadFactory daemonizedThreadFactory = new ThreadFactory() {
        ThreadFactory delegate = Executors.defaultThreadFactory();
        @Override
        public Thread newThread(Runnable r) {
            Thread result = delegate.newThread(r);
            result.setDaemon(true);
            return result;
        }
    };
    
    private static final ExecutorService serializedExecutor = Executors.newSingleThreadExecutor(daemonizedThreadFactory);

    private static void registerExitHook(final Client c) {
        Runtime.getRuntime().addShutdownHook(new Thread(
            new Runnable() {
                public void run() {
                    BTraceRuntime rt = c.getRuntime();
                    if (rt != null) rt.handleExit(0);
                }
            }));
    }

    public static void premain(String args, Instrumentation inst) {
        main(args, inst);
    }

    public static void agentmain(String args, Instrumentation inst) {
        main(args, inst);
    }

    private static synchronized void main(final String args, final Instrumentation inst) {
        if (Main.inst != null) {
            return;
        } else {
            Main.inst = inst;
        }


        if (isDebug()) debugPrint("parsing command line arguments");
        parseArgs(args);
        if (isDebug()) debugPrint("parsed command line arguments");

        String bootClassPath = argMap.get("bootClassPath");
        if (bootClassPath != null) {
            if (isDebug()) {
                 debugPrint("Bootstrap ClassPath: " + bootClassPath);
            }
            StringTokenizer tokenizer = new StringTokenizer(bootClassPath, File.pathSeparator);
            try {
                while (tokenizer.hasMoreTokens()) {
                    String path = tokenizer.nextToken();
                    inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(path)));
                }
            } catch (IOException ex) {
                debugPrint("adding to boot classpath failed!");
                debugPrint(ex);
                return;
            }
        }

        String systemClassPath = argMap.get("systemClassPath");
        if (systemClassPath != null) {
            if (isDebug()) {
                 debugPrint("System ClassPath: " + systemClassPath);
            }
            StringTokenizer tokenizer = new StringTokenizer(systemClassPath, File.pathSeparator);
            try {
                while (tokenizer.hasMoreTokens()) {
                    String path = tokenizer.nextToken();
                    inst.appendToSystemClassLoaderSearch(new JarFile(new File(path)));
                }
            } catch (IOException ex) {
                debugPrint("adding to boot classpath failed!");
                debugPrint(ex);
                return;
            }
        }

        String tmp = argMap.get("noServer");
        boolean noServer = tmp != null && !"false".equals(tmp);
        if (noServer) {
            if (isDebug()) debugPrint("noServer is true, server not started");
            return;
        }
        Thread agentThread = new Thread(new Runnable() {
            public void run() {
                BTraceRuntime.enter();
                try {
                    startServer();
                } finally {
                    BTraceRuntime.leave();
                }
            }
        });
        BTraceRuntime.enter();
        try {
            agentThread.setDaemon(true);
            if (isDebug()) debugPrint("starting agent thread");
            agentThread.start();
        } finally {
            BTraceRuntime.leave();
        }
    }

    private static void usage() {
        System.out.println(Messages.get("btrace.agent.usage"));
        System.exit(0);
    }


    private static void parseArgs(String args) {
        if (args == null) {
            args = "";
        }
        String[] pairs = args.split(",");
        argMap = new HashMap();
        for (String s : pairs) {
            int i = s.indexOf('=');
            String key, value = "";
            if (i != -1) {
                key = s.substring(0,i).trim();
                if (i+1 < s.length()) {
                    value = s.substring(i+1).trim();
                }
            } else {
                key = s;
            }
            argMap.put(key, value);
        }

        String p = argMap.get("help");
        if (p != null) {
            usage();
        }
        p = argMap.get("debug");
        debugMode = p != null && !"false".equals(p);
        if (isDebug()) debugPrint("debugMode is " + debugMode);
        p = argMap.get("trackRetransforms");
        trackRetransforms = p != null && !"false".equals(p);
        if (isRetransformTracking()) debugPrint("trackRetransforms is " + trackRetransforms);
        scriptOutputFile = argMap.get("scriptOutputFile");
        if (scriptOutputFile != null && scriptOutputFile.length() > 0) {
            if (isDebug()) debugPrint("scriptOutputFile is " + scriptOutputFile);
        }
        p = argMap.get("fileRollMilliseconds");
        if (p != null && p.length() > 0) {
            Long msParsed = null;
            try {
                msParsed = Long.parseLong(p);
                fileRollMilliseconds = msParsed;
            } catch (NumberFormatException nfe) {
                fileRollMilliseconds = null;
            }
            if (fileRollMilliseconds != null) {
                if (isDebug()) debugPrint("fileRollMilliseconds is " + fileRollMilliseconds);
            }
        }
	p = argMap.get("unsafe");
        unsafeMode = "true".equals(p);
        if (isDebug()) debugPrint("unsafeMode is " + unsafeMode);
        p = argMap.get("dumpClasses");
        dumpClasses = p != null && !"false".equals(p);
        if (isDebug()) debugPrint("dumpClasses is " + dumpClasses);
        if (dumpClasses) {
            dumpDir = argMap.get("dumpDir");
            if (dumpDir == null) {
                dumpDir = ".";
            }
            if (isDebug()) debugPrint("dumpDir is " + dumpDir);
        }

        p = argMap.get("stdout");
        boolean traceToStdOut = p != null && !"false".equals(p);
        if (isDebug()) debugPrint("stdout is " + traceToStdOut);

        probeDescPath = argMap.get("probeDescPath");
        if (probeDescPath == null) {
            probeDescPath = ".";
        }
        if (isDebug()) debugPrint("probe descriptor path is " + probeDescPath);
        ProbeDescriptorLoader.init(probeDescPath);
        p = argMap.get("script");
        if (p != null) {
            StringTokenizer tokenizer = new StringTokenizer(p, ",");

	    if (isDebug()) {
                debugPrint(((tokenizer.countTokens() == 1) ? "initial script is " : "initial scripts are " ) + p);
            }
            while (tokenizer.hasMoreTokens()) {
                loadBTraceScript(tokenizer.nextToken(), traceToStdOut);
            }
        }
        p = argMap.get("scriptdir");
        if (p != null) {
            File scriptdir = new File(p);
            if (scriptdir != null && scriptdir.isDirectory()) {
                if (isDebug()) debugPrint("found scriptdir: " + scriptdir.getAbsolutePath());
                File[] files = scriptdir.listFiles();
                if (files != null) {
                    for (File file : files) {
                       loadBTraceScript(file.getAbsolutePath(), traceToStdOut);
                    }
                }
            }
        }
    }

    // This is really a *private* interface to Glassfish monitoring.
    // For now, please avoid using this in any other scenario. 
    public static void handleFlashLightClient(byte[] code, PrintWriter traceWriter) {
        handleNewClient(code, traceWriter);
    }

    // This is really a *private* interface to Glassfish monitoring.
    // For now, please avoid using this in any other scenario. 
    public static void handleFlashLightClient(byte[] code) {
        try {
            String twn = "flashlighttrace" + (new Date()).getTime();
            PrintWriter traceWriter = null;
            traceWriter = new PrintWriter(new BufferedWriter(new FileWriter(new File(twn + ".btrace"))));
            handleFlashLightClient(code, traceWriter);
        } catch (IOException ioexp) {
            if (isDebug()) {
                debugPrint(ioexp);
            }
        }
    }

    private static void loadBTraceScript(String filename, boolean traceToStdOut) {
        try {
            if (! filename.endsWith(".class")) {
                if (isDebug()) { 
                    debugPrint("refusing " + filename + ". script should be a pre-compiled .class file");
                }
                return;
            }
            File traceScript = new File(filename);
            if (! traceScript.exists()) {
                if (isDebug()) debugPrint("script " + traceScript + " does not exist!");
                return;
            }

            PrintWriter traceWriter = null;
            if (traceToStdOut) {
                traceWriter = new PrintWriter(System.out);
            } else {
                String agentName = System.getProperty("btrace.agent", null);
            	String currentBtraceScriptOutput = scriptOutputFile;
            	
                if (currentBtraceScriptOutput == null || currentBtraceScriptOutput.length() == 0) {
                    currentBtraceScriptOutput = filename + (agentName != null ? "." + agentName  : "") + ".btrace";
                    if (isDebug()) debugPrint("scriptOutputFile not specified. defaulting to " + currentBtraceScriptOutput);
                }
                if (fileRollMilliseconds != null && fileRollMilliseconds.longValue() > 0) {
                    traceWriter = new PrintWriter(new BufferedWriter(TraceOutputWriter.rollingFileWriter(new File(currentBtraceScriptOutput), 100, fileRollMilliseconds, TimeUnit.MILLISECONDS)));
                } else {
                    traceWriter = new PrintWriter(new BufferedWriter(TraceOutputWriter.fileWriter(new File(currentBtraceScriptOutput))));
                }
            }

            Client client = new FileClient(inst, traceScript, traceWriter);

            handleNewClient(client);
        } catch (RuntimeException re) {
            if (isDebug()) debugPrint(re);
        } catch (IOException ioexp) {
            if (isDebug()) debugPrint(ioexp);
        }
    }

    public static final int BTRACE_DEFAULT_PORT = 2020;

    //-- Internals only below this point
    private static void startServer() {
        int port = BTRACE_DEFAULT_PORT;
        String p = argMap.get("port");
        if (p != null) {
            try {
                port = Integer.parseInt(p);
            } catch (NumberFormatException exp) {
                error("invalid port assuming default..");
            }
        }
        ServerSocket ss;
        try {
            if (isDebug()) debugPrint("starting server at " + port);
            System.setProperty("btrace.port", String.valueOf(port));
            if (scriptOutputFile != null && scriptOutputFile.length() > 0) {
                System.setProperty("btrace.output", scriptOutputFile);
            }
            ss = new ServerSocket(port);
        } catch (IOException ioexp) {
            ioexp.printStackTrace();
            return;
        }

        while (true) {
            try {
                if (isDebug()) debugPrint("waiting for clients");
                Socket sock = ss.accept();
                if (isDebug()) debugPrint("client accepted " + sock);
                Client client = new RemoteClient(inst, sock);
                registerExitHook(client);
                handleNewClient(client);
            } catch (RuntimeException re) {
                if (isDebug()) debugPrint(re);
            } catch (IOException ioexp) {
                if (isDebug()) debugPrint(ioexp);
            }
        }
    }

    private static void handleNewClient(byte[] code, PrintWriter traceWriter) {
        try {
            handleNewClient(new FileClient(inst, code, traceWriter));
        } catch (RuntimeException re) {
            if (isDebug()) {
                debugPrint(re);
            }
        } catch (IOException ioexp) {
            if (isDebug()) {
                debugPrint(ioexp);
            }
        }
    }

    private static void handleNewClient(final Client client) {
        serializedExecutor.submit(new Runnable() {

            public void run() {
                try {
                    if (isDebug()) debugPrint("new Client created " + client);
                    if (client.shouldAddTransformer()) {
                        client.registerTransformer();
                        Class[] classes = inst.getAllLoadedClasses();
                        ArrayList list = new ArrayList();
                        if (isDebug()) debugPrint("filtering loaded classes");
                        for (Class c : classes) {
                            if (inst.isModifiableClass(c) &&
                                client.isCandidate(c)) {
                                if (isDebug()) debugPrint("candidate " + c + " added");
                                list.add(c);
                            }
                        }
                        list.trimToSize();
                        int size = list.size();
                        if (isDebug()) debugPrint("added as ClassFileTransformer");
                        if (size > 0) {
                            classes = new Class[size];
                            list.toArray(classes);
                            client.startRetransformClasses(size);
                            if (isDebug()) {
                                for(Class c : classes) {
                                    try {
                                        inst.retransformClasses(c);
                                    } catch (VerifyError e) {
                                        debugPrint("verification error: " + c.getName());
                                    }
                                }
                            } else {
                                inst.retransformClasses(classes);
                            }
                            client.skipRetransforms();
                        }
                    }
                    client.getRuntime().send(new OkayCommand());
                } catch (UnmodifiableClassException uce) {
                    if (isDebug()) {
                        debugPrint(uce);
                    }
                    client.getRuntime().send(new ErrorCommand(uce));
                }
            }
        });
        
    }

    private static void error(String msg) {
        System.err.println(msg);
    }

    static void dumpClass(String btraceClassName, String targetClassName, byte[] code) {
        if (dumpClasses) {
            try {
                targetClassName = targetClassName.replace(".", File.separator).replace("/", File.separator);
                int index = targetClassName.lastIndexOf(File.separatorChar);
                StringBuilder buf = new StringBuilder();
                if (!dumpDir.equals(".")) {
                    buf.append(dumpDir);
                    buf.append(File.separatorChar);
                }
                String dir = buf.toString();
                if (index != -1) {
                    dir += targetClassName.substring(0, index);
                }
                new File(dir).mkdirs();
                String file;
                if (index != -1) {
                    file = targetClassName.substring(index+1);
                } else {
                    file = targetClassName;
                }
                file += ".class";
                new File(dir).mkdirs();
                File out = new File(dir, file);
                FileOutputStream fos = new FileOutputStream(out);
                try {
                    fos.write(code);
                } finally {
                    fos.close();
                }
            } catch (Exception exp) {
                exp.printStackTrace();
            }
        }
    }

    /**
     * Maps a list of @OnProbe's to a list @OnMethod's using
     * probe descriptor XML files.
     */
    static List mapOnProbes(List onProbes) {
        List res = new ArrayList();
        for (OnProbe op : onProbes) {
            String ns = op.getNamespace();
            if (isDebug()) debugPrint("about to load probe descriptor for " + ns);
            // load probe descriptor for this namespace
            ProbeDescriptor probeDesc = ProbeDescriptorLoader.load(ns);
            if (probeDesc == null) {
                if (isDebug()) debugPrint("failed to find probe descriptor for " + ns);
                continue;
            }
            // find particular probe mappings using "local" name
            OnProbe foundProbe = probeDesc.findProbe(op.getName());
            if (foundProbe == null) {
                if (isDebug()) debugPrint("no probe mappings for " + op.getName());
                continue;
            }
            if (isDebug()) debugPrint("found probe mappings for " + op.getName());
            Collection omColl = foundProbe.getOnMethods();
            for (OnMethod om : omColl) {
                 // copy the info in a new OnMethod so that
                 // we can set target method name and descriptor
                 // Note that the probe descriptor cache is used
                 // across BTrace sessions. So, we should not update
                 // cached OnProbes (and their OnMethods).
                 OnMethod omn = new OnMethod();
                 omn.copyFrom(om);
                 omn.setTargetName(op.getTargetName());
                 omn.setTargetDescriptor(op.getTargetDescriptor());
                 res.add(omn);
            }
        }
        return res;
    }

    static boolean isDebug() {
        return debugMode;
    }

    static boolean isRetransformTracking() {
        return trackRetransforms;
    }

    static boolean isUnsafe() {
        return unsafeMode;
    }

    static void debugPrint(String msg) {
        System.out.println("btrace DEBUG: " + msg);
    }

    static void debugPrint(Throwable th) {
        System.out.println("btrace DEBUG: " + th);
        th.printStackTrace();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy