diagnosticFramework.hpp 14.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef SHARE_VM_SERVICES_DIAGNOSTICFRAMEWORK_HPP
#define SHARE_VM_SERVICES_DIAGNOSTICFRAMEWORK_HPP

#include "classfile/vmSymbols.hpp"
#include "memory/allocation.hpp"
#include "runtime/arguments.hpp"
#include "runtime/os.hpp"
#include "runtime/vm_version.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/ostream.hpp"


// CmdLine is the class used to handle a command line containing a single
// diagnostic command and its arguments. It provides methods to access the
// command name and the beginning of the arguments. The class is also
// able to identify commented command lines and the "stop" keyword
class CmdLine : public StackObj {
private:
  const char* _cmd;
  size_t      _cmd_len;
  const char* _args;
  size_t      _args_len;
public:
  CmdLine(const char* line, size_t len, bool no_command_name);
  const char* args_addr() const { return _args; }
  size_t args_len() const { return _args_len; }
  const char* cmd_addr() const { return _cmd; }
  size_t cmd_len() const { return _cmd_len; }
  bool is_empty() { return _cmd_len == 0; }
  bool is_executable() { return is_empty() || _cmd[0] != '#'; }
  bool is_stop() { return !is_empty() && strncmp("stop", _cmd, _cmd_len) == 0; }
};

// Iterator class taking a character string in input and returning a CmdLine
// instance for each command line. The argument delimiter has to be specified.
class DCmdIter : public StackObj {
  friend class DCmd;
private:
  const char* _str;
  char        _delim;
  size_t      _len;
  size_t      _cursor;
public:

  DCmdIter(const char* str, char delim) {
    _str = str;
    _delim = delim;
    _len = strlen(str);
    _cursor = 0;
  }
  bool has_next() { return _cursor < _len; }
  CmdLine next() {
    assert(_cursor <= _len, "Cannot iterate more");
    size_t n = _cursor;
    while (n < _len && _str[n] != _delim) n++;
    CmdLine line(&(_str[_cursor]), n - _cursor, false);
    _cursor = n + 1;
    // The default copy constructor of CmdLine is used to return a CmdLine
    // instance to the caller.
    return line;
  }
};

// Iterator class to iterate over diagnostic command arguments
class DCmdArgIter : public ResourceObj {
  const char* _buffer;
  size_t      _len;
  size_t      _cursor;
  const char* _key_addr;
  size_t      _key_len;
  const char* _value_addr;
  size_t      _value_len;
  char        _delim;
public:
  DCmdArgIter(const char* buf, size_t len, char delim) {
    _buffer = buf;
    _len = len;
    _delim = delim;
    _cursor = 0;
  }
  bool next(TRAPS);
  const char* key_addr() { return _key_addr; }
  size_t key_length() { return _key_len; }
  const char* value_addr() { return _value_addr; }
  size_t value_length() { return _value_len; }
};

// A DCmdInfo instance provides a description of a diagnostic command. It is
// used to export the description to the JMX interface of the framework.
class DCmdInfo : public ResourceObj {
protected:
  const char* _name;
  const char* _description;
  const char* _impact;
  int         _num_arguments;
  bool        _is_enabled;
public:
  DCmdInfo(const char* name,
          const char* description,
          const char* impact,
          int num_arguments,
          bool enabled) {
    this->_name = name;
    this->_description = description;
    this->_impact = impact;
    this->_num_arguments = num_arguments;
    this->_is_enabled = enabled;
  }
  const char* name() const { return _name; }
  const char* description() const { return _description; }
  const char* impact() const { return _impact; }
  int num_arguments() const { return _num_arguments; }
  bool is_enabled() const { return _is_enabled; }

  static bool by_name(void* name, DCmdInfo* info);
};

// A DCmdArgumentInfo instance provides a description of a diagnostic command
// argument. It is used to export the description to the JMX interface of the
// framework.
class DCmdArgumentInfo : public ResourceObj {
protected:
  const char* _name;
  const char* _description;
  const char* _type;
  const char* _default_string;
  bool        _mandatory;
  bool        _option;
  int         _position;
public:
  DCmdArgumentInfo(const char* name, const char* description, const char* type,
                   const char* default_string, bool mandatory, bool option) {
    this->_name = name;
    this->_description = description;
    this->_type = type;
    this->_default_string = default_string;
    this->_option = option;
    this->_mandatory = mandatory;
    this->_option = option;
    this->_position = -1;
  }
  DCmdArgumentInfo(const char* name, const char* description, const char* type,
                   const char* default_string, bool mandatory, bool option,
                   int position) {
    this->_name = name;
    this->_description = description;
    this->_type = type;
    this->_default_string = default_string;
    this->_option = option;
    this->_mandatory = mandatory;
    this->_option = option;
    this->_position = position;
  }
  const char* name() const { return _name; }
  const char* description() const { return _description; }
  const char* type() const { return _type; }
  const char* default_string() const { return _default_string; }
  bool is_mandatory() const { return _mandatory; }
  bool is_option() const { return _option; }
  int position() const { return _position; }
};

