blob: ac90be9a399c8d4bcc117c5b1b0ea595099895b9 [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 Tomas Hurka
* Ian Formanek
* Misha Dmitriev
*/
#ifdef WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <fcntl.h>
#include <time.h>
#endif
#ifdef SOLARIS
#define _STRUCTURED_PROC 1
#include <sys/procfs.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "jni.h"
#include "jvmti.h"
#include "org_netbeans_lib_profiler_server_system_Stacks.h"
#include "common_functions.h"
#include "Threads.h"
#define NEEDS_CONVERSION (sizeof(jmethodID)!=sizeof(jint))
#define NO_OF_BASE_BITS 2
#define NO_OF_MASK_BITS (32-NO_OF_BASE_BITS)
#define NO_OF_BASE_ADDRESS (1<<NO_OF_BASE_BITS)
#define OFFSET_MASK ((1L<<NO_OF_MASK_BITS)-1)
#define BASE_ADDRESS_MASK (~OFFSET_MASK)
#define MAX_FRAMES 16384
#define PACKEDARR_ITEMS 4
static jvmtiFrameInfo *_stack_frames_buffer = NULL;
static jint *_stack_id_buffer = NULL;
static jclass threadType = NULL;
static jclass intArrType = NULL;
static long base_addresses[NO_OF_BASE_ADDRESS]={-1L,-1L,-1L,-1L};
static jint convert_jmethodID_to_jint(jmethodID jmethod) {
if (NEEDS_CONVERSION) {
long base_address=(long)jmethod&BASE_ADDRESS_MASK;
unsigned int i;
for (i=0;i<NO_OF_BASE_ADDRESS;i++) {
long ba = base_addresses[i];
if (ba == -1L) {
base_addresses[i] = base_address;
//fprintf(stderr,"Profiler Agent: Registering new base %lx\n",base_address);
}
if (base_addresses[i]==base_address) {
jint offset = (long)jmethod&OFFSET_MASK;
offset |= i<<NO_OF_MASK_BITS;
//fprintf(stderr,"M %p -> %x\n",jmethod,offset);
return offset;
}
}
fprintf(stderr,"Profiler Agent Warning: Cannot convert %p\n",jmethod);
return 0;
} else {
return (jint)jmethod;
}
}
static jmethodID convert_jint_to_jmethodID(jint method) {
if (NEEDS_CONVERSION) {
int offset = method&OFFSET_MASK;
int base_id = ((unsigned int)method)>>NO_OF_MASK_BITS;
jmethodID jmethod = (jmethodID)(base_addresses[base_id]|offset);
//fprintf(stderr,"X %x -> %p\n",method,jmethod);
//fflush(stderr);
return jmethod;
} else {
return (jmethodID)method;
}
}
/*
* Class: org_netbeans_lib_profiler_server_system_Stacks
* Method: getCurrentJavaStackDepth
* Signature: (Ljava/lang/Thread;)I
*/
JNIEXPORT jint JNICALL Java_org_netbeans_lib_profiler_server_system_Stacks_getCurrentJavaStackDepth
(JNIEnv *env, jclass clz, jobject jni_thread)
{
jint count;
(*_jvmti)->GetFrameCount(_jvmti, jni_thread, &count);
return count;
}
/*
* Class: org_netbeans_lib_profiler_server_system_Stacks
* Method: createNativeStackFrameBuffer
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_org_netbeans_lib_profiler_server_system_Stacks_createNativeStackFrameBuffer
(JNIEnv *env, jclass clz, jint sizeInFrames)
{
if (_stack_frames_buffer != NULL) {
Java_org_netbeans_lib_profiler_server_system_Stacks_clearNativeStackFrameBuffer(env, clz);
}
_stack_frames_buffer = calloc(sizeInFrames, sizeof(jvmtiFrameInfo));
_stack_id_buffer = calloc(sizeInFrames, sizeof(jint));
}
/*
* Class: org_netbeans_lib_profiler_server_system_Stacks
* Method: clearNativeStackFrameBuffer
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_netbeans_lib_profiler_server_system_Stacks_clearNativeStackFrameBuffer
(JNIEnv *env, jclass clz)
{
if (_stack_frames_buffer != NULL) {
free(_stack_frames_buffer);
}
if (_stack_id_buffer != NULL) {
free(_stack_id_buffer);
}
_stack_frames_buffer = NULL;
_stack_id_buffer = NULL;
}
/*
* Class: org_netbeans_lib_profiler_server_system_Stacks
* Method: getCurrentStackFrameIds
* Signature: (Ljava/lang/Thread;I[I)I
*/
JNIEXPORT jint JNICALL Java_org_netbeans_lib_profiler_server_system_Stacks_getCurrentStackFrameIds
(JNIEnv *env, jclass clz, jthread jni_thread, jint depth, jintArray ret)
{
jint i, count;
if (_stack_frames_buffer == NULL) {
/* Can happen if profiling stopped concurrently */
return 0;
}
(*_jvmti)->GetStackTrace(_jvmti, jni_thread, 0, depth, _stack_frames_buffer, &count);
for (i = 0; i < count; i++) {
_stack_id_buffer[i] = convert_jmethodID_to_jint(_stack_frames_buffer[i].method);
}
(*env)->SetIntArrayRegion(env, ret, 0, count, _stack_id_buffer);
return count;
}
static jbyte *byteData;
static jint *strOffsets;
static int byteDataLen, dataOfs, ofsIdx;
static void copy_into_data_array(char *s) {
int len = strlen(s);
if (dataOfs + len > byteDataLen) {
jbyte *oldByteData = byteData;
int newLen = byteDataLen * 2;
if (newLen < dataOfs + len) {
newLen = dataOfs+len;
}
byteData = malloc(newLen);
memcpy(byteData, oldByteData, dataOfs);
free(oldByteData);
byteDataLen = newLen;
}
strncpy((char*)(byteData + dataOfs), s, len);
strOffsets[ofsIdx++] = dataOfs;
dataOfs += len;
}
static void copy_dummy_names_into_data_array() {
copy_into_data_array("<unknown class>");
copy_into_data_array("<unknown method>");
copy_into_data_array("()V");
copy_into_data_array("0");
}
/*
* Class: org_netbeans_lib_profiler_server_system_Stacks
* Method: getMethodNamesForJMethodIds
* Signature: (I[I[I)[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_netbeans_lib_profiler_server_system_Stacks_getMethodNamesForJMethodIds
(JNIEnv *env, jclass clz, jint nMethods, jintArray jmethodIds, jintArray packedArrayOffsets)
{
jvmtiError res;
int i, len;
jint *methodIds;
jbyteArray ret;
// fprintf (stderr, "1");
methodIds = (jint*) malloc(sizeof(jint) * nMethods);
(*env)->GetIntArrayRegion(env, jmethodIds, 0, nMethods, methodIds);
strOffsets = (jint*) malloc(sizeof(jint) * nMethods * PACKEDARR_ITEMS);
byteDataLen = nMethods * PACKEDARR_ITEMS * 10; /* The initial size for the packed strings array */
byteData = (jbyte*) malloc(byteDataLen);
// fprintf (stderr, "2");
dataOfs = ofsIdx = 0;
for (i = 0; i < nMethods; i++) {
jclass declaringClass;
char *className, *genericSignature, *methodName, *methodSig, *genericMethodSig;
jboolean native = JNI_FALSE;
jmethodID methodID = convert_jint_to_jmethodID(methodIds[i]);
//fprintf (stderr, "Going to call GetMethodDeclaringClass for methodId = %d\n", *(int*)methodID);
res = (*_jvmti)->GetMethodDeclaringClass(_jvmti, methodID, &declaringClass);
if (res != JVMTI_ERROR_NONE || declaringClass == NULL || *((int*)declaringClass) == 0) { /* Also a bug workaround */
fprintf(stderr, "Profiler Agent Warning: Invalid declaringClass obtained from jmethodID\n");
fprintf(stderr, "Profiler Agent Warning: mId = %p, *mId = %d\n", methodID, *(int*)methodID);
fprintf(stderr, "Profiler Agent Warning: dCl = %p", declaringClass);
if (declaringClass != NULL) {
fprintf(stderr, ", *dCl = %d\n", *((int*)declaringClass));
} else {
fprintf(stderr, "\n");
}
// fprintf(stderr, "*** res = %d", res);
copy_dummy_names_into_data_array();
continue;
}
// fprintf (stderr, "Going to call GetClassSignature for methodId = %d, last res = %d, declaring class: %d\n", *(int*)methodID, res, *((int*)declaringClass));
res = (*_jvmti)->GetClassSignature(_jvmti, declaringClass, &className, &genericSignature);
if (res != JVMTI_ERROR_NONE) {
fprintf(stderr, "Profiler Agent Warning: Couldn't obtain name of declaringClass = %p\n", declaringClass);
copy_dummy_names_into_data_array();
continue;
}
// fprintf (stderr, "Going to call GetMethodName for methodId = %d, last res = %d, signature: %s\n", *(int*)methodID, res, genericSignature);
res = (*_jvmti)->GetMethodName(_jvmti, methodID, &methodName, &methodSig, &genericMethodSig);
if (res != JVMTI_ERROR_NONE) {
fprintf(stderr, "Profiler Agent Warning: Couldn't obtain name for methodID = %p\n", methodID);
copy_dummy_names_into_data_array();
continue;
}
// fprintf (stderr, "Going to call IsMethodNative for methodId = %d, last res = %d, signature: %s\n", *(int*)methodID, res, genericSignature);
res = (*_jvmti)->IsMethodNative(_jvmti, methodID, &native);
if (res != JVMTI_ERROR_NONE) {
fprintf(stderr, "Profiler Agent Warning: Couldn't obtain native flag for methodID = %p\n", methodID);
}
// fprintf (stderr, "Going to copy results, last res = %d, method name: %s, sig: %s, genSig: %s, native %d\n", res, methodName, methodSig, genericMethodSig, native);
len = strlen(className);
if (className[0] == 'L' && className[len-1] == ';') {
className[len-1] = 0;
copy_into_data_array(className+1);
} else {
copy_into_data_array(className);
}
copy_into_data_array(methodName);
copy_into_data_array(methodSig);
copy_into_data_array(native?"1":"0");
(*_jvmti)->Deallocate(_jvmti, (void*)className);
if (genericSignature != NULL) {
(*_jvmti)->Deallocate(_jvmti, (void*)genericSignature);
}
(*_jvmti)->Deallocate(_jvmti, (void*)methodName);
(*_jvmti)->Deallocate(_jvmti, (void*)methodSig);
if (genericMethodSig != NULL) {
(*_jvmti)->Deallocate(_jvmti, (void*)genericMethodSig);
}
}
// fprintf (stderr, "3");
free(methodIds);
ret = (*env)->NewByteArray(env, dataOfs);
(*env)->SetByteArrayRegion(env, ret, 0, dataOfs, byteData);
(*env)->SetIntArrayRegion(env, packedArrayOffsets, 0, nMethods*PACKEDARR_ITEMS, strOffsets);
// fprintf (stderr, "4");
free(strOffsets);
free(byteData);
return ret;
}
/*
* Class: org_netbeans_lib_profiler_server_system_Stacks
* Method: getAllStackTraces
* Signature: ([[Ljava/lang/Thread;[[I[[[I)V
*/
JNIEXPORT void JNICALL Java_org_netbeans_lib_profiler_server_system_Stacks_getAllStackTraces
(JNIEnv *env, jclass clz, jobjectArray threads, jobjectArray states, jobjectArray frames)
{
jobjectArray jthreadArr;
jobjectArray statesArr;
jobjectArray methodIdArrArr;
jvmtiStackInfo *stack_info;
jint *state_buffer;
jint thread_count;
int ti;
jvmtiError err;
err = (*_jvmti)->GetAllStackTraces(_jvmti, MAX_FRAMES, &stack_info, &thread_count);
if (err != JVMTI_ERROR_NONE) {
return;
}
if (threadType == NULL) {
threadType = (*env)->FindClass(env, "java/lang/Thread");
threadType = (*env)->NewGlobalRef(env, threadType);
}
if (intArrType == NULL) {
intArrType = (*env)->FindClass(env, "[I");
intArrType = (*env)->NewGlobalRef(env, intArrType);
}
jthreadArr = (*env)->NewObjectArray(env, thread_count, threadType, NULL);
(*env)->SetObjectArrayElement(env, threads, 0, jthreadArr);
statesArr = (*env)->NewIntArray(env, thread_count);
(*env)->SetObjectArrayElement(env, states, 0, statesArr);
methodIdArrArr = (*env)->NewObjectArray(env, thread_count, intArrType, NULL);
(*env)->SetObjectArrayElement(env, frames, 0, methodIdArrArr);
state_buffer = calloc(thread_count, sizeof(jint));
for (ti = 0; ti < thread_count; ti++) {
jvmtiStackInfo *infop = &stack_info[ti];
jthread thread = infop->thread;
jint state = infop->state;
jvmtiFrameInfo *frames = infop->frame_buffer;
jobjectArray jmethodIdArr;
jint *id_buffer;
int fi;
(*env)->SetObjectArrayElement(env, jthreadArr, ti, thread);
state_buffer[ti] = convert_JVMTI_thread_status_to_jfluid_status(state);
jmethodIdArr = (*env)->NewIntArray(env, infop->frame_count);
(*env)->SetObjectArrayElement(env, methodIdArrArr, ti, jmethodIdArr);
id_buffer = calloc(infop->frame_count, sizeof(jint));
for (fi = 0; fi < infop->frame_count; fi++) {
id_buffer[fi] = convert_jmethodID_to_jint(frames[fi].method);
}
(*env)->SetIntArrayRegion(env, jmethodIdArr, 0, infop->frame_count, id_buffer);
free(id_buffer);
}
(*env)->SetIntArrayRegion(env, statesArr, 0, thread_count, state_buffer);
/* this one Deallocate call frees all data allocated by GetAllStackTraces */
err = (*_jvmti)->Deallocate(_jvmti, (unsigned char*)stack_info);
assert(err == JVMTI_ERROR_NONE);
free(state_buffer);
}