1 /*
   2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "gc_implementation/shared/vmGCOperations.hpp"
  27 #include "runtime/javaCalls.hpp"
  28 #include "services/diagnosticArgument.hpp"
  29 #include "services/diagnosticCommand.hpp"
  30 #include "services/diagnosticFramework.hpp"
  31 #include "services/heapDumper.hpp"
  32 #include "services/management.hpp"
  33 
  34 void DCmdRegistrant::register_dcmds(){
  35   // Registration of the diagnostic commands
  36   // First boolean argument specifies if the command is enabled
  37   // Second boolean argument specifies if the command is hidden
  38   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HelpDCmd>(true, false));
  39   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VersionDCmd>(true, false));
  40   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CommandLineDCmd>(true, false));
  41   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintSystemPropertiesDCmd>(true, false));
  42   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintVMFlagsDCmd>(true, false));
  43   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VMUptimeDCmd>(true, false));
  44   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemGCDCmd>(true, false));
  45   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RunFinalizationDCmd>(true, false));
  46 #ifndef SERVICES_KERNEL   // Heap dumping not supported
  47   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(true, false));
  48 #endif // SERVICES_KERNEL
  49   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(true, false));
  50   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(true, false));
  51 
  52 }
  53 
  54 #ifndef HAVE_EXTRA_DCMD
  55 void DCmdRegistrant::register_dcmds_ext(){
  56    // Do nothing here
  57 }
  58 #endif
  59 
  60 
  61 HelpDCmd::HelpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap),
  62   _all("-all", "Show help for all commands", "BOOLEAN", false, "false"),
  63   _cmd("command name", "The name of the command for which we want help",
  64         "STRING", false) {
  65   _dcmdparser.add_dcmd_option(&_all);
  66   _dcmdparser.add_dcmd_argument(&_cmd);
  67 };
  68 
  69 void HelpDCmd::execute(TRAPS) {
  70   if (_all.value()) {
  71     GrowableArray<const char*>* cmd_list = DCmdFactory::DCmd_list();
  72     for (int i = 0; i < cmd_list->length(); i++) {
  73       DCmdFactory* factory = DCmdFactory::factory(cmd_list->at(i),
  74                                                   strlen(cmd_list->at(i)));
  75       if (!factory->is_hidden()) {
  76         output()->print_cr("%s%s", factory->name(),
  77                            factory->is_enabled() ? "" : " [disabled]");
  78         output()->print_cr("\t%s", factory->description());
  79         output()->cr();
  80       }
  81       factory = factory->next();
  82     }
  83   } else if (_cmd.has_value()) {
  84     DCmd* cmd = NULL;
  85     DCmdFactory* factory = DCmdFactory::factory(_cmd.value(),
  86                                                 strlen(_cmd.value()));
  87     if (factory != NULL) {
  88       output()->print_cr("%s%s", factory->name(),
  89                          factory->is_enabled() ? "" : " [disabled]");
  90       output()->print_cr(factory->description());
  91       output()->print_cr("\nImpact: %s", factory->impact());
  92       output()->cr();
  93       cmd = factory->create_resource_instance(output());
  94       if (cmd != NULL) {
  95         DCmdMark mark(cmd);
  96         cmd->print_help(factory->name());
  97       }
  98     } else {
  99       output()->print_cr("Help unavailable : '%s' : No such command", _cmd.value());
 100     }
 101   } else {
 102     output()->print_cr("The following commands are available:");
 103     GrowableArray<const char *>* cmd_list = DCmdFactory::DCmd_list();
 104     for (int i = 0; i < cmd_list->length(); i++) {
 105       DCmdFactory* factory = DCmdFactory::factory(cmd_list->at(i),
 106                                                   strlen(cmd_list->at(i)));
 107       if (!factory->is_hidden()) {
 108         output()->print_cr("%s%s", factory->name(),
 109                            factory->is_enabled() ? "" : " [disabled]");
 110       }
 111       factory = factory->_next;
 112     }
 113     output()->print_cr("\nFor more information about a specific command use 'help <command>'.");
 114   }
 115 }
 116 
 117 int HelpDCmd::num_arguments() {
 118   ResourceMark rm;
 119   HelpDCmd* dcmd = new HelpDCmd(NULL, false);
 120   if (dcmd != NULL) {
 121     DCmdMark mark(dcmd);
 122     return dcmd->_dcmdparser.num_arguments();
 123   } else {
 124     return 0;
 125   }
 126 }
 127 
 128 void VersionDCmd::execute(TRAPS) {
 129   output()->print_cr("%s version %s", Abstract_VM_Version::vm_name(),
 130           Abstract_VM_Version::vm_release());
 131   JDK_Version jdk_version = JDK_Version::current();
 132   if (jdk_version.update_version() > 0) {
 133     output()->print_cr("JDK %d.%d_%02d", jdk_version.major_version(),
 134             jdk_version.minor_version(), jdk_version.update_version());
 135   } else {
 136     output()->print_cr("JDK %d.%d", jdk_version.major_version(),
 137             jdk_version.minor_version());
 138   }
 139 }
 140 
 141 PrintVMFlagsDCmd::PrintVMFlagsDCmd(outputStream* output, bool heap) :
 142                                    DCmdWithParser(output, heap),
 143   _all("-all", "Print all flags supported by the VM", "BOOLEAN", false, "false") {
 144   _dcmdparser.add_dcmd_option(&_all);
 145 }
 146 
 147 void PrintVMFlagsDCmd::execute(TRAPS) {
 148   if (_all.value()) {
 149     CommandLineFlags::printFlags(output(), true);
 150   } else {
 151     CommandLineFlags::printSetFlags(output());
 152   }
 153 }
 154 
 155 int PrintVMFlagsDCmd::num_arguments() {
 156     ResourceMark rm;
 157     PrintVMFlagsDCmd* dcmd = new PrintVMFlagsDCmd(NULL, false);
 158     if (dcmd != NULL) {
 159       DCmdMark mark(dcmd);
 160       return dcmd->_dcmdparser.num_arguments();
 161     } else {
 162       return 0;
 163     }
 164 }
 165 
 166 void PrintSystemPropertiesDCmd::execute(TRAPS) {
 167   // load sun.misc.VMSupport
 168   Symbol* klass = vmSymbols::sun_misc_VMSupport();
 169   klassOop k = SystemDictionary::resolve_or_fail(klass, true, CHECK);
 170   instanceKlassHandle ik (THREAD, k);
 171   if (ik->should_be_initialized()) {
 172     ik->initialize(THREAD);
 173   }
 174   if (HAS_PENDING_EXCEPTION) {
 175     java_lang_Throwable::print(PENDING_EXCEPTION, output());
 176     output()->cr();
 177     CLEAR_PENDING_EXCEPTION;
 178     return;
 179   }
 180 
 181   // invoke the serializePropertiesToByteArray method
 182   JavaValue result(T_OBJECT);
 183   JavaCallArguments args;
 184 
 185   Symbol* signature = vmSymbols::serializePropertiesToByteArray_signature();
 186   JavaCalls::call_static(&result,
 187                          ik,
 188                          vmSymbols::serializePropertiesToByteArray_name(),
 189                          signature,
 190                          &args,
 191                          THREAD);
 192   if (HAS_PENDING_EXCEPTION) {
 193     java_lang_Throwable::print(PENDING_EXCEPTION, output());
 194     output()->cr();
 195     CLEAR_PENDING_EXCEPTION;
 196     return;
 197   }
 198 
 199   // The result should be a [B
 200   oop res = (oop)result.get_jobject();
 201   assert(res->is_typeArray(), "just checking");
 202   assert(typeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking");
 203 
 204   // copy the bytes to the output stream
 205   typeArrayOop ba = typeArrayOop(res);
 206   jbyte* addr = typeArrayOop(res)->byte_at_addr(0);
 207   output()->print_raw((const char*)addr, ba->length());
 208 }
 209 
 210 VMUptimeDCmd::VMUptimeDCmd(outputStream* output, bool heap) :
 211                            DCmdWithParser(output, heap),
 212   _date("-date", "Add a prefix with current date", "BOOLEAN", false, "false") {
 213   _dcmdparser.add_dcmd_option(&_date);
 214 }
 215 
 216 void VMUptimeDCmd::execute(TRAPS) {
 217   if (_date.value()) {
 218     output()->date_stamp(true, "", ": ");
 219   }
 220   output()->time_stamp().update_to(tty->time_stamp().ticks());
 221   output()->stamp();
 222   output()->print_cr(" s");
 223 }
 224 
 225 int VMUptimeDCmd::num_arguments() {
 226   ResourceMark rm;
 227   VMUptimeDCmd* dcmd = new VMUptimeDCmd(NULL, false);
 228   if (dcmd != NULL) {
 229     DCmdMark mark(dcmd);
 230     return dcmd->_dcmdparser.num_arguments();
 231   } else {
 232     return 0;
 233   }
 234 }
 235 
 236 void SystemGCDCmd::execute(TRAPS) {
 237   Universe::heap()->collect(GCCause::_java_lang_system_gc);
 238 }
 239 
 240 void RunFinalizationDCmd::execute(TRAPS) {
 241   klassOop k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_System(),
 242                                                  true, CHECK);
 243   instanceKlassHandle klass(THREAD, k);
 244   JavaValue result(T_VOID);
 245   JavaCalls::call_static(&result, klass,
 246                          vmSymbols::run_finalization_name(),
 247                          vmSymbols::void_method_signature(), CHECK);
 248 }
 249 
 250 #ifndef SERVICES_KERNEL   // Heap dumping not supported
 251 HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) :
 252                            DCmdWithParser(output, heap),
 253   _filename("filename","Name of the dump file", "STRING",true),
 254   _all("-all", "Dump all objects, including unreachable objects",
 255        "BOOLEAN", false, "false") {
 256   _dcmdparser.add_dcmd_option(&_all);
 257   _dcmdparser.add_dcmd_argument(&_filename);
 258 }
 259 
 260 void HeapDumpDCmd::execute(TRAPS) {
 261   // Request a full GC before heap dump if _all is false
 262   // This helps reduces the amount of unreachable objects in the dump
 263   // and makes it easier to browse.
 264   HeapDumper dumper(!_all.value() /* request GC if _all is false*/);
 265   int res = dumper.dump(_filename.value());
 266   if (res == 0) {
 267     output()->print_cr("Heap dump file created");
 268   } else {
 269     // heap dump failed
 270     ResourceMark rm;
 271     char* error = dumper.error_as_C_string();
 272     if (error == NULL) {
 273       output()->print_cr("Dump failed - reason unknown");
 274     } else {
 275       output()->print_cr("%s", error);
 276     }
 277   }
 278 }
 279 
 280 int HeapDumpDCmd::num_arguments() {
 281   ResourceMark rm;
 282   HeapDumpDCmd* dcmd = new HeapDumpDCmd(NULL, false);
 283   if (dcmd != NULL) {
 284     DCmdMark mark(dcmd);
 285     return dcmd->_dcmdparser.num_arguments();
 286   } else {
 287     return 0;
 288   }
 289 }
 290 #endif // SERVICES_KERNEL
 291 
 292 ClassHistogramDCmd::ClassHistogramDCmd(outputStream* output, bool heap) :
 293                                        DCmdWithParser(output, heap),
 294   _all("-all", "Inspect all objects, including unreachable objects",
 295        "BOOLEAN", false, "false") {
 296   _dcmdparser.add_dcmd_option(&_all);
 297 }
 298 
 299 void ClassHistogramDCmd::execute(TRAPS) {
 300   VM_GC_HeapInspection heapop(output(),
 301                               !_all.value() /* request full gc if false */,
 302                               true /* need_prologue */);
 303   VMThread::execute(&heapop);
 304 }
 305 
 306 int ClassHistogramDCmd::num_arguments() {
 307   ResourceMark rm;
 308   ClassHistogramDCmd* dcmd = new ClassHistogramDCmd(NULL, false);
 309   if (dcmd != NULL) {
 310     DCmdMark mark(dcmd);
 311     return dcmd->_dcmdparser.num_arguments();
 312   } else {
 313     return 0;
 314   }
 315 }
 316 
 317 ThreadDumpDCmd::ThreadDumpDCmd(outputStream* output, bool heap) :
 318                                DCmdWithParser(output, heap),
 319   _locks("-l", "print java.util.concurrent locks", "BOOLEAN", false, "false") {
 320   _dcmdparser.add_dcmd_option(&_locks);
 321 }
 322 
 323 void ThreadDumpDCmd::execute(TRAPS) {
 324   // thread stacks
 325   VM_PrintThreads op1(output(), _locks.value());
 326   VMThread::execute(&op1);
 327 
 328   // JNI global handles
 329   VM_PrintJNI op2(output());
 330   VMThread::execute(&op2);
 331 
 332   // Deadlock detection
 333   VM_FindDeadlocks op3(output());
 334   VMThread::execute(&op3);
 335 }
 336 
 337 int ThreadDumpDCmd::num_arguments() {
 338   ResourceMark rm;
 339   ThreadDumpDCmd* dcmd = new ThreadDumpDCmd(NULL, false);
 340   if (dcmd != NULL) {
 341     DCmdMark mark(dcmd);
 342     return dcmd->_dcmdparser.num_arguments();
 343   } else {
 344     return 0;
 345   }
 346 }