Path: blob/master/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp
66644 views
/*1* Copyright (c) 2012, 2021, 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 "classfile/javaClasses.inline.hpp"26#include "classfile/vmSymbols.hpp"27#include "jfr/jfr.hpp"28#include "jfr/dcmd/jfrDcmds.hpp"29#include "jfr/jni/jfrJavaSupport.hpp"30#include "jfr/recorder/jfrRecorder.hpp"31#include "jfr/recorder/service/jfrOptionSet.hpp"32#include "logging/log.hpp"33#include "logging/logConfiguration.hpp"34#include "logging/logMessage.hpp"35#include "memory/arena.hpp"36#include "memory/resourceArea.hpp"37#include "oops/objArrayOop.inline.hpp"38#include "oops/oop.inline.hpp"39#include "oops/symbol.hpp"40#include "runtime/handles.inline.hpp"41#include "runtime/jniHandles.hpp"42#include "services/diagnosticArgument.hpp"43#include "services/diagnosticFramework.hpp"44#include "utilities/globalDefinitions.hpp"454647bool register_jfr_dcmds() {48uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean;49DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrCheckFlightRecordingDCmd>(full_export, true, false));50DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrDumpFlightRecordingDCmd>(full_export, true, false));51DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrStartFlightRecordingDCmd>(full_export, true, false));52DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrStopFlightRecordingDCmd>(full_export, true, false));53DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrConfigureFlightRecorderDCmd>(full_export, true, false));54return true;55}5657// JNIHandle management5859// ------------------------------------------------------------------60// push_jni_handle_block61//62// Push on a new block of JNI handles.63static void push_jni_handle_block(JavaThread* const thread) {64DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));6566// Allocate a new block for JNI handles.67// Inlined code from jni_PushLocalFrame()68JNIHandleBlock* prev_handles = thread->active_handles();69JNIHandleBlock* entry_handles = JNIHandleBlock::allocate_block(thread);70assert(entry_handles != NULL && prev_handles != NULL, "should not be NULL");71entry_handles->set_pop_frame_link(prev_handles); // make sure prev handles get gc'd.72thread->set_active_handles(entry_handles);73}7475// ------------------------------------------------------------------76// pop_jni_handle_block77//78// Pop off the current block of JNI handles.79static void pop_jni_handle_block(JavaThread* const thread) {80DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));8182// Release our JNI handle block83JNIHandleBlock* entry_handles = thread->active_handles();84JNIHandleBlock* prev_handles = entry_handles->pop_frame_link();85// restore86thread->set_active_handles(prev_handles);87entry_handles->set_pop_frame_link(NULL);88JNIHandleBlock::release_block(entry_handles, thread); // may block89}9091class JNIHandleBlockManager : public StackObj {92private:93JavaThread* const _thread;94public:95JNIHandleBlockManager(JavaThread* thread) : _thread(thread) {96push_jni_handle_block(_thread);97}9899~JNIHandleBlockManager() {100pop_jni_handle_block(_thread);101}102};103104static bool is_module_available(outputStream* output, TRAPS) {105return JfrJavaSupport::is_jdk_jfr_module_available(output, THREAD);106}107108static bool is_disabled(outputStream* output) {109if (Jfr::is_disabled()) {110if (output != NULL) {111output->print_cr("Flight Recorder is disabled.\n");112}113return true;114}115return false;116}117118static bool invalid_state(outputStream* out, TRAPS) {119DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));120return is_disabled(out) || !is_module_available(out, THREAD);121}122123static void handle_pending_exception(outputStream* output, bool startup, oop throwable) {124assert(throwable != NULL, "invariant");125126oop msg = java_lang_Throwable::message(throwable);127if (msg == NULL) {128return;129}130char* text = java_lang_String::as_utf8_string(msg);131if (text != NULL) {132if (startup) {133log_error(jfr,startup)("%s", text);134} else {135output->print_cr("%s", text);136}137}138}139140static void print_message(outputStream* output, oop content, TRAPS) {141objArrayOop lines = objArrayOop(content);142assert(lines != NULL, "invariant");143assert(lines->is_array(), "must be array");144const int length = lines->length();145for (int i = 0; i < length; ++i) {146const char* text = JfrJavaSupport::c_str(lines->obj_at(i), THREAD);147if (text == NULL) {148// An oome has been thrown and is pending.149break;150}151output->print_cr("%s", text);152}153}154155static void log(oop content, TRAPS) {156LogMessage(jfr,startup) msg;157objArrayOop lines = objArrayOop(content);158assert(lines != NULL, "invariant");159assert(lines->is_array(), "must be array");160const int length = lines->length();161for (int i = 0; i < length; ++i) {162const char* text = JfrJavaSupport::c_str(lines->obj_at(i), THREAD);163if (text == NULL) {164// An oome has been thrown and is pending.165break;166}167msg.info("%s", text);168}169}170171static void handle_dcmd_result(outputStream* output,172const oop result,173const DCmdSource source,174TRAPS) {175DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));176assert(output != NULL, "invariant");177ResourceMark rm(THREAD);178const bool startup = DCmd_Source_Internal == source;179if (HAS_PENDING_EXCEPTION) {180handle_pending_exception(output, startup, PENDING_EXCEPTION);181// Don't clear excption on startup, JVM should fail initialization.182if (!startup) {183CLEAR_PENDING_EXCEPTION;184}185return;186}187188assert(!HAS_PENDING_EXCEPTION, "invariant");189190if (startup) {191if (log_is_enabled(Warning, jfr, startup)) {192// if warning is set, assume user hasn't configured log level193// Log to Info and reset to Warning. This way user can disable194// default output by setting -Xlog:jfr+startup=error/off195LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(jfr, startup));196log(result, THREAD);197LogConfiguration::configure_stdout(LogLevel::Warning, true, LOG_TAGS(jfr, startup));198} else {199log(result, THREAD);200}201} else {202// Print output for jcmd or MXBean203print_message(output, result, THREAD);204}205}206207static oop construct_dcmd_instance(JfrJavaArguments* args, TRAPS) {208assert(args != NULL, "invariant");209DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));210assert(args->klass() != NULL, "invariant");211args->set_name("<init>");212args->set_signature("()V");213JfrJavaSupport::new_object(args, CHECK_NULL);214return args->result()->get_oop();215}216217JfrDCmd::JfrDCmd(outputStream* output, bool heap, int num_arguments) : DCmd(output, heap), _args(NULL), _num_arguments(num_arguments), _delimiter('\0') {}218219void JfrDCmd::invoke(JfrJavaArguments& method, TRAPS) const {220JavaValue constructor_result(T_OBJECT);221JfrJavaArguments constructor_args(&constructor_result);222constructor_args.set_klass(javaClass(), CHECK);223224HandleMark hm(THREAD);225JNIHandleBlockManager jni_handle_management(THREAD);226227const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);228229Handle h_dcmd_instance(THREAD, dcmd);230assert(h_dcmd_instance.not_null(), "invariant");231232method.set_receiver(h_dcmd_instance);233JfrJavaSupport::call_virtual(&method, THREAD);234}235236void JfrDCmd::parse(CmdLine* line, char delim, TRAPS) {237_args = line->args_addr();238_delimiter = delim;239// Error checking done in execute.240// Will not matter from DCmdFactory perspective241// where parse and execute are called consecutively.242}243244void JfrDCmd::execute(DCmdSource source, TRAPS) {245if (invalid_state(output(), THREAD)) {246return;247}248static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;C)[Ljava/lang/String;";249JavaValue result(T_OBJECT);250JfrJavaArguments execute(&result, javaClass(), "execute", signature, CHECK);251jstring argument = JfrJavaSupport::new_string(_args, CHECK);252jstring s = NULL;253if (source == DCmd_Source_Internal) {254s = JfrJavaSupport::new_string("internal", CHECK);255}256if (source == DCmd_Source_MBean) {257s = JfrJavaSupport::new_string("mbean", CHECK);258}259if (source == DCmd_Source_AttachAPI) {260s = JfrJavaSupport::new_string("attach", CHECK);261}262execute.push_jobject(s);263execute.push_jobject(argument);264execute.push_int(_delimiter);265invoke(execute, THREAD);266handle_dcmd_result(output(), result.get_oop(), source, THREAD);267}268269void JfrDCmd::print_help(const char* name) const {270static const char signature[] = "()[Ljava/lang/String;";271JavaThread* thread = JavaThread::current();272JavaValue result(T_OBJECT);273JfrJavaArguments printHelp(&result, javaClass(), "printHelp", signature, thread);274invoke(printHelp, thread);275handle_dcmd_result(output(), result.get_oop(), DCmd_Source_MBean, thread);276}277278static void initialize_dummy_descriptors(GrowableArray<DCmdArgumentInfo*>* array) {279assert(array != NULL, "invariant");280DCmdArgumentInfo * const dummy = new DCmdArgumentInfo(NULL,281NULL,282NULL,283NULL,284false,285true, // a DcmdFramework "option"286false);287for (int i = 0; i < array->max_length(); ++i) {288array->append(dummy);289}290}291292// Since the DcmdFramework does not support dynamically allocated strings,293// we keep them in a thread local arena. The arena is reset between invocations.294static THREAD_LOCAL Arena* dcmd_arena = NULL;295296static void prepare_dcmd_string_arena() {297if (dcmd_arena == NULL) {298dcmd_arena = new (mtTracing) Arena(mtTracing);299} else {300dcmd_arena->destruct_contents(); // will grow on next allocation301}302}303304static char* dcmd_arena_allocate(size_t size) {305assert(dcmd_arena != NULL, "invariant");306return (char*)dcmd_arena->Amalloc(size);307}308309static const char* get_as_dcmd_arena_string(oop string, JavaThread* t) {310char* str = NULL;311const typeArrayOop value = java_lang_String::value(string);312if (value != NULL) {313const size_t length = static_cast<size_t>(java_lang_String::utf8_length(string, value)) + 1;314str = dcmd_arena_allocate(length);315assert(str != NULL, "invariant");316java_lang_String::as_utf8_string(string, value, str, static_cast<int>(length));317}318return str;319}320321static const char* read_string_field(oop argument, const char* field_name, TRAPS) {322JavaValue result(T_OBJECT);323JfrJavaArguments args(&result);324args.set_klass(argument->klass());325args.set_name(field_name);326args.set_signature("Ljava/lang/String;");327args.set_receiver(argument);328JfrJavaSupport::get_field(&args, THREAD);329const oop string_oop = result.get_oop();330return string_oop != NULL ? get_as_dcmd_arena_string(string_oop, (JavaThread*)THREAD) : NULL;331}332333static bool read_boolean_field(oop argument, const char* field_name, TRAPS) {334JavaValue result(T_BOOLEAN);335JfrJavaArguments args(&result);336args.set_klass(argument->klass());337args.set_name(field_name);338args.set_signature("Z");339args.set_receiver(argument);340JfrJavaSupport::get_field(&args, THREAD);341return (result.get_jint() & 1) == 1;342}343344static DCmdArgumentInfo* create_info(oop argument, TRAPS) {345return new DCmdArgumentInfo(346read_string_field(argument, "name", THREAD),347read_string_field(argument, "description", THREAD),348read_string_field(argument, "type", THREAD),349read_string_field(argument, "defaultValue", THREAD),350read_boolean_field(argument, "mandatory", THREAD),351true, // a DcmdFramework "option"352read_boolean_field(argument, "allowMultiple", THREAD));353}354355GrowableArray<DCmdArgumentInfo*>* JfrDCmd::argument_info_array() const {356static const char signature[] = "()[Ljdk/jfr/internal/dcmd/Argument;";357JavaThread* thread = JavaThread::current();358GrowableArray<DCmdArgumentInfo*>* const array = new GrowableArray<DCmdArgumentInfo*>(_num_arguments);359JavaValue result(T_OBJECT);360JfrJavaArguments getArgumentInfos(&result, javaClass(), "getArgumentInfos", signature, thread);361invoke(getArgumentInfos, thread);362if (thread->has_pending_exception()) {363// Most likely an OOME, but the DCmdFramework is not the best place to handle it.364// We handle it locally by clearing the exception and returning an array with dummy descriptors.365// This lets the MBean server initialization routine complete successfully,366// but this particular command will have no argument descriptors exposed.367// Hence we postpone, or delegate, handling of OOME's to code that is better suited.368log_debug(jfr, system)("Exception in DCmd getArgumentInfos");369thread->clear_pending_exception();370initialize_dummy_descriptors(array);371assert(array->length() == _num_arguments, "invariant");372return array;373}374objArrayOop arguments = objArrayOop(result.get_oop());375assert(arguments != NULL, "invariant");376assert(arguments->is_array(), "must be array");377const int num_arguments = arguments->length();378assert(num_arguments == _num_arguments, "invariant");379prepare_dcmd_string_arena();380for (int i = 0; i < num_arguments; ++i) {381DCmdArgumentInfo* const dai = create_info(arguments->obj_at(i), thread);382assert(dai != NULL, "invariant");383array->append(dai);384}385return array;386}387388GrowableArray<const char*>* JfrDCmd::argument_name_array() const {389GrowableArray<DCmdArgumentInfo*>* argument_infos = argument_info_array();390GrowableArray<const char*>* array = new GrowableArray<const char*>(argument_infos->length());391for (int i = 0; i < argument_infos->length(); i++) {392array->append(argument_infos->at(i)->name());393}394return array;395}396397JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* output,398bool heap) : DCmdWithParser(output, heap),399_repository_path("repositorypath", "Path to repository,.e.g \\\"My Repository\\\"", "STRING", false, NULL),400_dump_path("dumppath", "Path to dump,.e.g \\\"My Dump path\\\"", "STRING", false, NULL),401_stack_depth("stackdepth", "Stack Depth", "JULONG", false, "64"),402_global_buffer_count("globalbuffercount", "Number of global buffers,", "JULONG", false, "20"),403_global_buffer_size("globalbuffersize", "Size of a global buffers,", "MEMORY SIZE", false, "512k"),404_thread_buffer_size("thread_buffer_size", "Size of a thread buffer", "MEMORY SIZE", false, "8k"),405_memory_size("memorysize", "Overall memory size, ", "MEMORY SIZE", false, "10m"),406_max_chunk_size("maxchunksize", "Size of an individual disk chunk", "MEMORY SIZE", false, "12m"),407_sample_threads("samplethreads", "Activate Thread sampling", "BOOLEAN", false, "true"),408_verbose(true) {409_dcmdparser.add_dcmd_option(&_repository_path);410_dcmdparser.add_dcmd_option(&_dump_path);411_dcmdparser.add_dcmd_option(&_stack_depth);412_dcmdparser.add_dcmd_option(&_global_buffer_count);413_dcmdparser.add_dcmd_option(&_global_buffer_size);414_dcmdparser.add_dcmd_option(&_thread_buffer_size);415_dcmdparser.add_dcmd_option(&_memory_size);416_dcmdparser.add_dcmd_option(&_max_chunk_size);417_dcmdparser.add_dcmd_option(&_sample_threads);418};419420void JfrConfigureFlightRecorderDCmd::print_help(const char* name) const {421outputStream* out = output();422// 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890423out->print_cr("Options:");424out->print_cr("");425out->print_cr(" globalbuffercount (Optional) Number of global buffers. This option is a legacy");426out->print_cr(" option: change the memorysize parameter to alter the number of");427out->print_cr(" global buffers. This value cannot be changed once JFR has been");428out->print_cr(" initalized. (STRING, default determined by the value for");429out->print_cr(" memorysize)");430out->print_cr("");431out->print_cr(" globalbuffersize (Optional) Size of the global buffers, in bytes. This option is a");432out->print_cr(" legacy option: change the memorysize parameter to alter the size");433out->print_cr(" of the global buffers. This value cannot be changed once JFR has");434out->print_cr(" been initalized. (STRING, default determined by the value for");435out->print_cr(" memorysize)");436out->print_cr("");437out->print_cr(" maxchunksize (Optional) Maximum size of an individual data chunk in bytes if");438out->print_cr(" one of the following suffixes is not used: 'm' or 'M' for");439out->print_cr(" megabytes OR 'g' or 'G' for gigabytes. This value cannot be");440out->print_cr(" changed once JFR has been initialized. (STRING, 12M)");441out->print_cr("");442out->print_cr(" memorysize (Optional) Overall memory size, in bytes if one of the following");443out->print_cr(" suffixes is not used: 'm' or 'M' for megabytes OR 'g' or 'G' for");444out->print_cr(" gigabytes. This value cannot be changed once JFR has been");445out->print_cr(" initialized. (STRING, 10M)");446out->print_cr("");447out->print_cr(" repositorypath (Optional) Path to the location where recordings are stored until");448out->print_cr(" they are written to a permanent file. (STRING, The default");449out->print_cr(" location is the temporary directory for the operating system. On");450out->print_cr(" Linux operating systems, the temporary directory is /tmp. On");451out->print_cr(" Windows, the temporary directory is specified by the TMP");452out->print_cr(" environment variable.)");453out->print_cr("");454out->print_cr(" stackdepth (Optional) Stack depth for stack traces. Setting this value");455out->print_cr(" greater than the default of 64 may cause a performance");456out->print_cr(" degradation. This value cannot be changed once JFR has been");457out->print_cr(" initialized. (LONG, 64)");458out->print_cr("");459out->print_cr(" thread_buffer_size (Optional) Local buffer size for each thread in bytes if one of");460out->print_cr(" the following suffixes is not used: 'k' or 'K' for kilobytes or");461out->print_cr(" 'm' or 'M' for megabytes. Overriding this parameter could reduce");462out->print_cr(" performance and is not recommended. This value cannot be changed");463out->print_cr(" once JFR has been initialized. (STRING, 8k)");464out->print_cr("");465out->print_cr(" samplethreads (Optional) Flag for activating thread sampling. (BOOLEAN, true)");466out->print_cr("");467out->print_cr("Options must be specified using the <key> or <key>=<value> syntax.");468out->print_cr("");469out->print_cr("Example usage:");470out->print_cr("");471out->print_cr(" $ jcmd <pid> JFR.configure");472out->print_cr(" $ jcmd <pid> JFR.configure repositorypath=/temporary");473out->print_cr(" $ jcmd <pid> JFR.configure stackdepth=256");474out->print_cr(" $ jcmd <pid> JFR.configure memorysize=100M");475out->print_cr("");476}477478int JfrConfigureFlightRecorderDCmd::num_arguments() {479ResourceMark rm;480JfrConfigureFlightRecorderDCmd* dcmd = new JfrConfigureFlightRecorderDCmd(NULL, false);481if (dcmd != NULL) {482DCmdMark mark(dcmd);483return dcmd->_dcmdparser.num_arguments();484}485return 0;486}487488void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) {489DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));490491if (invalid_state(output(), THREAD)) {492return;493}494495HandleMark hm(THREAD);496JNIHandleBlockManager jni_handle_management(THREAD);497498JavaValue result(T_OBJECT);499JfrJavaArguments constructor_args(&result);500constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdConfigure", CHECK);501const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);502Handle h_dcmd_instance(THREAD, dcmd);503assert(h_dcmd_instance.not_null(), "invariant");504505jstring repository_path = NULL;506if (_repository_path.is_set() && _repository_path.value() != NULL) {507repository_path = JfrJavaSupport::new_string(_repository_path.value(), CHECK);508}509510jstring dump_path = NULL;511if (_dump_path.is_set() && _dump_path.value() != NULL) {512dump_path = JfrJavaSupport::new_string(_dump_path.value(), CHECK);513}514515jobject stack_depth = NULL;516if (_stack_depth.is_set()) {517stack_depth = JfrJavaSupport::new_java_lang_Integer((jint)_stack_depth.value(), CHECK);518}519520jobject global_buffer_count = NULL;521if (_global_buffer_count.is_set()) {522global_buffer_count = JfrJavaSupport::new_java_lang_Long(_global_buffer_count.value(), CHECK);523}524525jobject global_buffer_size = NULL;526if (_global_buffer_size.is_set()) {527global_buffer_size = JfrJavaSupport::new_java_lang_Long(_global_buffer_size.value()._size, CHECK);528}529530jobject thread_buffer_size = NULL;531if (_thread_buffer_size.is_set()) {532thread_buffer_size = JfrJavaSupport::new_java_lang_Long(_thread_buffer_size.value()._size, CHECK);533}534535jobject max_chunk_size = NULL;536if (_max_chunk_size.is_set()) {537max_chunk_size = JfrJavaSupport::new_java_lang_Long(_max_chunk_size.value()._size, CHECK);538}539540jobject memory_size = NULL;541if (_memory_size.is_set()) {542memory_size = JfrJavaSupport::new_java_lang_Long(_memory_size.value()._size, CHECK);543}544545jobject sample_threads = NULL;546if (_sample_threads.is_set()) {547sample_threads = JfrJavaSupport::new_java_lang_Boolean(_sample_threads.value(), CHECK);548}549550static const char klass[] = "jdk/jfr/internal/dcmd/DCmdConfigure";551static const char method[] = "execute";552static const char signature[] = "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;"553"Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;"554"Ljava/lang/Long;Ljava/lang/Boolean;)[Ljava/lang/String;";555556JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);557execute_args.set_receiver(h_dcmd_instance);558559// params560execute_args.push_int(_verbose ? 1 : 0);561execute_args.push_jobject(repository_path);562execute_args.push_jobject(dump_path);563execute_args.push_jobject(stack_depth);564execute_args.push_jobject(global_buffer_count);565execute_args.push_jobject(global_buffer_size);566execute_args.push_jobject(thread_buffer_size);567execute_args.push_jobject(memory_size);568execute_args.push_jobject(max_chunk_size);569execute_args.push_jobject(sample_threads);570571JfrJavaSupport::call_virtual(&execute_args, THREAD);572handle_dcmd_result(output(), result.get_oop(), source, THREAD);573}574575576