389 lines · 15.3 KB · Java
RawDownload
/* * Copyright (c) 2005, 2008, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.tools.jmap; import java.lang.reflect.Method;import java.io.File;import java.io.IOException;import java.io.InputStream; import com.sun.tools.attach.VirtualMachine;import com.sun.tools.attach.AttachNotSupportedException;import sun.tools.attach.HotSpotVirtualMachine; /* * This class is the main class for the JMap utility. It parses its arguments * and decides if the command should be satisifed using the VM attach mechanism * or an SA tool. At this time the only option that uses the VM attach mechanism * is the -dump option to get a heap dump of a running application. All other * options are mapped to SA tools. */public class JMap {     // Options handled by the attach mechanism    private static String HISTO_OPTION = "-histo";    private static String LIVE_HISTO_OPTION = "-histo:live";    private static String DUMP_OPTION_PREFIX = "-dump:";     // These options imply the use of a SA tool    private static String SA_TOOL_OPTIONS =      "-heap|-heap:format=b|-permstat|-finalizerinfo";     // The -F (force) option is currently not passed through to SA    private static String FORCE_SA_OPTION = "-F";     // Default option (if nothing provided)    private static String DEFAULT_OPTION = "-pmap";     public static void main(String[] args) throws Exception {        if (args.length == 0) {            usage(); // no arguments        }         // used to indicate if we should use SA        boolean useSA = false;         // the chosen option (-heap, -dump:*, ... )        String option = null;         // First iterate over the options (arguments starting with -).  There should be        // one (but maybe two if -F is also used).        int optionCount = 0;        while (optionCount < args.length) {            String arg = args[optionCount];            if (!arg.startsWith("-")) {                break;            }            if (arg.equals(FORCE_SA_OPTION)) {                useSA = true;            } else {                if (option != null) {                    usage();  // option already specified                }                option = arg;            }            optionCount++;        }         // if no option provided then use default.        if (option == null) {            option = DEFAULT_OPTION;        }        if (option.matches(SA_TOOL_OPTIONS)) {            useSA = true;        }         // Next we check the parameter count. For the SA tools there are        // one or two parameters. For the built-in -dump option there is        // only one parameter (the process-id)        int paramCount = args.length - optionCount;        if (paramCount == 0 || paramCount > 2) {            usage();        }         if (optionCount == 0 || paramCount != 1) {            useSA = true;        } else {            // the parameter for the -dump option is a process-id.            // If it doesn't parse to a number then it must be SA            // debug server            if (!args[optionCount].matches("[0-9]+")) {                useSA = true;            }        }          // at this point we know if we are executing an SA tool or a built-in        // option.         if (useSA) {            // parameters (<pid> or <exe> <core>)            String params[] = new String[paramCount];            for (int i=optionCount; i<args.length; i++ ){                params[i-optionCount] = args[i];            }            runTool(option, params);         } else {            String pid = args[1];            // Here we handle the built-in options            // As more options are added we should create an abstract tool class and            // have a table to map the options            if (option.equals(HISTO_OPTION)) {                histo(pid, false);            } else if (option.equals(LIVE_HISTO_OPTION)) {                histo(pid, true);            } else if (option.startsWith(DUMP_OPTION_PREFIX)) {                dump(pid, option);            } else {                usage();            }        }    }     // Invoke SA tool  with the given arguments    private static void runTool(String option, String args[]) throws Exception {        String[][] tools = {            { "-pmap",           "sun.jvm.hotspot.tools.PMap"     },            { "-heap",           "sun.jvm.hotspot.tools.HeapSummary"     },            { "-heap:format=b",  "sun.jvm.hotspot.tools.HeapDumper"      },            { "-histo",          "sun.jvm.hotspot.tools.ObjectHistogram" },            { "-permstat",       "sun.jvm.hotspot.tools.PermStat"        },            { "-finalizerinfo",  "sun.jvm.hotspot.tools.FinalizerInfo"   },        };         String tool = null;         // -dump option needs to be handled in a special way        if (option.startsWith(DUMP_OPTION_PREFIX)) {            // first check that the option can be parsed            String fn = parseDumpOptions(option);            if (fn == null) usage();             // tool for heap dumping            tool = "sun.jvm.hotspot.tools.HeapDumper";             // HeapDumper -f <file>            args = prepend(fn, args);            args = prepend("-f", args);        } else {            int i=0;            while (i < tools.length) {                if (option.equals(tools[i][0])) {                    tool = tools[i][1];                    break;                }                i++;            }        }        if (tool == null) {            usage();   // no mapping to tool        }         // Tool not available on this  platform.        Class<?> c = loadClass(tool);        if (c == null) {            usage();        }         // invoke the main method with the arguments        Class[] argTypes = { String[].class } ;        Method m = c.getDeclaredMethod("main", argTypes);         Object[] invokeArgs = { args };        m.invoke(null, invokeArgs);    }     // loads the given class using the system class loader    private static Class loadClass(String name) {        //        // We specify the system clas loader so as to cater for development        // environments where this class is on the boot class path but sa-jdi.jar        // is on the system class path. Once the JDK is deployed then both        // tools.jar and sa-jdi.jar are on the system class path.        //        try {            return Class.forName(name, true,                                 ClassLoader.getSystemClassLoader());        } catch (Exception x)  { }        return null;    }     private static final String LIVE_OBJECTS_OPTION = "-live";    private static final String ALL_OBJECTS_OPTION = "-all";    private static void histo(String pid, boolean live) throws IOException {        VirtualMachine vm = attach(pid);        InputStream in = ((HotSpotVirtualMachine)vm).            heapHisto(live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION);        drain(vm, in);    }     private static void dump(String pid, String options) throws IOException {        // parse the options to get the dump filename        String filename = parseDumpOptions(options);        if (filename == null) {            usage();  // invalid options or no filename        }         // get the canonical path - important to avoid just passing        // a "heap.bin" and having the dump created in the target VM        // working directory rather than the directory where jmap        // is executed.        filename = new File(filename).getCanonicalPath();         // dump live objects only or not        boolean live = isDumpLiveObjects(options);         VirtualMachine vm = attach(pid);        System.out.println("Dumping heap to " + filename + " ...");        InputStream in = ((HotSpotVirtualMachine)vm).            dumpHeap((Object)filename,                     (live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION));        drain(vm, in);    }     // Parse the options to the -dump option. Valid options are format=b and    // file=<file>. Returns <file> if provided. Returns null if <file> not    // provided, or invalid option.    private static String parseDumpOptions(String arg) {        assert arg.startsWith(DUMP_OPTION_PREFIX);         String filename = null;         // options are separated by comma (,)        String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(",");         for (int i=0; i<options.length; i++) {            String option = options[i];             if (option.equals("format=b")) {                // ignore format (not needed at this time)            } else if (option.equals("live")) {                // a valid suboption            } else {                 // file=<file> - check that <file> is specified                if (option.startsWith("file=")) {                    filename = option.substring(5);                    if (filename.length() == 0) {                        return null;                    }                } else {                    return null;  // option not recognized                }            }        }        return filename;    }     private static boolean isDumpLiveObjects(String arg) {        // options are separated by comma (,)        String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(",");        for (String suboption : options) {            if (suboption.equals("live")) {                return true;            }        }        return false;    }     // Attach to <pid>, existing if we fail to attach    private static VirtualMachine attach(String pid) {        try {            return VirtualMachine.attach(pid);        } catch (Exception x) {            String msg = x.getMessage();            if (msg != null) {                System.err.println(pid + ": " + msg);            } else {                x.printStackTrace();            }            if ((x instanceof AttachNotSupportedException) && haveSA()) {                System.err.println("The -F option can be used when the " +                  "target process is not responding");            }            System.exit(1);            return null; // keep compiler happy        }    }     // Read the stream from the target VM until EOF, then detach    private static void drain(VirtualMachine vm, InputStream in) throws IOException {        // read to EOF and just print output        byte b[] = new byte[256];        int n;        do {            n = in.read(b);            if (n > 0) {                String s = new String(b, 0, n, "UTF-8");                System.out.print(s);            }        } while (n > 0);        in.close();        vm.detach();    }     // return a new string array with arg as the first element    private static String[] prepend(String arg, String args[]) {        String[] newargs = new String[args.length+1];        newargs[0] = arg;        System.arraycopy(args, 0, newargs, 1, args.length);        return newargs;    }     // returns true if SA is available    private static boolean haveSA() {        Class c = loadClass("sun.jvm.hotspot.tools.HeapSummary");        return (c != null);    }     // print usage message    private static void usage() {        System.out.println("Usage:");        if (haveSA()) {            System.out.println("    jmap [option] <pid>");            System.out.println("        (to connect to running process)");            System.out.println("    jmap [option] <executable <core>");            System.out.println("        (to connect to a core file)");            System.out.println("    jmap [option] [server_id@]<remote server IP or hostname>");            System.out.println("        (to connect to remote debug server)");            System.out.println("");            System.out.println("where <option> is one of:");            System.out.println("    <none>               to print same info as Solaris pmap");            System.out.println("    -heap                to print java heap summary");            System.out.println("    -histo[:live]        to print histogram of java object heap; if the \"live\"");            System.out.println("                         suboption is specified, only count live objects");            System.out.println("    -permstat            to print permanent generation statistics");            System.out.println("    -finalizerinfo       to print information on objects awaiting finalization");            System.out.println("    -dump:<dump-options> to dump java heap in hprof binary format");            System.out.println("                         dump-options:");            System.out.println("                           live         dump only live objects; if not specified,");            System.out.println("                                        all objects in the heap are dumped.");            System.out.println("                           format=b     binary format");            System.out.println("                           file=<file>  dump heap to <file>");            System.out.println("                         Example: jmap -dump:live,format=b,file=heap.bin <pid>");            System.out.println("    -F                   force. Use with -dump:<dump-options> <pid> or -histo");            System.out.println("                         to force a heap dump or histogram when <pid> does not");            System.out.println("                         respond. The \"live\" suboption is not supported");            System.out.println("                         in this mode.");            System.out.println("    -h | -help           to print this help message");            System.out.println("    -J<flag>             to pass <flag> directly to the runtime system");        } else {            System.out.println("    jmap -histo <pid>");            System.out.println("      (to connect to running process and print histogram of java object heap");            System.out.println("    jmap -dump:<dump-options> <pid>");            System.out.println("      (to connect to running process and dump java heap)");            System.out.println("");            System.out.println("    dump-options:");            System.out.println("      format=b     binary default");            System.out.println("      file=<file>  dump heap to <file>");            System.out.println("");            System.out.println("    Example:       jmap -dump:format=b,file=heap.bin <pid>");        }         System.exit(1);    }}