// The DCmdParser class can be used to create an argument parser for a
// diagnostic command. It is not mandatory to use it to parse arguments.
class DCmdParser {
private:
  GenDCmdArgument* _options;
  GenDCmdArgument* _arguments_list;
  char             _delim;
public:
  DCmdParser() {
    _options = NULL;
    _arguments_list = NULL;
  }
  void add_dcmd_option(GenDCmdArgument* arg);
  void add_dcmd_argument(GenDCmdArgument* arg);
  GenDCmdArgument* lookup_dcmd_option(const char* name, size_t len);
  GenDCmdArgument* arguments_list() { return _arguments_list; };
  void check(TRAPS);
  void parse(CmdLine* line, char delim, TRAPS);
  void print_help(outputStream* out, const char* cmd_name);
  void reset(TRAPS);
  void cleanup();
  int num_arguments();
  GrowableArray<const char*>* argument_name_array();
  GrowableArray<DCmdArgumentInfo*>* argument_info_array();
};

// The DCmd class is the parent class of all diagnostic commands
// Diagnostic command instances should not be instantiated directly but
// created using the associated factory. The factory can be retrieved with
// the DCmdFactory::getFactory() method.
// A diagnostic command instance can either be allocated in the resource Area
// or in the C-heap. Allocation in the resource area is recommended when the
// current thread is the only one which will access the diagnostic command
// instance. Allocation in the C-heap is required when the diagnostic command
// is accessed by several threads (for instance to perform asynchronous
// execution).
// To ensure a proper cleanup, it's highly recommended to use a DCmdMark for
// each diagnostic command instance. In case of a C-heap allocated diagnostic
// command instance, the DCmdMark must be created in the context of the last
// thread that will access the instance.
class DCmd : public ResourceObj {
protected:
  outputStream* _output;
  bool          _is_heap_allocated;
public:
  DCmd(outputStream* output, bool heap_allocated) {
    _output = output;
    _is_heap_allocated = heap_allocated;
  }

  static const char* name() { return "No Name";}
  static const char* description() { return "No Help";}
  static const char* disabled_message() { return "Diagnostic command currently disabled"; }
  static const char* impact() { return "Low: No impact"; }
  static int num_arguments() { return 0; }
  outputStream* output() { return _output; }
  bool is_heap_allocated()  { return _is_heap_allocated; }
F
fparain 已提交
244 245 246 247 248 249 250 251 252 253 254
  virtual void print_help(const char* name) {
    output()->print_cr("Syntax: %s", name);
  }
  virtual void parse(CmdLine* line, char delim, TRAPS) {
    DCmdArgIter iter(line->args_addr(), line->args_len(), delim);
    bool has_arg = iter.next(CHECK);
    if (has_arg) {
      THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
                "Unknown argument in diagnostic command");
    }
  }
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  virtual void execute(TRAPS) { }
  virtual void reset(TRAPS) { }
  virtual void cleanup() { }

  // support for the JMX interface
  virtual GrowableArray<const char*>* argument_name_array() {
    GrowableArray<const char*>* array = new GrowableArray<const char*>(0);
    return array;
  }
  virtual GrowableArray<DCmdArgumentInfo*>* argument_info_array() {
    GrowableArray<DCmdArgumentInfo*>* array = new GrowableArray<DCmdArgumentInfo*>(0);
    return array;
  }

  // main method to invoke the framework
  static void parse_and_execute(outputStream* out, const char* cmdline,
                                char delim, TRAPS);
};

