Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/src/os/bsd/dtrace/jvm_dtrace.c
32284 views
/*1* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include <door.h>25#include <errno.h>26#include <fcntl.h>27#include <limits.h>28#include <poll.h>29#include <signal.h>30#include <stdarg.h>31#include <stdio.h>32#include <stdlib.h>33#include <string.h>34#include <sys/types.h>35#include <sys/stat.h>36#include <thread.h>37#include <unistd.h>38#include "jvm_dtrace.h"3940// NOTE: These constants are used in JVM code as well.41// KEEP JVM CODE IN SYNC if you are going to change these...4243#define DTRACE_ALLOC_PROBES 0x144#define DTRACE_METHOD_PROBES 0x245#define DTRACE_MONITOR_PROBES 0x446#define DTRACE_ALL_PROBES -14748// generic error messages49#define JVM_ERR_OUT_OF_MEMORY "out of memory (native heap)"50#define JVM_ERR_INVALID_PARAM "invalid input parameter(s)"51#define JVM_ERR_NULL_PARAM "input paramater is NULL"5253// error messages for attach54#define JVM_ERR_CANT_OPEN_DOOR "cannot open door file"55#define JVM_ERR_CANT_CREATE_ATTACH_FILE "cannot create attach file"56#define JVM_ERR_DOOR_FILE_PERMISSION "door file is not secure"57#define JVM_ERR_CANT_SIGNAL "cannot send SIGQUIT to target"5859// error messages for enable probe60#define JVM_ERR_DOOR_CMD_SEND "door command send failed"61#define JVM_ERR_DOOR_CANT_READ_STATUS "cannot read door command status"62#define JVM_ERR_DOOR_CMD_STATUS "door command error status"6364// error message for detach65#define JVM_ERR_CANT_CLOSE_DOOR "cannot close door file"6667#define RESTARTABLE(_cmd, _result) do { \68do { \69_result = _cmd; \70} while((_result == -1) && (errno == EINTR)); \71} while(0)7273struct _jvm_t {74pid_t pid;75int door_fd;76};7778static int libjvm_dtrace_debug;79static void print_debug(const char* fmt,...) {80if (libjvm_dtrace_debug) {81va_list alist;82va_start(alist, fmt);83fputs("libjvm_dtrace DEBUG: ", stderr);84vfprintf(stderr, fmt, alist);85va_end(alist);86}87}8889/* Key for thread local error message */90static thread_key_t jvm_error_key;9192/* init function for this library */93static void init_jvm_dtrace() {94/* check for env. var for debug mode */95libjvm_dtrace_debug = getenv("LIBJVM_DTRACE_DEBUG") != NULL;96/* create key for thread local error message */97if (thr_keycreate(&jvm_error_key, NULL) != 0) {98print_debug("can't create thread_key_t for jvm error key\n");99// exit(1); ?100}101}102103#pragma init(init_jvm_dtrace)104105/* set thread local error message */106static void set_jvm_error(const char* msg) {107thr_setspecific(jvm_error_key, (void*)msg);108}109110/* clear thread local error message */111static void clear_jvm_error() {112thr_setspecific(jvm_error_key, NULL);113}114115/* file handling functions that can handle interrupt */116117static int file_open(const char* path, int flag) {118int ret;119RESTARTABLE(open(path, flag), ret);120return ret;121}122123static int file_close(int fd) {124return close(fd);125}126127static int file_read(int fd, char* buf, int len) {128int ret;129RESTARTABLE(read(fd, buf, len), ret);130return ret;131}132133/* send SIGQUIT signal to given process */134static int send_sigquit(pid_t pid) {135int ret;136RESTARTABLE(kill(pid, SIGQUIT), ret);137return ret;138}139140/* called to check permissions on attach file */141static int check_permission(const char* path) {142struct stat64 sb;143uid_t uid, gid;144int res;145146/*147* Check that the path is owned by the effective uid/gid of this148* process. Also check that group/other access is not allowed.149*/150uid = geteuid();151gid = getegid();152153res = stat64(path, &sb);154if (res != 0) {155print_debug("stat failed for %s\n", path);156return -1;157}158159if ((sb.st_uid != uid) || (sb.st_gid != gid) ||160((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0)) {161print_debug("well-known file %s is not secure\n", path);162return -1;163}164return 0;165}166167#define ATTACH_FILE_PATTERN "/tmp/.attach_pid%d"168169/* fill-in the name of attach file name in given buffer */170static void fill_attach_file_name(char* path, int len, pid_t pid) {171memset(path, 0, len);172sprintf(path, ATTACH_FILE_PATTERN, pid);173}174175#define DOOR_FILE_PATTERN "/tmp/.java_pid%d"176177/* open door file for the given JVM */178static int open_door(pid_t pid) {179char path[PATH_MAX + 1];180int fd;181182sprintf(path, DOOR_FILE_PATTERN, pid);183fd = file_open(path, O_RDONLY);184if (fd < 0) {185set_jvm_error(JVM_ERR_CANT_OPEN_DOOR);186print_debug("cannot open door file %s\n", path);187return -1;188}189print_debug("opened door file %s\n", path);190if (check_permission(path) != 0) {191set_jvm_error(JVM_ERR_DOOR_FILE_PERMISSION);192print_debug("check permission failed for %s\n", path);193file_close(fd);194fd = -1;195}196return fd;197}198199/* create attach file for given process */200static int create_attach_file(pid_t pid) {201char path[PATH_MAX + 1];202int fd;203fill_attach_file_name(path, sizeof(path), pid);204fd = file_open(path, O_CREAT | O_RDWR);205if (fd < 0) {206set_jvm_error(JVM_ERR_CANT_CREATE_ATTACH_FILE);207print_debug("cannot create file %s\n", path);208} else {209print_debug("created attach file %s\n", path);210}211return fd;212}213214/* delete attach file for given process */215static void delete_attach_file(pid_t pid) {216char path[PATH_MAX + 1];217fill_attach_file_name(path, sizeof(path), pid);218int res = unlink(path);219if (res) {220print_debug("cannot delete attach file %s\n", path);221} else {222print_debug("deleted attach file %s\n", path);223}224}225226/* attach to given JVM */227jvm_t* jvm_attach(pid_t pid) {228jvm_t* jvm;229int door_fd, attach_fd, i;230231jvm = (jvm_t*) calloc(1, sizeof(jvm_t));232if (jvm == NULL) {233set_jvm_error(JVM_ERR_OUT_OF_MEMORY);234print_debug("calloc failed in %s at %d\n", __FILE__, __LINE__);235return NULL;236}237jvm->pid = pid;238attach_fd = -1;239240door_fd = open_door(pid);241if (door_fd < 0) {242print_debug("trying to create attach file\n");243if ((attach_fd = create_attach_file(pid)) < 0) {244goto quit;245}246247/* send QUIT signal to the target so that it will248* check for the attach file.249*/250if (send_sigquit(pid) != 0) {251set_jvm_error(JVM_ERR_CANT_SIGNAL);252print_debug("sending SIGQUIT failed\n");253goto quit;254}255256/* give the target VM time to start the attach mechanism */257do {258int res;259RESTARTABLE(poll(0, 0, 200), res);260door_fd = open_door(pid);261i++;262} while (i <= 50 && door_fd == -1);263if (door_fd < 0) {264print_debug("Unable to open door to process %d\n", pid);265goto quit;266}267}268269quit:270if (attach_fd >= 0) {271file_close(attach_fd);272delete_attach_file(jvm->pid);273}274if (door_fd >= 0) {275jvm->door_fd = door_fd;276clear_jvm_error();277} else {278free(jvm);279jvm = NULL;280}281return jvm;282}283284/* return the last thread local error message */285const char* jvm_get_last_error() {286const char* res = NULL;287thr_getspecific(jvm_error_key, (void**)&res);288return res;289}290291/* detach the givenb JVM */292int jvm_detach(jvm_t* jvm) {293if (jvm) {294int res;295if (jvm->door_fd != -1) {296if (file_close(jvm->door_fd) != 0) {297set_jvm_error(JVM_ERR_CANT_CLOSE_DOOR);298res = -1;299} else {300clear_jvm_error();301res = 0;302}303}304free(jvm);305return res;306} else {307set_jvm_error(JVM_ERR_NULL_PARAM);308print_debug("jvm_t* is NULL\n");309return -1;310}311}312313/*314* A simple table to translate some known errors into reasonable315* error messages316*/317static struct {318int err;319const char* msg;320} const error_messages[] = {321{ 100, "Bad request" },322{ 101, "Protocol mismatch" },323{ 102, "Resource failure" },324{ 103, "Internal error" },325{ 104, "Permission denied" },326};327328/*329* Lookup the given error code and return the appropriate330* message. If not found return NULL.331*/332static const char* translate_error(int err) {333int table_size = sizeof(error_messages) / sizeof(error_messages[0]);334int i;335336for (i=0; i<table_size; i++) {337if (err == error_messages[i].err) {338return error_messages[i].msg;339}340}341return NULL;342}343344/*345* Current protocol version346*/347static const char* PROTOCOL_VERSION = "1";348349#define RES_BUF_SIZE 128350351/*352* Enqueue attach-on-demand command to the given JVM353*/354static355int enqueue_command(jvm_t* jvm, const char* cstr, int arg_count, const char** args) {356size_t size;357door_arg_t door_args;358char res_buffer[RES_BUF_SIZE];359int rc, i;360char* buf = NULL;361int result = -1;362363/*364* First we get the command string and create the start of the365* argument string to send to the target VM:366* <ver>\0<cmd>\0367*/368if (cstr == NULL) {369print_debug("command name is NULL\n");370goto quit;371}372size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2;373buf = (char*)malloc(size);374if (buf != NULL) {375char* pos = buf;376strcpy(buf, PROTOCOL_VERSION);377pos += strlen(PROTOCOL_VERSION)+1;378strcpy(pos, cstr);379} else {380set_jvm_error(JVM_ERR_OUT_OF_MEMORY);381print_debug("malloc failed at %d in %s\n", __LINE__, __FILE__);382goto quit;383}384385/*386* Next we iterate over the arguments and extend the buffer387* to include them.388*/389for (i=0; i<arg_count; i++) {390cstr = args[i];391if (cstr != NULL) {392size_t len = strlen(cstr);393char* newbuf = (char*)realloc(buf, size+len+1);394if (newbuf == NULL) {395set_jvm_error(JVM_ERR_OUT_OF_MEMORY);396print_debug("realloc failed in %s at %d\n", __FILE__, __LINE__);397goto quit;398}399buf = newbuf;400strcpy(buf+size, cstr);401size += len+1;402}403}404405/*406* The arguments to the door function are in 'buf' so we now407* do the door call408*/409door_args.data_ptr = buf;410door_args.data_size = size;411door_args.desc_ptr = NULL;412door_args.desc_num = 0;413door_args.rbuf = (char*)&res_buffer;414door_args.rsize = sizeof(res_buffer);415416RESTARTABLE(door_call(jvm->door_fd, &door_args), rc);417418/*419* door_call failed420*/421if (rc == -1) {422print_debug("door_call failed\n");423} else {424/*425* door_call succeeded but the call didn't return the the expected jint.426*/427if (door_args.data_size < sizeof(int)) {428print_debug("Enqueue error - reason unknown as result is truncated!");429} else {430int* res = (int*)(door_args.data_ptr);431if (*res != 0) {432const char* msg = translate_error(*res);433if (msg == NULL) {434print_debug("Unable to enqueue command to target VM: %d\n", *res);435} else {436print_debug("Unable to enqueue command to target VM: %s\n", msg);437}438} else {439/*440* The door call should return a file descriptor to one end of441* a socket pair442*/443if ((door_args.desc_ptr != NULL) &&444(door_args.desc_num == 1) &&445(door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) {446result = door_args.desc_ptr->d_data.d_desc.d_descriptor;447} else {448print_debug("Reply from enqueue missing descriptor!\n");449}450}451}452}453454quit:455if (buf) free(buf);456return result;457}458459/* read status code for a door command */460static int read_status(int fd) {461char ch, buf[16];462int index = 0;463464while (1) {465if (file_read(fd, &ch, sizeof(ch)) != sizeof(ch)) {466set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS);467print_debug("door cmd status: read status failed\n");468return -1;469}470buf[index++] = ch;471if (ch == '\n') {472buf[index - 1] = '\0';473return atoi(buf);474}475if (index == sizeof(buf)) {476set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS);477print_debug("door cmd status: read status overflow\n");478return -1;479}480}481}482483static const char* ENABLE_DPROBES_CMD = "enabledprobes";484485/* enable one or more DTrace probes for a given JVM */486int jvm_enable_dtprobes(jvm_t* jvm, int num_probe_types, const char** probe_types) {487int fd, status = 0;488char ch;489const char* args[1];490char buf[16];491int probe_type = 0, index;492int count = 0;493494if (jvm == NULL) {495set_jvm_error(JVM_ERR_NULL_PARAM);496print_debug("jvm_t* is NULL\n");497return -1;498}499500if (num_probe_types == 0 || probe_types == NULL ||501probe_types[0] == NULL) {502set_jvm_error(JVM_ERR_INVALID_PARAM);503print_debug("invalid probe type argument(s)\n");504return -1;505}506507for (index = 0; index < num_probe_types; index++) {508const char* p = probe_types[index];509if (strcmp(p, JVM_DTPROBE_OBJECT_ALLOC) == 0) {510probe_type |= DTRACE_ALLOC_PROBES;511count++;512} else if (strcmp(p, JVM_DTPROBE_METHOD_ENTRY) == 0 ||513strcmp(p, JVM_DTPROBE_METHOD_RETURN) == 0) {514probe_type |= DTRACE_METHOD_PROBES;515count++;516} else if (strcmp(p, JVM_DTPROBE_MONITOR_ENTER) == 0 ||517strcmp(p, JVM_DTPROBE_MONITOR_ENTERED) == 0 ||518strcmp(p, JVM_DTPROBE_MONITOR_EXIT) == 0 ||519strcmp(p, JVM_DTPROBE_MONITOR_WAIT) == 0 ||520strcmp(p, JVM_DTPROBE_MONITOR_WAITED) == 0 ||521strcmp(p, JVM_DTPROBE_MONITOR_NOTIFY) == 0 ||522strcmp(p, JVM_DTPROBE_MONITOR_NOTIFYALL) == 0) {523probe_type |= DTRACE_MONITOR_PROBES;524count++;525} else if (strcmp(p, JVM_DTPROBE_ALL) == 0) {526probe_type |= DTRACE_ALL_PROBES;527count++;528}529}530531if (count == 0) {532return count;533}534sprintf(buf, "%d", probe_type);535args[0] = buf;536537fd = enqueue_command(jvm, ENABLE_DPROBES_CMD, 1, args);538if (fd < 0) {539set_jvm_error(JVM_ERR_DOOR_CMD_SEND);540return -1;541}542543status = read_status(fd);544// non-zero status is error545if (status) {546set_jvm_error(JVM_ERR_DOOR_CMD_STATUS);547print_debug("%s command failed (status: %d) in target JVM\n",548ENABLE_DPROBES_CMD, status);549file_close(fd);550return -1;551}552// read from stream until EOF553while (file_read(fd, &ch, sizeof(ch)) == sizeof(ch)) {554if (libjvm_dtrace_debug) {555printf("%c", ch);556}557}558559file_close(fd);560clear_jvm_error();561return count;562}563564565