# HG changeset patch # User vladidan # Date 1323911181 18000 # Node ID 31f6f10e4379b2ff57c225b69a7a8f9640e54158 # Parent 6d7d0790074d4cf83064573ac8d3942dda4a0a16# Parent 3b688d6ff3d0b19895e5ecb1d4b026a8374376b3 Merge diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/attachListener.cpp --- a/src/share/vm/services/attachListener.cpp Fri Dec 09 19:28:34 2011 -0800 +++ b/src/share/vm/services/attachListener.cpp Wed Dec 14 20:06:21 2011 -0500 @@ -34,6 +34,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/os.hpp" #include "services/attachListener.hpp" +#include "services/diagnosticCommand.hpp" #include "services/heapDumper.hpp" volatile bool AttachListener::_initialized; @@ -148,6 +149,24 @@ return JNI_OK; } +// A jcmd attach operation request was received, which will now +// dispatch to the diagnostic commands used for serviceability functions. +static jint jcmd(AttachOperation* op, outputStream* out) { + Thread* THREAD = Thread::current(); + // All the supplied jcmd arguments are stored as a single + // string (op->arg(0)). This is parsed by the Dcmd framework. + DCmd::parse_and_execute(out, op->arg(0), ' ', THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, out); + CLEAR_PENDING_EXCEPTION; + // The exception has been printed on the output stream + // If the JVM returns JNI_ERR, the attachAPI throws a generic I/O + // exception and the content of the output stream is not processed. + // By returning JNI_OK, the exception will be displayed on the client side + } + return JNI_OK; +} + #ifndef SERVICES_KERNEL // Heap dumping not supported // Implementation of "dumpheap" command. // @@ -366,6 +385,7 @@ { "inspectheap", heap_inspection }, { "setflag", set_flag }, { "printflag", print_flag }, + { "jcmd", jcmd }, { NULL, NULL } }; diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/diagnosticArgument.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/services/diagnosticArgument.cpp Wed Dec 14 20:06:21 2011 -0500 @@ -0,0 +1,112 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/thread.hpp" +#include "services/diagnosticArgument.hpp" + +void GenDCmdArgument::read_value(const char* str, size_t len, TRAPS) { + if (is_set()) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Duplicates in diagnostic command arguments"); + } + parse_value(str, len, CHECK); + set_is_set(true); +} + +template <> void DCmdArgument::parse_value(const char* str, + size_t len, TRAPS) { + if (sscanf(str, INT64_FORMAT, &_value) != 1) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Integer parsing error in diagnostic command arguments"); + } +} + +template <> void DCmdArgument::init_value(TRAPS) { + if (has_default()) { + this->parse_value(_default_string, strlen(_default_string), THREAD); + if (HAS_PENDING_EXCEPTION) { + fatal("Default string must be parsable"); + } + } else { + set_value(0); + } +} + +template <> void DCmdArgument::destroy_value() { } + +template <> void DCmdArgument::parse_value(const char* str, + size_t len, TRAPS) { + if (len == 0) { + set_value(true); + } else { + if (strcasecmp(str, "true") == 0) { + set_value(true); + } else if (strcasecmp(str, "false") == 0) { + set_value(false); + } else { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Boolean parsing error in diagnostic command arguments"); + } + } +} + +template <> void DCmdArgument::init_value(TRAPS) { + if (has_default()) { + this->parse_value(_default_string, strlen(_default_string), THREAD); + if (HAS_PENDING_EXCEPTION) { + fatal("Default string must be parsable"); + } + } else { + set_value(false); + } +} + +template <> void DCmdArgument::destroy_value() { } + +template <> void DCmdArgument::parse_value(const char* str, + size_t len, TRAPS) { + _value = NEW_C_HEAP_ARRAY(char, len+1); + strncpy(_value, str, len); + _value[len] = 0; +} + +template <> void DCmdArgument::init_value(TRAPS) { + if (has_default()) { + this->parse_value(_default_string, strlen(_default_string), THREAD); + if (HAS_PENDING_EXCEPTION) { + fatal("Default string must be parsable"); + } + } else { + set_value(NULL); + } +} + +template <> void DCmdArgument::destroy_value() { + if (_value != NULL) { + FREE_C_HEAP_ARRAY(char, _value); + set_value(NULL); + } +} diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/diagnosticArgument.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/services/diagnosticArgument.hpp Wed Dec 14 20:06:21 2011 -0500 @@ -0,0 +1,102 @@ +/* + * 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_DIAGNOSTICARGUMENT_HPP +#define SHARE_VM_SERVICES_DIAGNOSTICARGUMENT_HPP + +#include "classfile/vmSymbols.hpp" +#include "memory/allocation.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.hpp" +#include "utilities/exceptions.hpp" + +class GenDCmdArgument : public ResourceObj { +protected: + GenDCmdArgument* _next; + const char* _name; + const char* _description; + const char* _type; + const char* _default_string; + bool _is_set; + bool _is_mandatory; + GenDCmdArgument(const char* name, const char* description, const char* type, + const char* default_string, bool mandatory) { + _name = name; + _description = description; + _type = type; + _default_string = default_string; + _is_mandatory = mandatory; + _is_set = false; + }; +public: + const char* name() { return _name; } + const char* description() { return _description; } + const char* type() { return _type; } + const char* default_string() { return _default_string; } + bool is_set() { return _is_set; } + void set_is_set(bool b) { _is_set = b; } + bool is_mandatory() { return _is_mandatory; } + bool has_value() { return _is_set || _default_string != NULL; } + bool has_default() { return _default_string != NULL; } + void read_value(const char* str, size_t len, TRAPS); + virtual void parse_value(const char* str, size_t len, TRAPS) = 0; + virtual void init_value(TRAPS) = 0; + virtual void reset(TRAPS) = 0; + virtual void cleanup() = 0; + void set_next(GenDCmdArgument* arg) { + _next = arg; + } + GenDCmdArgument* next() { + return _next; + } +}; + +template class DCmdArgument: public GenDCmdArgument { +private: + ArgType _value; +public: + DCmdArgument(const char* name, const char* description, const char* type, + bool mandatory) : + GenDCmdArgument(name, description, type, NULL, mandatory) { } + DCmdArgument(const char* name, const char* description, const char* type, + bool mandatory, const char* defaultvalue) : + GenDCmdArgument(name, description, type, defaultvalue, mandatory) + { } + ~DCmdArgument() { destroy_value(); } + ArgType value() { return _value;} + void set_value(ArgType v) { _value = v; } + void reset(TRAPS) { + destroy_value(); + init_value(CHECK); + _is_set = false; + } + void cleanup() { + destroy_value(); + } + void parse_value(const char* str, size_t len, TRAPS); + void init_value(TRAPS); + void destroy_value(); +}; + +#endif /* SHARE_VM_SERVICES_DIAGNOSTICARGUMENT_HPP */ diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/diagnosticCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/services/diagnosticCommand.cpp Wed Dec 14 20:06:21 2011 -0500 @@ -0,0 +1,131 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticCommand.hpp" +#include "services/diagnosticFramework.hpp" + +HelpDCmd::HelpDCmd(outputStream* output, bool heap) : DCmd(output, heap), + _all("-all", "Show help for all commands", "BOOLEAN", false, "false"), + _cmd("command name", "The name of the command for which we want help", + "STRING", false) { + _dcmdparser.add_dcmd_option(&_all); + _dcmdparser.add_dcmd_argument(&_cmd); +}; + +void HelpDCmd::parse(CmdLine* line, char delim, TRAPS) { + _dcmdparser.parse(line, delim, CHECK); +} + +void HelpDCmd::print_help(outputStream* out) { + _dcmdparser.print_help(out, name()); +} + +void HelpDCmd::execute(TRAPS) { + if (_all.value()) { + GrowableArray* cmd_list = DCmdFactory::DCmd_list(); + for (int i = 0; i < cmd_list->length(); i++) { + DCmdFactory* factory = DCmdFactory::factory(cmd_list->at(i), + strlen(cmd_list->at(i))); + if (!factory->is_hidden()) { + output()->print_cr("%s%s", factory->name(), + factory->is_enabled() ? "" : " [disabled]"); + output()->print_cr("\t%s", factory->description()); + output()->cr(); + } + factory = factory->next(); + } + } else if (_cmd.has_value()) { + DCmd* cmd = NULL; + DCmdFactory* factory = DCmdFactory::factory(_cmd.value(), + strlen(_cmd.value())); + if (factory != NULL) { + output()->print_cr("%s%s", factory->name(), + factory->is_enabled() ? "" : " [disabled]"); + output()->print_cr(factory->description()); + output()->print_cr("\nImpact: %s", factory->impact()); + cmd = factory->create_resource_instance(output()); + if (cmd != NULL) { + DCmdMark mark(cmd); + cmd->print_help(output()); + } + } else { + output()->print_cr("Help unavailable : '%s' : No such command", _cmd.value()); + } + } else { + output()->print_cr("The following commands are available:"); + GrowableArray* cmd_list = DCmdFactory::DCmd_list(); + for (int i = 0; i < cmd_list->length(); i++) { + DCmdFactory* factory = DCmdFactory::factory(cmd_list->at(i), + strlen(cmd_list->at(i))); + if (!factory->is_hidden()) { + output()->print_cr("%s%s", factory->name(), + factory->is_enabled() ? "" : " [disabled]"); + } + factory = factory->_next; + } + output()->print_cr("\nFor more information about a specific command use 'help '."); + } +} + +void HelpDCmd::reset(TRAPS) { + _dcmdparser.reset(CHECK); +} + +void HelpDCmd::cleanup() { + _dcmdparser.cleanup(); +} + +int HelpDCmd::num_arguments() { + ResourceMark rm; + HelpDCmd* dcmd = new HelpDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } else { + return 0; + } +} + +GrowableArray* HelpDCmd::argument_name_array() { + return _dcmdparser.argument_name_array(); +} + +GrowableArray* HelpDCmd::argument_info_array() { + return _dcmdparser.argument_info_array(); +} + +void VersionDCmd::execute(TRAPS) { + output()->print_cr("%s version %s", Abstract_VM_Version::vm_name(), + Abstract_VM_Version::vm_release()); + JDK_Version jdk_version = JDK_Version::current(); + if (jdk_version.update_version() > 0) { + output()->print_cr("JDK %d.%d_%02d", jdk_version.major_version(), + jdk_version.minor_version(), jdk_version.update_version()); + } else { + output()->print_cr("JDK %d.%d", jdk_version.major_version(), + jdk_version.minor_version()); + } +} diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/diagnosticCommand.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/services/diagnosticCommand.hpp Wed Dec 14 20:06:21 2011 -0500 @@ -0,0 +1,76 @@ +/* + * 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_DIAGNOSTICCOMMAND_HPP +#define SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP + +#include "runtime/arguments.hpp" +#include "classfile/vmSymbols.hpp" +#include "utilities/ostream.hpp" +#include "runtime/vm_version.hpp" +#include "runtime/vmThread.hpp" +#include "runtime/os.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticCommand.hpp" +#include "services/diagnosticFramework.hpp" + +class HelpDCmd : public DCmd { +protected: + DCmdParser _dcmdparser; + DCmdArgument _all; + DCmdArgument _cmd; +public: + HelpDCmd(outputStream* output, bool heap); + static const char* name() { return "help"; } + static const char* description() { + return "For more information about a specific command use 'help '. " + "With no argument this will show a list of available commands. " + "'help all' will show help for all commands."; + } + static const char* impact() { return "Low: "; } + static int num_arguments(); + virtual void parse(CmdLine* line, char delim, TRAPS); + virtual void execute(TRAPS); + virtual void reset(TRAPS); + virtual void cleanup(); + virtual void print_help(outputStream* out); + virtual GrowableArray* argument_name_array(); + virtual GrowableArray* argument_info_array(); +}; + +class VersionDCmd : public DCmd { +public: + VersionDCmd(outputStream* output, bool heap) : DCmd(output,heap) { } + static const char* name() { return "VM.version"; } + static const char* description() { + return "Print JVM version information."; + } + static const char* impact() { return "Low: "; } + static int num_arguments() { return 0; } + virtual void parse(CmdLine* line, char delim, TRAPS) { } + virtual void execute(TRAPS); + virtual void print_help(outputStream* out) { } +}; + +#endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/diagnosticFramework.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/services/diagnosticFramework.cpp Wed Dec 14 20:06:21 2011 -0500 @@ -0,0 +1,450 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "memory/oopFactory.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticFramework.hpp" +#include "services/management.hpp" + +CmdLine::CmdLine(const char* line, size_t len, bool no_command_name) { + assert(line != NULL, "Command line string should not be NULL"); + const char* line_end; + const char* cmd_end; + + _cmd = line; + line_end = &line[len]; + + // Skip whitespace in the beginning of the line. + while (_cmd < line_end && isspace((int) _cmd[0])) { + _cmd++; + } + cmd_end = _cmd; + + if (no_command_name) { + _cmd = NULL; + _cmd_len = 0; + } else { + // Look for end of the command name + while (cmd_end < line_end && !isspace((int) cmd_end[0])) { + cmd_end++; + } + _cmd_len = cmd_end - _cmd; + } + _args = cmd_end; + _args_len = line_end - _args; +} + +bool DCmdArgIter::next(TRAPS) { + if (_len == 0) return false; + // skipping spaces + while (_cursor < _len - 1 && isspace(_buffer[_cursor])) { + _cursor++; + } + // handling end of command line + if (_cursor >= _len - 1) { + _cursor = _len - 1; + _key_addr = &_buffer[_len - 1]; + _key_len = 0; + _value_addr = &_buffer[_len - 1]; + _value_len = 0; + return false; + } + // extracting first item, argument or option name + _key_addr = &_buffer[_cursor]; + while (_cursor <= _len - 1 && _buffer[_cursor] != '=' && _buffer[_cursor] != _delim) { + // argument can be surrounded by single or double quotes + if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') { + _key_addr++; + char quote = _buffer[_cursor]; + while (_cursor < _len - 1) { + _cursor++; + if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') { + break; + } + } + if (_buffer[_cursor] != quote) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Format error in diagnostic command arguments", false); + } + break; + } + _cursor++; + } + _key_len = &_buffer[_cursor] - _key_addr; + // check if the argument has the = format + if (_cursor <= _len -1 && _buffer[_cursor] == '=') { + _cursor++; + _value_addr = &_buffer[_cursor]; + // extract the value + while (_cursor <= _len - 1 && _buffer[_cursor] != _delim) { + // value can be surrounded by simple or double quotes + if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') { + _value_addr++; + char quote = _buffer[_cursor]; + while (_cursor < _len - 1) { + _cursor++; + if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') { + break; + } + } + if (_buffer[_cursor] != quote) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), + "Format error in diagnostic command arguments", false); + } + break; + } + _cursor++; + } + _value_len = &_buffer[_cursor] - _value_addr; + } else { + _value_addr = NULL; + _value_len = 0; + } + return _key_len != 0; +} + +bool DCmdInfo::by_name(void* cmd_name, DCmdInfo* info) { + if (info == NULL) return false; + return strcmp((const char*)cmd_name, info->name()) == 0; +} + +void DCmdParser::add_dcmd_option(GenDCmdArgument* arg) { + assert(arg != NULL, "Sanity"); + if (_options == NULL) { + _options = arg; + } else { + GenDCmdArgument* o = _options; + while (o->next() != NULL) { + o = o->next(); + } + o->set_next(arg); + } + arg->set_next(NULL); + Thread* THREAD = Thread::current(); + arg->init_value(THREAD); + if (HAS_PENDING_EXCEPTION) { + fatal("Initialization must be successful"); + } +} + +void DCmdParser::add_dcmd_argument(GenDCmdArgument* arg) { + assert(arg != NULL, "Sanity"); + if (_arguments_list == NULL) { + _arguments_list = arg; + } else { + GenDCmdArgument* a = _arguments_list; + while (a->next() != NULL) { + a = a->next(); + } + a->set_next(arg); + } + arg->set_next(NULL); + Thread* THREAD = Thread::current(); + arg->init_value(THREAD); + if (HAS_PENDING_EXCEPTION) { + fatal("Initialization must be successful"); + } +} + +void DCmdParser::parse(CmdLine* line, char delim, TRAPS) { + GenDCmdArgument* next_argument = _arguments_list; + DCmdArgIter iter(line->args_addr(), line->args_len(), delim); + bool cont = iter.next(CHECK); + while (cont) { + GenDCmdArgument* arg = lookup_dcmd_option(iter.key_addr(), + iter.key_length()); + if (arg != NULL) { + arg->read_value(iter.value_addr(), iter.value_length(), CHECK); + } else { + if (next_argument != NULL) { + arg = next_argument; + arg->read_value(iter.key_addr(), iter.key_length(), CHECK); + next_argument = next_argument->next(); + } else { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unknown argument in diagnostic command"); + } + } + cont = iter.next(CHECK); + } + check(CHECK); +} + +GenDCmdArgument* DCmdParser::lookup_dcmd_option(const char* name, size_t len) { + GenDCmdArgument* arg = _options; + while (arg != NULL) { + if (strlen(arg->name()) == len && + strncmp(name, arg->name(), len) == 0) { + return arg; + } + arg = arg->next(); + } + return NULL; +} + +void DCmdParser::check(TRAPS) { + GenDCmdArgument* arg = _arguments_list; + while (arg != NULL) { + if (arg->is_mandatory() && !arg->has_value()) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Missing argument for diagnostic command"); + } + arg = arg->next(); + } + arg = _options; + while (arg != NULL) { + if (arg->is_mandatory() && !arg->has_value()) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Missing option for diagnostic command"); + } + arg = arg->next(); + } +} + +void DCmdParser::print_help(outputStream* out, const char* cmd_name) { + out->print("\nSyntax : %s %s", cmd_name, _options == NULL ? "" : "[options]"); + GenDCmdArgument* arg = _arguments_list; + while (arg != NULL) { + if (arg->is_mandatory()) { + out->print(" <%s>", arg->name()); + } else { + out->print(" [<%s>]", arg->name()); + } + arg = arg->next(); + } + out->print_cr(""); + if (_arguments_list != NULL) { + out->print_cr("\nArguments:"); + arg = _arguments_list; + while (arg != NULL) { + out->print("\t%s : %s %s (%s, ", arg->name(), + arg->is_mandatory() ? "" : "[optional]", + arg->description(), arg->type()); + if (arg->has_default()) { + out->print(arg->default_string()); + } else { + out->print("no default value"); + } + out->print_cr(")"); + arg = arg->next(); + } + } + if (_options != NULL) { + out->print_cr("\nOptions: (options must be specified using the or = syntax)"); + arg = _options; + while (arg != NULL) { + out->print("\t%s : %s %s (%s, ", arg->name(), + arg->is_mandatory() ? "" : "[optional]", + arg->description(), arg->type()); + if (arg->has_default()) { + out->print(arg->default_string()); + } else { + out->print("no default value"); + } + out->print_cr(")"); + arg = arg->next(); + } + } +} + +void DCmdParser::reset(TRAPS) { + GenDCmdArgument* arg = _arguments_list; + while (arg != NULL) { + arg->reset(CHECK); + arg = arg->next(); + } + arg = _options; + while (arg != NULL) { + arg->reset(CHECK); + arg = arg->next(); + } +} + +void DCmdParser::cleanup() { + GenDCmdArgument* arg = _arguments_list; + while (arg != NULL) { + arg->cleanup(); + arg = arg->next(); + } + arg = _options; + while (arg != NULL) { + arg->cleanup(); + arg = arg->next(); + } +} + +int DCmdParser::num_arguments() { + GenDCmdArgument* arg = _arguments_list; + int count = 0; + while (arg != NULL) { + count++; + arg = arg->next(); + } + arg = _options; + while (arg != NULL) { + count++; + arg = arg->next(); + } + return count; +} + +GrowableArray* DCmdParser::argument_name_array() { + int count = num_arguments(); + GrowableArray* array = new GrowableArray(count); + GenDCmdArgument* arg = _arguments_list; + while (arg != NULL) { + array->append(arg->name()); + arg = arg->next(); + } + arg = _options; + while (arg != NULL) { + array->append(arg->name()); + arg = arg->next(); + } + return array; +} + +GrowableArray* DCmdParser::argument_info_array() { + int count = num_arguments(); + GrowableArray* array = new GrowableArray(count); + int idx = 0; + GenDCmdArgument* arg = _arguments_list; + while (arg != NULL) { + array->append(new DCmdArgumentInfo(arg->name(), arg->description(), + arg->type(), arg->default_string(), arg->is_mandatory(), + false, idx)); + idx++; + arg = arg->next(); + } + arg = _options; + while (arg != NULL) { + array->append(new DCmdArgumentInfo(arg->name(), arg->description(), + arg->type(), arg->default_string(), arg->is_mandatory(), + true)); + arg = arg->next(); + } + return array; +} + +DCmdFactory* DCmdFactory::_DCmdFactoryList = NULL; + +void DCmd::parse_and_execute(outputStream* out, const char* cmdline, + char delim, TRAPS) { + + if (cmdline == NULL) return; // Nothing to do! + DCmdIter iter(cmdline, '\n'); + + while (iter.has_next()) { + CmdLine line = iter.next(); + if (line.is_stop()) { + break; + } + if (line.is_executable()) { + DCmd* command = DCmdFactory::create_local_DCmd(line, out, CHECK); + assert(command != NULL, "command error must be handled before this line"); + DCmdMark mark(command); + command->parse(&line, delim, CHECK); + command->execute(CHECK); + } + } +} + +Mutex* DCmdFactory::_dcmdFactory_lock = new Mutex(Mutex::leaf, "DCmdFactory", true); + +DCmdFactory* DCmdFactory::factory(const char* name, size_t len) { + MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); + DCmdFactory* factory = _DCmdFactoryList; + while (factory != NULL) { + if (strlen(factory->name()) == len && + strncmp(name, factory->name(), len) == 0) { + return factory; + } + factory = factory->_next; + } + return NULL; +} + +int DCmdFactory::register_DCmdFactory(DCmdFactory* factory) { + MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); + factory->_next = _DCmdFactoryList; + _DCmdFactoryList = factory; + return 0; // Actually, there's no checks for duplicates +} + +DCmd* DCmdFactory::create_global_DCmd(CmdLine &line, outputStream* out, TRAPS) { + DCmdFactory* f = factory(line.cmd_addr(), line.cmd_len()); + if (f != NULL) { + if (f->is_enabled()) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + f->disabled_message()); + } + return f->create_Cheap_instance(out); + } + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + "Unknown diagnostic command"); +} + +DCmd* DCmdFactory::create_local_DCmd(CmdLine &line, outputStream* out, TRAPS) { + DCmdFactory* f = factory(line.cmd_addr(), line.cmd_len()); + if (f != NULL) { + if (!f->is_enabled()) { + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + f->disabled_message()); + } + return f->create_resource_instance(out); + } + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + "Unknown diagnostic command"); +} + +GrowableArray* DCmdFactory::DCmd_list() { + MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); + GrowableArray* array = new GrowableArray(); + DCmdFactory* factory = _DCmdFactoryList; + while (factory != NULL) { + if (!factory->is_hidden()) { + array->append(factory->name()); + } + factory = factory->next(); + } + return array; +} + +GrowableArray* DCmdFactory::DCmdInfo_list() { + MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag); + GrowableArray* array = new GrowableArray(); + DCmdFactory* factory = _DCmdFactoryList; + while (factory != NULL) { + if (!factory->is_hidden()) { + array->append(new DCmdInfo(factory->name(), + factory->description(), factory->impact(), + factory->num_arguments(), factory->is_enabled())); + } + factory = factory->next(); + } + return array; +} diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/diagnosticFramework.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/services/diagnosticFramework.hpp Wed Dec 14 20:06:21 2011 -0500 @@ -0,0 +1,362 @@ +/* + * 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* argument_name_array(); + GrowableArray* 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; } + virtual void print_help(outputStream* out) { }; + virtual void parse(CmdLine* line, char delim, TRAPS) { } + virtual void execute(TRAPS) { } + virtual void reset(TRAPS) { } + virtual void cleanup() { } + + // support for the JMX interface + virtual GrowableArray* argument_name_array() { + GrowableArray* array = new GrowableArray(0); + return array; + } + virtual GrowableArray* argument_info_array() { + GrowableArray* array = new GrowableArray(0); + return array; + } + + // main method to invoke the framework + static void parse_and_execute(outputStream* out, const char* cmdline, + char delim, TRAPS); +}; + +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* DCmd_list(); + static GrowableArray* 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 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 diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/jmm.h --- a/src/share/vm/services/jmm.h Fri Dec 09 19:28:34 2011 -0800 +++ b/src/share/vm/services/jmm.h Wed Dec 14 20:06:21 2011 -0500 @@ -48,7 +48,8 @@ JMM_VERSION_1_0 = 0x20010000, JMM_VERSION_1_1 = 0x20010100, // JDK 6 JMM_VERSION_1_2 = 0x20010200, // JDK 7 - JMM_VERSION = 0x20010201 + JMM_VERSION_1_2_1 = 0x20010201, // JDK 7 GA + JMM_VERSION = 0x20010202 }; typedef struct { @@ -188,6 +189,24 @@ /* -1 indicates gc_ext_attribute_values is not big enough */ } jmmGCStat; +typedef struct { + const char* name; + const char* description; + const char* impact; + int num_arguments; + jboolean enabled; +} dcmdInfo; + +typedef struct { + const char* name; + const char* description; + const char* type; + const char* default_string; + jboolean mandatory; + jboolean option; + int position; +} dcmdArgInfo; + typedef struct jmmInterface_1_ { void* reserved1; void* reserved2; @@ -296,6 +315,18 @@ void (JNICALL *SetGCNotificationEnabled) (JNIEnv *env, jobject mgr, jboolean enabled); + jobjectArray (JNICALL *GetDiagnosticCommands) (JNIEnv *env); + void (JNICALL *GetDiagnosticCommandInfo) + (JNIEnv *env, + jobjectArray cmds, + dcmdInfo *infoArray); + void (JNICALL *GetDiagnosticCommandArgumentsInfo) + (JNIEnv *env, + jstring commandName, + dcmdArgInfo *infoArray); + jstring (JNICALL *ExecuteDiagnosticCommand) + (JNIEnv *env, + jstring command); } JmmInterface; #ifdef __cplusplus diff -r 6d7d0790074d -r 31f6f10e4379 src/share/vm/services/management.cpp --- a/src/share/vm/services/management.cpp Fri Dec 09 19:28:34 2011 -0800 +++ b/src/share/vm/services/management.cpp Wed Dec 14 20:06:21 2011 -0500 @@ -40,7 +40,10 @@ #include "runtime/os.hpp" #include "runtime/serviceThread.hpp" #include "services/classLoadingService.hpp" +#include "services/diagnosticCommand.hpp" +#include "services/diagnosticFramework.hpp" #include "services/heapDumper.hpp" +#include "services/jmm.h" #include "services/lowMemoryDetector.hpp" #include "services/gcNotifier.hpp" #include "services/management.hpp" @@ -113,6 +116,9 @@ _optional_support.isSynchronizerUsageSupported = 1; #endif // SERVICES_KERNEL _optional_support.isThreadAllocatedMemorySupported = 1; + + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); } void Management::initialize(TRAPS) { @@ -2107,6 +2113,122 @@ #endif // SERVICES_KERNEL JVM_END +JVM_ENTRY(jobjectArray, jmm_GetDiagnosticCommands(JNIEnv *env)) + ResourceMark rm(THREAD); + GrowableArray* dcmd_list = DCmdFactory::DCmd_list(); + objArrayOop cmd_array_oop = oopFactory::new_objArray(SystemDictionary::String_klass(), + dcmd_list->length(), CHECK_NULL); + objArrayHandle cmd_array(THREAD, cmd_array_oop); + for (int i = 0; i < dcmd_list->length(); i++) { + oop cmd_name = java_lang_String::create_oop_from_str(dcmd_list->at(i), CHECK_NULL); + cmd_array->obj_at_put(i, cmd_name); + } + return (jobjectArray) JNIHandles::make_local(env, cmd_array()); +JVM_END + +JVM_ENTRY(void, jmm_GetDiagnosticCommandInfo(JNIEnv *env, jobjectArray cmds, + dcmdInfo* infoArray)) + if (cmds == NULL || infoArray == NULL) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + + ResourceMark rm(THREAD); + + objArrayOop ca = objArrayOop(JNIHandles::resolve_non_null(cmds)); + objArrayHandle cmds_ah(THREAD, ca); + + // Make sure we have a String array + klassOop element_klass = objArrayKlass::cast(cmds_ah->klass())->element_klass(); + if (element_klass != SystemDictionary::String_klass()) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Array element type is not String class"); + } + + GrowableArray* info_list = DCmdFactory::DCmdInfo_list(); + + int num_cmds = cmds_ah->length(); + for (int i = 0; i < num_cmds; i++) { + oop cmd = cmds_ah->obj_at(i); + if (cmd == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), + "Command name cannot be null."); + } + char* cmd_name = java_lang_String::as_utf8_string(cmd); + if (cmd_name == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), + "Command name cannot be null."); + } + int pos = info_list->find((void*)cmd_name,DCmdInfo::by_name); + if (pos == -1) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unknown diagnostic command"); + } + DCmdInfo* info = info_list->at(pos); + infoArray[i].name = info->name(); + infoArray[i].description = info->description(); + infoArray[i].impact = info->impact(); + infoArray[i].num_arguments = info->num_arguments(); + infoArray[i].enabled = info->is_enabled(); + } +JVM_END + +JVM_ENTRY(void, jmm_GetDiagnosticCommandArgumentsInfo(JNIEnv *env, + jstring command, dcmdArgInfo* infoArray)) + ResourceMark rm(THREAD); + oop cmd = JNIHandles::resolve_external_guard(command); + if (cmd == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), + "Command line cannot be null."); + } + char* cmd_name = java_lang_String::as_utf8_string(cmd); + if (cmd_name == NULL) { + THROW_MSG(vmSymbols::java_lang_NullPointerException(), + "Command line content cannot be null."); + } + DCmd* dcmd = NULL; + DCmdFactory*factory = DCmdFactory::factory(cmd_name, strlen(cmd_name)); + if (factory != NULL) { + dcmd = factory->create_resource_instance(NULL); + } + if (dcmd == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unknown diagnostic command"); + } + DCmdMark mark(dcmd); + GrowableArray* array = dcmd->argument_info_array(); + if (array->length() == 0) { + return; + } + for (int i = 0; i < array->length(); i++) { + infoArray[i].name = array->at(i)->name(); + infoArray[i].description = array->at(i)->description(); + infoArray[i].type = array->at(i)->type(); + infoArray[i].default_string = array->at(i)->default_string(); + infoArray[i].mandatory = array->at(i)->is_mandatory(); + infoArray[i].option = array->at(i)->is_option(); + infoArray[i].position = array->at(i)->position(); + } + return; +JVM_END + +JVM_ENTRY(jstring, jmm_ExecuteDiagnosticCommand(JNIEnv *env, jstring commandline)) + ResourceMark rm(THREAD); + oop cmd = JNIHandles::resolve_external_guard(commandline); + if (cmd == NULL) { + THROW_MSG_NULL(vmSymbols::java_lang_NullPointerException(), + "Command line cannot be null."); + } + char* cmdline = java_lang_String::as_utf8_string(cmd); + if (cmdline == NULL) { + THROW_MSG_NULL(vmSymbols::java_lang_NullPointerException(), + "Command line content cannot be null."); + } + bufferedStream output; + DCmd::parse_and_execute(&output, cmdline, ' ', CHECK_NULL); + oop result = java_lang_String::create_oop_from_str(output.as_string(), CHECK_NULL); + return (jstring) JNIHandles::make_local(env, result); +JVM_END + jlong Management::ticks_to_ms(jlong ticks) { assert(os::elapsed_frequency() > 0, "Must be non-zero"); return (jlong)(((double)ticks / (double)os::elapsed_frequency()) @@ -2149,7 +2271,11 @@ jmm_SetVMGlobal, NULL, jmm_DumpThreads, - jmm_SetGCNotificationEnabled + jmm_SetGCNotificationEnabled, + jmm_GetDiagnosticCommands, + jmm_GetDiagnosticCommandInfo, + jmm_GetDiagnosticCommandArgumentsInfo, + jmm_ExecuteDiagnosticCommand }; void* Management::get_jmm_interface(int version) {