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
34extern "C" {
35#include <stdint.h>
36#include <jvmpi.h>
37#include <opagent.h>
38}
39
40using namespace std;
41
42static bool debug = false;
43static op_agent_t agent_hdl;
44
45class class_details {
46public:
47	string name;
48	map<jmethodID, string> method_names;
49	map<jmethodID, string> method_signatures;
50};
51
52
53static pthread_mutex_t class_map_mutex = PTHREAD_MUTEX_INITIALIZER;
54static map <jobjectID, class_details> loaded_classes;
55
56void 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
74void 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
81JVMPI_Interface * jvmpi;
82
83void 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
140void 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
152void 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
163void 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
186extern "C" {
187JNIEXPORT 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