/** * @file jvmpi_oprofile.cpp * JVMPI agent implementation to report jitted JVM code to OProfile * * @remark Copyright 2007 OProfile authors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * @author Maynard Johnson * * Copyright IBM Corporation 2007 * */ #include <iostream> #include <map> #include <string> #include <cstring> #include <stdexcept> #include <cerrno> extern "C" { #include <stdint.h> #include <jvmpi.h> #include <opagent.h> } using namespace std; static bool debug = false; static op_agent_t agent_hdl; class class_details { public: string name; map<jmethodID, string> method_names; map<jmethodID, string> method_signatures; }; static pthread_mutex_t class_map_mutex = PTHREAD_MUTEX_INITIALIZER; static map <jobjectID, class_details> loaded_classes; void class_load(JVMPI_Event * event) { class_details cls; cls.name = event->u.class_load.class_name; JVMPI_Method * passed_methods = event->u.class_load.methods; for (int i = 0; i < event->u.class_load.num_methods; i++, passed_methods++) { cls.method_names[passed_methods->method_id] = passed_methods->method_name; cls.method_signatures[passed_methods->method_id] = passed_methods->method_signature; } pthread_mutex_lock(&class_map_mutex); loaded_classes[event->u.class_load.class_id] = cls; pthread_mutex_unlock(&class_map_mutex); } void class_unload(JVMPI_Event * event) { pthread_mutex_lock(&class_map_mutex); loaded_classes.erase(event->u.class_load.class_id); pthread_mutex_unlock(&class_map_mutex); } JVMPI_Interface * jvmpi; void compiled_method_load(JVMPI_Event * event) { jmethodID method = event->u.compiled_method_load.method_id; void * code_addr = event->u.compiled_method_load.code_addr; jint code_size = event->u.compiled_method_load.code_size; jvmpi->DisableGC(); /* Get the class of the method */ jobjectID classID = jvmpi->GetMethodClass(method); jvmpi->EnableGC(); pthread_mutex_lock(&class_map_mutex); map<jobjectID, class_details>::iterator iter = loaded_classes.find(classID); if (iter == loaded_classes.end()) { throw runtime_error("Error: Cannot find class for compiled" " method\n"); } class_details cls_info = ((class_details)iter->second); map<jmethodID, string>::iterator method_it = cls_info.method_names.find(method); if (method_it == cls_info.method_names.end()) { throw runtime_error("Error: Cannot find method name for " "compiled method\n"); } char const * method_name = ((string)method_it->second).c_str(); method_it = cls_info.method_signatures.find(method); if (method_it == cls_info.method_signatures.end()) { throw runtime_error("Error: Cannot find method signature " "for compiled method\n"); } char const * method_signature = ((string)method_it->second).c_str(); string const class_signature = "L" + cls_info.name + ";"; pthread_mutex_unlock(&class_map_mutex); if (debug) { cerr << "load: class=" << class_signature << ", method =" << method_name << ", method signature = " << method_signature << ", addr=" << code_addr << ", size=" << code_size << endl; } // produce a symbol name out of class name and method name int cnt = strlen(method_name) + strlen(class_signature.c_str()) + strlen(method_signature) + 2; char buf[cnt]; strncpy(buf, class_signature.c_str(), cnt - 1); strncat(buf, method_name, cnt - strlen(buf) - 1); strncat(buf, method_signature, cnt - strlen(buf) - 1); if (op_write_native_code(agent_hdl, buf, (uint64_t) code_addr, code_addr, code_size)) perror("Error: op_write_native_code()"); } void compiled_method_unload(JVMPI_Event * event) { void * code_addr = event->u.compiled_method_load.code_addr; if (debug) { cerr << "unload: addr=" << (unsigned long long) (uintptr_t) code_addr << endl; } if (op_unload_native_code(agent_hdl, (uint64_t)code_addr)) perror("Error: op_unload_native_code()"); } void jvm_shutdown(JVMPI_Event * event) { /* Checking event here is not really necessary; added only to silence * the 'unused parameter' compiler warning. */ if (event) if (op_close_agent(agent_hdl)) perror("Error: op_close_agent()"); } void jvm_notify_event(JVMPI_Event * event) { switch (event->event_type) { case JVMPI_EVENT_COMPILED_METHOD_LOAD: compiled_method_load(event); break; case JVMPI_EVENT_COMPILED_METHOD_UNLOAD: compiled_method_unload(event); break; case JVMPI_EVENT_JVM_SHUT_DOWN: jvm_shutdown(event); break; case JVMPI_EVENT_CLASS_LOAD: class_load(event); break; case JVMPI_EVENT_CLASS_UNLOAD: class_unload(event); break; default: break; } } extern "C" { JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM * jvm, char * options, void * reserved) { int err; if (options && strstr(options, "version")) { cerr << "jvmpi_oprofile: current libopagent version " << op_major_version() << "." << op_minor_version() << endl; throw runtime_error("Exiting"); } if (options && strstr(options, "debug=yes")) { debug = true; /* Add something braindead to silence the 'unused parameter' * compiler warning. */ if (reserved) debug = true; } if (debug) cerr << "jvmpi_oprofile: agent activated" << endl; agent_hdl = op_open_agent(); if (!agent_hdl) { perror("Error: op_open_agent()"); throw runtime_error("Exiting"); } /* The union below is used to avoid the 'dereferencing type-punned * pointer will break strict-aliasing rules' compiler warning on the * GetEnv call. */ union { JVMPI_Interface * jvmpi_ifc; void * jvmpi_ifc_ptr; } jvmpi_GetEnv_arg; err = jvm->GetEnv(&jvmpi_GetEnv_arg.jvmpi_ifc_ptr, JVMPI_VERSION_1); if (err < 0) { cerr << "GetEnv failed with rc=" << err << endl; throw runtime_error("Exiting"); } jvmpi = jvmpi_GetEnv_arg.jvmpi_ifc; jvmpi->EnableEvent(JVMPI_EVENT_COMPILED_METHOD_LOAD, NULL); jvmpi->EnableEvent(JVMPI_EVENT_COMPILED_METHOD_UNLOAD, NULL); jvmpi->EnableEvent(JVMPI_EVENT_JVM_SHUT_DOWN, NULL); jvmpi->EnableEvent(JVMPI_EVENT_CLASS_LOAD, NULL); jvmpi->NotifyEvent = jvm_notify_event; return JNI_OK; } }