blob: 49a432d6cc42ef85ac47411ef29679f90765d13c [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* author Ian Formanek
* Tomas Hurka
* Misha Dimitiev
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jvmti.h"
#include "common_functions.h"
jvmtiEnv *_jvmti;
jvmtiEventCallbacks *_jvmti_callbacks;
static jlong _nano_time;
static jvmtiEventCallbacks _jvmti_callbacks_static;
/** A convenience function for the high-resolution timer */
jlong get_nano_time() {
(*_jvmti)->GetTime(_jvmti, &_nano_time);
return _nano_time;
}
/** Report the correct usage in case we think the user is trying to launch the VM on its own */
void report_usage() {
fprintf(stderr, "Profiler Agent: -agentpath:<PATH>/profilerinterface should be called with two parameters:\n");
fprintf(stderr, "Profiler Agent: path to Profiler agent libraries and port number, separated by comma, for example:\n");
fprintf(stderr, "Profiler Agent: java -agentpath:/mypath/profilerinterface=/home/me/nb-profiler-server/profiler-ea-libs,5140\n");
}
static void initializeJVMTI(JavaVM *jvm) {
jvmtiError err;
jvmtiCapabilities capas;
jint res;
/* Obtain the JVMTI environment to be used by this agent */
#ifdef JNI_VERSION_1_6
(*jvm)->GetEnv(jvm, (void**)&_jvmti, JVMTI_VERSION_1_1);
#else
(*jvm)->GetEnv(jvm, (void**)&_jvmti, JVMTI_VERSION_1_0);
#endif
/* Enable runtime class redefinition capability */
err = (*_jvmti)->GetCapabilities(_jvmti, &capas);
assert(err == JVMTI_ERROR_NONE);
capas.can_redefine_classes = 1;
#ifdef JNI_VERSION_1_6
capas.can_retransform_classes = 1;
#endif
capas.can_generate_garbage_collection_events = 1;
capas.can_generate_native_method_bind_events = 1;
capas.can_generate_monitor_events = 1;
capas.can_get_current_thread_cpu_time = 1;
capas.can_generate_vm_object_alloc_events = 1;
capas.can_get_monitor_info = 1;
err = (*_jvmti)->AddCapabilities(_jvmti, &capas);
if (err != JVMTI_ERROR_NONE) {
fprintf(stderr, "Profiler Agent Error: Failed to obtain JVMTI capabilities, error code: %d\n", err);
}
/* Zero out the callbacks data structure for future use*/
_jvmti_callbacks = &_jvmti_callbacks_static;
memset(_jvmti_callbacks, 0, sizeof(jvmtiEventCallbacks));
/* Enable class load hook event, that captures class file bytes for classes loaded by non-system loaders */
_jvmti_callbacks->ClassFileLoadHook = class_file_load_hook;
_jvmti_callbacks->NativeMethodBind = native_method_bind_hook;
_jvmti_callbacks->MonitorContendedEnter = monitor_contended_enter_hook;
_jvmti_callbacks->MonitorContendedEntered = monitor_contended_entered_hook;
_jvmti_callbacks->VMObjectAlloc = vm_object_alloc;
res = (*_jvmti)->SetEventCallbacks(_jvmti, _jvmti_callbacks, sizeof(*_jvmti_callbacks));
assert (res == JVMTI_ERROR_NONE);
res = (*_jvmti)->SetEventNotificationMode(_jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
assert(res == JVMTI_ERROR_NONE);
res = (*_jvmti)->SetEventNotificationMode(_jvmti, JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, NULL);
assert(res == JVMTI_ERROR_NONE);
res = (*_jvmti)->SetEventNotificationMode(_jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL);
assert(res == JVMTI_ERROR_NONE);
res = (*_jvmti)->SetEventNotificationMode(_jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL);
assert(res == JVMTI_ERROR_NONE);
}
/* The VM calls this function when the native library is loaded (for example, through System.loadLibrary). */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
if (_jvmti == NULL) {
fprintf(stdout, "Profiler Agent: JNI OnLoad Initializing...\n");
initializeJVMTI(jvm);
fprintf(stdout, "Profiler Agent: JNI OnLoad Initialized successfully\n");
}
return JNI_VERSION_1_2;
}
/** This function is called automatically upon agent startup */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
fprintf(stdout, "Profiler Agent: Initializing...\n");
initializeJVMTI(jvm);
if (options != NULL) {
fprintf (stdout, "Profiler Agent: Options: >%s<\n", options);
} else {
fprintf (stdout, "Profiler Agent: No options\n");
}
/* If it looks like the VM was started not from the tool, but on its own, e.g. like
java -agentpath:/blahblah/profilerinterface=/foobar/profiler-ea-libs,5140
do some sanity checks for options and then eable the VM init event, so that we can start
our Java agent when the VM is initialized */
if (options != NULL && strlen(options) > 0) { /* The spec says no options means options == "", but in reality it's NULL */
if (strpbrk(options, ",") == NULL) {
report_usage();
return -1;
} else { /* We believe the options are correct */
parse_options_and_extract_params(options);
_jvmti_callbacks->VMInit = vm_init_hook;
(*_jvmti)->SetEventCallbacks(_jvmti, _jvmti_callbacks, sizeof(*_jvmti_callbacks));
(*_jvmti)->SetEventNotificationMode(_jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
}
} // in case of calibration, the arguments are just empty, this is OK
fprintf(stdout, "Profiler Agent: Initialized successfully\n");
return 0;
}