| /** |
| * @file jvmti_oprofile.c |
| * JVMTI 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 Jens Wilke |
| * @Modifications Daniel Hansel |
| * |
| * Copyright IBM Corporation 2007 |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <jvmti.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| #include "opagent.h" |
| |
| static int debug = 0; |
| static int can_get_line_numbers = 0; |
| static op_agent_t agent_hdl; |
| |
| /** |
| * Handle an error or a warning, return 0 if the checked error is |
| * JVMTI_ERROR_NONE, i.e. success |
| */ |
| static int handle_error(jvmtiError err, char const * msg, int severe) |
| { |
| if (err != JVMTI_ERROR_NONE) { |
| fprintf(stderr, "%s: %s, err code %i\n", |
| severe ? "Error" : "Warning", msg, err); |
| } |
| return err != JVMTI_ERROR_NONE; |
| } |
| |
| |
| /** |
| * returned array is map_length length, params map and map_length != 0 |
| * format of lineno information is JVMTI_JLOCATION_JVMBCI, map is an array |
| * of { address, code byte index }, table_ptr an array of { byte code index, |
| * lineno } |
| */ |
| static struct debug_line_info * |
| create_debug_line_info(jint map_length, jvmtiAddrLocationMap const * map, |
| jint entry_count, jvmtiLineNumberEntry* table_ptr, |
| char const * source_filename) |
| { |
| struct debug_line_info * debug_line; |
| int i, j; |
| if (debug) { |
| fprintf(stderr, "Source %s\n", source_filename); |
| for (i = 0; i < map_length; ++i) { |
| fprintf(stderr, "%p %lld\t", |
| map[i].start_address, |
| (long long)map[i].location); |
| } |
| fprintf(stderr, "\n"); |
| for (i = 0; i < entry_count; ++i) { |
| fprintf(stderr, "%lld %d\t", |
| (long long)table_ptr[i].start_location, |
| table_ptr[i].line_number); |
| } |
| fprintf(stderr, "\n"); |
| } |
| |
| debug_line = calloc(map_length, sizeof(struct debug_line_info)); |
| if (!debug_line) |
| return 0; |
| |
| for (i = 0; i < map_length; ++i) { |
| /* FIXME: likely to need a lower_bound on the array, but |
| * documentation is a bit obscure about the contents of these |
| * arrray |
| **/ |
| for (j = 0; j < entry_count - 1; ++j) { |
| if (table_ptr[j].start_location > map[i].location) |
| break; |
| } |
| debug_line[i].vma = (unsigned long)map[i].start_address; |
| debug_line[i].lineno = table_ptr[j].line_number; |
| debug_line[i].filename = source_filename; |
| } |
| |
| if (debug) { |
| for (i = 0; i < map_length; ++i) { |
| fprintf(stderr, "%lx %d\t", debug_line[i].vma, |
| debug_line[i].lineno); |
| } |
| fprintf(stderr, "\n"); |
| } |
| |
| return debug_line; |
| } |
| |
| |
| static void JNICALL cb_compiled_method_load(jvmtiEnv * jvmti, |
| jmethodID method, jint code_size, void const * code_addr, |
| jint map_length, jvmtiAddrLocationMap const * map, |
| void const * compile_info) |
| { |
| jclass declaring_class; |
| char * class_signature = NULL; |
| char * method_name = NULL; |
| char * method_signature = NULL; |
| jvmtiLineNumberEntry* table_ptr = NULL; |
| char * source_filename = NULL; |
| struct debug_line_info * debug_line = NULL; |
| jvmtiError err; |
| |
| /* shut up compiler warning */ |
| compile_info = compile_info; |
| |
| err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, |
| &declaring_class); |
| if (handle_error(err, "GetMethodDeclaringClass()", 1)) |
| goto cleanup2; |
| |
| if (can_get_line_numbers && map_length && map) { |
| jint entry_count; |
| |
| err = (*jvmti)->GetLineNumberTable(jvmti, method, |
| &entry_count, &table_ptr); |
| if (err == JVMTI_ERROR_NONE) { |
| err = (*jvmti)->GetSourceFileName(jvmti, |
| declaring_class, &source_filename); |
| if (err == JVMTI_ERROR_NONE) { |
| debug_line = |
| create_debug_line_info(map_length, map, |
| entry_count, table_ptr, |
| source_filename); |
| } else if (err != JVMTI_ERROR_ABSENT_INFORMATION) { |
| handle_error(err, "GetSourceFileName()", 1); |
| } |
| } else if (err != JVMTI_ERROR_NATIVE_METHOD && |
| err != JVMTI_ERROR_ABSENT_INFORMATION) { |
| handle_error(err, "GetLineNumberTable()", 1); |
| } |
| } |
| |
| err = (*jvmti)->GetClassSignature(jvmti, declaring_class, |
| &class_signature, NULL); |
| if (handle_error(err, "GetClassSignature()", 1)) |
| goto cleanup1; |
| |
| err = (*jvmti)->GetMethodName(jvmti, method, &method_name, |
| &method_signature, NULL); |
| if (handle_error(err, "GetMethodName()", 1)) |
| goto cleanup; |
| |
| if (debug) { |
| fprintf(stderr, "load: declaring_class=%p, class=%s, " |
| "method=%s, signature=%s, addr=%p, size=%i \n", |
| declaring_class, class_signature, method_name, |
| method_signature, code_addr, code_size); |
| } |
| |
| { |
| int cnt = strlen(method_name) + strlen(class_signature) + |
| strlen(method_signature) + 2; |
| char buf[cnt]; |
| strncpy(buf, class_signature, 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)(uintptr_t) code_addr, |
| code_addr, code_size)) { |
| perror("Error: op_write_native_code()"); |
| goto cleanup; |
| } |
| } |
| |
| if (debug_line) |
| if (op_write_debug_line_info(agent_hdl, code_addr, map_length, |
| debug_line)) |
| perror("Error: op_write_debug_line_info()"); |
| |
| cleanup: |
| (*jvmti)->Deallocate(jvmti, (unsigned char *)method_name); |
| (*jvmti)->Deallocate(jvmti, (unsigned char *)method_signature); |
| cleanup1: |
| (*jvmti)->Deallocate(jvmti, (unsigned char *)class_signature); |
| (*jvmti)->Deallocate(jvmti, (unsigned char *)table_ptr); |
| (*jvmti)->Deallocate(jvmti, (unsigned char *)source_filename); |
| cleanup2: |
| free(debug_line); |
| } |
| |
| |
| static void JNICALL cb_compiled_method_unload(jvmtiEnv * jvmti_env, |
| jmethodID method, void const * code_addr) |
| { |
| /* shut up compiler warning */ |
| jvmti_env = jvmti_env; |
| method = method; |
| |
| if (debug) |
| fprintf(stderr, "unload: addr=%p\n", code_addr); |
| if (op_unload_native_code(agent_hdl, (uint64_t)(uintptr_t) code_addr)) |
| perror("Error: op_unload_native_code()"); |
| } |
| |
| |
| static void JNICALL cb_dynamic_code_generated(jvmtiEnv * jvmti_env, |
| char const * name, void const * code_addr, jint code_size) |
| { |
| /* shut up compiler warning */ |
| jvmti_env = jvmti_env; |
| if (debug) { |
| fprintf(stderr, "dyncode: name=%s, addr=%p, size=%i \n", |
| name, code_addr, code_size); |
| } |
| if (op_write_native_code(agent_hdl, name, |
| (uint64_t)(uintptr_t) code_addr, |
| code_addr, code_size)) |
| perror("Error: op_write_native_code()"); |
| } |
| |
| |
| JNIEXPORT jint JNICALL |
| Agent_OnLoad(JavaVM * jvm, char * options, void * reserved) |
| { |
| jint rc; |
| jvmtiEnv * jvmti = NULL; |
| jvmtiEventCallbacks callbacks; |
| jvmtiCapabilities caps; |
| jvmtiJlocationFormat format; |
| jvmtiError error; |
| |
| /* shut up compiler warning */ |
| reserved = reserved; |
| |
| if (options && !strcmp("version", options)) { |
| fprintf(stderr, "jvmti_oprofile: current libopagent version %i.%i.\n", |
| op_major_version(), op_minor_version()); |
| return -1; |
| } |
| |
| if (options && !strcmp("debug", options)) |
| debug = 1; |
| |
| if (debug) |
| fprintf(stderr, "jvmti_oprofile: agent activated\n"); |
| |
| agent_hdl = op_open_agent(); |
| if (!agent_hdl) { |
| perror("Error: op_open_agent()"); |
| return -1; |
| } |
| |
| rc = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1); |
| if (rc != JNI_OK) { |
| fprintf(stderr, "Error: GetEnv(), rc=%i\n", rc); |
| return -1; |
| } |
| |
| memset(&caps, '\0', sizeof(caps)); |
| caps.can_generate_compiled_method_load_events = 1; |
| error = (*jvmti)->AddCapabilities(jvmti, &caps); |
| if (handle_error(error, "AddCapabilities()", 1)) |
| return -1; |
| |
| /* FIXME: settable through command line, default on/off? */ |
| error = (*jvmti)->GetJLocationFormat(jvmti, &format); |
| if (!handle_error(error, "GetJLocationFormat", 1) && |
| format == JVMTI_JLOCATION_JVMBCI) { |
| memset(&caps, '\0', sizeof(caps)); |
| caps.can_get_line_numbers = 1; |
| caps.can_get_source_file_name = 1; |
| error = (*jvmti)->AddCapabilities(jvmti, &caps); |
| if (!handle_error(error, "AddCapabilities()", 1)) |
| can_get_line_numbers = 1; |
| } |
| |
| memset(&callbacks, 0, sizeof(callbacks)); |
| callbacks.CompiledMethodLoad = cb_compiled_method_load; |
| callbacks.CompiledMethodUnload = cb_compiled_method_unload; |
| callbacks.DynamicCodeGenerated = cb_dynamic_code_generated; |
| error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, |
| sizeof(callbacks)); |
| if (handle_error(error, "SetEventCallbacks()", 1)) |
| return -1; |
| |
| error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, |
| JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); |
| if (handle_error(error, "SetEventNotificationMode() " |
| "JVMTI_EVENT_COMPILED_METHOD_LOAD", 1)) |
| return -1; |
| error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, |
| JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL); |
| if (handle_error(error, "SetEventNotificationMode() " |
| "JVMTI_EVENT_COMPILED_METHOD_UNLOAD", 1)) |
| return -1; |
| error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, |
| JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); |
| if (handle_error(error, "SetEventNotificationMode() " |
| "JVMTI_EVENT_DYNAMIC_CODE_GENERATED", 1)) |
| return -1; |
| return 0; |
| } |
| |
| |
| JNIEXPORT void JNICALL Agent_OnUnload(JavaVM * jvm) |
| { |
| /* shut up compiler warning */ |
| jvm = jvm; |
| if (op_close_agent(agent_hdl)) |
| perror("Error: op_close_agent()"); |
| } |