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