jdk/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
changeset 38360 fb63be22ffa6
parent 36511 9d0388c6b336
child 38361 8ea2d56bfdf3
equal deleted inserted replaced
38359:fd9b36598481 38360:fb63be22ffa6
    26 package sun.tools.jmap;
    26 package sun.tools.jmap;
    27 
    27 
    28 import java.io.File;
    28 import java.io.File;
    29 import java.io.IOException;
    29 import java.io.IOException;
    30 import java.io.InputStream;
    30 import java.io.InputStream;
       
    31 import java.io.UnsupportedEncodingException;
    31 
    32 
    32 import com.sun.tools.attach.VirtualMachine;
    33 import com.sun.tools.attach.VirtualMachine;
    33 import com.sun.tools.attach.AttachNotSupportedException;
    34 import com.sun.tools.attach.AttachNotSupportedException;
    34 import sun.tools.attach.HotSpotVirtualMachine;
    35 import sun.tools.attach.HotSpotVirtualMachine;
    35 import jdk.internal.vm.agent.spi.ToolProvider;
       
    36 import jdk.internal.vm.agent.spi.ToolProviderFinder;
       
    37 
    36 
    38 /*
    37 /*
    39  * This class is the main class for the JMap utility. It parses its arguments
    38  * This class is the main class for the JMap utility. It parses its arguments
    40  * and decides if the command should be satisfied using the VM attach mechanism
    39  * and decides if the command should be satisfied using the VM attach mechanism
    41  * or an SA tool. At this time the only option that uses the VM attach mechanism
    40  * or an SA tool. At this time the only option that uses the VM attach mechanism
    42  * is the -dump option to get a heap dump of a running application. All other
    41  * is the -dump option to get a heap dump of a running application. All other
    43  * options are mapped to SA tools.
    42  * options are mapped to SA tools.
    44  */
    43  */
    45 public class JMap {
    44 public class JMap {
    46 
    45 
    47     // Options handled by the attach mechanism
       
    48     private static String HISTO_OPTION = "-histo";
       
    49     private static String LIVE_HISTO_OPTION = "-histo:live";
       
    50     private static String DUMP_OPTION_PREFIX = "-dump:";
       
    51 
       
    52     // These options imply the use of a SA tool
       
    53     private static String SA_TOOL_OPTIONS =
       
    54       "-heap|-heap:format=b|-clstats|-finalizerinfo";
       
    55 
       
    56     // The -F (force) option is currently not passed through to SA
       
    57     private static String FORCE_SA_OPTION = "-F";
       
    58 
       
    59     // Default option (if nothing provided)
       
    60     private static String DEFAULT_OPTION = "-pmap";
       
    61 
       
    62     public static void main(String[] args) throws Exception {
    46     public static void main(String[] args) throws Exception {
    63         if (args.length == 0) {
    47         if (args.length == 0) {
    64             usage(1); // no arguments
    48             usage(1); // no arguments
    65         }
    49         }
    66 
    50 
    67         // used to indicate if we should use SA
    51         checkForUnsupportedOptions(args);
    68         boolean useSA = false;
    52 
    69 
    53         // the chosen option
    70         // the chosen option (-heap, -dump:*, ... )
       
    71         String option = null;
    54         String option = null;
    72 
    55 
    73         // First iterate over the options (arguments starting with -).  There should be
    56         // First iterate over the options (arguments starting with -).  There should be
    74         // one (but maybe two if -F is also used).
    57         // one.
    75         int optionCount = 0;
    58         int optionCount = 0;
    76         while (optionCount < args.length) {
    59         while (optionCount < args.length) {
    77             String arg = args[optionCount];
    60             String arg = args[optionCount];
    78             if (!arg.startsWith("-")) {
    61             if (!arg.startsWith("-")) {
    79                 break;
    62                 break;
    80             }
    63             }
    81             if (arg.equals("-help") || arg.equals("-h")) {
    64             if (arg.equals("-help") || arg.equals("-h")) {
    82                 usage(0);
    65                 usage(0);
    83             } else if (arg.equals(FORCE_SA_OPTION)) {
       
    84                 useSA = true;
       
    85             } else {
    66             } else {
    86                 if (option != null) {
    67                 if (option != null) {
    87                     usage(1);  // option already specified
    68                     usage(1);  // option already specified
    88                 }
    69                 }
    89                 option = arg;
    70                 option = arg;
    91             optionCount++;
    72             optionCount++;
    92         }
    73         }
    93 
    74 
    94         // if no option provided then use default.
    75         // if no option provided then use default.
    95         if (option == null) {
    76         if (option == null) {
    96             option = DEFAULT_OPTION;
    77             usage(0);
    97         }
    78         }
    98         if (option.matches(SA_TOOL_OPTIONS)) {
    79 
    99             useSA = true;
    80         // Next we check the parameter count.
   100         }
       
   101 
       
   102         // Next we check the parameter count. For the SA tools there are
       
   103         // one or two parameters. For the built-in -dump option there is
       
   104         // only one parameter (the process-id)
       
   105         int paramCount = args.length - optionCount;
    81         int paramCount = args.length - optionCount;
   106         if (paramCount == 0 || paramCount > 2) {
    82         if (paramCount != 1) {
   107             usage(1);
    83             usage(1);
   108         }
    84         }
   109 
    85 
   110         if (optionCount == 0 || paramCount != 1) {
    86         String pid = args[1];
   111             useSA = true;
    87         // Here we handle the built-in options
       
    88         // As more options are added we should create an abstract tool class and
       
    89         // have a table to map the options
       
    90         if (option.equals("-histo")) {
       
    91             histo(pid, "");
       
    92         } else if (option.startsWith("-histo:")) {
       
    93             histo(pid, option.substring("-histo:".length()));
       
    94         } else if (option.startsWith("-dump:")) {
       
    95             dump(pid, option.substring("-dump:".length()));
       
    96         } else if (option.equals("-finalizerinfo")) {
       
    97             executeCommandForPid(pid, "jcmd", "GC.finalizer_info");
       
    98         } else if (option.equals("-clstats")) {
       
    99             executeCommandForPid(pid, "jcmd", "GC.class_stats");
   112         } else {
   100         } else {
   113             // the parameter for the -dump option is a process-id.
   101           usage(1);
   114             // If it doesn't parse to a number then it must be SA
   102         }
   115             // debug server
   103     }
   116             if (!args[optionCount].matches("[0-9]+")) {
   104 
   117                 useSA = true;
   105     private static void executeCommandForPid(String pid, String command, Object ... args)
   118             }
   106         throws AttachNotSupportedException, IOException,
   119         }
   107                UnsupportedEncodingException {
   120 
   108         VirtualMachine vm = VirtualMachine.attach(pid);
   121 
   109 
   122         // at this point we know if we are executing an SA tool or a built-in
   110         // Cast to HotSpotVirtualMachine as this is an
   123         // option.
   111         // implementation specific method.
   124 
   112         HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm;
   125         if (useSA) {
   113         try (InputStream in = hvm.executeCommand(command, args)) {
   126             // parameters (<pid> or <exe> <core>)
   114           // read to EOF and just print output
   127             String params[] = new String[paramCount];
   115           byte b[] = new byte[256];
   128             for (int i=optionCount; i<args.length; i++ ){
   116           int n;
   129                 params[i-optionCount] = args[i];
   117           do {
   130             }
   118               n = in.read(b);
   131             runTool(option, params);
   119               if (n > 0) {
   132 
   120                   String s = new String(b, 0, n, "UTF-8");
   133         } else {
   121                   System.out.print(s);
   134             String pid = args[1];
   122               }
   135             // Here we handle the built-in options
   123           } while (n > 0);
   136             // As more options are added we should create an abstract tool class and
   124         }
   137             // have a table to map the options
   125         vm.detach();
   138             if (option.equals(HISTO_OPTION)) {
   126     }
   139                 histo(pid, false);
   127 
   140             } else if (option.equals(LIVE_HISTO_OPTION)) {
   128     private static void histo(String pid, String options)
   141                 histo(pid, true);
   129         throws AttachNotSupportedException, IOException,
   142             } else if (option.startsWith(DUMP_OPTION_PREFIX)) {
   130                UnsupportedEncodingException {
   143                 dump(pid, option);
   131         String liveopt = "-all";
   144             } else {
   132         if (options.equals("") || options.equals("all")) {
   145                 usage(1);
   133             //  pass
   146             }
   134         }
   147         }
   135         else if (options.equals("live")) {
   148     }
   136             liveopt = "-live";
   149 
   137         }
   150     // Invoke SA tool  with the given arguments
   138         else {
   151     private static void runTool(String option, String args[]) throws Exception {
   139             usage(1);
   152         String[][] tools = {
   140         }
   153             { "-pmap",          "pmap"             },
   141 
   154             { "-heap",          "heapSummary"      },
   142         // inspectHeap is not the same as jcmd GC.class_histogram
   155             { "-heap:format=b", "heapDumper"       },
   143         executeCommandForPid(pid, "inspectheap", liveopt);
   156             { "-histo",         "objectHistogram"  },
   144     }
   157             { "-clstats",       "classLoaderStats" },
   145 
   158             { "-finalizerinfo", "finalizerInfo"    },
   146     private static void dump(String pid, String options)
   159         };
   147         throws AttachNotSupportedException, IOException,
   160 
   148                UnsupportedEncodingException {
   161         String name = null;
   149 
   162 
   150         String subopts[] = options.split(",");
   163         // -dump option needs to be handled in a special way
   151         String filename = null;
   164         if (option.startsWith(DUMP_OPTION_PREFIX)) {
   152         String liveopt = "-all";
   165             // first check that the option can be parsed
   153 
   166             String fn = parseDumpOptions(option);
   154         for (int i = 0; i < subopts.length; i++) {
   167             if (fn == null) {
   155             String subopt = subopts[i];
   168                 usage(1);
   156             if (subopt.equals("live")) {
   169             }
   157                 liveopt = "-live";
   170 
   158             } else if (subopt.startsWith("file=")) {
   171             // tool for heap dumping
   159                 // file=<file> - check that <file> is specified
   172             name = "heapDumper";
   160                 if (subopt.length() > 5) {
   173 
   161                     filename = subopt.substring(5);
   174             // HeapDumper -f <file>
       
   175             args = prepend(fn, args);
       
   176             args = prepend("-f", args);
       
   177         } else {
       
   178             int i=0;
       
   179             while (i < tools.length) {
       
   180                 if (option.equals(tools[i][0])) {
       
   181                     name = tools[i][1];
       
   182                     break;
       
   183                 }
   162                 }
   184                 i++;
   163             }
   185             }
   164         }
   186         }
   165 
   187         if (name == null) {
       
   188             usage(1);   // no mapping to tool
       
   189         }
       
   190 
       
   191         // Tool not available on this platform.
       
   192         ToolProvider tool = ToolProviderFinder.find(name);
       
   193         if (tool == null) {
       
   194             usage(1);
       
   195         }
       
   196 
       
   197         // invoke the main method with the arguments
       
   198         tool.run(args);
       
   199     }
       
   200 
       
   201     private static final String LIVE_OBJECTS_OPTION = "-live";
       
   202     private static final String ALL_OBJECTS_OPTION = "-all";
       
   203     private static void histo(String pid, boolean live) throws IOException {
       
   204         VirtualMachine vm = attach(pid);
       
   205         InputStream in = ((HotSpotVirtualMachine)vm).
       
   206             heapHisto(live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION);
       
   207         drain(vm, in);
       
   208     }
       
   209 
       
   210     private static void dump(String pid, String options) throws IOException {
       
   211         // parse the options to get the dump filename
       
   212         String filename = parseDumpOptions(options);
       
   213         if (filename == null) {
   166         if (filename == null) {
   214             usage(1);  // invalid options or no filename
   167             usage(1);  // invalid options or no filename
   215         }
   168         }
   216 
   169 
   217         // get the canonical path - important to avoid just passing
   170         // get the canonical path - important to avoid just passing
   218         // a "heap.bin" and having the dump created in the target VM
   171         // a "heap.bin" and having the dump created in the target VM
   219         // working directory rather than the directory where jmap
   172         // working directory rather than the directory where jmap
   220         // is executed.
   173         // is executed.
   221         filename = new File(filename).getCanonicalPath();
   174         filename = new File(filename).getCanonicalPath();
   222 
   175         // dumpHeap is not the same as jcmd GC.heap_dump
   223         // dump live objects only or not
   176         executeCommandForPid(pid, "dumpheap", filename, liveopt);
   224         boolean live = isDumpLiveObjects(options);
   177     }
   225 
   178 
   226         VirtualMachine vm = attach(pid);
   179     private static void checkForUnsupportedOptions(String[] args) {
   227         System.out.println("Dumping heap to " + filename + " ...");
   180         // Check arguments for -F, -m, and non-numeric value
   228         InputStream in = ((HotSpotVirtualMachine)vm).
   181         // and warn the user that SA is not supported anymore
   229             dumpHeap((Object)filename,
   182 
   230                      (live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION));
   183         int paramCount = 0;
   231         drain(vm, in);
   184 
   232     }
   185         for (String s : args) {
   233 
   186             if (s.equals("-F")) {
   234     // Parse the options to the -dump option. Valid options are format=b and
   187                 SAOptionError("-F option used");
   235     // file=<file>. Returns <file> if provided. Returns null if <file> not
   188             }
   236     // provided, or invalid option.
   189 
   237     private static String parseDumpOptions(String arg) {
   190             if (s.equals("-heap")) {
   238         assert arg.startsWith(DUMP_OPTION_PREFIX);
   191                 SAOptionError("-heap option used");
   239 
   192             }
   240         String filename = null;
   193 
   241 
   194             /* Reimplemented using jcmd, output format is different
   242         // options are separated by comma (,)
   195                from original one
   243         String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(",");
   196 
   244 
   197             if (s.equals("-clstats")) {
   245         for (int i=0; i<options.length; i++) {
   198                 warnSA("-clstats option used");
   246             String option = options[i];
   199             }
   247 
   200 
   248             if (option.equals("format=b")) {
   201             if (s.equals("-finalizerinfo")) {
   249                 // ignore format (not needed at this time)
   202                 warnSA("-finalizerinfo option used");
   250             } else if (option.equals("live")) {
   203             }
   251                 // a valid suboption
   204             */
   252             } else {
   205 
   253 
   206             if (! s.startsWith("-")) {
   254                 // file=<file> - check that <file> is specified
   207                 if (! s.matches("[0-9]+")) {
   255                 if (option.startsWith("file=")) {
   208                     SAOptionError("non PID argument");
   256                     filename = option.substring(5);
       
   257                     if (filename.length() == 0) {
       
   258                         return null;
       
   259                     }
       
   260                 } else {
       
   261                     return null;  // option not recognized
       
   262                 }
   209                 }
   263             }
   210                 paramCount += 1;
   264         }
   211             }
   265         return filename;
   212         }
   266     }
   213 
   267 
   214         if (paramCount > 1) {
   268     private static boolean isDumpLiveObjects(String arg) {
   215             SAOptionError("More than one non-option argument");
   269         // options are separated by comma (,)
   216         }
   270         String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(",");
   217     }
   271         for (String suboption : options) {
   218 
   272             if (suboption.equals("live")) {
   219     private static void SAOptionError(String msg) {
   273                 return true;
   220         System.err.println("Error: " + msg);
   274             }
   221         System.err.println("Cannot connect to core dump or remote debug server. Use jhsdb jmap instead");
   275         }
   222         System.exit(1);
   276         return false;
       
   277     }
       
   278 
       
   279     // Attach to <pid>, existing if we fail to attach
       
   280     private static VirtualMachine attach(String pid) {
       
   281         try {
       
   282             return VirtualMachine.attach(pid);
       
   283         } catch (Exception x) {
       
   284             String msg = x.getMessage();
       
   285             if (msg != null) {
       
   286                 System.err.println(pid + ": " + msg);
       
   287             } else {
       
   288                 x.printStackTrace();
       
   289             }
       
   290             if ((x instanceof AttachNotSupportedException) && haveSA()) {
       
   291                 System.err.println("The -F option can be used when the " +
       
   292                   "target process is not responding");
       
   293             }
       
   294             System.exit(1);
       
   295             return null; // keep compiler happy
       
   296         }
       
   297     }
       
   298 
       
   299     // Read the stream from the target VM until EOF, then detach
       
   300     private static void drain(VirtualMachine vm, InputStream in) throws IOException {
       
   301         // read to EOF and just print output
       
   302         byte b[] = new byte[256];
       
   303         int n;
       
   304         do {
       
   305             n = in.read(b);
       
   306             if (n > 0) {
       
   307                 String s = new String(b, 0, n, "UTF-8");
       
   308                 System.out.print(s);
       
   309             }
       
   310         } while (n > 0);
       
   311         in.close();
       
   312         vm.detach();
       
   313     }
       
   314 
       
   315     // return a new string array with arg as the first element
       
   316     private static String[] prepend(String arg, String args[]) {
       
   317         String[] newargs = new String[args.length+1];
       
   318         newargs[0] = arg;
       
   319         System.arraycopy(args, 0, newargs, 1, args.length);
       
   320         return newargs;
       
   321     }
       
   322 
       
   323     // returns true if SA is available
       
   324     private static boolean haveSA() {
       
   325         return ToolProviderFinder.find("heapSummary") != null;
       
   326     }
   223     }
   327 
   224 
   328     // print usage message
   225     // print usage message
   329     private static void usage(int exit) {
   226     private static void usage(int exit) {
   330         System.err.println("Usage:");
   227         System.err.println("Usage:");
   331         if (haveSA()) {
   228         System.err.println("    jmap -clstats <pid>");
   332             System.err.println("    jmap [option] <pid>");
   229         System.err.println("        to connect to running process and print class loader statistics");
   333             System.err.println("        (to connect to running process)");
   230         System.err.println("    jmap -finalizerinfo <pid>");
   334             System.err.println("    jmap [option] <executable <core>");
   231         System.err.println("        to connect to running process and print information on objects awaiting finalization");
   335             System.err.println("        (to connect to a core file)");
   232         System.err.println("    jmap -histo[:live] <pid>");
   336             System.err.println("    jmap [option] [server_id@]<remote server IP or hostname>");
   233         System.err.println("        to connect to running process and print histogram of java object heap");
   337             System.err.println("        (to connect to remote debug server)");
   234         System.err.println("        if the \"live\" suboption is specified, only count live objects");
   338             System.err.println("");
   235         System.err.println("    jmap -dump:<dump-options> <pid>");
   339             System.err.println("where <option> is one of:");
   236         System.err.println("        to connect to running process and dump java heap");
   340             System.err.println("    <none>               to print same info as Solaris pmap");
   237         System.err.println("");
   341             System.err.println("    -heap                to print java heap summary");
   238         System.err.println("    dump-options:");
   342             System.err.println("    -histo[:live]        to print histogram of java object heap; if the \"live\"");
   239         System.err.println("      live         dump only live objects; if not specified,");
   343             System.err.println("                         suboption is specified, only count live objects");
   240         System.err.println("                   all objects in the heap are dumped.");
   344             System.err.println("    -clstats             to print class loader statistics");
   241         System.err.println("      format=b     binary format");
   345             System.err.println("    -finalizerinfo       to print information on objects awaiting finalization");
   242         System.err.println("      file=<file>  dump heap to <file>");
   346             System.err.println("    -dump:<dump-options> to dump java heap in hprof binary format");
   243         System.err.println("");
   347             System.err.println("                         dump-options:");
   244         System.err.println("    Example: jmap -dump:live,format=b,file=heap.bin <pid>");
   348             System.err.println("                           live         dump only live objects; if not specified,");
       
   349             System.err.println("                                        all objects in the heap are dumped.");
       
   350             System.err.println("                           format=b     binary format");
       
   351             System.err.println("                           file=<file>  dump heap to <file>");
       
   352             System.err.println("                         Example: jmap -dump:live,format=b,file=heap.bin <pid>");
       
   353             System.err.println("    -F                   force. Use with -dump:<dump-options> <pid> or -histo");
       
   354             System.err.println("                         to force a heap dump or histogram when <pid> does not");
       
   355             System.err.println("                         respond. The \"live\" suboption is not supported");
       
   356             System.err.println("                         in this mode.");
       
   357             System.err.println("    -h | -help           to print this help message");
       
   358             System.err.println("    -J<flag>             to pass <flag> directly to the runtime system");
       
   359         } else {
       
   360             System.err.println("    jmap -histo <pid>");
       
   361             System.err.println("      (to connect to running process and print histogram of java object heap");
       
   362             System.err.println("    jmap -dump:<dump-options> <pid>");
       
   363             System.err.println("      (to connect to running process and dump java heap)");
       
   364             System.err.println("");
       
   365             System.err.println("    dump-options:");
       
   366             System.err.println("      format=b     binary default");
       
   367             System.err.println("      file=<file>  dump heap to <file>");
       
   368             System.err.println("");
       
   369             System.err.println("    Example:       jmap -dump:format=b,file=heap.bin <pid>");
       
   370         }
       
   371 
       
   372         System.exit(exit);
   245         System.exit(exit);
   373     }
   246     }
   374 }
   247 }