• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
class_load(JVMPI_Event * event)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 
class_unload(JVMPI_Event * event)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 
compiled_method_load(JVMPI_Event * event)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 
compiled_method_unload(JVMPI_Event * event)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 
jvm_shutdown(JVMPI_Event * event)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 
jvm_notify_event(JVMPI_Event * event)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" {
JVM_OnLoad(JavaVM * jvm,char * options,void * reserved)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