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
36CmdLine::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
64bool 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
144bool 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
149void 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
168void 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
187void 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
220GenDCmdArgument* 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
232void 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
253void 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
299void 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
312void 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
325int 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
340GrowableArray<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
356GrowableArray<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
378DCmdFactory* DCmdFactory::_DCmdFactoryList = NULL;
379bool DCmdFactory::_has_pending_jmx_notification = false;
380
381void 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
411void DCmdWithParser::parse(CmdLine* line, char delim, TRAPS) {
412  _dcmdparser.parse(line, delim, CHECK);
413}
414
415void DCmdWithParser::print_help(const char* name) {
416  _dcmdparser.print_help(output(), name);
417}
418
419void DCmdWithParser::reset(TRAPS) {
420  _dcmdparser.reset(CHECK);
421}
422
423void DCmdWithParser::cleanup() {
424  _dcmdparser.cleanup();
425}
426
427GrowableArray<const char*>* DCmdWithParser::argument_name_array() {
428  return _dcmdparser.argument_name_array();
429}
430
431GrowableArray<DCmdArgumentInfo*>* DCmdWithParser::argument_info_array() {
432  return _dcmdparser.argument_info_array();
433}
434
435void 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
441void 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}
449void 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
490Mutex* DCmdFactory::_dcmdFactory_lock = new Mutex(Mutex::leaf, "DCmdFactory", true, Monitor::_safepoint_check_never);
491bool DCmdFactory::_send_jmx_notification = false;
492
493DCmdFactory* 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
510int 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
521DCmd* 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
535DCmd* 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
549GrowableArray<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
562GrowableArray<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}
577