8215622: Add dump to file support for jmap –histo
authorphh
Wed, 20 Feb 2019 11:36:02 -0800
changeset 53858 e7cf035682e3
parent 53851 cc4f5bf6b26b
child 53859 4974a793f0f9
8215622: Add dump to file support for jmap –histo Summary: Add "file=" to jmap "histo" jmap, Hotspot support in attachListener.cpp:heap_inspection. Reviewed-by: phh, sspitsyn Contributed-by: Lin Zang <zanglin5@jd.com>
src/hotspot/share/services/attachListener.cpp
src/hotspot/share/services/attachListener.hpp
src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java
src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java
src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java
src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java
src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java
src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
test/jdk/java/util/logging/TestLoggerWeakRefLeak.java
test/jdk/sun/tools/jmap/BasicJMapTest.java
--- a/src/hotspot/share/services/attachListener.cpp	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/hotspot/share/services/attachListener.cpp	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -257,19 +257,40 @@
 // See also: ClassHistogramDCmd class
 //
 // Input arguments :-
-//   arg0: "-live" or "-all"
+//   arg0: Name of the dump file or NULL
+//   arg1: "-live" or "-all"
 static jint heap_inspection(AttachOperation* op, outputStream* out) {
   bool live_objects_only = true;   // default is true to retain the behavior before this change is made
-  const char* arg0 = op->arg(0);
-  if (arg0 != NULL && (strlen(arg0) > 0)) {
-    if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) {
-      out->print_cr("Invalid argument to inspectheap operation: %s", arg0);
+  outputStream* os = out;   // if path not specified or path is NULL, use out
+  fileStream* fs = NULL;
+  const char* path = op->arg(0);
+  if (path != NULL) {
+    if (path[0] == '\0') {
+      out->print_cr("No dump file specified");
+    } else {
+      // create file
+      fs = new (ResourceObj::C_HEAP, mtInternal) fileStream(path);
+      if (fs == NULL) {
+        out->print_cr("Failed to allocate space for file: %s", path);
+        return JNI_ERR;
+      }
+      os = fs;
+    }
+  }
+  const char* arg1 = op->arg(1);
+  if (arg1 != NULL && (strlen(arg1) > 0)) {
+    if (strcmp(arg1, "-all") != 0 && strcmp(arg1, "-live") != 0) {
+      out->print_cr("Invalid argument to inspectheap operation: %s", arg1);
       return JNI_ERR;
     }
-    live_objects_only = strcmp(arg0, "-live") == 0;
+    live_objects_only = strcmp(arg1, "-live") == 0;
   }
-  VM_GC_HeapInspection heapop(out, live_objects_only /* request full gc */);
+  VM_GC_HeapInspection heapop(os, live_objects_only /* request full gc */);
   VMThread::execute(&heapop);
+  if (os != NULL && os != out) {
+    out->print_cr("Heap inspection file created: %s", path);
+    delete fs;
+  }
   return JNI_OK;
 }
 
--- a/src/hotspot/share/services/attachListener.hpp	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/hotspot/share/services/attachListener.hpp	Wed Feb 20 11:36:02 2019 -0800
@@ -106,7 +106,7 @@
   enum {
     name_length_max = 16,       // maximum length of  name
     arg_length_max = 1024,      // maximum length of argument
-    arg_count_max = 3           // maximum number of arguments
+    arg_count_max = 4           // maximum number of arguments
   };
 
   // name of special operation that can be enqueued when all
--- a/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2015, 2018, SAP SE. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -138,7 +138,7 @@
      * Execute the given command in the target VM.
      */
     InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
-        assert args.length <= 3;                // includes null
+        assert args.length <= 4;                // includes null
 
         // did we detach?
         synchronized (this) {
@@ -166,7 +166,7 @@
             writeString(s, PROTOCOL_VERSION);
             writeString(s, cmd);
 
-            for (int i=0; i<3; i++) {
+            for (int i = 0; i < 4; i++) {
                 if (i < args.length && args[i] != null) {
                     writeString(s, (String)args[i]);
                 } else {
--- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -143,7 +143,7 @@
      * Execute the given command in the target VM.
      */
     InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
-        assert args.length <= 3;                // includes null
+        assert args.length <= 4;                // includes null
 
         // did we detach?
         synchronized (this) {
@@ -171,7 +171,7 @@
             writeString(s, PROTOCOL_VERSION);
             writeString(s, cmd);
 
-            for (int i=0; i<3; i++) {
+            for (int i = 0; i < 4; i++) {
                 if (i < args.length && args[i] != null) {
                     writeString(s, (String)args[i]);
                 } else {
--- a/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -139,7 +139,7 @@
      * Execute the given command in the target VM.
      */
     InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
-        assert args.length <= 3;                // includes null
+        assert args.length <= 4;                // includes null
 
         // did we detach?
         synchronized (this) {
@@ -167,7 +167,7 @@
             writeString(s, PROTOCOL_VERSION);
             writeString(s, cmd);
 
-            for (int i=0; i<3; i++) {
+            for (int i = 0; i < 4; i++) {
                 if (i < args.length && args[i] != null) {
                     writeString(s, (String)args[i]);
                 } else {
--- a/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -126,7 +126,7 @@
      * Execute the given command in the target VM.
      */
     InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
-        assert args.length <= 3;                // includes null
+        assert args.length <= 4;                // includes null
 
         // first check that we are still attached
         int door;
--- a/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -77,7 +77,7 @@
     InputStream execute(String cmd, Object ... args)
         throws AgentLoadException, IOException
     {
-        assert args.length <= 3;        // includes null
+        assert args.length <= 4;        // includes null
 
         // create a pipe using a random name
         Random rnd = new Random();
--- a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -145,22 +145,52 @@
         vm.detach();
     }
 
+    private static String parseFileName(String opt) {
+        // opt starts with "file="
+        if (opt.length() > 5) {
+            //  pass whole "file=" string
+            String filename = opt.substring(5);
+            try {
+                // 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.
+                return new File(filename).getCanonicalPath();
+            } catch (IOException ioe) {
+              return null;
+            }
+        }
+        // no filename
+        return null;
+    }
+
     private static void histo(String pid, String options)
         throws AttachNotSupportedException, IOException,
                UnsupportedEncodingException {
         String liveopt = "-all";
-        if (options.isEmpty() || options.equals("all")) {
-            //  pass
-        }
-        else if (options.equals("live")) {
-            liveopt = "-live";
-        }
-        else {
-            usage(1);
+        String filename = null;
+        String subopts[] = options.split(",");
+
+        for (int i = 0; i < subopts.length; i++) {
+            String subopt = subopts[i];
+            if (subopt.equals("") || subopt.equals("all")) {
+                // pass
+            } else if (subopt.equals("live")) {
+                liveopt = "-live";
+            } else if (subopt.startsWith("file=")) {
+                filename = parseFileName(subopt);
+                if (filename == null) {
+                    usage(1); // invalid options or no filename
+                }
+            } else {
+                usage(1);
+            }
         }
 
+        System.out.flush();
+
         // inspectHeap is not the same as jcmd GC.class_histogram
-        executeCommandForPid(pid, "inspectheap", liveopt);
+        executeCommandForPid(pid, "inspectheap", filename, liveopt);
     }
 
     private static void dump(String pid, String options)
@@ -176,10 +206,7 @@
             if (subopt.equals("live")) {
                 liveopt = "-live";
             } else if (subopt.startsWith("file=")) {
-                // file=<file> - check that <file> is specified
-                if (subopt.length() > 5) {
-                    filename = subopt.substring(5);
-                }
+                filename = parseFileName(subopt);
             }
         }
 
@@ -187,11 +214,6 @@
             usage(1);  // 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();
         // dumpHeap is not the same as jcmd GC.heap_dump
         executeCommandForPid(pid, "dumpheap", filename, liveopt);
     }
@@ -246,21 +268,27 @@
         System.err.println("        to connect to running process and print class loader statistics");
         System.err.println("    jmap -finalizerinfo <pid>");
         System.err.println("        to connect to running process and print information on objects awaiting finalization");
-        System.err.println("    jmap -histo[:live] <pid>");
+        System.err.println("    jmap -histo[:[<histo-options>]] <pid>");
         System.err.println("        to connect to running process and print histogram of java object heap");
-        System.err.println("        if the \"live\" suboption is specified, only count live objects");
         System.err.println("    jmap -dump:<dump-options> <pid>");
         System.err.println("        to connect to running process and dump java heap");
         System.err.println("    jmap -? -h --help");
         System.err.println("        to print this help message");
         System.err.println("");
         System.err.println("    dump-options:");
-        System.err.println("      live         dump only live objects; if not specified,");
-        System.err.println("                   all objects in the heap are dumped.");
+        System.err.println("      live         dump only live objects");
+        System.err.println("      all          dump all objects in the heap (default if one of \"live\" or \"all\" is not specified");
         System.err.println("      format=b     binary format");
         System.err.println("      file=<file>  dump heap to <file>");
         System.err.println("");
         System.err.println("    Example: jmap -dump:live,format=b,file=heap.bin <pid>");
+        System.err.println("");
+        System.err.println("    histo-options:");
+        System.err.println("      live         count only live objects");
+        System.err.println("      all          count all objects in the heap (default if one of \"live\" or \"all\" is not specified)");
+        System.err.println("      file=<file>  dump data to <file>");
+        System.err.println("");
+        System.err.println("    Example: jmap -histo:live,file=/tmp/histo.data <pid>");
         System.exit(exit);
     }
 }
--- a/test/jdk/java/util/logging/TestLoggerWeakRefLeak.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/test/jdk/java/util/logging/TestLoggerWeakRefLeak.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -123,7 +123,7 @@
     }
 
     /**
-     * 'vm.heapHisto("-live")' will request a full GC
+     * 'vm.heapHisto("", "-live")' will request a full GC
      */
     private static int getInstanceCountFromHeapHisto() throws AttachNotSupportedException, Exception {
         int instanceCount = 0;
@@ -131,7 +131,7 @@
         HotSpotVirtualMachine vm = (HotSpotVirtualMachine) VirtualMachine
                 .attach(Long.toString(ProcessTools.getProcessId()));
         try {
-            try (InputStream heapHistoStream = vm.heapHisto("-live");
+            try (InputStream heapHistoStream = vm.heapHisto("", "-live");
                     BufferedReader in = new BufferedReader(new InputStreamReader(heapHistoStream))) {
                 String inputLine;
                 while ((inputLine = in.readLine()) != null) {
--- a/test/jdk/sun/tools/jmap/BasicJMapTest.java	Wed Feb 20 09:43:01 2019 -0800
+++ b/test/jdk/sun/tools/jmap/BasicJMapTest.java	Wed Feb 20 11:36:02 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -50,15 +50,22 @@
     public static void main(String[] args) throws Exception {
         testHisto();
         testHistoLive();
+        testHistoAll();
+        testHistoToFile();
+        testHistoLiveToFile();
+        testHistoAllToFile();
         testFinalizerInfo();
         testClstats();
         testDump();
         testDumpLive();
+        testDumpAll();
     }
 
     private static void testHisto() throws Exception {
-        OutputAnalyzer output = jmap("-histo");
+        OutputAnalyzer output = jmap("-histo:");
         output.shouldHaveExitValue(0);
+        OutputAnalyzer output1 = jmap("-histo");
+        output1.shouldHaveExitValue(0);
     }
 
     private static void testHistoLive() throws Exception {
@@ -66,6 +73,50 @@
         output.shouldHaveExitValue(0);
     }
 
+    private static void testHistoAll() throws Exception {
+        OutputAnalyzer output = jmap("-histo:all");
+        output.shouldHaveExitValue(0);
+    }
+
+    private static void testHistoToFile() throws Exception {
+        histoToFile(false);
+    }
+
+    private static void testHistoLiveToFile() throws Exception {
+        histoToFile(true);
+    }
+
+    private static void testHistoAllToFile() throws Exception {
+        boolean explicitAll = true;
+        histoToFile(false, explicitAll);
+    }
+
+    private static void histoToFile(boolean live) throws Exception {
+        boolean explicitAll = false;
+        histoToFile(live, explicitAll);
+    }
+
+    private static void histoToFile(boolean live, boolean explicitAll) throws Exception {
+        if (live == true && explicitAll == true) {
+            fail("Illegal argument setting for jmap -histo");
+        }
+        File file = new File("jmap.histo.file" + System.currentTimeMillis() + ".histo");
+        if (file.exists()) {
+            file.delete();
+        }
+        OutputAnalyzer output;
+        if (live) {
+            output = jmap("-histo:live,file=" + file.getName());
+        } else if (explicitAll == true) {
+            output = jmap("-histo:all,file=" + file.getName());
+        } else {
+            output = jmap("-histo:file=" + file.getName());
+        }
+        output.shouldHaveExitValue(0);
+        output.shouldContain("Heap inspection file created");
+        file.delete();
+    }
+
     private static void testFinalizerInfo() throws Exception {
         OutputAnalyzer output = jmap("-finalizerinfo");
         output.shouldHaveExitValue(0);
@@ -84,7 +135,20 @@
         dump(true);
     }
 
+    private static void testDumpAll() throws Exception {
+        boolean explicitAll = true;
+        dump(false, explicitAll);
+    }
+
     private static void dump(boolean live) throws Exception {
+        boolean explicitAll = false;
+        dump(live, explicitAll);
+    }
+
+    private static void dump(boolean live, boolean explicitAll) throws Exception {
+        if (live == true && explicitAll == true) {
+          fail("Illegal argument setting for jmap -dump");
+        }
         File dump = new File("jmap.dump." + System.currentTimeMillis() + ".hprof");
         if (dump.exists()) {
             dump.delete();
@@ -92,6 +156,8 @@
         OutputAnalyzer output;
         if (live) {
             output = jmap("-dump:live,format=b,file=" + dump.getName());
+        } else if (explicitAll == true) {
+            output = jmap("-dump:all,format=b,file=" + dump.getName());
         } else {
             output = jmap("-dump:format=b,file=" + dump.getName());
         }
@@ -121,7 +187,7 @@
         launcher.addToolArg(Long.toString(ProcessTools.getProcessId()));
 
         processBuilder.command(launcher.getCommand());
-        System.out.println(Arrays.toString(processBuilder.command().toArray()).replace(",", ""));
+        System.out.println(Arrays.toString(processBuilder.command().toArray()));
         OutputAnalyzer output = ProcessTools.executeProcess(processBuilder);
         System.out.println(output.getOutput());