Path: blob/master/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
41159 views
/*1* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jmap;2627import java.io.File;28import java.io.IOException;29import java.io.InputStream;30import java.io.UnsupportedEncodingException;31import java.util.Collection;3233import com.sun.tools.attach.VirtualMachine;34import com.sun.tools.attach.VirtualMachineDescriptor;35import com.sun.tools.attach.AttachNotSupportedException;36import sun.tools.attach.HotSpotVirtualMachine;37import sun.tools.common.ProcessArgumentMatcher;3839/*40* This class is the main class for the JMap utility. It parses its arguments41* and decides if the command should be satisfied using the VM attach mechanism42* or an SA tool. At this time the only option that uses the VM attach mechanism43* is the -dump option to get a heap dump of a running application. All other44* options are mapped to SA tools.45*/46public class JMap {4748public static void main(String[] args) throws Exception {49if (args.length == 0) {50usage(1); // no arguments51}5253checkForUnsupportedOptions(args);5455// the chosen option56String option = null;5758// First iterate over the options (arguments starting with -). There should be59// one.60int optionCount = 0;61while (optionCount < args.length) {62String arg = args[optionCount];63if (!arg.startsWith("-")) {64break;65}66if (arg.equals("-?") ||67arg.equals("-h") ||68arg.equals("--help") ||69// -help: legacy. Undocumented.70arg.equals("-help")) {71usage(0);72} else {73if (option != null) {74usage(1); // option already specified75}76option = arg;77}78optionCount++;79}8081// if no option provided then use default.82if (option == null) {83usage(0);84}8586// Next we check the parameter count.87int paramCount = args.length - optionCount;88if (paramCount != 1) {89usage(1);90}9192String pidArg = args[1];93// Here we handle the built-in options94// As more options are added we should create an abstract tool class and95// have a table to map the options96ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg);97Collection<String> pids = ap.getVirtualMachinePids(JMap.class);9899if (pids.isEmpty()) {100System.err.println("Could not find any processes matching : '" + pidArg + "'");101System.exit(1);102}103104for (String pid : pids) {105if (pids.size() > 1) {106System.out.println("Pid:" + pid);107}108if (option.equals("-histo")) {109histo(pid, "");110} else if (option.startsWith("-histo:")) {111histo(pid, option.substring("-histo:".length()));112} else if (option.startsWith("-dump:")) {113dump(pid, option.substring("-dump:".length()));114} else if (option.equals("-finalizerinfo")) {115executeCommandForPid(pid, "jcmd", "GC.finalizer_info");116} else if (option.equals("-clstats")) {117executeCommandForPid(pid, "jcmd", "VM.classloader_stats");118} else {119usage(1);120}121}122}123124private static void executeCommandForPid(String pid, String command, Object ... args)125throws AttachNotSupportedException, IOException,126UnsupportedEncodingException {127VirtualMachine vm = VirtualMachine.attach(pid);128129// Cast to HotSpotVirtualMachine as this is an130// implementation specific method.131HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm;132try (InputStream in = hvm.executeCommand(command, args)) {133// read to EOF and just print output134byte b[] = new byte[256];135int n;136do {137n = in.read(b);138if (n > 0) {139String s = new String(b, 0, n, "UTF-8");140System.out.print(s);141}142} while (n > 0);143}144vm.detach();145}146147private static String parseFileName(String opt) {148// opt starts with "file="149if (opt.length() > 5) {150// pass whole "file=" string151String filename = opt.substring(5);152try {153// Get the canonical path - important to avoid just154// passing a "heap.bin" and having the dump created155// in the target VM working directory rather than the156// directory where jmap is executed.157return new File(filename).getCanonicalPath();158} catch (IOException ioe) {159return null;160}161}162// no filename163return null;164}165166private static void histo(String pid, String options)167throws AttachNotSupportedException, IOException,168UnsupportedEncodingException {169String liveopt = "-all";170String filename = null;171String parallel = null;172String subopts[] = options.split(",");173174for (int i = 0; i < subopts.length; i++) {175String subopt = subopts[i];176if (subopt.equals("") || subopt.equals("all")) {177// pass178} else if (subopt.equals("live")) {179liveopt = "-live";180} else if (subopt.startsWith("file=")) {181filename = parseFileName(subopt);182if (filename == null) {183System.err.println("Fail: invalid option or no file name '" + subopt + "'");184usage(1);185}186} else if (subopt.startsWith("parallel=")) {187parallel = subopt.substring("parallel=".length());188if (parallel == null) {189System.err.println("Fail: no number provided in option: '" + subopt + "'");190usage(1);191}192} else {193System.err.println("Fail: invalid option: '" + subopt + "'");194usage(1);195}196}197198System.out.flush();199200// inspectHeap is not the same as jcmd GC.class_histogram201executeCommandForPid(pid, "inspectheap", liveopt, filename, parallel);202}203204private static void dump(String pid, String options)205throws AttachNotSupportedException, IOException,206UnsupportedEncodingException {207208String subopts[] = options.split(",");209String filename = null;210String liveopt = "-all";211String compress_level = null;212213for (int i = 0; i < subopts.length; i++) {214String subopt = subopts[i];215if (subopt.equals("") || subopt.equals("all")) {216// pass217} else if (subopt.equals("live")) {218liveopt = "-live";219} else if (subopt.startsWith("file=")) {220filename = parseFileName(subopt);221if (filename == null) {222System.err.println("Fail: invalid option or no file name '" + subopt + "'");223usage(1);224}225} else if (subopt.equals("format=b")) {226// ignore format (not needed at this time)227} else if (subopt.startsWith("gz=")) {228compress_level = subopt.substring("gz=".length());229if (compress_level.length() == 0) {230System.err.println("Fail: no number provided in option: '" + subopt + "'");231usage(1);232}233} else {234System.err.println("Fail: invalid option: '" + subopt + "'");235usage(1);236}237}238239if (filename == null) {240System.err.println("Fail: invalid option or no file name");241usage(1);242}243244System.out.flush();245246// dumpHeap is not the same as jcmd GC.heap_dump247executeCommandForPid(pid, "dumpheap", filename, liveopt, compress_level);248}249250private static void checkForUnsupportedOptions(String[] args) {251// Check arguments for -F, -m, and non-numeric value252// and warn the user that SA is not supported anymore253254int paramCount = 0;255256for (String s : args) {257if (s.equals("-F")) {258SAOptionError("-F option used");259}260261if (s.equals("-heap")) {262SAOptionError("-heap option used");263}264265/* Reimplemented using jcmd, output format is different266from original one267268if (s.equals("-clstats")) {269warnSA("-clstats option used");270}271272if (s.equals("-finalizerinfo")) {273warnSA("-finalizerinfo option used");274}275*/276277if (! s.startsWith("-")) {278paramCount += 1;279}280}281282if (paramCount > 1) {283SAOptionError("More than one non-option argument");284}285}286287private static void SAOptionError(String msg) {288System.err.println("Error: " + msg);289System.err.println("Cannot connect to core dump or remote debug server. Use jhsdb jmap instead");290System.exit(1);291}292293// print usage message294private static void usage(int exit) {295System.err.println("Usage:");296System.err.println(" jmap -clstats <pid>");297System.err.println(" to connect to running process and print class loader statistics");298System.err.println(" jmap -finalizerinfo <pid>");299System.err.println(" to connect to running process and print information on objects awaiting finalization");300System.err.println(" jmap -histo[:[<histo-options>]] <pid>");301System.err.println(" to connect to running process and print histogram of java object heap");302System.err.println(" jmap -dump:<dump-options> <pid>");303System.err.println(" to connect to running process and dump java heap");304System.err.println(" jmap -? -h --help");305System.err.println(" to print this help message");306System.err.println("");307System.err.println(" dump-options:");308System.err.println(" live dump only live objects (takes precedence if both \"live\" and \"all\" are specified)");309System.err.println(" all dump all objects in the heap (default if one of \"live\" or \"all\" is not specified)");310System.err.println(" format=b binary format");311System.err.println(" file=<file> dump heap to <file>");312System.err.println(" gz=<number> If specified, the heap dump is written in gzipped format using the given compression level.");313System.err.println(" 1 (recommended) is the fastest, 9 the strongest compression.");314System.err.println("");315System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin <pid>");316System.err.println("");317System.err.println(" histo-options:");318System.err.println(" live count only live objects (takes precedence if both \"live\" and \"all\" are specified)");319System.err.println(" all count all objects in the heap (default if one of \"live\" or \"all\" is not specified)");320System.err.println(" file=<file> dump data to <file>");321System.err.println(" parallel=<number> Number of parallel threads to use for heap inspection:");322System.err.println(" 0 (the default) means let the VM determine the number of threads to use");323System.err.println(" 1 means use one thread (disable parallelism).");324System.err.println(" For any other value the VM will try to use the specified number of threads, but might use fewer.");325System.err.println("");326System.err.println(" Example: jmap -histo:live,file=/tmp/histo.data <pid>");327System.exit(exit);328}329}330331332