1 /*
  2  * Copyright (c) 2005, 2021, 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.security.AccessController;
 32 import java.security.PrivilegedAction;
 33 import java.security.PrivilegedActionException;
 34 import java.security.PrivilegedExceptionAction;
 35 import java.util.ArrayList;
 36 import java.util.List;
 37 import javax.management.ObjectName;

 38 import com.sun.management.HotSpotDiagnosticMXBean;
 39 import com.sun.management.HotSpotDiagnosticMXBean.ThreadDumpFormat;
 40 import com.sun.management.VMOption;
 41 import jdk.internal.vm.ThreadDumper;

 42 import sun.management.Util;
 43 
 44 /**
 45  * Implementation of the diagnostic MBean for Hotspot VM.
 46  */
 47 public class HotSpotDiagnostic implements HotSpotDiagnosticMXBean {
 48     public HotSpotDiagnostic() {
 49     }
 50 
 51     @Override
 52     public void dumpHeap(String outputFile, boolean live) throws IOException {
 53 
 54         String propertyName = "jdk.management.heapdump.allowAnyFileSuffix";
 55         PrivilegedAction<Boolean> pa = () -> Boolean.parseBoolean(System.getProperty(propertyName, "false"));
 56         @SuppressWarnings("removal")
 57         boolean allowAnyFileSuffix = AccessController.doPrivileged(pa);
 58         if (!allowAnyFileSuffix && !outputFile.endsWith(".hprof")) {
 59             throw new IllegalArgumentException("heapdump file must have .hprof extention");
 60         }
 61 
 62         @SuppressWarnings("removal")
 63         SecurityManager security = System.getSecurityManager();
 64         if (security != null) {
 65             security.checkWrite(outputFile);
 66             Util.checkControlAccess();
 67         }
 68 
 69         dumpHeap0(outputFile, live);
 70     }
 71 
 72     private native void dumpHeap0(String outputFile, boolean live) throws IOException;
 73 
 74     @Override
 75     public List<VMOption> getDiagnosticOptions() {
 76         List<Flag> allFlags = Flag.getAllFlags();
 77         List<VMOption> result = new ArrayList<>();
 78         for (Flag flag : allFlags) {
 79             if (flag.isWriteable() && flag.isExternal()) {
 80                 result.add(flag.getVMOption());
 81             }
 82         }
 83         return result;
 84     }
 85 
 86     @Override
 87     public VMOption getVMOption(String name) {
 88         if (name == null) {
 89             throw new NullPointerException("name cannot be null");
 90         }
 91 
 92         Flag f = Flag.getFlag(name);
 93         if (f == null) {
 94             throw new IllegalArgumentException("VM option \"" +
 95                 name + "\" does not exist");
 96         }
 97         return f.getVMOption();
 98     }
 99 
100     @Override
101     public void setVMOption(String name, String value) {
102         if (name == null) {
103             throw new NullPointerException("name cannot be null");
104         }
105         if (value == null) {
106             throw new NullPointerException("value cannot be null");
107         }
108 
109         Util.checkControlAccess();
110         Flag flag = Flag.getFlag(name);
111         if (flag == null) {
112             throw new IllegalArgumentException("VM option \"" +
113                 name + "\" does not exist");
114         }
115         if (!flag.isWriteable()){
116             throw new IllegalArgumentException("VM Option \"" +
117                 name + "\" is not writeable");
118         }
119 
120         // Check the type of the value
121         Object v = flag.getValue();
122         if (v instanceof Long) {
123             try {
124                 long l = Long.parseLong(value);
125                 Flag.setLongValue(name, l);
126             } catch (NumberFormatException e) {
127                 throw new IllegalArgumentException("Invalid value:" +
128                         " VM Option \"" + name + "\"" +
129                         " expects numeric value", e);
130             }
131         } else if (v instanceof Double) {
132             try {
133                 double d = Double.parseDouble(value);
134                 Flag.setDoubleValue(name, d);
135             } catch (NumberFormatException e) {
136                 throw new IllegalArgumentException("Invalid value:" +
137                         " VM Option \"" + name + "\"" +
138                         " expects numeric value", e);
139             }
140         } else if (v instanceof Boolean) {
141             if (!value.equalsIgnoreCase("true") &&
142                 !value.equalsIgnoreCase("false")) {
143                 throw new IllegalArgumentException("Invalid value:" +
144                     " VM Option \"" + name + "\"" +
145                     " expects \"true\" or \"false\".");
146             }
147             Flag.setBooleanValue(name, Boolean.parseBoolean(value));
148         } else if (v instanceof String) {
149             Flag.setStringValue(name, value);
150         } else {
151             throw new IllegalArgumentException("VM Option \"" +
152                 name + "\" is of an unsupported type: " +
153                 v.getClass().getName());
154         }
155     }
156 
157     @Override
158     public ObjectName getObjectName() {
159         return Util.newObjectName("com.sun.management:type=HotSpotDiagnostic");
160     }
161 
162     @Override
163     @SuppressWarnings("removal")
164     public void dumpThreads(String outputFile, ThreadDumpFormat format) throws IOException {
165         Path file = Path.of(outputFile);
166         if (!file.isAbsolute())
167             throw new IllegalArgumentException("'outputFile' not absolute path");
168 
169         // need ManagementPermission("control")
170         @SuppressWarnings("removal")
171         SecurityManager sm = System.getSecurityManager();
172         if (sm != null)
173             Util.checkControlAccess();
174 
175         try (OutputStream out = Files.newOutputStream(file)) {
176             PrivilegedExceptionAction<Void> pa = () -> {
177                 dumpThreads(out, format);
178                 return null;
179             };
180             try {
181                 AccessController.doPrivileged(pa);
182             } catch (PrivilegedActionException pae) {
183                 Throwable cause = pae.getCause();
184                 if (cause instanceof IOException ioe)
185                     throw ioe;
186                 if (cause instanceof RuntimeException e)
187                     throw e;
188                 throw new RuntimeException(cause);
189             }
190         }
191     }
192 
193     private void dumpThreads(OutputStream out, ThreadDumpFormat format) throws IOException {
194         switch (format) {
195             case TEXT_PLAIN -> ThreadDumper.dumpThreads(out);
196             case JSON       -> ThreadDumper.dumpThreadsToJson(out);
197         }
198     }
199 }
--- EOF ---