1 /* 2 * Copyright (c) 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 import java.io.IOException; 24 import java.net.BindException; 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.List; 28 import java.util.Properties; 29 import java.util.concurrent.atomic.AtomicBoolean; 30 import java.util.function.Consumer; 31 import java.util.regex.Pattern; 32 import java.util.stream.Collectors; 33 import jdk.internal.agent.Agent; 34 import jdk.internal.agent.AgentConfigurationError; 35 36 import jdk.testlibrary.JDKToolLauncher; 37 import jdk.test.lib.process.ProcessTools; 38 39 /** 40 * A helper class for issuing ManagementAgent.* diagnostic commands and capturing 41 * their output. 42 */ 43 final class ManagementAgentJcmd { 44 private static final String CMD_STOP = "ManagementAgent.stop"; 45 private static final String CMD_START = "ManagementAgent.start"; 46 private static final String CMD_START_LOCAL = "ManagementAgent.start_local"; 47 private static final String CMD_STATUS = "ManagementAgent.status"; 48 private static final String CMD_PRINTPERF = "PerfCounter.print"; 49 50 private final String id; 51 private final boolean verbose; 52 53 public ManagementAgentJcmd(String targetApp, boolean verbose) { 54 this.id = targetApp; 55 this.verbose = verbose; 56 } 57 58 /** 59 * `jcmd` 60 * @return The JCMD output 61 * @throws IOException 62 * @throws InterruptedException 63 */ 64 public String list() throws IOException, InterruptedException { 65 return jcmd(); 66 } 67 68 /** 69 * `jcmd PerfCounter.print` 70 * @return Returns the available performance counters with their values as 71 * {@linkplain Properties} instance 72 * @throws IOException 73 * @throws InterruptedException 74 */ 75 public Properties perfCounters() throws IOException, InterruptedException { 76 return perfCounters(".*"); 77 } 78 79 /** 80 * `jcmd PerfCounter.print | grep {exp}>` 81 * @param regex Regular expression for including perf counters in the result 82 * @return Returns the matching performance counters with their values 83 * as {@linkplain Properties} instance 84 * @throws IOException 85 * @throws InterruptedException 86 */ 87 public Properties perfCounters(String regex) throws IOException, InterruptedException { 88 Pattern pat = Pattern.compile(regex); 89 Properties p = new Properties(); 90 for(String l : jcmd(CMD_PRINTPERF).split("\\n")) { 91 String[] kv = l.split("="); 92 if (kv.length > 1) { 93 if (pat.matcher(kv[0]).matches()) { 94 p.setProperty(kv[0], kv[1].replace("\"", "")); 95 } 96 } 97 } 98 return p; 99 } 100 101 /** 102 * `jcmd <app> ManagementAgent.stop` 103 * @return The JCMD output 104 * @throws IOException 105 * @throws InterruptedException 106 */ 107 public String stop() throws IOException, InterruptedException { 108 return jcmd(CMD_STOP); 109 } 110 111 /** 112 * `jcmd <app> ManagementAgent.start_local` 113 * @return The JCMD output 114 * @throws IOException 115 * @throws InterruptedException 116 */ 117 public String startLocal() throws IOException, InterruptedException { 118 return jcmd(CMD_START_LOCAL); 119 } 120 121 /** 122 * `jcmd <app> ManagementAgent.start <args>` 123 * @return The JCMD output 124 * @param params The arguments to <b>ManagementAgent.start</b> command 125 * @throws IOException 126 * @throws InterruptedException 127 */ 128 public String start(String ... params) throws IOException, InterruptedException { 129 return start(c->{}, params); 130 } 131 132 /** 133 * `jcmd <pp> ManagementAgent.start <args>` 134 * @param c A string consumer used to inspect the jcmd output line-by-line 135 * @param params The arguments to <b>ManagementAgent.start</b> command 136 * @return The JCMD output 137 * @throws IOException 138 * @throws InterruptedException 139 */ 140 public String start(Consumer<String> c, String ... params) throws IOException, InterruptedException { 141 List<String> args = new ArrayList<>(); 142 args.add(CMD_START); 143 args.addAll(Arrays.asList(params)); 144 return jcmd(c, args.toArray(new String[args.size()])); 145 } 146 147 public String status() throws IOException, InterruptedException { 148 return jcmd(CMD_STATUS); 149 } 150 151 /** 152 * Run the "jcmd" command 153 * 154 * @param command Command + arguments 155 * @return The JCMD output 156 * @throws IOException 157 * @throws InterruptedException 158 */ 159 private String jcmd(String ... command) throws IOException, InterruptedException { 160 if (command.length == 0) { 161 return jcmd(null, c->{}); 162 } else { 163 return jcmd(c->{}, command); 164 } 165 } 166 167 /** 168 * Run the "jcmd" command 169 * 170 * @param c {@linkplain Consumer} instance 171 * @param command Command + arguments 172 * @return The JCMD output 173 * @throws IOException 174 * @throws InterruptedException 175 */ 176 private String jcmd(Consumer<String> c, String ... command) throws IOException, InterruptedException { 177 return jcmd(id, c, command); 178 } 179 180 /** 181 * Run the "jcmd" command 182 * 183 * @param target The target application name (or PID) 184 * @param c {@linkplain Consumer} instance 185 * @param command Command + arguments 186 * @return The JCMD output 187 * @throws IOException 188 * @throws InterruptedException 189 */ 190 private String jcmd(String target, final Consumer<String> c, String ... command) throws IOException, InterruptedException { 191 dbg_print("[jcmd] " + (command.length > 0 ? command[0] : "list")); 192 193 JDKToolLauncher l = JDKToolLauncher.createUsingTestJDK("jcmd"); 194 l.addToolArg(target); 195 for (String cmd : command) { 196 l.addToolArg(cmd); 197 } 198 199 // this buffer will get filled in different threads 200 // -> must be the synchronized StringBuffer 201 StringBuffer output = new StringBuffer(); 202 203 AtomicBoolean portUnavailable = new AtomicBoolean(false); 204 Process p = ProcessTools.startProcess( 205 "jcmd", 206 new ProcessBuilder(l.getCommand()), 207 line -> { 208 if (line.contains("BindException") || 209 line.contains(Agent.getText(AgentConfigurationError.CONNECTOR_SERVER_IO_ERROR))) { 210 portUnavailable.set(true); 211 } else { 212 output.append(line).append('\n'); 213 c.accept(line); 214 } 215 } 216 ); 217 218 p.waitFor(); 219 dbg_print("[jcmd] --------"); 220 if (portUnavailable.get()) { 221 String cmd = Arrays.asList(l.getCommand()).stream() 222 .collect( 223 Collectors.joining(" ", "", ": Unable to bind address") 224 ); 225 throw new BindException(cmd); 226 } 227 228 return output.toString(); 229 } 230 231 private void dbg_print(String msg) { 232 if (verbose) { 233 System.out.println("DBG: " + msg); 234 } 235 } 236 }