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