attachListener.cpp revision 0:a61af66fc99e
1/* 2 * Copyright 2005-2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any questions. 22 * 23 */ 24 25# include "incls/_precompiled.incl" 26# include "incls/_attachListener.cpp.incl" 27 28volatile bool AttachListener::_initialized; 29 30// Implementation of "properties" command. 31// 32// Invokes sun.misc.VMSupport.serializePropertiesToByteArray to serialize 33// the system properties into a byte array. 34 35static klassOop load_and_initialize_klass(symbolHandle sh, TRAPS) { 36 klassOop k = SystemDictionary::resolve_or_fail(sh, true, CHECK_NULL); 37 instanceKlassHandle ik (THREAD, k); 38 if (ik->should_be_initialized()) { 39 ik->initialize(CHECK_NULL); 40 } 41 return ik(); 42} 43 44static jint get_properties(AttachOperation* op, outputStream* out, symbolHandle serializePropertiesMethod) { 45 Thread* THREAD = Thread::current(); 46 HandleMark hm; 47 48 // load sun.misc.VMSupport 49 symbolHandle klass = vmSymbolHandles::sun_misc_VMSupport(); 50 klassOop k = load_and_initialize_klass(klass, THREAD); 51 if (HAS_PENDING_EXCEPTION) { 52 java_lang_Throwable::print(PENDING_EXCEPTION, out); 53 CLEAR_PENDING_EXCEPTION; 54 return JNI_ERR; 55 } 56 instanceKlassHandle ik(THREAD, k); 57 58 // invoke the serializePropertiesToByteArray method 59 JavaValue result(T_OBJECT); 60 JavaCallArguments args; 61 62 63 symbolHandle signature = vmSymbolHandles::serializePropertiesToByteArray_signature(); 64 JavaCalls::call_static(&result, 65 ik, 66 serializePropertiesMethod, 67 signature, 68 &args, 69 THREAD); 70 if (HAS_PENDING_EXCEPTION) { 71 java_lang_Throwable::print(PENDING_EXCEPTION, out); 72 CLEAR_PENDING_EXCEPTION; 73 return JNI_ERR; 74 } 75 76 // The result should be a [B 77 oop res = (oop)result.get_jobject(); 78 assert(res->is_typeArray(), "just checking"); 79 assert(typeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking"); 80 81 // copy the bytes to the output stream 82 typeArrayOop ba = typeArrayOop(res); 83 jbyte* addr = typeArrayOop(res)->byte_at_addr(0); 84 out->print_raw((const char*)addr, ba->length()); 85 86 return JNI_OK; 87} 88 89// Implementation of "properties" command. 90static jint get_system_properties(AttachOperation* op, outputStream* out) { 91 return get_properties(op, out, vmSymbolHandles::serializePropertiesToByteArray_name()); 92} 93 94// Implementation of "agent_properties" command. 95static jint get_agent_properties(AttachOperation* op, outputStream* out) { 96 return get_properties(op, out, vmSymbolHandles::serializeAgentPropertiesToByteArray_name()); 97} 98 99// Implementation of "datadump" command. 100// 101// Raises a SIGBREAK signal so that VM dump threads, does deadlock detection, 102// etc. In theory this command should only post a DataDumpRequest to any 103// JVMTI environment that has enabled this event. However it's useful to 104// trigger the SIGBREAK handler. 105 106static jint data_dump(AttachOperation* op, outputStream* out) { 107 if (!ReduceSignalUsage) { 108 AttachListener::pd_data_dump(); 109 } else { 110 if (JvmtiExport::should_post_data_dump()) { 111 JvmtiExport::post_data_dump(); 112 } 113 } 114 return JNI_OK; 115} 116 117// Implementation of "threaddump" command - essentially a remote ctrl-break 118// 119static jint thread_dump(AttachOperation* op, outputStream* out) { 120 bool print_concurrent_locks = false; 121 if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) { 122 print_concurrent_locks = true; 123 } 124 125 // thread stacks 126 VM_PrintThreads op1(out, print_concurrent_locks); 127 VMThread::execute(&op1); 128 129 // JNI global handles 130 VM_PrintJNI op2(out); 131 VMThread::execute(&op2); 132 133 // Deadlock detection 134 VM_FindDeadlocks op3(out); 135 VMThread::execute(&op3); 136 137 return JNI_OK; 138} 139 140#ifndef SERVICES_KERNEL // Heap dumping not supported 141// Implementation of "dumpheap" command. 142// 143// Input arguments :- 144// arg0: Name of the dump file 145// arg1: "-live" or "-all" 146jint dump_heap(AttachOperation* op, outputStream* out) { 147 const char* path = op->arg(0); 148 if (path == NULL || path[0] == '\0') { 149 out->print_cr("No dump file specified"); 150 } else { 151 bool live_objects_only = true; // default is true to retain the behavior before this change is made 152 const char* arg1 = op->arg(1); 153 if (arg1 != NULL && (strlen(arg1) > 0)) { 154 if (strcmp(arg1, "-all") != 0 && strcmp(arg1, "-live") != 0) { 155 out->print_cr("Invalid argument to dumpheap operation: %s", arg1); 156 return JNI_ERR; 157 } 158 live_objects_only = strcmp(arg1, "-live") == 0; 159 } 160 161 // Request a full GC before heap dump if live_objects_only = true 162 // This helps reduces the amount of unreachable objects in the dump 163 // and makes it easier to browse. 164 HeapDumper dumper(live_objects_only /* request GC */); 165 int res = dumper.dump(op->arg(0)); 166 if (res == 0) { 167 out->print_cr("Heap dump file created"); 168 } else { 169 // heap dump failed 170 ResourceMark rm; 171 char* error = dumper.error_as_C_string(); 172 if (error == NULL) { 173 out->print_cr("Dump failed - reason unknown"); 174 } else { 175 out->print_cr("%s", error); 176 } 177 } 178 } 179 return JNI_OK; 180} 181#endif // SERVICES_KERNEL 182 183// Implementation of "inspectheap" command 184// 185// Input arguments :- 186// arg0: "-live" or "-all" 187static jint heap_inspection(AttachOperation* op, outputStream* out) { 188 bool live_objects_only = true; // default is true to retain the behavior before this change is made 189 const char* arg0 = op->arg(0); 190 if (arg0 != NULL && (strlen(arg0) > 0)) { 191 if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) { 192 out->print_cr("Invalid argument to inspectheap operation: %s", arg0); 193 return JNI_ERR; 194 } 195 live_objects_only = strcmp(arg0, "-live") == 0; 196 } 197 VM_GC_HeapInspection heapop(out, live_objects_only /* request gc */); 198 VMThread::execute(&heapop); 199 return JNI_OK; 200} 201 202// set a boolean global flag using value from AttachOperation 203static jint set_bool_flag(const char* name, AttachOperation* op, outputStream* out) { 204 bool value = true; 205 const char* arg1; 206 if ((arg1 = op->arg(1)) != NULL) { 207 int tmp; 208 int n = sscanf(arg1, "%d", &tmp); 209 if (n != 1) { 210 out->print_cr("flag value has to be boolean (1 or 0)"); 211 return JNI_ERR; 212 } 213 value = (tmp != 0); 214 } 215 bool res = CommandLineFlags::boolAtPut((char*)name, &value, ATTACH_ON_DEMAND); 216 if (! res) { 217 out->print_cr("setting flag %s failed", name); 218 } 219 return res? JNI_OK : JNI_ERR; 220} 221 222// set a intx global flag using value from AttachOperation 223static jint set_intx_flag(const char* name, AttachOperation* op, outputStream* out) { 224 intx value; 225 const char* arg1; 226 if ((arg1 = op->arg(1)) != NULL) { 227 int n = sscanf(arg1, INTX_FORMAT, &value); 228 if (n != 1) { 229 out->print_cr("flag value has to be integer"); 230 return JNI_ERR; 231 } 232 } 233 bool res = CommandLineFlags::intxAtPut((char*)name, &value, ATTACH_ON_DEMAND); 234 if (! res) { 235 out->print_cr("setting flag %s failed", name); 236 } 237 238 return res? JNI_OK : JNI_ERR; 239} 240 241// set a uintx global flag using value from AttachOperation 242static jint set_uintx_flag(const char* name, AttachOperation* op, outputStream* out) { 243 uintx value; 244 const char* arg1; 245 if ((arg1 = op->arg(1)) != NULL) { 246 int n = sscanf(arg1, UINTX_FORMAT, &value); 247 if (n != 1) { 248 out->print_cr("flag value has to be integer"); 249 return JNI_ERR; 250 } 251 } 252 bool res = CommandLineFlags::uintxAtPut((char*)name, &value, ATTACH_ON_DEMAND); 253 if (! res) { 254 out->print_cr("setting flag %s failed", name); 255 } 256 257 return res? JNI_OK : JNI_ERR; 258} 259 260// set a string global flag using value from AttachOperation 261static jint set_ccstr_flag(const char* name, AttachOperation* op, outputStream* out) { 262 const char* value; 263 if ((value = op->arg(1)) == NULL) { 264 out->print_cr("flag value has to be a string"); 265 return JNI_ERR; 266 } 267 bool res = CommandLineFlags::ccstrAtPut((char*)name, &value, ATTACH_ON_DEMAND); 268 if (res) { 269 FREE_C_HEAP_ARRAY(char, value); 270 } else { 271 out->print_cr("setting flag %s failed", name); 272 } 273 274 return res? JNI_OK : JNI_ERR; 275} 276 277// Implementation of "setflag" command 278static jint set_flag(AttachOperation* op, outputStream* out) { 279 280 const char* name = NULL; 281 if ((name = op->arg(0)) == NULL) { 282 out->print_cr("flag name is missing"); 283 return JNI_ERR; 284 } 285 286 Flag* f = Flag::find_flag((char*)name, strlen(name)); 287 if (f && f->is_external() && f->is_writeable()) { 288 if (f->is_bool()) { 289 return set_bool_flag(name, op, out); 290 } else if (f->is_intx()) { 291 return set_intx_flag(name, op, out); 292 } else if (f->is_uintx()) { 293 return set_uintx_flag(name, op, out); 294 } else if (f->is_ccstr()) { 295 return set_ccstr_flag(name, op, out); 296 } else { 297 ShouldNotReachHere(); 298 return JNI_ERR; 299 } 300 } else { 301 return AttachListener::pd_set_flag(op, out); 302 } 303} 304 305// Implementation of "printflag" command 306static jint print_flag(AttachOperation* op, outputStream* out) { 307 const char* name = NULL; 308 if ((name = op->arg(0)) == NULL) { 309 out->print_cr("flag name is missing"); 310 return JNI_ERR; 311 } 312 Flag* f = Flag::find_flag((char*)name, strlen(name)); 313 if (f) { 314 f->print_as_flag(out); 315 out->print_cr(""); 316 } else { 317 out->print_cr("no such flag '%s'", name); 318 } 319 return JNI_OK; 320} 321 322// Table to map operation names to functions. 323 324// names must be of length <= AttachOperation::name_length_max 325static AttachOperationFunctionInfo funcs[] = { 326 { "agentProperties", get_agent_properties }, 327 { "datadump", data_dump }, 328#ifndef SERVICES_KERNEL 329 { "dumpheap", dump_heap }, 330#endif // SERVICES_KERNEL 331 { "load", JvmtiExport::load_agent_library }, 332 { "properties", get_system_properties }, 333 { "threaddump", thread_dump }, 334 { "inspectheap", heap_inspection }, 335 { "setflag", set_flag }, 336 { "printflag", print_flag }, 337 { NULL, NULL } 338}; 339 340 341 342// The Attach Listener threads services a queue. It dequeues an operation 343// from the queue, examines the operation name (command), and dispatches 344// to the corresponding function to perform the operation. 345 346static void attach_listener_thread_entry(JavaThread* thread, TRAPS) { 347 os::set_priority(thread, NearMaxPriority); 348 349 if (AttachListener::pd_init() != 0) { 350 return; 351 } 352 AttachListener::set_initialized(); 353 354 for (;;) { 355 AttachOperation* op = AttachListener::dequeue(); 356 if (op == NULL) { 357 return; // dequeue failed or shutdown 358 } 359 360 ResourceMark rm; 361 bufferedStream st; 362 jint res = JNI_OK; 363 364 // handle special detachall operation 365 if (strcmp(op->name(), AttachOperation::detachall_operation_name()) == 0) { 366 AttachListener::detachall(); 367 } else { 368 // find the function to dispatch too 369 AttachOperationFunctionInfo* info = NULL; 370 for (int i=0; funcs[i].name != NULL; i++) { 371 const char* name = funcs[i].name; 372 assert(strlen(name) <= AttachOperation::name_length_max, "operation <= name_length_max"); 373 if (strcmp(op->name(), name) == 0) { 374 info = &(funcs[i]); 375 break; 376 } 377 } 378 379 // check for platform dependent attach operation 380 if (info == NULL) { 381 info = AttachListener::pd_find_operation(op->name()); 382 } 383 384 if (info != NULL) { 385 // dispatch to the function that implements this operation 386 res = (info->func)(op, &st); 387 } else { 388 st.print("Operation %s not recognized!", op->name()); 389 res = JNI_ERR; 390 } 391 } 392 393 // operation complete - send result and output to client 394 op->complete(res, &st); 395 } 396} 397 398// Starts the Attach Listener thread 399void AttachListener::init() { 400 EXCEPTION_MARK; 401 klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK); 402 instanceKlassHandle klass (THREAD, k); 403 instanceHandle thread_oop = klass->allocate_instance_handle(CHECK); 404 405 const char thread_name[] = "Attach Listener"; 406 Handle string = java_lang_String::create_from_str(thread_name, CHECK); 407 408 // Initialize thread_oop to put it into the system threadGroup 409 Handle thread_group (THREAD, Universe::system_thread_group()); 410 JavaValue result(T_VOID); 411 JavaCalls::call_special(&result, thread_oop, 412 klass, 413 vmSymbolHandles::object_initializer_name(), 414 vmSymbolHandles::threadgroup_string_void_signature(), 415 thread_group, 416 string, 417 CHECK); 418 419 KlassHandle group(THREAD, SystemDictionary::threadGroup_klass()); 420 JavaCalls::call_special(&result, 421 thread_group, 422 group, 423 vmSymbolHandles::add_method_name(), 424 vmSymbolHandles::thread_void_signature(), 425 thread_oop, // ARG 1 426 CHECK); 427 428 { MutexLocker mu(Threads_lock); 429 JavaThread* listener_thread = new JavaThread(&attach_listener_thread_entry); 430 431 // Check that thread and osthread were created 432 if (listener_thread == NULL || listener_thread->osthread() == NULL) { 433 vm_exit_during_initialization("java.lang.OutOfMemoryError", 434 "unable to create new native thread"); 435 } 436 437 java_lang_Thread::set_thread(thread_oop(), listener_thread); 438 java_lang_Thread::set_daemon(thread_oop()); 439 440 listener_thread->set_threadObj(thread_oop()); 441 Threads::add(listener_thread); 442 Thread::start(listener_thread); 443 } 444} 445 446// Performs clean-up tasks on platforms where we can detect that the last 447// client has detached 448void AttachListener::detachall() { 449 // call the platform dependent clean-up 450 pd_detachall(); 451} 452