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