duke@435: /* allwin@5412: * Copyright (c) 2005, 2013, 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 duke@435: #include duke@435: #include duke@435: #include duke@435: #include duke@435: duke@435: // stropts.h uses STR in stream ioctl defines duke@435: #undef STR duke@435: #include duke@435: #undef STR duke@435: #define STR(a) #a duke@435: duke@435: // The attach mechanism on Solaris is implemented using the Doors IPC duke@435: // mechanism. The first tool to attempt to attach causes the attach duke@435: // listener thread to startup. This thread creats a door that is duke@435: // associated with a function that enqueues an operation to the attach duke@435: // listener. The door is attached to a file in the file system so that duke@435: // client (tools) can locate it. To enqueue an operation to the VM the duke@435: // client calls through the door which invokes the enqueue function in duke@435: // this process. The credentials of the client are checked and if the duke@435: // effective uid matches this process then the operation is enqueued. duke@435: // When an operation completes the attach listener is required to send the duke@435: // operation result and any result data to the client. In this implementation duke@435: // the result is returned via a UNIX domain socket. A pair of connected duke@435: // sockets (socketpair) is created in the enqueue function and the file duke@435: // descriptor for one of the sockets is returned to the client as the duke@435: // return from the door call. The other end is retained in this process. duke@435: // When the operation completes the result is sent to the client and duke@435: // the socket is closed. duke@435: duke@435: // forward reference duke@435: class SolarisAttachOperation; duke@435: duke@435: class SolarisAttachListener: AllStatic { duke@435: private: duke@435: duke@435: // the path to which we attach the door file descriptor duke@435: static char _door_path[PATH_MAX+1]; duke@435: static volatile bool _has_door_path; duke@435: duke@435: // door descriptor returned by door_create duke@435: static int _door_descriptor; duke@435: duke@435: static void set_door_path(char* path) { duke@435: if (path == NULL) { duke@435: _has_door_path = false; duke@435: } else { duke@435: strncpy(_door_path, path, PATH_MAX); duke@435: _door_path[PATH_MAX] = '\0'; // ensure it's nul terminated duke@435: _has_door_path = true; duke@435: } duke@435: } duke@435: duke@435: static void set_door_descriptor(int dd) { _door_descriptor = dd; } duke@435: duke@435: // mutex to protect operation list duke@435: static mutex_t _mutex; duke@435: duke@435: // semaphore to wakeup listener thread duke@435: static sema_t _wakeup; duke@435: duke@435: static mutex_t* mutex() { return &_mutex; } duke@435: static sema_t* wakeup() { return &_wakeup; } duke@435: duke@435: // enqueued operation list duke@435: static SolarisAttachOperation* _head; duke@435: static SolarisAttachOperation* _tail; duke@435: duke@435: static SolarisAttachOperation* head() { return _head; } duke@435: static void set_head(SolarisAttachOperation* head) { _head = head; } duke@435: duke@435: static SolarisAttachOperation* tail() { return _tail; } duke@435: static void set_tail(SolarisAttachOperation* tail) { _tail = tail; } duke@435: duke@435: // create the door duke@435: static int create_door(); duke@435: duke@435: public: duke@435: enum { duke@435: ATTACH_PROTOCOL_VER = 1 // protocol version duke@435: }; duke@435: enum { duke@435: ATTACH_ERROR_BADREQUEST = 100, // error code returned by duke@435: ATTACH_ERROR_BADVERSION = 101, // the door call duke@435: ATTACH_ERROR_RESOURCE = 102, duke@435: ATTACH_ERROR_INTERNAL = 103, duke@435: ATTACH_ERROR_DENIED = 104 duke@435: }; duke@435: duke@435: // initialize the listener duke@435: static int init(); duke@435: duke@435: static bool has_door_path() { return _has_door_path; } duke@435: static char* door_path() { return _door_path; } duke@435: static int door_descriptor() { return _door_descriptor; } duke@435: duke@435: // enqueue an operation duke@435: static void enqueue(SolarisAttachOperation* op); duke@435: duke@435: // dequeue an operation duke@435: static SolarisAttachOperation* dequeue(); duke@435: }; duke@435: duke@435: duke@435: // SolarisAttachOperation is an AttachOperation that additionally encapsulates duke@435: // a socket connection to the requesting client/tool. SolarisAttachOperation duke@435: // can additionally be held in a linked list. duke@435: duke@435: class SolarisAttachOperation: public AttachOperation { duke@435: private: duke@435: friend class SolarisAttachListener; duke@435: duke@435: // connection to client duke@435: int _socket; duke@435: duke@435: // linked list support duke@435: SolarisAttachOperation* _next; duke@435: duke@435: SolarisAttachOperation* next() { return _next; } duke@435: void set_next(SolarisAttachOperation* next) { _next = next; } duke@435: duke@435: public: duke@435: void complete(jint res, bufferedStream* st); duke@435: duke@435: int socket() const { return _socket; } duke@435: void set_socket(int s) { _socket = s; } duke@435: duke@435: SolarisAttachOperation(char* name) : AttachOperation(name) { duke@435: set_socket(-1); duke@435: set_next(NULL); duke@435: } duke@435: }; duke@435: duke@435: // statics duke@435: char SolarisAttachListener::_door_path[PATH_MAX+1]; duke@435: volatile bool SolarisAttachListener::_has_door_path; duke@435: int SolarisAttachListener::_door_descriptor = -1; duke@435: mutex_t SolarisAttachListener::_mutex; duke@435: sema_t SolarisAttachListener::_wakeup; duke@435: SolarisAttachOperation* SolarisAttachListener::_head = NULL; duke@435: SolarisAttachOperation* SolarisAttachListener::_tail = NULL; duke@435: duke@435: // Supporting class to help split a buffer into individual components duke@435: class ArgumentIterator : public StackObj { duke@435: private: duke@435: char* _pos; duke@435: char* _end; duke@435: public: duke@435: ArgumentIterator(char* arg_buffer, size_t arg_size) { duke@435: _pos = arg_buffer; duke@435: _end = _pos + arg_size - 1; duke@435: } duke@435: char* next() { duke@435: if (*_pos == '\0') { duke@435: return NULL; duke@435: } duke@435: char* res = _pos; duke@435: char* next_pos = strchr(_pos, '\0'); duke@435: if (next_pos < _end) { duke@435: next_pos++; duke@435: } duke@435: _pos = next_pos; duke@435: return res; duke@435: } duke@435: }; duke@435: duke@435: // Calls from the door function to check that the client credentials duke@435: // match this process. Returns 0 if credentials okay, otherwise -1. duke@435: static int check_credentials() { duke@435: door_cred_t cred_info; duke@435: duke@435: // get client credentials duke@435: if (door_cred(&cred_info) == -1) { duke@435: return -1; // unable to get them duke@435: } duke@435: duke@435: // get our euid/eguid (probably could cache these) duke@435: uid_t euid = geteuid(); duke@435: gid_t egid = getegid(); duke@435: duke@435: // check that the effective uid/gid matches - discuss this with Jeff. duke@435: if (cred_info.dc_euid == euid && cred_info.dc_egid == egid) { duke@435: return 0; // okay duke@435: } else { duke@435: return -1; // denied duke@435: } duke@435: } duke@435: duke@435: duke@435: // Parses the argument buffer to create an AttachOperation that we should duke@435: // enqueue to the attach listener. duke@435: // The buffer is expected to be formatted as follows: duke@435: // 00000 duke@435: // where is the version number (must be "1"), is the command duke@435: // name ("load, "datadump", ...) and is an argument. duke@435: // duke@435: static SolarisAttachOperation* create_operation(char* argp, size_t arg_size, int* err) { duke@435: // assume bad request until parsed duke@435: *err = SolarisAttachListener::ATTACH_ERROR_BADREQUEST; duke@435: duke@435: if (arg_size < 2 || argp[arg_size-1] != '\0') { duke@435: return NULL; // no ver or not null terminated duke@435: } duke@435: duke@435: // Use supporting class to iterate over the buffer duke@435: ArgumentIterator args(argp, arg_size); duke@435: duke@435: // First check the protocol version duke@435: char* ver = args.next(); duke@435: if (ver == NULL) { duke@435: return NULL; duke@435: } duke@435: if (atoi(ver) != SolarisAttachListener::ATTACH_PROTOCOL_VER) { duke@435: *err = SolarisAttachListener::ATTACH_ERROR_BADVERSION; duke@435: return NULL; duke@435: } duke@435: duke@435: // Get command name and create the operation duke@435: char* name = args.next(); duke@435: if (name == NULL || strlen(name) > AttachOperation::name_length_max) { duke@435: return NULL; duke@435: } duke@435: SolarisAttachOperation* op = new SolarisAttachOperation(name); duke@435: duke@435: // Iterate over the arguments duke@435: for (int i=0; iset_arg(i, NULL); duke@435: } else { duke@435: if (strlen(arg) > AttachOperation::arg_length_max) { duke@435: delete op; duke@435: return NULL; duke@435: } duke@435: op->set_arg(i, arg); duke@435: } duke@435: } duke@435: duke@435: // return operation duke@435: *err = 0; duke@435: return op; duke@435: } duke@435: duke@435: // create special operation to indicate all clients have detached duke@435: static SolarisAttachOperation* create_detachall_operation() { duke@435: return new SolarisAttachOperation(AttachOperation::detachall_operation_name()); duke@435: } duke@435: duke@435: // This is door function which the client executes via a door_call. duke@435: extern "C" { duke@435: static void enqueue_proc(void* cookie, char* argp, size_t arg_size, duke@435: door_desc_t* dt, uint_t n_desc) duke@435: { duke@435: int return_fd = -1; duke@435: SolarisAttachOperation* op = NULL; duke@435: duke@435: // no listener duke@435: jint res = 0; duke@435: if (!AttachListener::is_initialized()) { duke@435: // how did we get here? duke@435: debug_only(warning("door_call when not enabled")); duke@435: res = (jint)SolarisAttachListener::ATTACH_ERROR_INTERNAL; duke@435: } duke@435: duke@435: // check client credentials duke@435: if (res == 0) { duke@435: if (check_credentials() != 0) { duke@435: res = (jint)SolarisAttachListener::ATTACH_ERROR_DENIED; duke@435: } duke@435: } duke@435: duke@435: // if we are stopped at ShowMessageBoxOnError then maybe we can duke@435: // load a diagnostic library duke@435: if (res == 0 && is_error_reported()) { duke@435: if (ShowMessageBoxOnError) { duke@435: // TBD - support loading of diagnostic library here duke@435: } duke@435: duke@435: // can't enqueue operation after fatal error duke@435: res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE; duke@435: } duke@435: duke@435: // create the operation duke@435: if (res == 0) { duke@435: int err; duke@435: op = create_operation(argp, arg_size, &err); duke@435: res = (op == NULL) ? (jint)err : 0; duke@435: } duke@435: duke@435: // create a pair of connected sockets. Store the file descriptor duke@435: // for one end in the operation and enqueue the operation. The duke@435: // file descriptor for the other end wil be returned to the client. duke@435: if (res == 0) { duke@435: int s[2]; duke@435: if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) < 0) { duke@435: delete op; duke@435: res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE; duke@435: } else { duke@435: op->set_socket(s[0]); duke@435: return_fd = s[1]; duke@435: SolarisAttachListener::enqueue(op); duke@435: } duke@435: } duke@435: duke@435: // Return 0 (success) + file descriptor, or non-0 (error) duke@435: if (res == 0) { duke@435: door_desc_t desc; dlong@3728: // DOOR_RELEASE flag makes sure fd is closed after passing it to dlong@3728: // the client. See door_return(3DOOR) man page. dlong@3728: desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE; duke@435: desc.d_data.d_desc.d_descriptor = return_fd; duke@435: door_return((char*)&res, sizeof(res), &desc, 1); duke@435: } else { duke@435: door_return((char*)&res, sizeof(res), NULL, 0); duke@435: } duke@435: } duke@435: } duke@435: duke@435: // atexit hook to detach the door and remove the file duke@435: extern "C" { duke@435: static void listener_cleanup() { duke@435: static int cleanup_done; duke@435: if (!cleanup_done) { duke@435: cleanup_done = 1; duke@435: int dd = SolarisAttachListener::door_descriptor(); duke@435: if (dd >= 0) { duke@435: ::close(dd); duke@435: } duke@435: if (SolarisAttachListener::has_door_path()) { duke@435: char* path = SolarisAttachListener::door_path(); duke@435: ::fdetach(path); duke@435: ::unlink(path); duke@435: } duke@435: } duke@435: } duke@435: } duke@435: duke@435: // Create the door duke@435: int SolarisAttachListener::create_door() { duke@435: char door_path[PATH_MAX+1]; alanb@2030: char initial_path[PATH_MAX+1]; duke@435: int fd, res; duke@435: duke@435: // register exit function duke@435: ::atexit(listener_cleanup); duke@435: duke@435: // create the door descriptor duke@435: int dd = ::door_create(enqueue_proc, NULL, 0); duke@435: if (dd < 0) { duke@435: return -1; duke@435: } duke@435: alanb@2030: // create initial file to attach door descriptor coleenp@1788: snprintf(door_path, sizeof(door_path), "%s/.java_pid%d", coleenp@1788: os::get_temp_directory(), os::current_process_id()); alanb@2030: snprintf(initial_path, sizeof(initial_path), "%s.tmp", door_path); alanb@2030: RESTARTABLE(::creat(initial_path, S_IRUSR | S_IWUSR), fd); duke@435: if (fd == -1) { alanb@2030: debug_only(warning("attempt to create %s failed", initial_path)); alanb@2030: ::door_revoke(dd); duke@435: return -1; duke@435: } duke@435: assert(fd >= 0, "bad file descriptor"); rdurbin@5264: ::close(fd); duke@435: duke@435: // attach the door descriptor to the file alanb@2030: if ((res = ::fattach(dd, initial_path)) == -1) { duke@435: // if busy then detach and try again duke@435: if (errno == EBUSY) { alanb@2030: ::fdetach(initial_path); alanb@2030: res = ::fattach(dd, initial_path); duke@435: } duke@435: if (res == -1) { duke@435: ::door_revoke(dd); duke@435: dd = -1; duke@435: } duke@435: } alanb@2030: alanb@2030: // rename file so that clients can attach alanb@2030: if (dd >= 0) { alanb@2030: if (::rename(initial_path, door_path) == -1) { rdurbin@5264: ::close(dd); alanb@2030: ::fdetach(initial_path); alanb@2030: dd = -1; alanb@2030: } alanb@2030: } duke@435: if (dd >= 0) { duke@435: set_door_descriptor(dd); alanb@2030: set_door_path(door_path); duke@435: } else { alanb@2030: // unable to create door, attach it to file, or rename file into place alanb@2030: ::unlink(initial_path); duke@435: return -1; duke@435: } duke@435: duke@435: return 0; duke@435: } duke@435: duke@435: // Initialization - create the door, locks, and other initialization duke@435: int SolarisAttachListener::init() { duke@435: if (create_door()) { duke@435: return -1; duke@435: } duke@435: duke@435: int status = os::Solaris::mutex_init(&_mutex); duke@435: assert_status(status==0, status, "mutex_init"); duke@435: duke@435: status = ::sema_init(&_wakeup, 0, NULL, NULL); duke@435: assert_status(status==0, status, "sema_init"); duke@435: duke@435: set_head(NULL); duke@435: set_tail(NULL); duke@435: duke@435: return 0; duke@435: } duke@435: duke@435: // Dequeue an operation duke@435: SolarisAttachOperation* SolarisAttachListener::dequeue() { duke@435: for (;;) { duke@435: int res; duke@435: duke@435: // wait for somebody to enqueue something duke@435: while ((res = ::sema_wait(wakeup())) == EINTR) duke@435: ; duke@435: if (res) { duke@435: warning("sema_wait failed: %s", strerror(res)); duke@435: return NULL; duke@435: } duke@435: duke@435: // lock the list duke@435: res = os::Solaris::mutex_lock(mutex()); duke@435: assert(res == 0, "mutex_lock failed"); duke@435: duke@435: // remove the head of the list duke@435: SolarisAttachOperation* op = head(); duke@435: if (op != NULL) { duke@435: set_head(op->next()); duke@435: if (head() == NULL) { duke@435: set_tail(NULL); duke@435: } duke@435: } duke@435: duke@435: // unlock duke@435: os::Solaris::mutex_unlock(mutex()); duke@435: duke@435: // if we got an operation when return it. duke@435: if (op != NULL) { duke@435: return op; duke@435: } duke@435: } duke@435: } duke@435: duke@435: // Enqueue an operation duke@435: void SolarisAttachListener::enqueue(SolarisAttachOperation* op) { duke@435: // lock list duke@435: int res = os::Solaris::mutex_lock(mutex()); duke@435: assert(res == 0, "mutex_lock failed"); duke@435: duke@435: // enqueue at tail duke@435: op->set_next(NULL); duke@435: if (head() == 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: // wakeup the attach listener duke@435: RESTARTABLE(::sema_post(wakeup()), res); duke@435: assert(res == 0, "sema_post failed"); duke@435: duke@435: // unlock duke@435: os::Solaris::mutex_unlock(mutex()); duke@435: } duke@435: duke@435: duke@435: // support function - writes the (entire) buffer to a socket duke@435: static int write_fully(int s, char* buf, int len) { duke@435: do { duke@435: int n = ::write(s, buf, len); duke@435: if (n == -1) { duke@435: if (errno != EINTR) return -1; duke@435: } else { duke@435: buf += n; duke@435: len -= n; duke@435: } duke@435: } duke@435: while (len > 0); duke@435: return 0; duke@435: } duke@435: duke@435: // Complete an operation by sending the operation result and any result duke@435: // output to the client. At this time the socket is in blocking mode so duke@435: // potentially we can block if there is a lot of data and the client is duke@435: // non-responsive. For most operations this is a non-issue because the duke@435: // default send buffer is sufficient to buffer everything. In the future duke@435: // if there are operations that involves a very big reply then it the duke@435: // socket could be made non-blocking and a timeout could be used. duke@435: duke@435: void SolarisAttachOperation::complete(jint res, bufferedStream* st) { duke@435: if (this->socket() >= 0) { 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: // write operation result duke@435: char msg[32]; duke@435: sprintf(msg, "%d\n", res); duke@435: int rc = write_fully(this->socket(), msg, strlen(msg)); duke@435: duke@435: // write any result data duke@435: if (rc == 0) { duke@435: write_fully(this->socket(), (char*) st->base(), st->size()); duke@435: ::shutdown(this->socket(), 2); duke@435: } duke@435: duke@435: // close socket and we're done rdurbin@5264: ::close(this->socket()); duke@435: duke@435: // were we externally suspended while we were waiting? duke@435: thread->check_and_wait_while_suspended(); duke@435: } duke@435: delete this; duke@435: } duke@435: duke@435: duke@435: // AttachListener 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 = SolarisAttachListener::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: allwin@5412: allwin@5412: // Performs initialization at vm startup allwin@5412: // For Solaris we remove any stale .java_pid file which could cause allwin@5412: // an attaching process to think we are ready to receive a door_call allwin@5412: // before we are properly initialized allwin@5412: allwin@5412: void AttachListener::vm_start() { allwin@5412: char fn[PATH_MAX+1]; allwin@5412: struct stat64 st; allwin@5412: int ret; allwin@5412: allwin@5412: int n = snprintf(fn, sizeof(fn), "%s/.java_pid%d", allwin@5412: os::get_temp_directory(), os::current_process_id()); allwin@5412: assert(n < sizeof(fn), "java_pid file name buffer overflow"); allwin@5412: allwin@5412: RESTARTABLE(::stat64(fn, &st), ret); allwin@5412: if (ret == 0) { allwin@5412: ret = ::unlink(fn); allwin@5412: if (ret == -1) { allwin@5412: debug_only(warning("failed to remove stale attach pid file at %s", fn)); allwin@5412: } allwin@5412: } allwin@5412: } allwin@5412: duke@435: int AttachListener::pd_init() { 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() duke@435: duke@435: int ret_code = SolarisAttachListener::init(); 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 ret_code; duke@435: } duke@435: duke@435: // Attach Listener is started lazily except in the case when duke@435: // +ReduseSignalUsage is used duke@435: bool AttachListener::init_at_startup() { duke@435: if (ReduceSignalUsage) { duke@435: return true; duke@435: } else { duke@435: return false; duke@435: } duke@435: } duke@435: duke@435: // If the file .attach_pid exists in the working directory duke@435: // or /tmp then this is the trigger to start the attach mechanism duke@435: bool AttachListener::is_init_trigger() { duke@435: if (init_at_startup() || is_initialized()) { duke@435: return false; // initialized at startup or already initialized duke@435: } coleenp@1852: char fn[PATH_MAX+1]; duke@435: sprintf(fn, ".attach_pid%d", os::current_process_id()); duke@435: int ret; duke@435: struct stat64 st; duke@435: RESTARTABLE(::stat64(fn, &st), ret); duke@435: if (ret == -1) { coleenp@1788: snprintf(fn, sizeof(fn), "%s/.attach_pid%d", coleenp@1788: os::get_temp_directory(), os::current_process_id()); duke@435: RESTARTABLE(::stat64(fn, &st), ret); duke@435: } duke@435: if (ret == 0) { duke@435: // simple check to avoid starting the attach mechanism when duke@435: // a bogus user creates the file duke@435: if (st.st_uid == geteuid()) { duke@435: init(); duke@435: return true; duke@435: } duke@435: } duke@435: return false; duke@435: } duke@435: duke@435: // if VM aborts then detach/cleanup duke@435: void AttachListener::abort() { duke@435: listener_cleanup(); duke@435: } duke@435: duke@435: void AttachListener::pd_data_dump() { duke@435: os::signal_notify(SIGQUIT); duke@435: } duke@435: duke@435: static jint enable_dprobes(AttachOperation* op, outputStream* out) { duke@435: const char* probe = op->arg(0); duke@435: if (probe == NULL || probe[0] == '\0') { duke@435: out->print_cr("No probe specified"); duke@435: return JNI_ERR; duke@435: } else { duke@435: int probe_typess = atoi(probe); duke@435: if (errno) { duke@435: out->print_cr("invalid probe type"); duke@435: return JNI_ERR; duke@435: } else { duke@435: DTrace::enable_dprobes(probe_typess); duke@435: return JNI_OK; duke@435: } duke@435: } duke@435: } duke@435: duke@435: // platform specific operations table duke@435: static AttachOperationFunctionInfo funcs[] = { duke@435: { "enabledprobes", enable_dprobes }, duke@435: { NULL, NULL } duke@435: }; duke@435: duke@435: AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* name) { duke@435: int i; duke@435: for (i = 0; funcs[i].name != NULL; i++) { duke@435: if (strcmp(funcs[i].name, name) == 0) { duke@435: return &funcs[i]; duke@435: } duke@435: } duke@435: return NULL; duke@435: } duke@435: duke@435: // Solaris specific global flag set. Currently, we support only duke@435: // changing ExtendedDTraceProbes flag. duke@435: jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { duke@435: const char* name = op->arg(0); duke@435: assert(name != NULL, "flag name should not be null"); duke@435: bool flag = true; duke@435: const char* arg1; duke@435: if ((arg1 = op->arg(1)) != NULL) { duke@435: flag = (atoi(arg1) != 0); duke@435: if (errno) { duke@435: out->print_cr("flag value has to be an integer"); duke@435: return JNI_ERR; duke@435: } duke@435: } duke@435: fparain@1759: if (strcmp(name, "ExtendedDTraceProbes") == 0) { fparain@1759: DTrace::set_extended_dprobes(flag); fparain@1759: return JNI_OK; duke@435: } duke@435: fparain@1759: if (strcmp(name, "DTraceMonitorProbes") == 0) { fparain@1759: DTrace::set_monitor_dprobes(flag); fparain@1759: return JNI_OK; fparain@1759: } fparain@1759: fparain@1759: out->print_cr("flag '%s' cannot be changed", name); fparain@1759: return JNI_ERR; duke@435: } duke@435: duke@435: void AttachListener::pd_detachall() { duke@435: DTrace::detach_all_clients(); duke@435: }