1/** 2 * @file jvmti_oprofile.c 3 * JVMTI 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 Jens Wilke 22 * @Modifications Daniel Hansel 23 * 24 * Copyright IBM Corporation 2007 25 * 26 */ 27 28#include <stdio.h> 29#include <jvmti.h> 30#include <string.h> 31#include <stdint.h> 32#include <stdlib.h> 33#include <errno.h> 34 35#include "opagent.h" 36 37static int debug = 0; 38static int can_get_line_numbers = 0; 39static op_agent_t agent_hdl; 40 41/** 42 * Handle an error or a warning, return 0 if the checked error is 43 * JVMTI_ERROR_NONE, i.e. success 44 */ 45static int handle_error(jvmtiError err, char const * msg, int severe) 46{ 47 if (err != JVMTI_ERROR_NONE) { 48 fprintf(stderr, "%s: %s, err code %i\n", 49 severe ? "Error" : "Warning", msg, err); 50 } 51 return err != JVMTI_ERROR_NONE; 52} 53 54 55/** 56 * returned array is map_length length, params map and map_length != 0 57 * format of lineno information is JVMTI_JLOCATION_JVMBCI, map is an array 58 * of { address, code byte index }, table_ptr an array of { byte code index, 59 * lineno } 60 */ 61static struct debug_line_info * 62create_debug_line_info(jint map_length, jvmtiAddrLocationMap const * map, 63 jint entry_count, jvmtiLineNumberEntry* table_ptr, 64 char const * source_filename) 65{ 66 struct debug_line_info * debug_line; 67 int i, j; 68 if (debug) { 69 fprintf(stderr, "Source %s\n", source_filename); 70 for (i = 0; i < map_length; ++i) { 71 fprintf(stderr, "%p %lld\t", 72 map[i].start_address, 73 (long long)map[i].location); 74 } 75 fprintf(stderr, "\n"); 76 for (i = 0; i < entry_count; ++i) { 77 fprintf(stderr, "%lld %d\t", 78 (long long)table_ptr[i].start_location, 79 table_ptr[i].line_number); 80 } 81 fprintf(stderr, "\n"); 82 } 83 84 debug_line = calloc(map_length, sizeof(struct debug_line_info)); 85 if (!debug_line) 86 return 0; 87 88 for (i = 0; i < map_length; ++i) { 89 /* FIXME: likely to need a lower_bound on the array, but 90 * documentation is a bit obscure about the contents of these 91 * arrray 92 **/ 93 for (j = 0; j < entry_count - 1; ++j) { 94 if (table_ptr[j].start_location > map[i].location) 95 break; 96 } 97 debug_line[i].vma = (unsigned long)map[i].start_address; 98 debug_line[i].lineno = table_ptr[j].line_number; 99 debug_line[i].filename = source_filename; 100 } 101 102 if (debug) { 103 for (i = 0; i < map_length; ++i) { 104 fprintf(stderr, "%lx %d\t", debug_line[i].vma, 105 debug_line[i].lineno); 106 } 107 fprintf(stderr, "\n"); 108 } 109 110 return debug_line; 111} 112 113 114static void JNICALL cb_compiled_method_load(jvmtiEnv * jvmti, 115 jmethodID method, jint code_size, void const * code_addr, 116 jint map_length, jvmtiAddrLocationMap const * map, 117 void const * compile_info) 118{ 119 jclass declaring_class; 120 char * class_signature = NULL; 121 char * method_name = NULL; 122 char * method_signature = NULL; 123 jvmtiLineNumberEntry* table_ptr = NULL; 124 char * source_filename = NULL; 125 struct debug_line_info * debug_line = NULL; 126 jvmtiError err; 127 128 /* shut up compiler warning */ 129 compile_info = compile_info; 130 131 err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, 132 &declaring_class); 133 if (handle_error(err, "GetMethodDeclaringClass()", 1)) 134 goto cleanup2; 135 136 if (can_get_line_numbers && map_length && map) { 137 jint entry_count; 138 139 err = (*jvmti)->GetLineNumberTable(jvmti, method, 140 &entry_count, &table_ptr); 141 if (err == JVMTI_ERROR_NONE) { 142 err = (*jvmti)->GetSourceFileName(jvmti, 143 declaring_class, &source_filename); 144 if (err == JVMTI_ERROR_NONE) { 145 debug_line = 146 create_debug_line_info(map_length, map, 147 entry_count, table_ptr, 148 source_filename); 149 } else if (err != JVMTI_ERROR_ABSENT_INFORMATION) { 150 handle_error(err, "GetSourceFileName()", 1); 151 } 152 } else if (err != JVMTI_ERROR_NATIVE_METHOD && 153 err != JVMTI_ERROR_ABSENT_INFORMATION) { 154 handle_error(err, "GetLineNumberTable()", 1); 155 } 156 } 157 158 err = (*jvmti)->GetClassSignature(jvmti, declaring_class, 159 &class_signature, NULL); 160 if (handle_error(err, "GetClassSignature()", 1)) 161 goto cleanup1; 162 163 err = (*jvmti)->GetMethodName(jvmti, method, &method_name, 164 &method_signature, NULL); 165 if (handle_error(err, "GetMethodName()", 1)) 166 goto cleanup; 167 168 if (debug) { 169 fprintf(stderr, "load: declaring_class=%p, class=%s, " 170 "method=%s, signature=%s, addr=%p, size=%i \n", 171 declaring_class, class_signature, method_name, 172 method_signature, code_addr, code_size); 173 } 174 175 { 176 int cnt = strlen(method_name) + strlen(class_signature) + 177 strlen(method_signature) + 2; 178 char buf[cnt]; 179 strncpy(buf, class_signature, cnt - 1); 180 strncat(buf, method_name, cnt - strlen(buf) - 1); 181 strncat(buf, method_signature, cnt - strlen(buf) - 1); 182 if (op_write_native_code(agent_hdl, buf, 183 (uint64_t)(uintptr_t) code_addr, 184 code_addr, code_size)) { 185 perror("Error: op_write_native_code()"); 186 goto cleanup; 187 } 188 } 189 190 if (debug_line) 191 if (op_write_debug_line_info(agent_hdl, code_addr, map_length, 192 debug_line)) 193 perror("Error: op_write_debug_line_info()"); 194 195cleanup: 196 (*jvmti)->Deallocate(jvmti, (unsigned char *)method_name); 197 (*jvmti)->Deallocate(jvmti, (unsigned char *)method_signature); 198cleanup1: 199 (*jvmti)->Deallocate(jvmti, (unsigned char *)class_signature); 200 (*jvmti)->Deallocate(jvmti, (unsigned char *)table_ptr); 201 (*jvmti)->Deallocate(jvmti, (unsigned char *)source_filename); 202cleanup2: 203 free(debug_line); 204} 205 206 207static void JNICALL cb_compiled_method_unload(jvmtiEnv * jvmti_env, 208 jmethodID method, void const * code_addr) 209{ 210 /* shut up compiler warning */ 211 jvmti_env = jvmti_env; 212 method = method; 213 214 if (debug) 215 fprintf(stderr, "unload: addr=%p\n", code_addr); 216 if (op_unload_native_code(agent_hdl, (uint64_t)(uintptr_t) code_addr)) 217 perror("Error: op_unload_native_code()"); 218} 219 220 221static void JNICALL cb_dynamic_code_generated(jvmtiEnv * jvmti_env, 222 char const * name, void const * code_addr, jint code_size) 223{ 224 /* shut up compiler warning */ 225 jvmti_env = jvmti_env; 226 if (debug) { 227 fprintf(stderr, "dyncode: name=%s, addr=%p, size=%i \n", 228 name, code_addr, code_size); 229 } 230 if (op_write_native_code(agent_hdl, name, 231 (uint64_t)(uintptr_t) code_addr, 232 code_addr, code_size)) 233 perror("Error: op_write_native_code()"); 234} 235 236 237JNIEXPORT jint JNICALL 238Agent_OnLoad(JavaVM * jvm, char * options, void * reserved) 239{ 240 jint rc; 241 jvmtiEnv * jvmti = NULL; 242 jvmtiEventCallbacks callbacks; 243 jvmtiCapabilities caps; 244 jvmtiJlocationFormat format; 245 jvmtiError error; 246 247 /* shut up compiler warning */ 248 reserved = reserved; 249 250 if (options && !strcmp("version", options)) { 251 fprintf(stderr, "jvmti_oprofile: current libopagent version %i.%i.\n", 252 op_major_version(), op_minor_version()); 253 return -1; 254 } 255 256 if (options && !strcmp("debug", options)) 257 debug = 1; 258 259 if (debug) 260 fprintf(stderr, "jvmti_oprofile: agent activated\n"); 261 262 agent_hdl = op_open_agent(); 263 if (!agent_hdl) { 264 perror("Error: op_open_agent()"); 265 return -1; 266 } 267 268 rc = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1); 269 if (rc != JNI_OK) { 270 fprintf(stderr, "Error: GetEnv(), rc=%i\n", rc); 271 return -1; 272 } 273 274 memset(&caps, '\0', sizeof(caps)); 275 caps.can_generate_compiled_method_load_events = 1; 276 error = (*jvmti)->AddCapabilities(jvmti, &caps); 277 if (handle_error(error, "AddCapabilities()", 1)) 278 return -1; 279 280 /* FIXME: settable through command line, default on/off? */ 281 error = (*jvmti)->GetJLocationFormat(jvmti, &format); 282 if (!handle_error(error, "GetJLocationFormat", 1) && 283 format == JVMTI_JLOCATION_JVMBCI) { 284 memset(&caps, '\0', sizeof(caps)); 285 caps.can_get_line_numbers = 1; 286 caps.can_get_source_file_name = 1; 287 error = (*jvmti)->AddCapabilities(jvmti, &caps); 288 if (!handle_error(error, "AddCapabilities()", 1)) 289 can_get_line_numbers = 1; 290 } 291 292 memset(&callbacks, 0, sizeof(callbacks)); 293 callbacks.CompiledMethodLoad = cb_compiled_method_load; 294 callbacks.CompiledMethodUnload = cb_compiled_method_unload; 295 callbacks.DynamicCodeGenerated = cb_dynamic_code_generated; 296 error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, 297 sizeof(callbacks)); 298 if (handle_error(error, "SetEventCallbacks()", 1)) 299 return -1; 300 301 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 302 JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); 303 if (handle_error(error, "SetEventNotificationMode() " 304 "JVMTI_EVENT_COMPILED_METHOD_LOAD", 1)) 305 return -1; 306 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 307 JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL); 308 if (handle_error(error, "SetEventNotificationMode() " 309 "JVMTI_EVENT_COMPILED_METHOD_UNLOAD", 1)) 310 return -1; 311 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 312 JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); 313 if (handle_error(error, "SetEventNotificationMode() " 314 "JVMTI_EVENT_DYNAMIC_CODE_GENERATED", 1)) 315 return -1; 316 return 0; 317} 318 319 320JNIEXPORT void JNICALL Agent_OnUnload(JavaVM * jvm) 321{ 322 /* shut up compiler warning */ 323 jvm = jvm; 324 if (op_close_agent(agent_hdl)) 325 perror("Error: op_close_agent()"); 326} 327