duke@435: /* trims@1907: * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * trims@1907: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA trims@1907: * or visit www.oracle.com if you need additional information or have any trims@1907: * questions. duke@435: * duke@435: */ duke@435: duke@435: # include "incls/_precompiled.incl" duke@435: # include "incls/_attachListener.cpp.incl" duke@435: duke@435: volatile bool AttachListener::_initialized; duke@435: duke@435: // Implementation of "properties" command. duke@435: // duke@435: // Invokes sun.misc.VMSupport.serializePropertiesToByteArray to serialize duke@435: // the system properties into a byte array. duke@435: duke@435: static klassOop load_and_initialize_klass(symbolHandle sh, TRAPS) { duke@435: klassOop k = SystemDictionary::resolve_or_fail(sh, true, CHECK_NULL); duke@435: instanceKlassHandle ik (THREAD, k); duke@435: if (ik->should_be_initialized()) { duke@435: ik->initialize(CHECK_NULL); duke@435: } duke@435: return ik(); duke@435: } duke@435: duke@435: static jint get_properties(AttachOperation* op, outputStream* out, symbolHandle serializePropertiesMethod) { duke@435: Thread* THREAD = Thread::current(); duke@435: HandleMark hm; duke@435: duke@435: // load sun.misc.VMSupport duke@435: symbolHandle klass = vmSymbolHandles::sun_misc_VMSupport(); duke@435: klassOop k = load_and_initialize_klass(klass, THREAD); duke@435: if (HAS_PENDING_EXCEPTION) { duke@435: java_lang_Throwable::print(PENDING_EXCEPTION, out); duke@435: CLEAR_PENDING_EXCEPTION; duke@435: return JNI_ERR; duke@435: } duke@435: instanceKlassHandle ik(THREAD, k); duke@435: duke@435: // invoke the serializePropertiesToByteArray method duke@435: JavaValue result(T_OBJECT); duke@435: JavaCallArguments args; duke@435: duke@435: duke@435: symbolHandle signature = vmSymbolHandles::serializePropertiesToByteArray_signature(); duke@435: JavaCalls::call_static(&result, duke@435: ik, duke@435: serializePropertiesMethod, duke@435: signature, duke@435: &args, duke@435: THREAD); duke@435: if (HAS_PENDING_EXCEPTION) { duke@435: java_lang_Throwable::print(PENDING_EXCEPTION, out); duke@435: CLEAR_PENDING_EXCEPTION; duke@435: return JNI_ERR; duke@435: } duke@435: duke@435: // The result should be a [B duke@435: oop res = (oop)result.get_jobject(); duke@435: assert(res->is_typeArray(), "just checking"); duke@435: assert(typeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking"); duke@435: duke@435: // copy the bytes to the output stream duke@435: typeArrayOop ba = typeArrayOop(res); duke@435: jbyte* addr = typeArrayOop(res)->byte_at_addr(0); duke@435: out->print_raw((const char*)addr, ba->length()); duke@435: duke@435: return JNI_OK; duke@435: } duke@435: duke@435: // Implementation of "properties" command. duke@435: static jint get_system_properties(AttachOperation* op, outputStream* out) { duke@435: return get_properties(op, out, vmSymbolHandles::serializePropertiesToByteArray_name()); duke@435: } duke@435: duke@435: // Implementation of "agent_properties" command. duke@435: static jint get_agent_properties(AttachOperation* op, outputStream* out) { duke@435: return get_properties(op, out, vmSymbolHandles::serializeAgentPropertiesToByteArray_name()); duke@435: } duke@435: duke@435: // Implementation of "datadump" command. duke@435: // duke@435: // Raises a SIGBREAK signal so that VM dump threads, does deadlock detection, duke@435: // etc. In theory this command should only post a DataDumpRequest to any duke@435: // JVMTI environment that has enabled this event. However it's useful to duke@435: // trigger the SIGBREAK handler. duke@435: duke@435: static jint data_dump(AttachOperation* op, outputStream* out) { duke@435: if (!ReduceSignalUsage) { duke@435: AttachListener::pd_data_dump(); duke@435: } else { duke@435: if (JvmtiExport::should_post_data_dump()) { duke@435: JvmtiExport::post_data_dump(); duke@435: } duke@435: } duke@435: return JNI_OK; duke@435: } duke@435: duke@435: // Implementation of "threaddump" command - essentially a remote ctrl-break duke@435: // duke@435: static jint thread_dump(AttachOperation* op, outputStream* out) { duke@435: bool print_concurrent_locks = false; duke@435: if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) { duke@435: print_concurrent_locks = true; duke@435: } duke@435: duke@435: // thread stacks duke@435: VM_PrintThreads op1(out, print_concurrent_locks); duke@435: VMThread::execute(&op1); duke@435: duke@435: // JNI global handles duke@435: VM_PrintJNI op2(out); duke@435: VMThread::execute(&op2); duke@435: duke@435: // Deadlock detection duke@435: VM_FindDeadlocks op3(out); duke@435: VMThread::execute(&op3); duke@435: duke@435: return JNI_OK; duke@435: } duke@435: duke@435: #ifndef SERVICES_KERNEL // Heap dumping not supported duke@435: // Implementation of "dumpheap" command. duke@435: // duke@435: // Input arguments :- duke@435: // arg0: Name of the dump file duke@435: // arg1: "-live" or "-all" duke@435: jint dump_heap(AttachOperation* op, outputStream* out) { duke@435: const char* path = op->arg(0); duke@435: if (path == NULL || path[0] == '\0') { duke@435: out->print_cr("No dump file specified"); duke@435: } else { duke@435: bool live_objects_only = true; // default is true to retain the behavior before this change is made duke@435: const char* arg1 = op->arg(1); duke@435: if (arg1 != NULL && (strlen(arg1) > 0)) { duke@435: if (strcmp(arg1, "-all") != 0 && strcmp(arg1, "-live") != 0) { duke@435: out->print_cr("Invalid argument to dumpheap operation: %s", arg1); duke@435: return JNI_ERR; duke@435: } duke@435: live_objects_only = strcmp(arg1, "-live") == 0; duke@435: } duke@435: duke@435: // Request a full GC before heap dump if live_objects_only = true duke@435: // This helps reduces the amount of unreachable objects in the dump duke@435: // and makes it easier to browse. duke@435: HeapDumper dumper(live_objects_only /* request GC */); duke@435: int res = dumper.dump(op->arg(0)); duke@435: if (res == 0) { duke@435: out->print_cr("Heap dump file created"); duke@435: } else { duke@435: // heap dump failed duke@435: ResourceMark rm; duke@435: char* error = dumper.error_as_C_string(); duke@435: if (error == NULL) { duke@435: out->print_cr("Dump failed - reason unknown"); duke@435: } else { duke@435: out->print_cr("%s", error); duke@435: } duke@435: } duke@435: } duke@435: return JNI_OK; duke@435: } duke@435: #endif // SERVICES_KERNEL duke@435: duke@435: // Implementation of "inspectheap" command duke@435: // duke@435: // Input arguments :- duke@435: // arg0: "-live" or "-all" duke@435: static jint heap_inspection(AttachOperation* op, outputStream* out) { duke@435: bool live_objects_only = true; // default is true to retain the behavior before this change is made duke@435: const char* arg0 = op->arg(0); duke@435: if (arg0 != NULL && (strlen(arg0) > 0)) { duke@435: if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) { duke@435: out->print_cr("Invalid argument to inspectheap operation: %s", arg0); duke@435: return JNI_ERR; duke@435: } duke@435: live_objects_only = strcmp(arg0, "-live") == 0; duke@435: } ysr@1050: VM_GC_HeapInspection heapop(out, live_objects_only /* request full gc */, true /* need_prologue */); duke@435: VMThread::execute(&heapop); duke@435: return JNI_OK; duke@435: } duke@435: duke@435: // set a boolean global flag using value from AttachOperation duke@435: static jint set_bool_flag(const char* name, AttachOperation* op, outputStream* out) { duke@435: bool value = true; duke@435: const char* arg1; duke@435: if ((arg1 = op->arg(1)) != NULL) { duke@435: int tmp; duke@435: int n = sscanf(arg1, "%d", &tmp); duke@435: if (n != 1) { phh@1502: out->print_cr("flag value must be a boolean (1 or 0)"); duke@435: return JNI_ERR; duke@435: } duke@435: value = (tmp != 0); duke@435: } duke@435: bool res = CommandLineFlags::boolAtPut((char*)name, &value, ATTACH_ON_DEMAND); duke@435: if (! res) { duke@435: out->print_cr("setting flag %s failed", name); duke@435: } duke@435: return res? JNI_OK : JNI_ERR; duke@435: } duke@435: duke@435: // set a intx global flag using value from AttachOperation duke@435: static jint set_intx_flag(const char* name, AttachOperation* op, outputStream* out) { duke@435: intx value; duke@435: const char* arg1; duke@435: if ((arg1 = op->arg(1)) != NULL) { duke@435: int n = sscanf(arg1, INTX_FORMAT, &value); duke@435: if (n != 1) { phh@1502: out->print_cr("flag value must be an integer"); duke@435: return JNI_ERR; duke@435: } duke@435: } phh@1502: bool res = CommandLineFlags::intxAtPut((char*)name, &value, ATTACH_ON_DEMAND); duke@435: if (! res) { duke@435: out->print_cr("setting flag %s failed", name); duke@435: } duke@435: duke@435: return res? JNI_OK : JNI_ERR; duke@435: } duke@435: duke@435: // set a uintx global flag using value from AttachOperation duke@435: static jint set_uintx_flag(const char* name, AttachOperation* op, outputStream* out) { duke@435: uintx value; duke@435: const char* arg1; duke@435: if ((arg1 = op->arg(1)) != NULL) { duke@435: int n = sscanf(arg1, UINTX_FORMAT, &value); duke@435: if (n != 1) { phh@1502: out->print_cr("flag value must be an unsigned integer"); duke@435: return JNI_ERR; duke@435: } duke@435: } phh@1502: bool res = CommandLineFlags::uintxAtPut((char*)name, &value, ATTACH_ON_DEMAND); phh@1502: if (! res) { phh@1502: out->print_cr("setting flag %s failed", name); phh@1502: } phh@1502: phh@1502: return res? JNI_OK : JNI_ERR; phh@1502: } phh@1502: phh@1502: // set a uint64_t global flag using value from AttachOperation phh@1502: static jint set_uint64_t_flag(const char* name, AttachOperation* op, outputStream* out) { phh@1502: uint64_t value; phh@1502: const char* arg1; phh@1502: if ((arg1 = op->arg(1)) != NULL) { phh@1502: int n = sscanf(arg1, UINT64_FORMAT, &value); phh@1502: if (n != 1) { phh@1502: out->print_cr("flag value must be an unsigned 64-bit integer"); phh@1502: return JNI_ERR; phh@1502: } phh@1502: } phh@1502: bool res = CommandLineFlags::uint64_tAtPut((char*)name, &value, ATTACH_ON_DEMAND); duke@435: if (! res) { duke@435: out->print_cr("setting flag %s failed", name); duke@435: } duke@435: duke@435: return res? JNI_OK : JNI_ERR; duke@435: } duke@435: duke@435: // set a string global flag using value from AttachOperation duke@435: static jint set_ccstr_flag(const char* name, AttachOperation* op, outputStream* out) { duke@435: const char* value; duke@435: if ((value = op->arg(1)) == NULL) { phh@1502: out->print_cr("flag value must be a string"); duke@435: return JNI_ERR; duke@435: } phh@1502: bool res = CommandLineFlags::ccstrAtPut((char*)name, &value, ATTACH_ON_DEMAND); duke@435: if (res) { duke@435: FREE_C_HEAP_ARRAY(char, value); duke@435: } else { duke@435: out->print_cr("setting flag %s failed", name); duke@435: } duke@435: duke@435: return res? JNI_OK : JNI_ERR; duke@435: } duke@435: duke@435: // Implementation of "setflag" command duke@435: static jint set_flag(AttachOperation* op, outputStream* out) { duke@435: duke@435: const char* name = NULL; duke@435: if ((name = op->arg(0)) == NULL) { duke@435: out->print_cr("flag name is missing"); duke@435: return JNI_ERR; duke@435: } duke@435: duke@435: Flag* f = Flag::find_flag((char*)name, strlen(name)); duke@435: if (f && f->is_external() && f->is_writeable()) { duke@435: if (f->is_bool()) { duke@435: return set_bool_flag(name, op, out); duke@435: } else if (f->is_intx()) { duke@435: return set_intx_flag(name, op, out); duke@435: } else if (f->is_uintx()) { duke@435: return set_uintx_flag(name, op, out); phh@1502: } else if (f->is_uint64_t()) { phh@1502: return set_uint64_t_flag(name, op, out); duke@435: } else if (f->is_ccstr()) { duke@435: return set_ccstr_flag(name, op, out); duke@435: } else { duke@435: ShouldNotReachHere(); duke@435: return JNI_ERR; duke@435: } duke@435: } else { duke@435: return AttachListener::pd_set_flag(op, out); duke@435: } duke@435: } duke@435: duke@435: // Implementation of "printflag" command duke@435: static jint print_flag(AttachOperation* op, outputStream* out) { duke@435: const char* name = NULL; duke@435: if ((name = op->arg(0)) == NULL) { duke@435: out->print_cr("flag name is missing"); duke@435: return JNI_ERR; duke@435: } duke@435: Flag* f = Flag::find_flag((char*)name, strlen(name)); duke@435: if (f) { duke@435: f->print_as_flag(out); duke@435: out->print_cr(""); duke@435: } else { duke@435: out->print_cr("no such flag '%s'", name); duke@435: } duke@435: return JNI_OK; duke@435: } duke@435: duke@435: // Table to map operation names to functions. duke@435: duke@435: // names must be of length <= AttachOperation::name_length_max duke@435: static AttachOperationFunctionInfo funcs[] = { duke@435: { "agentProperties", get_agent_properties }, duke@435: { "datadump", data_dump }, duke@435: #ifndef SERVICES_KERNEL duke@435: { "dumpheap", dump_heap }, duke@435: #endif // SERVICES_KERNEL duke@435: { "load", JvmtiExport::load_agent_library }, duke@435: { "properties", get_system_properties }, duke@435: { "threaddump", thread_dump }, duke@435: { "inspectheap", heap_inspection }, duke@435: { "setflag", set_flag }, duke@435: { "printflag", print_flag }, duke@435: { NULL, NULL } duke@435: }; duke@435: duke@435: duke@435: duke@435: // The Attach Listener threads services a queue. It dequeues an operation duke@435: // from the queue, examines the operation name (command), and dispatches duke@435: // to the corresponding function to perform the operation. duke@435: duke@435: static void attach_listener_thread_entry(JavaThread* thread, TRAPS) { duke@435: os::set_priority(thread, NearMaxPriority); duke@435: duke@435: if (AttachListener::pd_init() != 0) { duke@435: return; duke@435: } duke@435: AttachListener::set_initialized(); duke@435: duke@435: for (;;) { duke@435: AttachOperation* op = AttachListener::dequeue(); duke@435: if (op == NULL) { duke@435: return; // dequeue failed or shutdown duke@435: } duke@435: duke@435: ResourceMark rm; duke@435: bufferedStream st; duke@435: jint res = JNI_OK; duke@435: duke@435: // handle special detachall operation duke@435: if (strcmp(op->name(), AttachOperation::detachall_operation_name()) == 0) { duke@435: AttachListener::detachall(); duke@435: } else { duke@435: // find the function to dispatch too duke@435: AttachOperationFunctionInfo* info = NULL; duke@435: for (int i=0; funcs[i].name != NULL; i++) { duke@435: const char* name = funcs[i].name; duke@435: assert(strlen(name) <= AttachOperation::name_length_max, "operation <= name_length_max"); duke@435: if (strcmp(op->name(), name) == 0) { duke@435: info = &(funcs[i]); duke@435: break; duke@435: } duke@435: } duke@435: duke@435: // check for platform dependent attach operation duke@435: if (info == NULL) { duke@435: info = AttachListener::pd_find_operation(op->name()); duke@435: } duke@435: duke@435: if (info != NULL) { duke@435: // dispatch to the function that implements this operation duke@435: res = (info->func)(op, &st); duke@435: } else { duke@435: st.print("Operation %s not recognized!", op->name()); duke@435: res = JNI_ERR; duke@435: } duke@435: } duke@435: duke@435: // operation complete - send result and output to client duke@435: op->complete(res, &st); duke@435: } duke@435: } duke@435: duke@435: // Starts the Attach Listener thread duke@435: void AttachListener::init() { duke@435: EXCEPTION_MARK; duke@435: klassOop k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(), true, CHECK); duke@435: instanceKlassHandle klass (THREAD, k); duke@435: instanceHandle thread_oop = klass->allocate_instance_handle(CHECK); duke@435: duke@435: const char thread_name[] = "Attach Listener"; duke@435: Handle string = java_lang_String::create_from_str(thread_name, CHECK); duke@435: duke@435: // Initialize thread_oop to put it into the system threadGroup duke@435: Handle thread_group (THREAD, Universe::system_thread_group()); duke@435: JavaValue result(T_VOID); duke@435: JavaCalls::call_special(&result, thread_oop, duke@435: klass, duke@435: vmSymbolHandles::object_initializer_name(), duke@435: vmSymbolHandles::threadgroup_string_void_signature(), duke@435: thread_group, duke@435: string, duke@435: CHECK); duke@435: never@1577: KlassHandle group(THREAD, SystemDictionary::ThreadGroup_klass()); duke@435: JavaCalls::call_special(&result, duke@435: thread_group, duke@435: group, duke@435: vmSymbolHandles::add_method_name(), duke@435: vmSymbolHandles::thread_void_signature(), duke@435: thread_oop, // ARG 1 duke@435: CHECK); duke@435: duke@435: { MutexLocker mu(Threads_lock); duke@435: JavaThread* listener_thread = new JavaThread(&attach_listener_thread_entry); duke@435: duke@435: // Check that thread and osthread were created duke@435: if (listener_thread == NULL || listener_thread->osthread() == NULL) { duke@435: vm_exit_during_initialization("java.lang.OutOfMemoryError", duke@435: "unable to create new native thread"); duke@435: } duke@435: duke@435: java_lang_Thread::set_thread(thread_oop(), listener_thread); duke@435: java_lang_Thread::set_daemon(thread_oop()); duke@435: duke@435: listener_thread->set_threadObj(thread_oop()); duke@435: Threads::add(listener_thread); duke@435: Thread::start(listener_thread); duke@435: } duke@435: } duke@435: duke@435: // Performs clean-up tasks on platforms where we can detect that the last duke@435: // client has detached duke@435: void AttachListener::detachall() { duke@435: // call the platform dependent clean-up duke@435: pd_detachall(); duke@435: }