F
fparain 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
class DCmdWithParser : public DCmd {
protected:
  DCmdParser _dcmdparser;
public:
  DCmdWithParser (outputStream *output, bool heap=false) : DCmd(output, heap) { }
  static const char* name() { return "No Name";}
  static const char* description() { return "No Help";}
  static const char* disabled_message() { return "Diagnostic command currently disabled"; }
  static const char* impact() { return "Low: No impact"; }
  static int num_arguments() { return 0; }
  virtual void parse(CmdLine *line, char delim, TRAPS);
  virtual void execute(TRAPS) { }
  virtual void reset(TRAPS);
  virtual void cleanup();
  virtual void print_help(const char* name);
  virtual GrowableArray<const char*>* argument_name_array();
  virtual GrowableArray<DCmdArgumentInfo*>* argument_info_array();
};

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
class DCmdMark : public StackObj {
  DCmd* _ref;
public:
  DCmdMark(DCmd* cmd) { _ref = cmd; }
  ~DCmdMark() {
    if (_ref != NULL) {
      _ref->cleanup();
      if (_ref->is_heap_allocated()) {
        delete _ref;
      }
    }
  }
};

// Diagnostic commands are not directly instantiated but created with a factory.
// Each diagnostic command class has its own factory. The DCmdFactory class also
// manages the status of the diagnostic command (hidden, enabled). A DCmdFactory
// has to be registered to make the diagnostic command available (see
// management.cpp)
class DCmdFactory: public CHeapObj {
private:
  static Mutex*       _dcmdFactory_lock;
  // Pointer to the next factory in the singly-linked list of registered
  // diagnostic commands
  DCmdFactory*        _next;
  // When disabled, a diagnostic command cannot be executed. Any attempt to
  // execute it will result in the printing of the disabled message without
  // instantiating the command.
  bool                _enabled;
  // When hidden, a diagnostic command doesn't appear in the list of commands
  // provided by the 'help' command.
  bool                _hidden;
  int                 _num_arguments;
  static DCmdFactory* _DCmdFactoryList;
public:
  DCmdFactory(int num_arguments, bool enabled, bool hidden) {
    _next = NULL;
    _enabled = enabled;
    _hidden = hidden;
    _num_arguments = num_arguments;
  }
  bool is_enabled() const { return _enabled; }
  void set_enabled(bool b) { _enabled = b; }
  bool is_hidden() const { return _hidden; }
  void set_hidden(bool b) { _hidden = b; }
  int num_arguments() { return _num_arguments; }
  DCmdFactory* next() { return _next; }
  virtual DCmd* create_Cheap_instance(outputStream* output) = 0;
  virtual DCmd* create_resource_instance(outputStream* output) = 0;
  virtual const char* name() const = 0;
  virtual const char* description() const = 0;
  virtual const char* impact() const = 0;
  virtual const char* disabled_message() const = 0;
  // Register a DCmdFactory to make a diagnostic command available.
  // Once registered, a diagnostic command must not be unregistered.
  // To prevent a diagnostic command from being executed, just set the
  // enabled flag to false.
  static int register_DCmdFactory(DCmdFactory* factory);
  static DCmdFactory* factory(const char* cmd, size_t len);
  // Returns a C-heap allocated diagnostic command for the given command line
  static DCmd* create_global_DCmd(CmdLine &line, outputStream* out, TRAPS);
  // Returns a resourceArea allocated diagnostic command for the given command line
  static DCmd* create_local_DCmd(CmdLine &line, outputStream* out, TRAPS);
  static GrowableArray<const char*>* DCmd_list();
  static GrowableArray<DCmdInfo*>* DCmdInfo_list();

  friend class HelpDCmd;
};

// Template to easily create DCmdFactory instances. See management.cpp
// where this template is used to create and register factories.
template <class DCmdClass> class DCmdFactoryImpl : public DCmdFactory {
public:
  DCmdFactoryImpl(bool enabled, bool hidden) :
    DCmdFactory(DCmdClass::num_arguments(), enabled, hidden) { }
  // Returns a C-heap allocated instance
  virtual DCmd* create_Cheap_instance(outputStream* output) {
    return new (ResourceObj::C_HEAP) DCmdClass(output, true);
  }
  // Returns a resourceArea allocated instance
  virtual DCmd* create_resource_instance(outputStream* output) {
    return new DCmdClass(output, false);
  }
  virtual const char* name() const {
    return DCmdClass::name();
  }
  virtual const char* description() const {
    return DCmdClass::description();
  }
  virtual const char* impact() const {
    return DCmdClass::impact();
  }
  virtual const char* disabled_message() const {
     return DCmdClass::disabled_message();
  }
};

#endif // SHARE_VM_SERVICES_DIAGNOSTICFRAMEWORK_HPP