1 /* 2 * Copyright (c) 2004, 2017, 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 26 package sun.tools.jstat; 27 28 import java.io.*; 29 import java.net.*; 30 import java.util.*; 31 import java.util.regex.*; 32 import sun.jvmstat.monitor.Monitor; 33 import sun.jvmstat.monitor.VmIdentifier; 34 35 /** 36 * Class for processing command line arguments and providing method 37 * level access to arguments. 38 * 39 * @author Brian Doherty 40 * @since 1.5 41 */ 42 public class Arguments { 43 44 private static final boolean debug = Boolean.getBoolean("jstat.debug"); 45 private static final boolean showUnsupported = 46 Boolean.getBoolean("jstat.showUnsupported"); 47 48 private static final String JVMSTAT_USERDIR = ".jvmstat"; 49 private static final String OPTIONS_FILENAME = "jstat_options"; 50 private static final String UNSUPPORTED_OPTIONS_FILENAME = "jstat_unsupported_options"; 51 private static final String ALL_NAMES = "\\w*"; 52 53 private Comparator<Monitor> comparator; 54 private int headerRate; 55 private boolean help; 56 private boolean list; 57 private boolean options; 58 private boolean constants; 59 private boolean constantsOnly; 60 private boolean strings; 61 private boolean timestamp; 62 private boolean snap; 63 private boolean verbose; 64 private String specialOption; 65 private String names; 66 67 private OptionFormat optionFormat; 68 69 private int count = -1; 70 private int interval = -1; 71 private String vmIdString; 72 73 private VmIdentifier vmId; 74 75 public static void printUsage(PrintStream ps) { 76 ps.println("Usage: jstat --help|-options"); 77 ps.println(" jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]"); 78 ps.println(); 79 ps.println("Definitions:"); 80 ps.println(" <option> An option reported by the -options option"); 81 ps.println(" <vmid> Virtual Machine Identifier. A vmid takes the following form:"); 82 ps.println(" <lvmid>[@<hostname>[:<port>]]"); 83 ps.println(" Where <lvmid> is the local vm identifier for the target"); 84 ps.println(" Java virtual machine, typically a process id; <hostname> is"); 85 ps.println(" the name of the host running the target Java virtual machine;"); 86 ps.println(" and <port> is the port number for the rmiregistry on the"); 87 ps.println(" target host. See the jvmstat documentation for a more complete"); 88 ps.println(" description of the Virtual Machine Identifier."); 89 ps.println(" <lines> Number of samples between header lines."); 90 ps.println(" <interval> Sampling interval. The following forms are allowed:"); 91 ps.println(" <n>[\"ms\"|\"s\"]"); 92 ps.println(" Where <n> is an integer and the suffix specifies the units as "); 93 ps.println(" milliseconds(\"ms\") or seconds(\"s\"). The default units are \"ms\"."); 94 ps.println(" <count> Number of samples to take before terminating."); 95 ps.println(" -J<flag> Pass <flag> directly to the runtime system."); 96 ps.println(" -? -h --help Prints this help message."); 97 ps.println(" -help Prints this help message."); 98 99 // undocumented options: 100 // -list [<vmid>] - list counter names 101 // -snap <vmid> - snapshot counter values as name=value pairs 102 // -name <pattern> - output counters matching given pattern 103 // -a - sort in ascending order (default) 104 // -d - sort in descending order 105 // -v - verbose output (-snap) 106 // -constants - output constants with -name output 107 // -strings - output strings with -name output 108 // -help - same as -? ... 109 } 110 111 private static int toMillis(String s) throws IllegalArgumentException { 112 113 String[] unitStrings = { "ms", "s" }; // ordered from most specific to 114 // least specific 115 String unitString = null; 116 String valueString = s; 117 118 for (int i = 0; i < unitStrings.length; i++) { 119 int index = s.indexOf(unitStrings[i]); 120 if (index > 0) { 121 unitString = s.substring(index); 122 valueString = s.substring(0, index); 123 break; 124 } 125 } 126 127 try { 128 int value = Integer.parseInt(valueString); 129 130 if (unitString == null || unitString.compareTo("ms") == 0) { 131 return value; 132 } else if (unitString.compareTo("s") == 0) { 133 return value * 1000; 134 } else { 135 throw new IllegalArgumentException( 136 "Unknow time unit: " + unitString); 137 } 138 } catch (NumberFormatException e) { 139 throw new IllegalArgumentException( 140 "Could not convert interval: " + s); 141 } 142 } 143 144 public Arguments(String[] args) throws IllegalArgumentException { 145 int argc = 0; 146 147 if (args.length == 0) { 148 help = true; 149 return; 150 } 151 152 if ((args[0].compareTo("-?") == 0) 153 || (args[0].compareTo("-h") == 0) 154 || (args[0].compareTo("--help") == 0) 155 // -help: legacy. 156 || (args[0].compareTo("-help") == 0)) { 157 help = true; 158 return; 159 } else if (args[0].compareTo("-options") == 0) { 160 options = true; 161 return; 162 } else if (args[0].compareTo("-list") == 0) { 163 list = true; 164 if (args.length > 2) { 165 throw new IllegalArgumentException("invalid argument count"); 166 } 167 // list can take one arg - a vmid - fall through for arg processing 168 argc++; 169 } 170 171 for ( ; (argc < args.length) && (args[argc].startsWith("-")); argc++) { 172 String arg = args[argc]; 173 174 if (arg.compareTo("-a") == 0) { 175 comparator = new AscendingMonitorComparator(); 176 } else if (arg.compareTo("-d") == 0) { 177 comparator = new DescendingMonitorComparator(); 178 } else if (arg.compareTo("-t") == 0) { 179 timestamp = true; 180 } else if (arg.compareTo("-v") == 0) { 181 verbose = true; 182 } else if ((arg.compareTo("-constants") == 0) 183 || (arg.compareTo("-c") == 0)) { 184 constants = true; 185 } else if ((arg.compareTo("-strings") == 0) 186 || (arg.compareTo("-s") == 0)) { 187 strings = true; 188 } else if (arg.startsWith("-h")) { 189 String value; 190 if (arg.compareTo("-h") != 0) { 191 value = arg.substring(2); 192 } else { 193 argc++; 194 if (argc >= args.length) { 195 throw new IllegalArgumentException( 196 "-h requires an integer argument"); 197 } 198 value = args[argc]; 199 } 200 try { 201 headerRate = Integer.parseInt(value); 202 } catch (NumberFormatException e) { 203 headerRate = -1; 204 } 205 if (headerRate < 0) { 206 throw new IllegalArgumentException( 207 "illegal -h argument: " + value); 208 } 209 } else if (arg.startsWith("-name")) { 210 if (arg.startsWith("-name=")) { 211 names = arg.substring(7); 212 } else { 213 argc++; 214 if (argc >= args.length) { 215 throw new IllegalArgumentException( 216 "option argument expected"); 217 } 218 names = args[argc]; 219 } 220 } else { 221 /* 222 * there are scenarios here: special jstat_options file option 223 * or the rare case of a negative lvmid. The negative lvmid 224 * can occur in some operating environments (such as Windows 225 * 95/98/ME), so we provide for this case here by checking if 226 * the argument has any numerical characters. This assumes that 227 * there are no special jstat_options that contain numerical 228 * characters in their name. 229 */ 230 231 // extract the lvmid part of possible lvmid@host.domain:port 232 String lvmidStr = null; 233 int at_index = args[argc].indexOf('@'); 234 if (at_index < 0) { 235 lvmidStr = args[argc]; 236 } else { 237 lvmidStr = args[argc].substring(0, at_index); 238 } 239 240 // try to parse the lvmid part as an integer 241 try { 242 int vmid = Integer.parseInt(lvmidStr); 243 // it parsed, assume a negative lvmid and continue 244 break; 245 } catch (NumberFormatException nfe) { 246 // it didn't parse. check for the -snap or jstat_options 247 // file options. 248 if ((argc == 0) && (args[argc].compareTo("-snap") == 0)) { 249 snap = true; 250 } else if (argc == 0) { 251 specialOption = args[argc].substring(1); 252 } else { 253 throw new IllegalArgumentException( 254 "illegal argument: " + args[argc]); 255 } 256 } 257 } 258 } 259 260 // prevent 'jstat <pid>' from being accepted as a valid argument 261 if (!(specialOption != null || list || snap || names != null)) { 262 throw new IllegalArgumentException("-<option> required"); 263 } 264 265 switch (args.length - argc) { 266 case 3: 267 if (snap) { 268 throw new IllegalArgumentException("invalid argument count"); 269 } 270 try { 271 count = Integer.parseInt(args[args.length-1]); 272 } catch (NumberFormatException e) { 273 throw new IllegalArgumentException("illegal count value: " 274 + args[args.length-1]); 275 } 276 interval = toMillis(args[args.length-2]); 277 vmIdString = args[args.length-3]; 278 break; 279 case 2: 280 if (snap) { 281 throw new IllegalArgumentException("invalid argument count"); 282 } 283 interval = toMillis(args[args.length-1]); 284 vmIdString = args[args.length-2]; 285 break; 286 case 1: 287 vmIdString = args[args.length-1]; 288 break; 289 case 0: 290 if (!list) { 291 throw new IllegalArgumentException("invalid argument count"); 292 } 293 break; 294 default: 295 throw new IllegalArgumentException("invalid argument count"); 296 } 297 298 // set count and interval to their default values if not set above. 299 if (count == -1 && interval == -1) { 300 // default is for a single sample 301 count = 1; 302 interval = 0; 303 } 304 305 // validate arguments 306 if (comparator == null) { 307 comparator = new AscendingMonitorComparator(); 308 } 309 310 // allow ',' characters to separate names, convert to '|' chars 311 names = (names == null) ? ALL_NAMES : names.replace(',', '|'); 312 313 // verify that the given pattern parses without errors 314 try { 315 Pattern pattern = Pattern.compile(names); 316 } catch (PatternSyntaxException e) { 317 throw new IllegalArgumentException("Bad name pattern: " 318 + e.getMessage()); 319 } 320 321 // verify that the special option is valid and get it's formatter 322 if (specialOption != null) { 323 OptionFinder finder = new OptionFinder(optionsSources()); 324 optionFormat = finder.getOptionFormat(specialOption, timestamp); 325 if (optionFormat == null) { 326 throw new IllegalArgumentException("Unknown option: -" 327 + specialOption); 328 } 329 } 330 331 // verify that the vm identifier is valied 332 try { 333 vmId = new VmIdentifier(vmIdString); 334 } catch (URISyntaxException e) { 335 IllegalArgumentException iae = new IllegalArgumentException( 336 "Malformed VM Identifier: " + vmIdString); 337 iae.initCause(e); 338 throw iae; 339 } 340 } 341 342 public Comparator<Monitor> comparator() { 343 return comparator; 344 } 345 346 public boolean isHelp() { 347 return help; 348 } 349 350 public boolean isList() { 351 return list; 352 } 353 354 public boolean isSnap() { 355 return snap; 356 } 357 358 public boolean isOptions() { 359 return options; 360 } 361 362 public boolean isVerbose() { 363 return verbose; 364 } 365 366 public boolean printConstants() { 367 return constants; 368 } 369 370 public boolean isConstantsOnly() { 371 return constantsOnly; 372 } 373 374 public boolean printStrings() { 375 return strings; 376 } 377 378 public boolean showUnsupported() { 379 return showUnsupported; 380 } 381 382 public int headerRate() { 383 return headerRate; 384 } 385 386 public String counterNames() { 387 return names; 388 } 389 390 public VmIdentifier vmId() { 391 return vmId; 392 } 393 394 public String vmIdString() { 395 return vmIdString; 396 } 397 398 public int sampleInterval() { 399 return interval; 400 } 401 402 public int sampleCount() { 403 return count; 404 } 405 406 public boolean isTimestamp() { 407 return timestamp; 408 } 409 410 public boolean isSpecialOption() { 411 return specialOption != null; 412 } 413 414 public String specialOption() { 415 return specialOption; 416 } 417 418 public OptionFormat optionFormat() { 419 return optionFormat; 420 } 421 422 public List<URL> optionsSources() { 423 List<URL> sources = new ArrayList<URL>(); 424 int i = 0; 425 426 String filename = OPTIONS_FILENAME; 427 428 try { 429 String userHome = System.getProperty("user.home"); 430 String userDir = userHome + "/" + JVMSTAT_USERDIR; 431 File home = new File(userDir + "/" + filename); 432 sources.add(home.toURI().toURL()); 433 } catch (Exception e) { 434 if (debug) { 435 System.err.println(e.getMessage()); 436 e.printStackTrace(); 437 } 438 throw new IllegalArgumentException("Internal Error: Bad URL: " 439 + e.getMessage()); 440 } 441 URL u = this.getClass().getResource("resources/" + filename); 442 assert u != null; 443 sources.add(u); 444 445 if (showUnsupported) { 446 u = this.getClass().getResource("resources/" + UNSUPPORTED_OPTIONS_FILENAME); 447 assert u != null; 448 sources.add(u); 449 } 450 return sources; 451 } 452 }