Home | History | Annotate | Download | only in jvmpi
      1 /**
      2  * @file jvmpi_oprofile.cpp
      3  * JVMPI agent implementation to report jitted JVM code to OProfile
      4  *
      5  * @remark Copyright 2007 OProfile authors
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2.1 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this library; if not, write to the Free Software
     19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     20  *
     21  * @author Maynard Johnson
     22  *
     23  * Copyright IBM Corporation 2007
     24  *
     25  */
     26 
     27 #include <iostream>
     28 #include <map>
     29 #include <string>
     30 #include <cstring>
     31 #include <stdexcept>
     32 #include <cerrno>
     33 
     34 extern "C" {
     35 #include <stdint.h>
     36 #include <jvmpi.h>
     37 #include <opagent.h>
     38 }
     39 
     40 using namespace std;
     41 
     42 static bool debug = false;
     43 static op_agent_t agent_hdl;
     44 
     45 class class_details {
     46 public:
     47 	string name;
     48 	map<jmethodID, string> method_names;
     49 	map<jmethodID, string> method_signatures;
     50 };
     51 
     52 
     53 static pthread_mutex_t class_map_mutex = PTHREAD_MUTEX_INITIALIZER;
     54 static map <jobjectID, class_details> loaded_classes;
     55 
     56 void class_load(JVMPI_Event * event)
     57 {
     58 	class_details cls;
     59 	cls.name = event->u.class_load.class_name;
     60 	JVMPI_Method * passed_methods = event->u.class_load.methods;
     61 	for (int i = 0; i < event->u.class_load.num_methods;
     62 	     i++, passed_methods++) {
     63 		cls.method_names[passed_methods->method_id] =
     64 			passed_methods->method_name;
     65 		cls.method_signatures[passed_methods->method_id] =
     66 			passed_methods->method_signature;
     67 	}
     68 
     69 	pthread_mutex_lock(&class_map_mutex);
     70 	loaded_classes[event->u.class_load.class_id] = cls;
     71 	pthread_mutex_unlock(&class_map_mutex);
     72 }
     73 
     74 void class_unload(JVMPI_Event * event)
     75 {
     76 	pthread_mutex_lock(&class_map_mutex);
     77 	loaded_classes.erase(event->u.class_load.class_id);
     78 	pthread_mutex_unlock(&class_map_mutex);
     79 }
     80 
     81 JVMPI_Interface * jvmpi;
     82 
     83 void compiled_method_load(JVMPI_Event * event)
     84 {
     85 	jmethodID method = event->u.compiled_method_load.method_id;
     86 	void * code_addr =  event->u.compiled_method_load.code_addr;
     87 	jint code_size =  event->u.compiled_method_load.code_size;
     88 
     89 	jvmpi->DisableGC();
     90 	 /* Get the class of the method */
     91 	jobjectID classID = jvmpi->GetMethodClass(method);
     92 	jvmpi->EnableGC();
     93 
     94 	pthread_mutex_lock(&class_map_mutex);
     95 	map<jobjectID, class_details>::iterator iter =
     96 		loaded_classes.find(classID);
     97 	if (iter == loaded_classes.end()) {
     98 		throw runtime_error("Error: Cannot find class for compiled"
     99 				    " method\n");
    100 	}
    101 
    102 	class_details cls_info = ((class_details)iter->second);
    103 	map<jmethodID, string>::iterator method_it =
    104 		cls_info.method_names.find(method);
    105 	if (method_it == cls_info.method_names.end()) {
    106 		throw runtime_error("Error: Cannot find method name for "
    107 				    "compiled method\n");
    108 	}
    109 	char const * method_name = ((string)method_it->second).c_str();
    110 	method_it = cls_info.method_signatures.find(method);
    111 	if (method_it == cls_info.method_signatures.end()) {
    112 		throw runtime_error("Error: Cannot find method signature "
    113 				    "for compiled method\n");
    114 	}
    115 	char const * method_signature = ((string)method_it->second).c_str();
    116 
    117 	string const class_signature = "L" + cls_info.name + ";";
    118 	pthread_mutex_unlock(&class_map_mutex);
    119 
    120 	if (debug) {
    121 		cerr << "load: class=" << class_signature << ", method ="
    122 		     << method_name << ", method signature = "
    123 		     << method_signature
    124 		     << ", addr=" << code_addr << ", size="
    125 		     << code_size << endl;
    126 	}
    127 
    128 	// produce a symbol name out of class name and method name
    129 	int cnt = strlen(method_name) + strlen(class_signature.c_str()) +
    130 		strlen(method_signature) + 2;
    131 	char buf[cnt];
    132 	strncpy(buf, class_signature.c_str(), cnt - 1);
    133 	strncat(buf, method_name, cnt - strlen(buf) - 1);
    134 	strncat(buf, method_signature, cnt - strlen(buf) - 1);
    135 	if (op_write_native_code(agent_hdl, buf, (uint64_t) code_addr,
    136 				 code_addr, code_size))
    137 		perror("Error: op_write_native_code()");
    138 }
    139 
    140 void compiled_method_unload(JVMPI_Event * event)
    141 {
    142 	void * code_addr =  event->u.compiled_method_load.code_addr;
    143 	if (debug) {
    144 		cerr << "unload: addr="
    145 			<< (unsigned long long) (uintptr_t) code_addr
    146 			<< endl;
    147 	}
    148 	if (op_unload_native_code(agent_hdl, (uint64_t)code_addr))
    149 		perror("Error: op_unload_native_code()");
    150 }
    151 
    152 void jvm_shutdown(JVMPI_Event * event)
    153 {
    154 	/* Checking event here is not really necessary; added only to silence
    155 	 * the 'unused parameter' compiler warning.
    156 	 */
    157 	if (event)
    158 		if (op_close_agent(agent_hdl))
    159 			perror("Error: op_close_agent()");
    160 }
    161 
    162 
    163 void jvm_notify_event(JVMPI_Event * event)
    164 {
    165 	switch (event->event_type) {
    166 	case JVMPI_EVENT_COMPILED_METHOD_LOAD:
    167 		compiled_method_load(event);
    168 		break;
    169 	case JVMPI_EVENT_COMPILED_METHOD_UNLOAD:
    170 		compiled_method_unload(event);
    171 		break;
    172 	case JVMPI_EVENT_JVM_SHUT_DOWN:
    173 		jvm_shutdown(event);
    174 		break;
    175 	case JVMPI_EVENT_CLASS_LOAD:
    176 		class_load(event);
    177 		break;
    178 	case JVMPI_EVENT_CLASS_UNLOAD:
    179 		class_unload(event);
    180 		break;
    181 	default:
    182 		break;
    183 	}
    184 }
    185 
    186 extern "C" {
    187 JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM * jvm, char * options,
    188                                   void * reserved)
    189 {
    190 	int err;
    191 
    192 	if (options && strstr(options, "version")) {
    193 		cerr << "jvmpi_oprofile: current libopagent version "
    194 				 << op_major_version() << "." << op_minor_version()
    195 				 << endl;
    196 		throw runtime_error("Exiting");
    197 	}
    198 
    199 	if (options && strstr(options, "debug=yes")) {
    200 		debug = true;
    201 		/* Add something braindead to silence the 'unused parameter'
    202 		 * compiler warning.
    203 		 */
    204 		if (reserved)
    205 			debug = true;
    206 	}
    207 
    208 	if (debug)
    209 		cerr << "jvmpi_oprofile: agent activated" << endl;
    210 
    211 	agent_hdl = op_open_agent();
    212 	if (!agent_hdl) {
    213 		perror("Error: op_open_agent()");
    214 		throw runtime_error("Exiting");
    215 	}
    216 
    217 	/* The union below is used to avoid the 'dereferencing type-punned
    218 	 * pointer will break strict-aliasing rules' compiler warning on the
    219 	 * GetEnv call.
    220 	 */
    221 	union {
    222 		JVMPI_Interface * jvmpi_ifc;
    223 		void * jvmpi_ifc_ptr;
    224 	} jvmpi_GetEnv_arg;
    225 	err = jvm->GetEnv(&jvmpi_GetEnv_arg.jvmpi_ifc_ptr, JVMPI_VERSION_1);
    226 	if (err < 0) {
    227 		cerr << "GetEnv failed with rc=" << err << endl;
    228 		throw runtime_error("Exiting");
    229 	}
    230 	jvmpi = jvmpi_GetEnv_arg.jvmpi_ifc;
    231 	jvmpi->EnableEvent(JVMPI_EVENT_COMPILED_METHOD_LOAD, NULL);
    232 	jvmpi->EnableEvent(JVMPI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
    233 	jvmpi->EnableEvent(JVMPI_EVENT_JVM_SHUT_DOWN, NULL);
    234 	jvmpi->EnableEvent(JVMPI_EVENT_CLASS_LOAD, NULL);
    235 
    236 	jvmpi->NotifyEvent = jvm_notify_event;
    237 	return JNI_OK;
    238 }
    239 }
    240