1 /*
  2  * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package com.sun.management.internal;
 26 
 27 import java.io.IOException;
 28 import java.io.OutputStream;
 29 import java.nio.file.Files;
 30 import java.nio.file.Path;
 31 import java.nio.file.StandardOpenOption;
 32 import java.util.ArrayList;
 33 import java.util.List;
 34 import javax.management.ObjectName;
 35 import com.sun.management.HotSpotDiagnosticMXBean;
 36 import com.sun.management.HotSpotDiagnosticMXBean.ThreadDumpFormat;
 37 import com.sun.management.VMOption;
 38 import jdk.internal.vm.ThreadDumper;
 39 import sun.management.Util;
 40 
 41 /**
 42  * Implementation of the diagnostic MBean for Hotspot VM.
 43  */
 44 public class HotSpotDiagnostic implements HotSpotDiagnosticMXBean {
 45     public HotSpotDiagnostic() {
 46     }
 47 
 48     @Override
 49     public void dumpHeap(String outputFile, boolean live) throws IOException {
 50 
 51         String propertyName = "jdk.management.heapdump.allowAnyFileSuffix";
 52         boolean allowAnyFileSuffix = Boolean.getBoolean(propertyName);
 53         if (!allowAnyFileSuffix && !outputFile.endsWith(".hprof")) {
 54             throw new IllegalArgumentException("heapdump file must have .hprof extension");
 55         }
 56 
 57         dumpHeap0(outputFile, live);
 58     }
 59 
 60     private native void dumpHeap0(String outputFile, boolean live) throws IOException;
 61 
 62     @Override
 63     public List<VMOption> getDiagnosticOptions() {
 64         List<Flag> allFlags = Flag.getAllFlags();
 65         List<VMOption> result = new ArrayList<>();
 66         for (Flag flag : allFlags) {
 67             if (flag.isWriteable() && flag.isExternal()) {
 68                 result.add(flag.getVMOption());
 69             }
 70         }
 71         return result;
 72     }
 73 
 74     @Override
 75     public VMOption getVMOption(String name) {
 76         if (name == null) {
 77             throw new NullPointerException("name cannot be null");
 78         }
 79 
 80         Flag f = Flag.getFlag(name);
 81         if (f == null) {
 82             throw new IllegalArgumentException("VM option \"" +
 83                 name + "\" does not exist");
 84         }
 85         return f.getVMOption();
 86     }
 87 
 88     @Override
 89     public void setVMOption(String name, String value) {
 90         if (name == null) {
 91             throw new NullPointerException("name cannot be null");
 92         }
 93         if (value == null) {
 94             throw new NullPointerException("value cannot be null");
 95         }
 96 
 97         Flag flag = Flag.getFlag(name);
 98         if (flag == null) {
 99             throw new IllegalArgumentException("VM option \"" +
100                 name + "\" does not exist");
101         }
102         if (!flag.isWriteable()){
103             throw new IllegalArgumentException("VM Option \"" +
104                 name + "\" is not writeable");
105         }
106 
107         // Check the type of the value
108         Object v = flag.getValue();
109         if (v instanceof Long) {
110             try {
111                 long l = Long.parseLong(value);
112                 Flag.setLongValue(name, l);
113             } catch (NumberFormatException e) {
114                 throw new IllegalArgumentException("Invalid value:" +
115                         " VM Option \"" + name + "\"" +
116                         " expects numeric value", e);
117             }
118         } else if (v instanceof Double) {
119             try {
120                 double d = Double.parseDouble(value);
121                 Flag.setDoubleValue(name, d);
122             } catch (NumberFormatException e) {
123                 throw new IllegalArgumentException("Invalid value:" +
124                         " VM Option \"" + name + "\"" +
125                         " expects numeric value", e);
126             }
127         } else if (v instanceof Boolean) {
128             if (!value.equalsIgnoreCase("true") &&
129                 !value.equalsIgnoreCase("false")) {
130                 throw new IllegalArgumentException("Invalid value:" +
131                     " VM Option \"" + name + "\"" +
132                     " expects \"true\" or \"false\".");
133             }
134             Flag.setBooleanValue(name, Boolean.parseBoolean(value));
135         } else if (v instanceof String) {
136             Flag.setStringValue(name, value);
137         } else {
138             throw new IllegalArgumentException("VM Option \"" +
139                 name + "\" is of an unsupported type: " +
140                 v.getClass().getName());
141         }
142     }
143 
144     @Override
145     public ObjectName getObjectName() {
146         return Util.newObjectName("com.sun.management:type=HotSpotDiagnostic");
147     }
148 
149     @Override
150     public void dumpThreads(String outputFile, ThreadDumpFormat format) throws IOException {
151         Path file = Path.of(outputFile);
152         if (!file.isAbsolute())
153             throw new IllegalArgumentException("'outputFile' not absolute path");
154 
155         try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW)) {
156             dumpThreads(out, format);
157         }
158     }
159 
160     private void dumpThreads(OutputStream out, ThreadDumpFormat format) throws IOException {
161         switch (format) {
162             case TEXT_PLAIN -> ThreadDumper.dumpThreads(out);
163             case JSON       -> ThreadDumper.dumpThreadsToJson(out);
164         }
165     }
166 }