/**
* @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;
}
}