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