Path: blob/master/src/hotspot/share/services/diagnosticFramework.cpp
41144 views
/*1* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "jvm.h"26#include "memory/oopFactory.hpp"27#include "memory/resourceArea.hpp"28#include "oops/oop.inline.hpp"29#include "runtime/handles.inline.hpp"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"3536CmdLine::CmdLine(const char* line, size_t len, bool no_command_name)37: _cmd(line), _cmd_len(0), _args(NULL), _args_len(0)38{39assert(line != NULL, "Command line string should not be NULL");40const char* line_end;41const char* cmd_end;4243_cmd = line;44line_end = &line[len];4546// Skip whitespace in the beginning of the line.47while (_cmd < line_end && isspace((int) _cmd[0])) {48_cmd++;49}50cmd_end = _cmd;5152if (no_command_name) {53_cmd = NULL;54_cmd_len = 0;55} else {56// Look for end of the command name57while (cmd_end < line_end && !isspace((int) cmd_end[0])) {58cmd_end++;59}60_cmd_len = cmd_end - _cmd;61}62_args = cmd_end;63_args_len = line_end - _args;64}6566bool DCmdArgIter::next(TRAPS) {67if (_len == 0) return false;68// skipping delimiters69while (_cursor < _len - 1 && _buffer[_cursor] == _delim) {70_cursor++;71}72// handling end of command line73if (_cursor == _len - 1 && _buffer[_cursor] == _delim) {74_key_addr = &_buffer[_cursor];75_key_len = 0;76_value_addr = &_buffer[_cursor];77_value_len = 0;78return false;79}80// extracting first item, argument or option name81_key_addr = &_buffer[_cursor];82bool arg_had_quotes = false;83while (_cursor <= _len - 1 && _buffer[_cursor] != '=' && _buffer[_cursor] != _delim) {84// argument can be surrounded by single or double quotes85if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') {86_key_addr++;87char quote = _buffer[_cursor];88arg_had_quotes = true;89while (_cursor < _len - 1) {90_cursor++;91if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') {92break;93}94}95if (_buffer[_cursor] != quote) {96THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),97"Format error in diagnostic command arguments", false);98}99break;100}101_cursor++;102}103_key_len = &_buffer[_cursor] - _key_addr;104if (arg_had_quotes) {105// if the argument was quoted, we need to step past the last quote here106_cursor++;107}108// check if the argument has the <key>=<value> format109if (_cursor <= _len -1 && _buffer[_cursor] == '=') {110_cursor++;111_value_addr = &_buffer[_cursor];112bool value_had_quotes = false;113// extract the value114while (_cursor <= _len - 1 && _buffer[_cursor] != _delim) {115// value can be surrounded by simple or double quotes116if (_buffer[_cursor] == '\"' || _buffer[_cursor] == '\'') {117_value_addr++;118char quote = _buffer[_cursor];119value_had_quotes = true;120while (_cursor < _len - 1) {121_cursor++;122if (_buffer[_cursor] == quote && _buffer[_cursor - 1] != '\\') {123break;124}125}126if (_buffer[_cursor] != quote) {127THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),128"Format error in diagnostic command arguments", false);129}130break;131}132_cursor++;133}134_value_len = &_buffer[_cursor] - _value_addr;135if (value_had_quotes) {136// if the value was quoted, we need to step past the last quote here137_cursor++;138}139} else {140_value_addr = NULL;141_value_len = 0;142}143return _key_len != 0;144}145146bool DCmdInfo::by_name(void* cmd_name, DCmdInfo* info) {147if (info == NULL) return false;148return strcmp((const char*)cmd_name, info->name()) == 0;149}150151void DCmdParser::add_dcmd_option(GenDCmdArgument* arg) {152assert(arg != NULL, "Sanity");153if (_options == NULL) {154_options = arg;155} else {156GenDCmdArgument* o = _options;157while (o->next() != NULL) {158o = o->next();159}160o->set_next(arg);161}162arg->set_next(NULL);163JavaThread* THREAD = JavaThread::current(); // For exception macros.164arg->init_value(THREAD);165if (HAS_PENDING_EXCEPTION) {166fatal("Initialization must be successful");167}168}169170void DCmdParser::add_dcmd_argument(GenDCmdArgument* arg) {171assert(arg != NULL, "Sanity");172if (_arguments_list == NULL) {173_arguments_list = arg;174} else {175GenDCmdArgument* a = _arguments_list;176while (a->next() != NULL) {177a = a->next();178}179a->set_next(arg);180}181arg->set_next(NULL);182JavaThread* THREAD = JavaThread::current(); // For exception macros.183arg->init_value(THREAD);184if (HAS_PENDING_EXCEPTION) {185fatal("Initialization must be successful");186}187}188189void DCmdParser::parse(CmdLine* line, char delim, TRAPS) {190GenDCmdArgument* next_argument = _arguments_list;191DCmdArgIter iter(line->args_addr(), line->args_len(), delim);192bool cont = iter.next(CHECK);193while (cont) {194GenDCmdArgument* arg = lookup_dcmd_option(iter.key_addr(),195iter.key_length());196if (arg != NULL) {197arg->read_value(iter.value_addr(), iter.value_length(), CHECK);198} else {199if (next_argument != NULL) {200arg = next_argument;201arg->read_value(iter.key_addr(), iter.key_length(), CHECK);202next_argument = next_argument->next();203} else {204const size_t buflen = 120;205const size_t argbuflen = 30;206char buf[buflen];207char argbuf[argbuflen];208size_t len = MIN2<size_t>(iter.key_length(), argbuflen - 1);209210strncpy(argbuf, iter.key_addr(), len);211argbuf[len] = '\0';212jio_snprintf(buf, buflen - 1, "Unknown argument '%s' in diagnostic command.", argbuf);213214THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf);215}216}217cont = iter.next(CHECK);218}219check(CHECK);220}221222GenDCmdArgument* DCmdParser::lookup_dcmd_option(const char* name, size_t len) {223GenDCmdArgument* arg = _options;224while (arg != NULL) {225if (strlen(arg->name()) == len &&226strncmp(name, arg->name(), len) == 0) {227return arg;228}229arg = arg->next();230}231return NULL;232}233234void DCmdParser::check(TRAPS) {235const size_t buflen = 256;236char buf[buflen];237GenDCmdArgument* arg = _arguments_list;238while (arg != NULL) {239if (arg->is_mandatory() && !arg->has_value()) {240jio_snprintf(buf, buflen - 1, "The argument '%s' is mandatory.", arg->name());241THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf);242}243arg = arg->next();244}245arg = _options;246while (arg != NULL) {247if (arg->is_mandatory() && !arg->has_value()) {248jio_snprintf(buf, buflen - 1, "The option '%s' is mandatory.", arg->name());249THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), buf);250}251arg = arg->next();252}253}254255void DCmdParser::print_help(outputStream* out, const char* cmd_name) const {256out->print("Syntax : %s %s", cmd_name, _options == NULL ? "" : "[options]");257GenDCmdArgument* arg = _arguments_list;258while (arg != NULL) {259if (arg->is_mandatory()) {260out->print(" <%s>", arg->name());261} else {262out->print(" [<%s>]", arg->name());263}264arg = arg->next();265}266out->cr();267if (_arguments_list != NULL) {268out->print_cr("\nArguments:");269arg = _arguments_list;270while (arg != NULL) {271out->print("\t%s : %s %s (%s, ", arg->name(),272arg->is_mandatory() ? "" : "[optional]",273arg->description(), arg->type());274if (arg->has_default()) {275out->print("%s", arg->default_string());276} else {277out->print("no default value");278}279out->print_cr(")");280arg = arg->next();281}282}283if (_options != NULL) {284out->print_cr("\nOptions: (options must be specified using the <key> or <key>=<value> syntax)");285arg = _options;286while (arg != NULL) {287out->print("\t%s : %s %s (%s, ", arg->name(),288arg->is_mandatory() ? "" : "[optional]",289arg->description(), arg->type());290if (arg->has_default()) {291out->print("%s", arg->default_string());292} else {293out->print("no default value");294}295out->print_cr(")");296arg = arg->next();297}298}299}300301void DCmdParser::reset(TRAPS) {302GenDCmdArgument* arg = _arguments_list;303while (arg != NULL) {304arg->reset(CHECK);305arg = arg->next();306}307arg = _options;308while (arg != NULL) {309arg->reset(CHECK);310arg = arg->next();311}312}313314void DCmdParser::cleanup() {315GenDCmdArgument* arg = _arguments_list;316while (arg != NULL) {317arg->cleanup();318arg = arg->next();319}320arg = _options;321while (arg != NULL) {322arg->cleanup();323arg = arg->next();324}325}326327int DCmdParser::num_arguments() const {328GenDCmdArgument* arg = _arguments_list;329int count = 0;330while (arg != NULL) {331count++;332arg = arg->next();333}334arg = _options;335while (arg != NULL) {336count++;337arg = arg->next();338}339return count;340}341342GrowableArray<const char *>* DCmdParser::argument_name_array() const {343int count = num_arguments();344GrowableArray<const char *>* array = new GrowableArray<const char *>(count);345GenDCmdArgument* arg = _arguments_list;346while (arg != NULL) {347array->append(arg->name());348arg = arg->next();349}350arg = _options;351while (arg != NULL) {352array->append(arg->name());353arg = arg->next();354}355return array;356}357358GrowableArray<DCmdArgumentInfo*>* DCmdParser::argument_info_array() const {359int count = num_arguments();360GrowableArray<DCmdArgumentInfo*>* array = new GrowableArray<DCmdArgumentInfo *>(count);361int idx = 0;362GenDCmdArgument* arg = _arguments_list;363while (arg != NULL) {364array->append(new DCmdArgumentInfo(arg->name(), arg->description(),365arg->type(), arg->default_string(), arg->is_mandatory(),366false, arg->allow_multiple(), idx));367idx++;368arg = arg->next();369}370arg = _options;371while (arg != NULL) {372array->append(new DCmdArgumentInfo(arg->name(), arg->description(),373arg->type(), arg->default_string(), arg->is_mandatory(),374true, arg->allow_multiple()));375arg = arg->next();376}377return array;378}379380DCmdFactory* DCmdFactory::_DCmdFactoryList = NULL;381bool DCmdFactory::_has_pending_jmx_notification = false;382383void DCmd::parse_and_execute(DCmdSource source, outputStream* out,384const char* cmdline, char delim, TRAPS) {385386if (cmdline == NULL) return; // Nothing to do!387DCmdIter iter(cmdline, '\n');388389int count = 0;390while (iter.has_next()) {391if(source == DCmd_Source_MBean && count > 0) {392// When diagnostic commands are invoked via JMX, each command line393// must contains one and only one command because of the Permission394// checks performed by the DiagnosticCommandMBean395THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),396"Invalid syntax");397}398CmdLine line = iter.next();399if (line.is_stop()) {400break;401}402if (line.is_executable()) {403ResourceMark rm;404DCmd* command = DCmdFactory::create_local_DCmd(source, line, out, CHECK);405assert(command != NULL, "command error must be handled before this line");406DCmdMark mark(command);407command->parse(&line, delim, CHECK);408command->execute(source, CHECK);409}410count++;411}412}413414void DCmdWithParser::parse(CmdLine* line, char delim, TRAPS) {415_dcmdparser.parse(line, delim, CHECK);416}417418void DCmdWithParser::print_help(const char* name) const {419_dcmdparser.print_help(output(), name);420}421422void DCmdWithParser::reset(TRAPS) {423_dcmdparser.reset(CHECK);424}425426void DCmdWithParser::cleanup() {427_dcmdparser.cleanup();428}429430GrowableArray<const char*>* DCmdWithParser::argument_name_array() const {431return _dcmdparser.argument_name_array();432}433434GrowableArray<DCmdArgumentInfo*>* DCmdWithParser::argument_info_array() const {435return _dcmdparser.argument_info_array();436}437438void DCmdFactory::push_jmx_notification_request() {439MutexLocker ml(Notification_lock, Mutex::_no_safepoint_check_flag);440_has_pending_jmx_notification = true;441Notification_lock->notify_all();442}443444void DCmdFactory::send_notification(TRAPS) {445DCmdFactory::send_notification_internal(THREAD);446// Clearing pending exception to avoid premature termination of447// the service thread448if (HAS_PENDING_EXCEPTION) {449CLEAR_PENDING_EXCEPTION;450}451}452void DCmdFactory::send_notification_internal(TRAPS) {453ResourceMark rm(THREAD);454HandleMark hm(THREAD);455bool notif = false;456{457MutexLocker ml(THREAD, Notification_lock, Mutex::_no_safepoint_check_flag);458notif = _has_pending_jmx_notification;459_has_pending_jmx_notification = false;460}461if (notif) {462463Klass* k = Management::com_sun_management_internal_DiagnosticCommandImpl_klass(CHECK);464InstanceKlass* dcmd_mbean_klass = InstanceKlass::cast(k);465466JavaValue result(T_OBJECT);467JavaCalls::call_static(&result,468dcmd_mbean_klass,469vmSymbols::getDiagnosticCommandMBean_name(),470vmSymbols::getDiagnosticCommandMBean_signature(),471CHECK);472473instanceOop m = (instanceOop) result.get_oop();474instanceHandle dcmd_mbean_h(THREAD, m);475476if (!dcmd_mbean_h->is_a(k)) {477THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),478"DiagnosticCommandImpl.getDiagnosticCommandMBean didn't return a DiagnosticCommandMBean instance");479}480481JavaValue result2(T_VOID);482JavaCallArguments args2(dcmd_mbean_h);483484JavaCalls::call_virtual(&result2,485dcmd_mbean_klass,486vmSymbols::createDiagnosticFrameworkNotification_name(),487vmSymbols::void_method_signature(),488&args2,489CHECK);490}491}492493bool DCmdFactory::_send_jmx_notification = false;494495DCmdFactory* DCmdFactory::factory(DCmdSource source, const char* name, size_t len) {496MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);497DCmdFactory* factory = _DCmdFactoryList;498while (factory != NULL) {499if (strlen(factory->name()) == len &&500strncmp(name, factory->name(), len) == 0) {501if(factory->export_flags() & source) {502return factory;503} else {504return NULL;505}506}507factory = factory->_next;508}509return NULL;510}511512int DCmdFactory::register_DCmdFactory(DCmdFactory* factory) {513MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);514factory->_next = _DCmdFactoryList;515_DCmdFactoryList = factory;516if (_send_jmx_notification && !factory->_hidden517&& (factory->_export_flags & DCmd_Source_MBean)) {518DCmdFactory::push_jmx_notification_request();519}520return 0; // Actually, there's no checks for duplicates521}522523DCmd* DCmdFactory::create_local_DCmd(DCmdSource source, CmdLine &line,524outputStream* out, TRAPS) {525DCmdFactory* f = factory(source, line.cmd_addr(), line.cmd_len());526if (f != NULL) {527if (!f->is_enabled()) {528THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(),529f->disabled_message());530}531return f->create_resource_instance(out);532}533THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(),534"Unknown diagnostic command");535}536537GrowableArray<const char*>* DCmdFactory::DCmd_list(DCmdSource source) {538MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);539GrowableArray<const char*>* array = new GrowableArray<const char*>();540DCmdFactory* factory = _DCmdFactoryList;541while (factory != NULL) {542if (!factory->is_hidden() && (factory->export_flags() & source)) {543array->append(factory->name());544}545factory = factory->next();546}547return array;548}549550GrowableArray<DCmdInfo*>* DCmdFactory::DCmdInfo_list(DCmdSource source ) {551MutexLocker ml(DCmdFactory_lock, Mutex::_no_safepoint_check_flag);552GrowableArray<DCmdInfo*>* array = new GrowableArray<DCmdInfo*>();553DCmdFactory* factory = _DCmdFactoryList;554while (factory != NULL) {555if (!factory->is_hidden() && (factory->export_flags() & source)) {556array->append(new DCmdInfo(factory->name(),557factory->description(), factory->impact(),558factory->permission(), factory->num_arguments(),559factory->is_enabled()));560}561factory = factory->next();562}563return array;564}565566567