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