duke@435: /* stefank@2314: * Copyright (c) 2005, 2010, 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: stefank@2314: #include "precompiled.hpp" stefank@2314: #include "runtime/interfaceSupport.hpp" stefank@2314: #include "runtime/os.hpp" stefank@2314: #include "services/attachListener.hpp" stefank@2314: #include "services/dtraceAttacher.hpp" duke@435: duke@435: #include duke@435: #include // SIGBREAK duke@435: duke@435: // The AttachListener thread services a queue of operations. It blocks in the dequeue duke@435: // function until an operation is enqueued. A client enqueues an operation by creating duke@435: // a thread in this process using the Win32 CreateRemoteThread function. That thread duke@435: // executes a small stub generated by the client. The stub invokes the duke@435: // JVM_EnqueueOperation function which checks the operation parameters and enqueues duke@435: // the operation to the queue serviced by the attach listener. The thread created by duke@435: // the client is a native thread and is restricted to a single page of stack. To keep duke@435: // it simple operations are pre-allocated at initialization time. An enqueue thus duke@435: // takes a preallocated operation, populates the operation parameters, adds it to duke@435: // queue and wakes up the attach listener. duke@435: // duke@435: // When an operation has completed the attach listener is required to send the duke@435: // operation result and any result data to the client. In this implementation the duke@435: // client is a pipe server. In the enqueue operation it provides the name of pipe duke@435: // to this process. When the operation is completed this process opens the pipe and duke@435: // sends the result and output back to the client. Note that writing to the pipe duke@435: // (and flushing the output) is a blocking operation. This means that a non-responsive duke@435: // client could potentially hang the attach listener thread indefinitely. In that duke@435: // case no new operations would be executed but the VM would continue as normal. duke@435: // As only suitably privileged processes can open this process we concluded that duke@435: // this wasn't worth worrying about. duke@435: duke@435: duke@435: // forward reference duke@435: class Win32AttachOperation; duke@435: duke@435: duke@435: class Win32AttachListener: AllStatic { duke@435: private: duke@435: enum { duke@435: preallocate_count = 4 // number of preallocated operations duke@435: }; duke@435: duke@435: // protects the preallocated list and the operation list duke@435: static HANDLE _mutex; duke@435: duke@435: // head of preallocated operations list duke@435: static Win32AttachOperation* _avail; duke@435: duke@435: // head and tail of enqueue operations list duke@435: static Win32AttachOperation* _head; duke@435: static Win32AttachOperation* _tail; duke@435: duke@435: duke@435: static Win32AttachOperation* head() { return _head; } duke@435: static void set_head(Win32AttachOperation* head) { _head = head; } duke@435: duke@435: static Win32AttachOperation* tail() { return _tail; } duke@435: static void set_tail(Win32AttachOperation* tail) { _tail = tail; } duke@435: duke@435: duke@435: // used to wakeup the listener duke@435: static HANDLE _wakeup; duke@435: static HANDLE wakeup() { return _wakeup; } duke@435: duke@435: public: duke@435: enum { duke@435: ATTACH_ERROR_DISABLED = 100, // error codes duke@435: ATTACH_ERROR_RESOURCE = 101, duke@435: ATTACH_ERROR_ILLEGALARG = 102, duke@435: ATTACH_ERROR_INTERNAL = 103 duke@435: }; duke@435: duke@435: static int init(); duke@435: static HANDLE mutex() { return _mutex; } duke@435: duke@435: static Win32AttachOperation* available() { return _avail; } duke@435: static void set_available(Win32AttachOperation* avail) { _avail = avail; } duke@435: duke@435: // enqueue an operation to the end of the list duke@435: static int enqueue(char* cmd, char* arg1, char* arg2, char* arg3, char* pipename); duke@435: duke@435: // dequeue an operation from from head of the list duke@435: static Win32AttachOperation* dequeue(); duke@435: }; duke@435: duke@435: // statics duke@435: HANDLE Win32AttachListener::_mutex; duke@435: HANDLE Win32AttachListener::_wakeup; duke@435: Win32AttachOperation* Win32AttachListener::_avail; duke@435: Win32AttachOperation* Win32AttachListener::_head; duke@435: Win32AttachOperation* Win32AttachListener::_tail; duke@435: duke@435: duke@435: // Win32AttachOperation is an AttachOperation that additionally encapsulates the name duke@435: // of a pipe which is used to send the operation reply/output to the client. duke@435: // Win32AttachOperation can also be linked in a list. duke@435: duke@435: class Win32AttachOperation: public AttachOperation { duke@435: private: duke@435: friend class Win32AttachListener; duke@435: duke@435: enum { duke@435: pipe_name_max = 256 // maximum pipe name duke@435: }; duke@435: duke@435: char _pipe[pipe_name_max+1]; duke@435: duke@435: const char* pipe() const { return _pipe; } duke@435: void set_pipe(const char* pipe) { duke@435: assert(strlen(pipe) <= pipe_name_max, "execeds maximum length of pipe name"); duke@435: strcpy(_pipe, pipe); duke@435: } duke@435: duke@435: HANDLE open_pipe(); duke@435: static BOOL write_pipe(HANDLE hPipe, char* buf, int len); duke@435: duke@435: Win32AttachOperation* _next; duke@435: duke@435: Win32AttachOperation* next() const { return _next; } duke@435: void set_next(Win32AttachOperation* next) { _next = next; } duke@435: duke@435: // noarg constructor as operation is preallocated duke@435: Win32AttachOperation() : AttachOperation("") { duke@435: set_pipe(""); duke@435: set_next(NULL); duke@435: } duke@435: duke@435: public: duke@435: void Win32AttachOperation::complete(jint result, bufferedStream* result_stream); duke@435: }; duke@435: duke@435: duke@435: // preallocate the required number of operations duke@435: int Win32AttachListener::init() { duke@435: _mutex = (void*)::CreateMutex(NULL, FALSE, NULL); duke@435: guarantee(_mutex != (HANDLE)NULL, "mutex creation failed"); duke@435: duke@435: _wakeup = ::CreateSemaphore(NULL, 0, 1, NULL); duke@435: guarantee(_wakeup != (HANDLE)NULL, "semaphore creation failed"); duke@435: duke@435: set_head(NULL); duke@435: set_tail(NULL); duke@435: duke@435: // preallocate a few operations duke@435: set_available(NULL); duke@435: for (int i=0; iset_next(available()); duke@435: set_available(op); duke@435: } duke@435: duke@435: return 0; duke@435: } duke@435: duke@435: // Enqueue an operation. This is called from a native thread that is not attached to VM. duke@435: // Also we need to be careful not to execute anything that results in more than a 4k stack. duke@435: // duke@435: int Win32AttachListener::enqueue(char* cmd, char* arg0, char* arg1, char* arg2, char* pipename) { duke@435: // listener not running duke@435: if (!AttachListener::is_initialized()) { duke@435: return ATTACH_ERROR_DISABLED; duke@435: } duke@435: duke@435: // check that all paramteres to the operation duke@435: if (strlen(cmd) > AttachOperation::name_length_max) return ATTACH_ERROR_ILLEGALARG; duke@435: if (strlen(arg0) > AttachOperation::arg_length_max) return ATTACH_ERROR_ILLEGALARG; duke@435: if (strlen(arg0) > AttachOperation::arg_length_max) return ATTACH_ERROR_ILLEGALARG; duke@435: if (strlen(pipename) > Win32AttachOperation::pipe_name_max) return ATTACH_ERROR_ILLEGALARG; duke@435: duke@435: // check for a well-formed pipename duke@435: if (strstr(pipename, "\\\\.\\pipe\\") != pipename) return ATTACH_ERROR_ILLEGALARG; duke@435: duke@435: // grab the lock for the list duke@435: DWORD res = ::WaitForSingleObject(mutex(), INFINITE); duke@435: if (res != WAIT_OBJECT_0) { duke@435: return ATTACH_ERROR_INTERNAL; duke@435: } duke@435: duke@435: // try to get an operation from the available list duke@435: Win32AttachOperation* op = available(); duke@435: if (op != NULL) { duke@435: set_available(op->next()); duke@435: duke@435: // add to end (tail) of list duke@435: op->set_next(NULL); duke@435: if (tail() == NULL) { duke@435: set_head(op); duke@435: } else { duke@435: tail()->set_next(op); duke@435: } duke@435: set_tail(op); duke@435: duke@435: op->set_name(cmd); duke@435: op->set_arg(0, arg0); duke@435: op->set_arg(1, arg1); duke@435: op->set_arg(2, arg2); duke@435: op->set_pipe(pipename); duke@435: duke@435: // wakeup the thread waiting for operations duke@435: ::ReleaseSemaphore(wakeup(), 1, NULL); duke@435: } duke@435: ::ReleaseMutex(mutex()); duke@435: duke@435: return (op != NULL) ? 0 : ATTACH_ERROR_RESOURCE; duke@435: } duke@435: duke@435: duke@435: // dequeue the operation from the head of the operation list. If duke@435: Win32AttachOperation* Win32AttachListener::dequeue() { duke@435: for (;;) { duke@435: DWORD res = ::WaitForSingleObject(wakeup(), INFINITE); duke@435: guarantee(res == WAIT_OBJECT_0, "wait failed"); duke@435: duke@435: res = ::WaitForSingleObject(mutex(), INFINITE); duke@435: guarantee(res == WAIT_OBJECT_0, "wait failed"); duke@435: duke@435: Win32AttachOperation* op = head(); duke@435: if (op != NULL) { duke@435: set_head(op->next()); duke@435: if (head() == NULL) { // list is empty duke@435: set_tail(NULL); duke@435: } duke@435: } duke@435: ::ReleaseMutex(mutex()); duke@435: duke@435: if (op != NULL) { duke@435: return op; duke@435: } duke@435: } duke@435: } duke@435: duke@435: duke@435: // open the pipe to the client duke@435: HANDLE Win32AttachOperation::open_pipe() { duke@435: HANDLE hPipe; duke@435: duke@435: hPipe = ::CreateFile( pipe(), // pipe name duke@435: GENERIC_WRITE, // write only duke@435: 0, // no sharing duke@435: NULL, // default security attributes duke@435: OPEN_EXISTING, // opens existing pipe duke@435: 0, // default attributes duke@435: NULL); // no template file duke@435: duke@435: if (hPipe != INVALID_HANDLE_VALUE) { duke@435: // shouldn't happen as there is a pipe created per operation duke@435: if (::GetLastError() == ERROR_PIPE_BUSY) { duke@435: return INVALID_HANDLE_VALUE; duke@435: } duke@435: } duke@435: return hPipe; duke@435: } duke@435: duke@435: // write to the pipe duke@435: BOOL Win32AttachOperation::write_pipe(HANDLE hPipe, char* buf, int len) { duke@435: do { duke@435: DWORD nwrote; duke@435: duke@435: BOOL fSuccess = WriteFile( hPipe, // pipe handle duke@435: (LPCVOID)buf, // message duke@435: (DWORD)len, // message length duke@435: &nwrote, // bytes written duke@435: NULL); // not overlapped duke@435: if (!fSuccess) { duke@435: return fSuccess; duke@435: } duke@435: buf += nwrote; duke@435: len -= nwrote; duke@435: } duke@435: while (len > 0); duke@435: return TRUE; duke@435: } duke@435: duke@435: // Complete the operation: duke@435: // - open the pipe to the client duke@435: // - write the operation result (a jint) duke@435: // - write the operation output (the result stream) duke@435: // duke@435: void Win32AttachOperation::complete(jint result, bufferedStream* result_stream) { duke@435: JavaThread* thread = JavaThread::current(); duke@435: ThreadBlockInVM tbivm(thread); duke@435: duke@435: thread->set_suspend_equivalent(); duke@435: // cleared by handle_special_suspend_equivalent_condition() or duke@435: // java_suspend_self() via check_and_wait_while_suspended() duke@435: duke@435: HANDLE hPipe = open_pipe(); duke@435: if (hPipe != INVALID_HANDLE_VALUE) { duke@435: BOOL fSuccess; duke@435: duke@435: char msg[32]; duke@435: sprintf(msg, "%d\n", result); duke@435: duke@435: fSuccess = write_pipe(hPipe, msg, (int)strlen(msg)); duke@435: if (fSuccess) { duke@435: write_pipe(hPipe, (char*) result_stream->base(), (int)(result_stream->size())); duke@435: } duke@435: duke@435: // Need to flush buffers duke@435: FlushFileBuffers(hPipe); duke@435: CloseHandle(hPipe); duke@435: } duke@435: duke@435: DWORD res = ::WaitForSingleObject(Win32AttachListener::mutex(), INFINITE); duke@435: if (res == WAIT_OBJECT_0) { duke@435: duke@435: // put the operation back on the available list duke@435: set_next(Win32AttachListener::available()); duke@435: Win32AttachListener::set_available(this); duke@435: duke@435: ::ReleaseMutex(Win32AttachListener::mutex()); duke@435: } duke@435: duke@435: // were we externally suspended while we were waiting? duke@435: thread->check_and_wait_while_suspended(); duke@435: } duke@435: duke@435: duke@435: // AttachOperation functions duke@435: duke@435: AttachOperation* AttachListener::dequeue() { duke@435: JavaThread* thread = JavaThread::current(); duke@435: ThreadBlockInVM tbivm(thread); duke@435: duke@435: thread->set_suspend_equivalent(); duke@435: // cleared by handle_special_suspend_equivalent_condition() or duke@435: // java_suspend_self() via check_and_wait_while_suspended() duke@435: duke@435: AttachOperation* op = Win32AttachListener::dequeue(); duke@435: duke@435: // were we externally suspended while we were waiting? duke@435: thread->check_and_wait_while_suspended(); duke@435: duke@435: return op; duke@435: } duke@435: duke@435: int AttachListener::pd_init() { duke@435: return Win32AttachListener::init(); duke@435: } duke@435: duke@435: // always startup on Windows NT/2000/XP duke@435: bool AttachListener::init_at_startup() { duke@435: return os::win32::is_nt(); duke@435: } duke@435: duke@435: // no trigger mechanism on Windows to start Attach Listener lazily duke@435: bool AttachListener::is_init_trigger() { duke@435: return false; duke@435: } duke@435: duke@435: void AttachListener::abort() { duke@435: // nothing to do duke@435: } duke@435: duke@435: void AttachListener::pd_data_dump() { duke@435: os::signal_notify(SIGBREAK); duke@435: } duke@435: duke@435: AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* n) { duke@435: return NULL; duke@435: } duke@435: duke@435: jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { duke@435: out->print_cr("flag '%s' cannot be changed", op->arg(0)); duke@435: return JNI_ERR; duke@435: } duke@435: duke@435: void AttachListener::pd_detachall() { duke@435: // do nothing for now duke@435: } duke@435: duke@435: // Native thread started by remote client executes this. duke@435: extern "C" { duke@435: JNIEXPORT jint JNICALL duke@435: JVM_EnqueueOperation(char* cmd, char* arg0, char* arg1, char* arg2, char* pipename) { duke@435: return (jint)Win32AttachListener::enqueue(cmd, arg0, arg1, arg2, pipename); duke@435: } duke@435: duke@435: } // extern