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