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