1 /* 2 * Copyright (c) 2011, 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. 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 */ 24 25 #include "precompiled.hpp" 26 #include "memory/oopFactory.hpp" 27 #include "runtime/javaCalls.hpp" 28 #include "runtime/mutexLocker.hpp" 29 #include "services/diagnosticArgument.hpp" 30 #include "services/diagnosticFramework.hpp" 31 #include "services/management.hpp" 32 33 CmdLine::CmdLine(const char* line, size_t len, bool no_command_name) { 34 assert(line != NULL, "Command line string should not be NULL"); 35 const char* line_end; 36 const char* cmd_end; 37 38 _cmd = line; 39 line_end = &line[len]; 40 41 // Skip whitespace in the beginning of the line. 42 while (_cmd < line_end && isspace((int) _cmd[0])) { 43 _cmd++; 44 } 45 cmd_end = _cmd; 46 47 if (no_command_name) { 48 _cmd = NULL; 49 _cmd_len = 0; 50 } else { 51 // Look for end of the command name 52 while (cmd_end < line_end && !isspace((int) cmd_end[0])) { 53 cmd_end++; 54 } 55 _cmd_len = cmd_end - _cmd; 56 } 57 _args = cmd_end; 58 _args_len = line_end - _args; 59 } 60 61 bool DCmdArgIter::next(TRAPS) { 62 if (_len == 0) return false; 63 // skipping delimiters 64 while (_cursor < _len - 1 && _buffer[_cursor] == _delim) { 65 _cursor++; 66 } 67 // handling end of command line 68 if (_cursor == _len - 1 && _buffer[_cursor] == _delim) { 69 _key_addr = &_buffer[_cursor]; 70 _key_len = 0; 71 _value_addr = &_buffer[_cursor]; 72 _value_len = 0; 73 return false; 74 } 75 // extracting first item, argument or option name 76 _key_addr = &_buffer[_cursor]; 77 bool arg_had_quotes = false; 78 while (_cursor <= _len - 1 && _buffer[_cursor] != '=' && _buffer[_cursor] != _delim) { 79 // argument can be surrounded by single or double quotes 80 if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') { 81 _key_addr++; 82 char quote = _buffer[_cursor]; 83 arg_had_quotes = true; 84 while (_cursor < _len - 1) { 85 _cursor++; 86 if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') { 87 break; 88 } 89 } 90 if (_buffer[_cursor] != quote) { 91 THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), 92 "Format error in diagnostic command arguments", false); 93 } 94 break; 95 } 96 _cursor++; 97 } 98 _key_len = &_buffer[_cursor] - _key_addr; 99 if (arg_had_quotes) { 100 // if the argument was quoted, we need to step past the last quote here 101 _cursor++; 102 } 103 // check if the argument has the <key>=<value> format 104 if (_cursor <= _len -1 && _buffer[_cursor] == '=') { 105 _cursor++; 106 _value_addr = &_buffer[_cursor]; 107 bool value_had_quotes = false; 108 // extract the value 109 while (_cursor <= _len - 1 && _buffer[_cursor] != _delim) { 110 // value can be surrounded by simple or double quotes 111 if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') { 112 _value_addr++; 113 char quote = _buffer[_cursor]; 114 value_had_quotes = true; 115 while (_cursor < _len - 1) { 116 _cursor++; 117 if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') { 118 break; 119 } 120 } 121 if (_buffer[_cursor] != quote) { 122 THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), 123 "Format error in diagnostic command arguments", false); 124 } 125 break; 126 } 127 _cursor++; 128 } 129 _value_len = &_buffer[_cursor] - _value_addr; 130 if (value_had_quotes) { 131 // if the value was quoted, we need to step past the last quote here 132 _cursor++; 133 } 134 } else { 135 _value_addr = NULL; 136 _value_len = 0; 137 } 138 return _key_len != 0; 139 } 140 141 bool DCmdInfo::by_name(void* cmd_name, DCmdInfo* info) { 142 if (info == NULL) return false; 143 return strcmp((const char*)cmd_name, info->name()) == 0; 144 } 145 146 void DCmdParser::add_dcmd_option(GenDCmdArgument* arg) { 147 assert(arg != NULL, "Sanity"); 148 if (_options == NULL) { 149 _options = arg; 150 } else { 151 GenDCmdArgument* o = _options; 152 while (o->next() != NULL) { 153 o = o->next(); 154 } 155 o->set_next(arg); 156 } 157 arg->set_next(NULL); 158 Thread* THREAD = Thread::current(); 159 arg->init_value(THREAD); 160 if (HAS_PENDING_EXCEPTION) { 161 fatal("Initialization must be successful"); 162 } 163 } 164 165 void DCmdParser::add_dcmd_argument(GenDCmdArgument* arg) { 166 assert(arg != NULL, "Sanity"); 167 if (_arguments_list == NULL) { 168 _arguments_list = arg; 169 } else { 170 GenDCmdArgument* a = _arguments_list; 171 while (a->next() != NULL) { 172 a = a->next(); 173 } 174 a->set_next(arg); 175 } 176 arg->set_next(NULL); 177 Thread* THREAD = Thread::current(); 178 arg->init_value(THREAD); 179 if (HAS_PENDING_EXCEPTION) { 180 fatal("Initialization must be successful"); 181 } 182 } 183 184 void DCmdParser::parse(CmdLine* line, char delim, TRAPS) { 185 GenDCmdArgument* next_argument = _arguments_list; 186 DCmdArgIter iter(line->args_addr(), line->args_len(), delim); 187 bool cont = iter.next(CHECK); 188 while (cont) { 189 GenDCmdArgument* arg = lookup_dcmd_option(iter.key_addr(), 190 iter.key_length()); 191 if (arg != NULL) { 192 arg->read_value(iter.value_addr(), iter.value_length(), CHECK); 193 } else { 194 if (next_argument != NULL) { 195 arg = next_argument; 196 arg->read_value(iter.key_addr(), iter.key_length(), CHECK); 197 next_argument = next_argument->next(); 198 } else { 199 const size_t buflen = 120; 200 const size_t argbuflen = 30; 201 char buf[buflen]; 202 char argbuf[argbuflen]; 203 size_t len = MIN2<size_t>(iter.key_length(), argbuflen - 1); 204 205 strncpy(argbuf, iter.key_addr(), len); 206 argbuf[len] = '\0'; 207 jio_snprintf(buf, buflen - 1, "Unknown argument '%s' in diagnostic command.", argbuf); 208 209 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf); 210 } 211 } 212 cont = iter.next(CHECK); 213 } 214 check(CHECK); 215 } 216 217 GenDCmdArgument* DCmdParser::lookup_dcmd_option(const char* name, size_t len) { 218 GenDCmdArgument* arg = _options; 219 while (arg != NULL) { 220 if (strlen(arg->name()) == len && 221 strncmp(name, arg->name(), len) == 0) { 222 return arg; 223 } 224 arg = arg->next(); 225 } 226 return NULL; 227 } 228 229 void DCmdParser::check(TRAPS) { 230 const size_t buflen = 256; 231 char buf[buflen]; 232 GenDCmdArgument* arg = _arguments_list; 233 while (arg != NULL) { 234 if (arg->is_mandatory() && !arg->has_value()) { 235 jio_snprintf(buf, buflen - 1, "The argument '%s' is mandatory.", arg->name()); 236 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf); 237 } 238 arg = arg->next(); 239 } 240 arg = _options; 241 while (arg != NULL) { 242 if (arg->is_mandatory() && !arg->has_value()) { 243 jio_snprintf(buf, buflen - 1, "The option '%s' is mandatory.", arg->name()); 244 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf); 245 } 246 arg = arg->next(); 247 } 248 } 249 250 void DCmdParser::print_help(outputStream* out, const char* cmd_name) { 251 out->print("Syntax : %s %s", cmd_name, _options == NULL ? "" : "[options]"); 252 GenDCmdArgument* arg = _arguments_list; 253 while (arg != NULL) { 254 if (arg->is_mandatory()) { 255 out->print(" <%s>", arg->name()); 256 } else { 257 out->print(" [<%s>]", arg->name()); 258 } 259 arg = arg->next(); 260 } 261 out->cr(); 262 if (_arguments_list != NULL) { 263 out->print_cr("\nArguments:"); 264 arg = _arguments_list; 265 while (arg != NULL) { 266 out->print("\t%s : %s %s (%s, ", arg->name(), 267 arg->is_mandatory() ? "" : "[optional]", 268 arg->description(), arg->type()); 269 if (arg->has_default()) { 270 out->print("%s", arg->default_string()); 271 } else { 272 out->print("no default value"); 273 } 274 out->print_cr(")"); 275 arg = arg->next(); 276 } 277 } 278 if (_options != NULL) { 279 out->print_cr("\nOptions: (options must be specified using the <key> or <key>=<value> syntax)"); 280 arg = _options; 281 while (arg != NULL) { 282 out->print("\t%s : %s %s (%s, ", arg->name(), 283 arg->is_mandatory() ? "" : "[optional]", 284 arg->description(), arg->type()); 285 if (arg->has_default()) { 286 out->print("%s", arg->default_string()); 287 } else { 288 out->print("no default value"); 289 } 290 out->print_cr(")"); 291 arg = arg->next(); 292 } 293 } 294 } 295 296 void DCmdParser::reset(TRAPS) { 297 GenDCmdArgument* arg = _arguments_list; 298 while (arg != NULL) { 299 arg->reset(CHECK); 300 arg = arg->next(); 301 } 302 arg = _options; 303 while (arg != NULL) { 304 arg->reset(CHECK); 305 arg = arg->next(); 306 } 307 } 308 309 void DCmdParser::cleanup() { 310 GenDCmdArgument* arg = _arguments_list; 311 while (arg != NULL) { 312 arg->cleanup(); 313 arg = arg->next(); 314 } 315 arg = _options; 316 while (arg != NULL) { 317 arg->cleanup(); 318 arg = arg->next(); 319 } 320 } 321 322 int DCmdParser::num_arguments() { 323 GenDCmdArgument* arg = _arguments_list; 324 int count = 0; 325 while (arg != NULL) { 326 count++; 327 arg = arg->next(); 328 } 329 arg = _options; 330 while (arg != NULL) { 331 count++; 332 arg = arg->next(); 333 } 334 return count; 335 } 336 337 GrowableArray<const char *>* DCmdParser::argument_name_array() { 338 int count = num_arguments(); 339 GrowableArray<const char *>* array = new GrowableArray<const char *>(count); 340 GenDCmdArgument* arg = _arguments_list; 341 while (arg != NULL) { 342 array->append(arg->name()); 343 arg = arg->next(); 344 } 345 arg = _options; 346 while (arg != NULL) { 347 array->append(arg->name()); 348 arg = arg->next(); 349 } 350 return array; 351 } 352 353 GrowableArray<DCmdArgumentInfo*>* DCmdParser::argument_info_array() { 354 int count = num_arguments(); 355 GrowableArray<DCmdArgumentInfo*>* array = new GrowableArray<DCmdArgumentInfo *>(count); 356 int idx = 0; 357 GenDCmdArgument* arg = _arguments_list; 358 while (arg != NULL) { 359 array->append(new DCmdArgumentInfo(arg->name(), arg->description(), 360 arg->type(), arg->default_string(), arg->is_mandatory(), 361 false, arg->allow_multiple(), idx)); 362 idx++; 363 arg = arg->next(); 364 } 365 arg = _options; 366 while (arg != NULL) { 367 array->append(new DCmdArgumentInfo(arg->name(), arg->description(), 368 arg->type(), arg->default_string(), arg->is_mandatory(), 369 true, arg->allow_multiple())); 370 arg = arg->next(); 371 } 372 return array; 373 } 374 375 DCmdFactory* DCmdFactory::_DCmdFactoryList = NULL; 376 bool DCmdFactory::_has_pending_jmx_notification = false; 377 378 void DCmd::parse_and_execute(DCmdSource source, outputStream* out, 379 const char* cmdline, char delim, TRAPS) { 380 381 if (cmdline == NULL) return; // Nothing to do! 382 DCmdIter iter(cmdline, '\n'); 383 384 int count = 0; 385 while (iter.has_next()) { 386 if(source == DCmd_Source_MBean && count > 0) { 387 // When diagnostic commands are invoked via JMX, each command line 388 // must contains one and only one command because of the Permission 389 // checks performed by the DiagnosticCommandMBean 390 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 391 "Invalid syntax"); 392 } 393 CmdLine line = iter.next(); 394 if (line.is_stop()) { 395 break; 396 } 397 if (line.is_executable()) { 398 DCmd* command = DCmdFactory::create_local_DCmd(source, line, out, CHECK); 399 assert(command != NULL, "command error must be handled before this line"); 400 DCmdMark mark(command); 401 command->parse(&line, delim, CHECK); 402 command->execute(source, CHECK); 403 } 404 count++; 405 } 406 } 407 408 void DCmdWithParser::parse(CmdLine* line, char delim, TRAPS) { 409 _dcmdparser.parse(line, delim, CHECK); 410 } 411 412 void DCmdWithParser::print_help(const char* name) { 413 _dcmdparser.print_help(output(), name); 414 } 415 416 void DCmdWithParser::reset(TRAPS) { 417 _dcmdparser.reset(CHECK); 418 } 419 420 void DCmdWithParser::cleanup() { 421 _dcmdparser.cleanup(); 422 } 423 424 GrowableArray<const char*>* DCmdWithParser::argument_name_array() { 425 return _dcmdparser.argument_name_array(); 426 } 427 428 GrowableArray<DCmdArgumentInfo*>* DCmdWithParser::argument_info_array() { 429 return _dcmdparser.argument_info_array(); 430 } 431 432 void DCmdFactory::push_jmx_notification_request() { 433 MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); 434 _has_pending_jmx_notification = true; 435 Service_lock->notify_all(); 436 } 437 438 void DCmdFactory::send_notification(TRAPS) { 439 DCmdFactory::send_notification_internal(THREAD); 440 // Clearing pending exception to avoid premature termination of 441 // the service thread 442 if (HAS_PENDING_EXCEPTION) { 443 CLEAR_PENDING_EXCEPTION; 444 } 445 } 446 void DCmdFactory::send_notification_internal(TRAPS) { 447 ResourceMark rm(THREAD); 448 HandleMark hm(THREAD); 449 bool notif = false; 450 { 451 MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); 452 notif = _has_pending_jmx_notification; 453 _has_pending_jmx_notification = false; 454 } 455 if (notif) { 456 457 Klass* k = Management::sun_management_ManagementFactoryHelper_klass(CHECK); 458 instanceKlassHandle mgmt_factory_helper_klass(THREAD, k); 459 460 JavaValue result(T_OBJECT); 461 JavaCalls::call_static(&result, 462 mgmt_factory_helper_klass, 463 vmSymbols::getDiagnosticCommandMBean_name(), 464 vmSymbols::getDiagnosticCommandMBean_signature(), 465 CHECK); 466 467 instanceOop m = (instanceOop) result.get_jobject(); 468 instanceHandle dcmd_mbean_h(THREAD, m); 469 470 Klass* k2 = Management::sun_management_DiagnosticCommandImpl_klass(CHECK); 471 instanceKlassHandle dcmd_mbean_klass(THREAD, k2); 472 473 if (!dcmd_mbean_h->is_a(k2)) { 474 THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), 475 "ManagementFactory.getDiagnosticCommandMBean didn't return a DiagnosticCommandMBean instance"); 476 } 477 478 JavaValue result2(T_VOID); 479 JavaCallArguments args2(dcmd_mbean_h); 480 481 JavaCalls::call_virtual(&result2, 482 dcmd_mbean_klass, 483 vmSymbols::createDiagnosticFrameworkNotification_name(), 484 vmSymbols::void_method_signature(), 485 &args2, 486 CHECK); 487 } 488 } 489 490 Mutex* DCmdFactory::_dcmdFactory_lock = new Mutex(Mutex::leaf, "DCmdFactory", true); 491 bool DCmdFactory::_send_jmx_notification = false; 492 493 DCmdFactory* DCmdFactory::factory(DCmdSource source, const char* name, size_t len) { 494 MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); 495 DCmdFactory* factory = _DCmdFactoryList; 496 while (factory != NULL) { 497 if (strlen(factory->name()) == len && 498 strncmp(name, factory->name(), len) == 0) { 499 if(factory->export_flags() & source) { 500 return factory; 501 } else { 502 return NULL; 503 } 504 } 505 factory = factory->_next; 506 } 507 return NULL; 508 } 509 510 int DCmdFactory::register_DCmdFactory(DCmdFactory* factory) { 511 MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); 512 factory->_next = _DCmdFactoryList; 513 _DCmdFactoryList = factory; 514 if (_send_jmx_notification && !factory->_hidden 515 && (factory->_export_flags & DCmd_Source_MBean)) { 516 DCmdFactory::push_jmx_notification_request(); 517 } 518 return 0; // Actually, there's no checks for duplicates 519 } 520 521 DCmd* DCmdFactory::create_global_DCmd(DCmdSource source, CmdLine &line, 522 outputStream* out, TRAPS) { 523 DCmdFactory* f = factory(source, line.cmd_addr(), line.cmd_len()); 524 if (f != NULL) { 525 if (f->is_enabled()) { 526 THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), 527 f->disabled_message()); 528 } 529 return f->create_Cheap_instance(out); 530 } 531 THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), 532 "Unknown diagnostic command"); 533 } 534 535 DCmd* DCmdFactory::create_local_DCmd(DCmdSource source, CmdLine &line, 536 outputStream* out, TRAPS) { 537 DCmdFactory* f = factory(source, line.cmd_addr(), line.cmd_len()); 538 if (f != NULL) { 539 if (!f->is_enabled()) { 540 THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), 541 f->disabled_message()); 542 } 543 return f->create_resource_instance(out); 544 } 545 THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), 546 "Unknown diagnostic command"); 547 } 548 549 GrowableArray<const char*>* DCmdFactory::DCmd_list(DCmdSource source) { 550 MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); 551 GrowableArray<const char*>* array = new GrowableArray<const char*>(); 552 DCmdFactory* factory = _DCmdFactoryList; 553 while (factory != NULL) { 554 if (!factory->is_hidden() && (factory->export_flags() & source)) { 555 array->append(factory->name()); 556 } 557 factory = factory->next(); 558 } 559 return array; 560 } 561 562 GrowableArray<DCmdInfo*>* DCmdFactory::DCmdInfo_list(DCmdSource source ) { 563 MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); 564 GrowableArray<DCmdInfo*>* array = new GrowableArray<DCmdInfo*>(); 565 DCmdFactory* factory = _DCmdFactoryList; 566 while (factory != NULL) { 567 if (!factory->is_hidden() && (factory->export_flags() & source)) { 568 array->append(new DCmdInfo(factory->name(), 569 factory->description(), factory->impact(), 570 factory->permission(), factory->num_arguments(), 571 factory->is_enabled())); 572 } 573 factory = factory->next(); 574 } 575 return array; 576 }