Path: blob/master/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java
41161 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.attach;2627import com.sun.tools.attach.AttachNotSupportedException;28import com.sun.tools.attach.VirtualMachine;29import com.sun.tools.attach.AgentLoadException;30import com.sun.tools.attach.AgentInitializationException;31import com.sun.tools.attach.spi.AttachProvider;32import jdk.internal.misc.VM;3334import java.io.BufferedReader;35import java.io.InputStream;36import java.io.IOException;37import java.io.InputStreamReader;38import java.security.AccessController;39import java.security.PrivilegedAction;40import java.util.Properties;41import java.util.stream.Collectors;4243/*44* The HotSpot implementation of com.sun.tools.attach.VirtualMachine.45*/4647@SuppressWarnings("removal")48public abstract class HotSpotVirtualMachine extends VirtualMachine {4950private static final long CURRENT_PID;51private static final boolean ALLOW_ATTACH_SELF;52static {53PrivilegedAction<ProcessHandle> pa = ProcessHandle::current;54CURRENT_PID = AccessController.doPrivileged(pa).pid();5556String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");57ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);58}5960HotSpotVirtualMachine(AttachProvider provider, String id)61throws AttachNotSupportedException, IOException62{63super(provider, id);6465int pid;66try {67pid = Integer.parseInt(id);68} catch (NumberFormatException e) {69throw new AttachNotSupportedException("Invalid process identifier");70}7172// The tool should be a different VM to the target. This check will73// eventually be enforced by the target VM.74if (!ALLOW_ATTACH_SELF && (pid == 0 || pid == CURRENT_PID)) {75throw new IOException("Can not attach to current VM");76}77}7879/*80* Load agent library81* If isAbsolute is true then the agent library is the absolute path82* to the library and thus will not be expanded in the target VM.83* if isAbsolute is false then the agent library is just a library84* name and it will be expended in the target VM.85*/86private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options)87throws AgentLoadException, AgentInitializationException, IOException88{89if (agentLibrary == null) {90throw new NullPointerException("agentLibrary cannot be null");91}9293String msgPrefix = "return code: ";94InputStream in = execute("load",95agentLibrary,96isAbsolute ? "true" : "false",97options);98try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {99String result = reader.readLine();100if (result == null) {101throw new AgentLoadException("Target VM did not respond");102} else if (result.startsWith(msgPrefix)) {103int retCode = Integer.parseInt(result.substring(msgPrefix.length()));104if (retCode != 0) {105throw new AgentInitializationException("Agent_OnAttach failed", retCode);106}107} else {108throw new AgentLoadException(result);109}110}111}112113/*114* Load agent library - library name will be expanded in target VM115*/116public void loadAgentLibrary(String agentLibrary, String options)117throws AgentLoadException, AgentInitializationException, IOException118{119loadAgentLibrary(agentLibrary, false, options);120}121122/*123* Load agent - absolute path of library provided to target VM124*/125public void loadAgentPath(String agentLibrary, String options)126throws AgentLoadException, AgentInitializationException, IOException127{128loadAgentLibrary(agentLibrary, true, options);129}130131/*132* Load JPLIS agent which will load the agent JAR file and invoke133* the agentmain method.134*/135public void loadAgent(String agent, String options)136throws AgentLoadException, AgentInitializationException, IOException137{138if (agent == null) {139throw new NullPointerException("agent cannot be null");140}141142String args = agent;143if (options != null) {144args = args + "=" + options;145}146try {147loadAgentLibrary("instrument", args);148} catch (AgentInitializationException x) {149/*150* Translate interesting errors into the right exception and151* message (FIXME: create a better interface to the instrument152* implementation so this isn't necessary)153*/154int rc = x.returnValue();155switch (rc) {156case JNI_ENOMEM:157throw new AgentLoadException("Insuffient memory");158case ATTACH_ERROR_BADJAR:159throw new AgentLoadException(160"Agent JAR not found or no Agent-Class attribute");161case ATTACH_ERROR_NOTONCP:162throw new AgentLoadException(163"Unable to add JAR file to system class path");164case ATTACH_ERROR_STARTFAIL:165throw new AgentInitializationException(166"Agent JAR loaded but agent failed to initialize");167default :168throw new AgentLoadException("" +169"Failed to load agent - unknown reason: " + rc);170}171}172}173174/*175* The possible errors returned by JPLIS's agentmain176*/177private static final int JNI_ENOMEM = -4;178private static final int ATTACH_ERROR_BADJAR = 100;179private static final int ATTACH_ERROR_NOTONCP = 101;180private static final int ATTACH_ERROR_STARTFAIL = 102;181182183/*184* Send "properties" command to target VM185*/186public Properties getSystemProperties() throws IOException {187InputStream in = null;188Properties props = new Properties();189try {190in = executeCommand("properties");191props.load(in);192} finally {193if (in != null) in.close();194}195return props;196}197198public Properties getAgentProperties() throws IOException {199InputStream in = null;200Properties props = new Properties();201try {202in = executeCommand("agentProperties");203props.load(in);204} finally {205if (in != null) in.close();206}207return props;208}209210private static final String MANAGEMENT_PREFIX = "com.sun.management.";211212private static boolean checkedKeyName(Object key) {213if (!(key instanceof String)) {214throw new IllegalArgumentException("Invalid option (not a String): "+key);215}216if (!((String)key).startsWith(MANAGEMENT_PREFIX)) {217throw new IllegalArgumentException("Invalid option: "+key);218}219return true;220}221222private static String stripKeyName(Object key) {223return ((String)key).substring(MANAGEMENT_PREFIX.length());224}225226@Override227public void startManagementAgent(Properties agentProperties) throws IOException {228if (agentProperties == null) {229throw new NullPointerException("agentProperties cannot be null");230}231// Convert the arguments into arguments suitable for the Diagnostic Command:232// "ManagementAgent.start jmxremote.port=5555 jmxremote.authenticate=false"233String args = agentProperties.entrySet().stream()234.filter(entry -> checkedKeyName(entry.getKey()))235.map(entry -> stripKeyName(entry.getKey()) + "=" + escape(entry.getValue()))236.collect(Collectors.joining(" "));237executeJCmd("ManagementAgent.start " + args).close();238}239240private String escape(Object arg) {241String value = arg.toString();242if (value.contains(" ")) {243return "'" + value + "'";244}245return value;246}247248@Override249public String startLocalManagementAgent() throws IOException {250executeJCmd("ManagementAgent.start_local").close();251String prop = MANAGEMENT_PREFIX + "jmxremote.localConnectorAddress";252return getAgentProperties().getProperty(prop);253}254255256// --- HotSpot specific methods ---257258// same as SIGQUIT259public void localDataDump() throws IOException {260executeCommand("datadump").close();261}262263// Remote ctrl-break. The output of the ctrl-break actions can264// be read from the input stream.265public InputStream remoteDataDump(Object ... args) throws IOException {266return executeCommand("threaddump", args);267}268269// Remote heap dump. The output (error message) can be read from the270// returned input stream.271public InputStream dumpHeap(Object ... args) throws IOException {272return executeCommand("dumpheap", args);273}274275// Heap histogram (heap inspection in HotSpot)276public InputStream heapHisto(Object ... args) throws IOException {277return executeCommand("inspectheap", args);278}279280// set JVM command line flag281public InputStream setFlag(String name, String value) throws IOException {282return executeCommand("setflag", name, value);283}284285// print command line flag286public InputStream printFlag(String name) throws IOException {287return executeCommand("printflag", name);288}289290public InputStream executeJCmd(String command) throws IOException {291return executeCommand("jcmd", command);292}293294295// -- Supporting methods296297/*298* Execute the given command in the target VM - specific platform299* implementation must implement this.300*/301abstract InputStream execute(String cmd, Object ... args)302throws AgentLoadException, IOException;303304/*305* Convenience method for simple commands306*/307public InputStream executeCommand(String cmd, Object ... args) throws IOException {308try {309return execute(cmd, args);310} catch (AgentLoadException x) {311throw new InternalError("Should not get here", x);312}313}314315316/*317* Utility method to read an 'int' from the input stream. Ideally318* we should be using java.util.Scanner here but this implementation319* guarantees not to read ahead.320*/321int readInt(InputStream in) throws IOException {322StringBuilder sb = new StringBuilder();323324// read to \n or EOF325int n;326byte buf[] = new byte[1];327do {328n = in.read(buf, 0, 1);329if (n > 0) {330char c = (char)buf[0];331if (c == '\n') {332break; // EOL found333} else {334sb.append(c);335}336}337} while (n > 0);338339if (sb.length() == 0) {340throw new IOException("Premature EOF");341}342343int value;344try {345value = Integer.parseInt(sb.toString());346} catch (NumberFormatException x) {347throw new IOException("Non-numeric value found - int expected");348}349return value;350}351352/*353* Utility method to read data into a String.354*/355String readErrorMessage(InputStream in) throws IOException {356String s;357StringBuilder message = new StringBuilder();358BufferedReader br = new BufferedReader(new InputStreamReader(in));359while ((s = br.readLine()) != null) {360message.append(s);361}362return message.toString();363}364365366// -- attach timeout support367368private static long defaultAttachTimeout = 10000;369private volatile long attachTimeout;370371/*372* Return attach timeout based on the value of the sun.tools.attach.attachTimeout373* property, or the default timeout if the property is not set to a positive374* value.375*/376long attachTimeout() {377if (attachTimeout == 0) {378synchronized(this) {379if (attachTimeout == 0) {380try {381String s =382System.getProperty("sun.tools.attach.attachTimeout");383attachTimeout = Long.parseLong(s);384} catch (SecurityException se) {385} catch (NumberFormatException ne) {386}387if (attachTimeout <= 0) {388attachTimeout = defaultAttachTimeout;389}390}391}392}393return attachTimeout;394}395}396397398