Skip to main content

Source code file content

Revision: aa955f6dbe228289c7fa608a5bf1a18b0d5cc674

copyright FIT added
» Project Revision History

» Checkout URL

main / JVMAgent.c

Size: 86178 bytes, 1 line
/*
 * File:   main.c
 * Author: Filip Voracek
 *
 * Created on 28. September 2011, 11:50
 *
 * (c) Filip Voracek, FIT CTU in Prague, 2012
 */

#include "./logger/logger.h"
#include "./parser/parser.h"
#include "./threadRelationControl.h"
#include "accessInfo.h"

#define START_MAX_MONITORS_INFO 10
#define START_MAX_STACK_TRACES 10
#define START_MAX_LOCK_MONITORS 10
#define START_MAX_LOCK_MONITORS_TAGS 10
#define START_MAX_DATA_RACES_INFO 10
#define START_MAX_THREAD_RELATIONS 10
#define START_MAX_JOIN_ZERO_THREADS 10

typedef enum {
    PACKAGES,
    CLASSES,
    SUPER_CLASSES,
    FIELDS
} ParameterType;

typedef struct {
    jlong threadSourceTag;
    int threadState;
    jlong klassTag;
    jlong objectTag;
    char * threadName; // only for output purposes
    jfieldID field;
    jint owned_monitor_count;
    jlong * owned_monitors_tags;
    bool modification; // false for read-only access, true for modification
    int numberOfLockTags;
    jlong * lockTags;
    int numberOfStackTraces;
    int max_stack_traces;
    char ** stackTraces;
} OwnedMonitorsInfo;

typedef struct {
    jlong threadSourceTag;
    char * threadName; // only for output purposes
    int numberOfLockTags;
    int maxLockTags;
    jlong * lockTags;
} LockMonitors;

typedef struct {
    char * fromThreadName;
    char * fieldName;
    char * classSignature;
    jlong objectTag;
    char * fieldSignature;
    char * stackTrace;
} DataRace;

typedef struct {
    jlong threadSourceTag;
    jlong threadCalledTag;
    bool enteredJoinParameterZero; // If true, thread called the Thread.join() method with entry parameter 0 (reusable boolean)
} JoinZeroThread; // used for determining if thread entered method join with parameter 0

static OwnedMonitorsInfo * ownedMonitorsInfos;
static int max_monitors_infos = START_MAX_MONITORS_INFO;
static int numberOfMonitorsInfos = 0;

static LockMonitors * lockMonitors;
static int max_lock_monitors = START_MAX_LOCK_MONITORS;
static int numberOfLockMonitors = 0;

static jvmtiCapabilities capa;
static jvmtiError jvmError = JVMTI_ERROR_NONE;

static jrawMonitorID rawMonitorId;
static jvmtiEnv * global_jvmti_env;
static jlong tagCounter = 1; // 0 is NULL !!!

static DataRace * dataRaces;
static int max_data_races = START_MAX_DATA_RACES_INFO;
static int numberOfDataRaces = 0;

static JoinZeroThread * joinZeroThreads;
static int max_join_zero_threads = START_MAX_JOIN_ZERO_THREADS;
static int numberOfJoinZeroThreads = 0;

/**
 * Used for synchronizing with java tester
 */
static char* AGENT_DATA_RACE_UNIQUE_OUTPUT = "Potential data race accessing or modifying field:";

/* Every JVMTI interface returns an error code, which should be checked
 *   to avoid any cascading errors down the line.
 *   The interface GetErrorName() returns the actual enumeration constant
 *   name, making the error messages much easier to understand.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum, const char *str) {
    if (errnum != JVMTI_ERROR_NONE) {
        char *errnum_str = NULL;
        (void) (*jvmti)->GetErrorName(jvmti, errnum, &errnum_str);

        char tmp[BUFFER_SIZE];
        int bufferNeeded = sprintf(tmp, "JVMTI: %d(%s): %s", errnum,
                (errnum_str == NULL ? "Unknown" : errnum_str), (str == NULL ? "" : str));
        check_buffer_overflow(bufferNeeded);

        if (errnum == JVMTI_ERROR_ABSENT_INFORMATION) {
            debug("check_jvmti_error", tmp);
        } else {
            warn("check_jvmti_error", tmp);
        }
        //warn("check_jvmti_error", tmp);
    }
}

/**
 * Deallocate given pointer using jvmti deallocate method
 * @param jvmti
 * @param p
 */
void deallocate(jvmtiEnv *jvmti, void *p) {
    if (p) {
        jvmError = (*jvmti)->Deallocate(jvmti, (unsigned char *) p);
        check_jvmti_error(jvmti, jvmError, "Unable to deallocate");
    }
}

/**
 * Returns true if given strings are the same. If first or second are null, false is returned.
 * If the given nullEquals parameter is true, then if both are null, true is returned.
 * If the given nullEquals parameter is false, then if both are null, false is returned.
 * @param str1
 * @param str2
 * @param nullEquals
 */
static bool equals(const char * str1, const char * str2, bool nullEquals) {
    if (nullEquals && str1 == NULL && str2 == NULL) {
        return true;
    }

    if (str1 == NULL || str2 == NULL) {
        return false;
    }

    return (!strcmp(str1, str2)) ? true : false;
}

static void enterRawMonitor(jvmtiEnv *jvmti_env) {
    jvmError = (*jvmti_env)->RawMonitorEnter(jvmti_env, rawMonitorId);
    check_jvmti_error(jvmti_env, jvmError, "Entering raw monitor failed");
}

static void exitRawMonitor(jvmtiEnv *jvmti_env) {
    jvmError = (*jvmti_env)->RawMonitorExit(jvmti_env, rawMonitorId);
    check_jvmti_error(jvmti_env, jvmError, "Exiting raw monitor failed");
}

/**
 * Returns tag of given object. If there is no tag, returns 0
 * @param jvmti_env
 * @param object
 * @return
 */
static jlong getTag(jvmtiEnv *jvmti_env, jobject object) {
    if (object == NULL) {
        return 0;
    }
    jlong tag = 0;
    jvmError = (*jvmti_env)->GetTag(jvmti_env, object, &tag);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get tag");
    return tag;
}

/**
 * Returns tag of given object. If there is no tag, new one will be set.
 * If given object NULL, exception will be probably thrown.
 * @param jvmti_env
 * @param object
 * @return
 */
static jlong obtainTag(jvmtiEnv *jvmti_env, jobject object) {
    jlong tag = 0;
    jvmError = (*jvmti_env)->GetTag(jvmti_env, object, &tag);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain tag");

    if (tag == 0) {
        tag = tagCounter;
        jvmError = (*jvmti_env)->SetTag(jvmti_env, object, tagCounter);
        check_jvmti_error(jvmti_env, jvmError, "Unable to set tag");
        debug("obtainTag", "tagCounter = %ld", (long) tagCounter);
        tagCounter++;
    }

    return tag;
}

static jint getFieldModifiers(jvmtiEnv * jvmti_env, jclass klass, jfieldID field) {
    jint modifiers = 0;
    jvmError = (*jvmti_env)->GetFieldModifiers(jvmti_env, klass, field, &modifiers);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get field modifiers");

    return modifiers;
}

static bool hasFieldFlag(jvmtiEnv * jvmti_env, jclass klass, jfieldID field, int flagPosition) {
    int modifiers = (int) getFieldModifiers(jvmti_env, klass, field);
    return ((modifiers & flagPosition) != 0) ? true : false;
}

static bool isFieldFinal(jvmtiEnv * jvmti_env, jclass klass, jfieldID field) {
    return hasFieldFlag(jvmti_env, klass, field, 0x0010);
}

static bool isFieldStatic(jvmtiEnv * jvmti_env, jclass klass, jfieldID field) {
    return hasFieldFlag(jvmti_env, klass, field, 0x0008);
}

static bool isFieldVolatile(jvmtiEnv * jvmti_env, jclass klass, jfieldID field) {
    return hasFieldFlag(jvmti_env, klass, field, 0x0040);
}

static void setFieldAccessWatch(jvmtiEnv *jvmti_env, jclass klass, jfieldID id) {
    jvmtiError error = JVMTI_ERROR_NONE;
    error = (*jvmti_env)->SetFieldAccessWatch(jvmti_env, klass, id);
    check_jvmti_error(jvmti_env, error, "Unable to set field access watch.");
    info("setFieldAccessWatch", "field access watch set!");
}

static void setFieldModificationWatch(jvmtiEnv *jvmti_env, jclass klass, jfieldID id) {
    jvmtiError error = JVMTI_ERROR_NONE;
    error = (*jvmti_env)->SetFieldModificationWatch(jvmti_env, klass, id);
    check_jvmti_error(jvmti_env, error, "Unable to set field modification watch.");
    info("setFieldModificationWatch", "field modification watch set!");
}

/*
static void getClassSourceFileName(jvmtiEnv *jvmti_env, jclass klass) {
    char *sig = NULL;
    jvmError = (*jvmti_env)->GetSourceFileName(jvmti_env, klass, &sig);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class source file name.");

    info("getClassSourceFileName", "source = %s", sig);
    deallocate(jvmti_env, sig);
}
*/

/**
 * Obtain thread name of given thread and output it (if name exists)
 * @param jvmti_env
 * @param thread
 */
static void fillDataRaceThreadName(jvmtiEnv *jvmti_env, jthread thread) {
    jvmtiThreadInfo threadInfo;
    jvmError = (*jvmti_env)->GetThreadInfo(jvmti_env, thread, &threadInfo);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain thread info.");

    dataRaces[numberOfDataRaces].fromThreadName = threadInfo.name;
}

static void fillDataRaceClassSignature(jvmtiEnv *jvmti_env, jclass klass) {
    char *sig = NULL;
    char *gen = NULL;

    jvmError = (*jvmti_env)->GetClassSignature(jvmti_env, klass, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class signature.");
    dataRaces[numberOfDataRaces].classSignature = sig;
    deallocate(jvmti_env, gen);
}

/**
 * Return signature without first and last letter
 * @param signature
 * @return
 */
static char* adjustSignature(jvmtiEnv *jvmti_env, char * signature) {
    trace("adjustSignature", "starting");
    unsigned int length = strlen(signature) - 2; //  -2 for first and last letters
    char * adjustedSig = (char*) calloc(length + 1, sizeof (char)); // +1 for \0

    unsigned int i;
    for (i = 0; i < length; i++) {
        adjustedSig[i] = signature[i + 1];
    }
    adjustedSig[length] = '\0';

    deallocate(jvmti_env, signature);
    return adjustedSig;
}

/**
 * If shouldAdjust is true,
 * return signature without first and last letter of given class.\n
 * If shouldAdjust is false, just return class signature.\n
 * If something fails, null is return.\n
 * Note that returned string has to be deallocated if shouldAdjust is false.
 * If shouldAdjust is true than returned string has to be freed.
 * @param jvmti_env
 * @param klass
 * @return
 */
static char * getClassAdjustedSignature(jvmtiEnv *jvmti_env, jclass klass,
        bool shouldAdjust) {

    char *sig = NULL;
    char *gen = NULL;

    jvmError = (*jvmti_env)->GetClassSignature(jvmti_env, klass, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get class signature.");

    deallocate(jvmti_env, gen);
    if (sig) {
        if (shouldAdjust) {
            return adjustSignature(jvmti_env, sig);
        } else {
            return sig;
        }
    } else {
        return NULL;
    }
}

/**
 * Return signature without first and last letter of declaring class of given method or null.
 * Note that returned char* has to be deallocated.
 * @param jvmti_env
 * @param method
 * @return
 */
static char * getDeclaringClassAdjustedSignature(jvmtiEnv *jvmti_env, jmethodID method) {
    trace("getDeclaringClassAdjustedSignature", "starting");
    jclass declaring_class = NULL;
    jvmError = (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method,
            &declaring_class);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get declaring class");

    return getClassAdjustedSignature(jvmti_env, declaring_class, true);
}

static void fillDataRaceFieldNameAndFieldSignature(jvmtiEnv* jvmti_env, jclass klass, jfieldID field) {
    char* name = NULL;
    char* sig = NULL;
    char* gen = NULL;

    jvmError = (*jvmti_env)->GetFieldName(jvmti_env, klass, field, &name, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain field name.");

    fillDataRaceClassSignature(jvmti_env, klass);
    dataRaces[numberOfDataRaces].fieldName = name;
    dataRaces[numberOfDataRaces].fieldSignature = sig;

    deallocate(jvmti_env, gen);
}

/**
 * Return field name or null. Returned char has to be deallocated
 * @param jvmti_env
 * @param klass
 * @param field
 * @return
 */
static char * getFieldName(jvmtiEnv* jvmti_env, jclass klass, jfieldID field) {
    char* name = NULL;
    char* sig = NULL;
    char* gen = NULL;

    jvmError = (*jvmti_env)->GetFieldName(jvmti_env, klass, field, &name, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get field name.");

    deallocate(jvmti_env, sig);
    deallocate(jvmti_env, gen);

    return name;
}

/**
 * Return thread name or null. Returned string has to be deallocated.
 * @param jvmti_env
 * @param thread
 * @return
 */
static char * getThreadName(jvmtiEnv *jvmti_env, jthread thread) {
    jvmtiThreadInfo threadInfo;
    jvmError = (*jvmti_env)->GetThreadInfo(jvmti_env, thread, &threadInfo);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get thread info.");
    return threadInfo.name;
}

/**
 * Just print (using info) class and field signature
 * @param jvmti_env
 * @param klass
 * @param field
 */
static void infoFieldNameAndSignature(jvmtiEnv* jvmti_env, jclass klass, jfieldID field) {
    char *classSig = NULL;
    char *classGen = NULL;
    char* name = NULL;
    char* sig = NULL;
    char* gen = NULL;

    jvmError = (*jvmti_env)->GetClassSignature(jvmti_env, klass, &classSig, &classGen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class signature.");

    jvmError = (*jvmti_env)->GetFieldName(jvmti_env, klass, field, &name, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain field name.");

    info("infoFieldNameAndSignature", "class sig = %s, field name = %s, field signature = %s",
            classSig, name, sig);

    deallocate(jvmti_env, classSig);
    deallocate(jvmti_env, classGen);
    deallocate(jvmti_env, name);
    deallocate(jvmti_env, sig);
    deallocate(jvmti_env, gen);
}

static int findLastSlash(char * st) {
    int size = strlen(st);

    int i;
    for (i = size - 3; i >= 0; i--) { // 2 for \0 and ; and some char
        if (st[i] == '/') {
            return i;
        }
    }
    return -1;
}

/**
 * True if user did not give any specific field, class, super class or package
 * && class has a class loader
 *
 * @return
 */
static bool shouldWatchAllFields(jvmtiEnv *jvmti_env, jclass klass) {
    if (numberOfPackages == 0 && numberOfFields == 0 && numberOfClasses == 0
            && numberOfSuperClasses == 0) {

        jobject classloader = NULL;
        jvmError = (*jvmti_env)->GetClassLoader(jvmti_env, klass, &classloader);
        check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class loader.");

        if (classloader != NULL) {
            return true;
        }
    }

    return false;
}

static bool findIfClassShouldBeWatched(jvmtiEnv *jvmti_env, jclass klass, char * klassNameWithPackage,
        ParameterType * type, int * position, int * positions, int *positionsLength) {

    if (shouldWatchAllFields(jvmti_env, klass)) {
        return true;
    }

    int size = findLastSlash(klassNameWithPackage);
    int length = strlen(klassNameWithPackage);

    bool belongs = false;

    // packages
    int i, j;
    for (i = 0; i < numberOfPackages; i++) {
        belongs = true;
        if (size < (int) strlen(packagesNames[i])) {
            continue;
        }
        for (j = 1; j <= size; j++) { // 1 for L
            if (packagesNames[i][j - 1] != klassNameWithPackage[j]) {
                belongs = false;
                break;
            }
        }
        if (belongs) {
            *type = PACKAGES;
            *position = i;
            return true;
        }
    }

    // classes
    for (i = 0; i < numberOfClasses; i++) {
        belongs = true;
        if (length <= (int) strlen(classesNames[i])) {
            continue;
        }
        for (j = 1; j < length - 1; j++) {
            if (classesNames[i][j - 1] != klassNameWithPackage[j]) {
                belongs = false;
                break;
            }
        }
        if (belongs) {
            *type = CLASSES;
            *position = i;
            return true;
        }
    }

    // super classes
    for (i = 0; i < numberOfSuperClasses; i++) {
        belongs = true;
        if (length <= (int) strlen(superClassesNames[i])) {
            continue;
        }
        for (j = 1; j < length - 1; j++) {
            if (superClassesNames[i][j - 1] != klassNameWithPackage[j]) {
                belongs = false;
                break;
            }
        }
        if (belongs) {
            *type = SUPER_CLASSES;
            *position = i;
            return true;
        }
    }

    // fields
    *positionsLength = 0;
    *type = PACKAGES; // anything but FIELDS
    for (i = 0; i < numberOfFields; i++) {
        belongs = true;
        if (length <= (int) strlen(fieldsNames[i].classSignature)) {
            continue;
        }
        for (j = 1; j < length - 1; j++) {
            if (fieldsNames[i].classSignature[j - 1] != klassNameWithPackage[j]) {
                belongs = false;
            }
        }
        if (belongs) {
            *type = FIELDS;
            positions[*positionsLength] = i;
            (*positionsLength)++;
        }
    }

    if (*type == FIELDS) {
        return true;
    }

    return false;
}

/**
 * Check if field is volatile, final or neither and do proper action
 * @param jvmti_env
 * @param klass
 * @param fieldID
 */
static void checkFieldAndSetWatches(jvmtiEnv *jvmti_env, jclass klass, jfieldID fieldID) {
    if (isFieldVolatile(jvmti_env, klass, fieldID)) {
        return; // we do not want to watch volatile fields
    } else if (isFieldFinal(jvmti_env, klass, fieldID)) {
        infoFieldNameAndSignature(jvmti_env, klass, fieldID);
        info("setWatches", "field is final");
        return; // we do not want to watch final fields either (for now)
    } else {
        infoFieldNameAndSignature(jvmti_env, klass, fieldID);

        // setting fields watches
        setFieldAccessWatch(jvmti_env, klass, fieldID);
        setFieldModificationWatch(jvmti_env, klass, fieldID);
    }
}

static void setWatches(jvmtiEnv *jvmti_env, jclass klass) {
    jint field_count = 0;
    jfieldID* fields = NULL;
    jvmError = (*jvmti_env)->GetClassFields(jvmti_env, klass, &field_count, &fields);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class fields.");

    int i;
    for (i = 0; i < field_count; i++) {
        checkFieldAndSetWatches(jvmti_env, klass, fields[i]);
    }

    deallocate(jvmti_env, fields);
}

static void setWatchesForSuperClass(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jclass klass) {
    while (true) {
        setWatches(jvmti_env, klass);
        klass = (*jni_env)->GetSuperclass(jni_env, klass);
        if (!klass) {
            break;
        }
    }
}

static jfieldID findFieldId(jvmtiEnv *jvmti_env, jclass klass, char * fieldName) {
    jint field_count = 0;
    jfieldID* fields = NULL;
    jvmError = (*jvmti_env)->GetClassFields(jvmti_env, klass, &field_count, &fields);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class fields.");

    bool found = false;
    int i;
    for (i = 0; i < field_count; i++) {
        char * name = NULL;
        char * sig = NULL;
        char * gen = NULL;

        jvmError = (*jvmti_env)->GetFieldName(jvmti_env, klass, fields[i], &name, &sig, &gen);
        check_jvmti_error(jvmti_env, jvmError, "Unable to obtain field name.");

        deallocate(jvmti_env, sig);
        deallocate(jvmti_env, gen);

        if (equals(name, fieldName, false)) {
            deallocate(jvmti_env, name);
            found = true;
            break;
        }
        deallocate(jvmti_env, name);
    }

    jfieldID field = NULL;
    if (found) {
        field = fields[i];
    }
    deallocate(jvmti_env, fields);

    return field;
}

static void setWatchesForField(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jclass klass, int pos) {
    jfieldID fieldID = NULL;
    if (fieldsNames[pos].fieldSignature != NULL) {
        fieldID = (*jni_env)->GetFieldID(jni_env, klass,
                fieldsNames[pos].fieldName, fieldsNames[pos].fieldSignature);
    } else {
        fieldID = findFieldId(jvmti_env, klass, fieldsNames[pos].fieldName);
    }

    if (fieldID != NULL) {
        checkFieldAndSetWatches(jvmti_env, klass, fieldID);
    }
}

static void setWatchesForSignature(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jclass klass, char * sig) {
    int position = -1;
    int positions[numberOfFields]; // many fields for same class
    int positionsLength = -1;
    ParameterType type = PACKAGES;

    bool belongs = findIfClassShouldBeWatched(jvmti_env, klass, sig, &type, &position, positions, &positionsLength);
    if (belongs) {
        if (type == FIELDS) {
            int i;
            for (i = 0; i < positionsLength; i++) {
                setWatchesForField(jvmti_env, jni_env, klass, positions[i]);
            }
        } else if (type == SUPER_CLASSES) {
            setWatchesForSuperClass(jvmti_env, jni_env, klass);
        } else {
            setWatches(jvmti_env, klass);
        }
    }
}

void JNICALL
ClassPrepare(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jclass klass) {
    // raw monitor
    enterRawMonitor(jvmti_env);

    char *sig = NULL;
    char *gen = NULL;

    jvmError = (*jvmti_env)->GetClassSignature(jvmti_env, klass, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class signature.");

    if (sig) {
        setWatchesForSignature(jvmti_env, jni_env, klass, sig);
        deallocate(jvmti_env, sig);
    }
    deallocate(jvmti_env, gen);

    // end of raw monitor
    exitRawMonitor(jvmti_env);
}

static bool hasClassGivenSignature(jvmtiEnv *jvmti_env, jclass klass, const char * requiredSig) {
    bool hasName = false;
    char * sig = getClassAdjustedSignature(jvmti_env, klass, false);

    if (equals(sig, requiredSig, false)) {
        hasName = true;
    }
    deallocate(jvmti_env, sig);

    return hasName;
}

static bool isClassAThread(jvmtiEnv *jvmti_env, jclass klass) {
    return hasClassGivenSignature(jvmti_env, klass, "Ljava/lang/Thread;");
}

static bool isThreadDeclaringClass(jvmtiEnv *jvmti_env, jmethodID method) {
    jclass declaring_class = NULL;
    jvmError = (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &declaring_class);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get declaring class");

    return isClassAThread(jvmti_env, declaring_class);
}

static bool isClassALock(jvmtiEnv *jvmti_env, jclass klass) {
    return hasClassGivenSignature(jvmti_env, klass, "Ljava/util/concurrent/locks/Lock;");
}

static bool isClassImplementingALock(jvmtiEnv *jvmti_env, jclass declaring_class) {
    jint interface_count = 0;
    jclass* interfaces = NULL;
    jvmError = (*jvmti_env)->GetImplementedInterfaces(jvmti_env, declaring_class,
            &interface_count, &interfaces);

    jint i;
    for (i = 0; i < interface_count; i++) {
        if (isClassALock(jvmti_env, interfaces[i])) {
            deallocate(jvmti_env, interfaces);
            return true;
        }
    }

    deallocate(jvmti_env, interfaces);
    return false;
}

static bool isClassOrSuperClassesImplementingALock(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jmethodID method) {
    jclass declaring_class = NULL;
    jvmError = (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &declaring_class);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get declaring class");

    while (true) {
        if (isClassImplementingALock(jvmti_env, declaring_class)) {
            return true;
        }

        // getting superclass
        declaring_class = (*jni_env)->GetSuperclass(jni_env, declaring_class);
        if (!declaring_class) {
            return false;
        }
    }
}

/**
 * Returns method name or null.
 * Note that if returned string is not null, it needs to be <STRONG>deallocate</STRONG>.
 * @param jvmti_env
 * @param method
 * @return
 */
static char * getMethodName(jvmtiEnv *jvmti_env, jmethodID method) {
    char* name = NULL;
    char* sig = NULL;
    char* gen = NULL;

    jvmError = (*jvmti_env)->GetMethodName(jvmti_env, method, &name, &sig, &gen);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get method name");

    deallocate(jvmti_env, sig);
    deallocate(jvmti_env, gen);

    return name;
}

/**
 * Returns true if given methodId has given name, false otherwise.
 * Note that given name will <STRONG>NOT</STRONG> be deallocated nor freed in this method.
 * @param jvmti_env
 * @param method
 * @param name
 * @return
 */
static bool hasMethodGivenName(jvmtiEnv *jvmti_env, jmethodID methodId, const char * name) {
    bool isName = false;
    char * methodName = getMethodName(jvmti_env, methodId);

    if (equals(methodName, name, false)) {
        isName = true;
    }
    deallocate(jvmti_env, methodName);

    return isName;
}

static bool isMethodAStart(jvmtiEnv *jvmti_env, jmethodID method) {
    return hasMethodGivenName(jvmti_env, method, "start");
}

/**
 * Returns arguments size of given method.
 * Note that every method has one implicit argument.
 * Also note that two-word arguments use two slots.
 * @param jvmti_env
 * @param method
 * @return
 */
static jint getArgumentsSize(jvmtiEnv *jvmti_env, jmethodID method) {
    jint argumentsSize = 0;
    jvmError = (*jvmti_env)->GetArgumentsSize(jvmti_env, method, &argumentsSize);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get arguments size");
    return argumentsSize;
}

static bool isMethod(jvmtiEnv *jvmti_env, jmethodID method, const char * methodName,
        const int paramNumber) {

    bool isMethod = hasMethodGivenName(jvmti_env, method, methodName);
    return (isMethod && (getArgumentsSize(jvmti_env, method) == paramNumber)) ? true : false;
}

static bool isJoinParameterZero(jvmtiEnv *jvmti_env, jthread thread) {
    jlong value = -1;
    jvmError = (*jvmti_env)->GetLocalLong(jvmti_env, thread, 0, 1, &value);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get local long from join method");
    return (value == 0) ? true : false;
}

static bool isMethodAJoin(jvmtiEnv *jvmti_env, jmethodID method) {
    // we are interested only in join(0) - method (join() call join(0))
    return isMethod(jvmti_env, method, "join", 3);
}

static bool isMethodALock(jvmtiEnv *jvmti_env, jmethodID method) {
    return isMethod(jvmti_env, method, "lock", 1);
}

static bool isMethodAnUnlock(jvmtiEnv *jvmti_env, jmethodID method) {
    return isMethod(jvmti_env, method, "unlock", 1);
}

/**
 * Returns position of given thread in joinZeroThreads. If given thread has no record, returns -1.
 * @param jvmti_env
 * @param thread
 * @return position or -1
 */
static int getJoinZeroThreadPosition(jvmtiEnv *jvmti_env, jthread thread) {
    jlong tag = getTag(jvmti_env, thread);
    if (tag == 0) {
        return -1;
    }

    int i;
    for (i = 0; i < numberOfJoinZeroThreads; i++) {
        if (joinZeroThreads[i].threadSourceTag == tag) {
            return i;
        }
    }

    return -1;
}

static void addThreadRelationStart(jvmtiEnv *jvmti_env, jthread sourceThread) {
    jobject calledThread = NULL;
    jvmError = (*jvmti_env)->GetLocalInstance(jvmti_env, sourceThread, 0, &calledThread);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get called thread");

    jlong sourceThreadTag = obtainTag(jvmti_env, sourceThread);
    jlong calledThreadTag = obtainTag(jvmti_env, calledThread);

    if (isDebug()) {
        debug("addThreadRelationStart", "adding START relation between %s and %s",
                getThreadName(jvmti_env, sourceThread), getThreadName(jvmti_env, calledThread));
    }

    addRelation(sourceThreadTag, calledThreadTag, START);
}

static void addThreadRelationJoin(jvmtiEnv *jvmti_env, jthread sourceThread) {
    jlong sourceThreadTag = obtainTag(jvmti_env, sourceThread);

    int pos = getJoinZeroThreadPosition(jvmti_env, sourceThread);
    jlong calledThreadTag = joinZeroThreads[pos].threadCalledTag;

    if (isDebug()) {
        debug("addThreadRelationJoin", "adding JOIN relation between %s and %ld (pos=%d)",
                getThreadName(jvmti_env, sourceThread), calledThreadTag, pos);
    }

    addRelation(sourceThreadTag, calledThreadTag, JOIN);
}

static void resizeMonitorsInfosIfNeeded() {
    if (numberOfMonitorsInfos >= max_monitors_infos) {
        debug("resizeMonitorsInfosIfNeeded", "resizing monitors infos");
        max_monitors_infos *= 2;
        ownedMonitorsInfos = (OwnedMonitorsInfo*)
                realloc(ownedMonitorsInfos, sizeof (OwnedMonitorsInfo) * max_monitors_infos);
    }
}

static void resizeLockMonitorsIfNeeded() {
    if (numberOfLockMonitors >= max_lock_monitors) {
        debug("resizeLockMonitorsIfNeeded", "resizing lock monitors");
        max_lock_monitors *= 2;
        lockMonitors = (LockMonitors*)
                realloc(lockMonitors, sizeof (LockMonitors) * max_lock_monitors);
    }
}

static void resizeLockMonitorsLockTagsIfNeeded(int indexOfLockMonitor) {
    if (lockMonitors[indexOfLockMonitor].numberOfLockTags >= lockMonitors[indexOfLockMonitor].maxLockTags) {
        debug("resizeLockMonitorsLockTagsIfNeeded", "resizing lock monitors lock tags");
        lockMonitors[indexOfLockMonitor].maxLockTags *= 2;
        lockMonitors[indexOfLockMonitor].lockTags = (jlong*)
                realloc(lockMonitors[indexOfLockMonitor].lockTags,
                sizeof (jlong) * lockMonitors[indexOfLockMonitor].maxLockTags);
    }
}

/**
 * Returns position of given thread in lockMonitors. If there is no thread record for given thread, -1 is returned.
 * @param jvmti_env
 * @param thread
 * @return
 */
static int findLockMonitorsPosition(jvmtiEnv *jvmti_env, jthread thread) {
    jlong givenThreadTag = getTag(jvmti_env, thread);

    if (givenThreadTag == 0) {
        return -1;
    }
    int i;
    for (i = 0; i < numberOfLockMonitors; i++) {
        if (lockMonitors[i].threadSourceTag == givenThreadTag) {
            return i;
        }
    }

    return -1;
}

/**
 * Returns tag of object on which lock method has been called.
 * Note that this method has to be called right after MethodExit is called for lock (or unlock)
 * @param jvmti_env
 * @param thread
 */
static jlong obtainLockObjectTag(jvmtiEnv *jvmti_env, jthread thread) {
    jobject lockObject = NULL;
    jvmError = (*jvmti_env)->GetLocalInstance(jvmti_env, thread, 0, &lockObject);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get lock object");
    return obtainTag(jvmti_env, lockObject);
}

static void addNewLockToThread(jvmtiEnv *jvmti_env, jthread thread) {
    jlong threadTag = obtainTag(jvmti_env, thread);
    lockMonitors[numberOfLockMonitors].threadSourceTag = threadTag;
    lockMonitors[numberOfLockMonitors].maxLockTags = START_MAX_LOCK_MONITORS_TAGS;
    lockMonitors[numberOfLockMonitors].lockTags = (jlong*) calloc(START_MAX_LOCK_MONITORS_TAGS, sizeof (jlong));
    lockMonitors[numberOfLockMonitors].numberOfLockTags = 0;
    lockMonitors[numberOfLockMonitors].threadName = getThreadName(jvmti_env, thread);

    int index1 = lockMonitors[numberOfLockMonitors].numberOfLockTags;
    lockMonitors[numberOfLockMonitors].lockTags
            [index1] = obtainLockObjectTag(jvmti_env, thread);
    lockMonitors[numberOfLockMonitors].numberOfLockTags++;
    numberOfLockMonitors++;

    debug("addNewLockToThread", "new lock monitors added for thread name %s with tag %ld\n",
            lockMonitors[numberOfLockMonitors].threadName, (long) threadTag);
}

static void addLockToPosition(jvmtiEnv *jvmti_env, jthread thread, int position) {
    jlong lockObjectTag = obtainLockObjectTag(jvmti_env, thread);
    resizeLockMonitorsLockTagsIfNeeded(position);
    int index1 = lockMonitors[position].numberOfLockTags;
    lockMonitors[position].lockTags[index1] = lockObjectTag;
    lockMonitors[position].numberOfLockTags++;
    debug("addLockToPosition", "lock monitors changed");
}

static void addLockToThread(jvmtiEnv *jvmti_env, jthread thread) {
    resizeLockMonitorsIfNeeded();
    int position = findLockMonitorsPosition(jvmti_env, thread);
    if (position == -1) {
        addNewLockToThread(jvmti_env, thread);
    } else {
        addLockToPosition(jvmti_env, thread, position);
    }
}

static void removeLockFromThread(jvmtiEnv *jvmti_env, jthread thread) {
    int position = findLockMonitorsPosition(jvmti_env, thread);
    if (position != -1) {
        jlong lockObjectTag = obtainLockObjectTag(jvmti_env, thread);
        int i;
        for (i = 0; i < lockMonitors[position].numberOfLockTags; i++) {
            if (lockMonitors[position].lockTags[i] == lockObjectTag) {
                lockMonitors[position].lockTags[i] = 0;
            }
        }
    }
}

static void resizeJoinZeroThreadsIfNeeded() {
    if (numberOfJoinZeroThreads >= max_join_zero_threads) {
        debug("resizeJoinZeroThreadsIfNeeded", "resizing join zero threads");
        max_join_zero_threads *= 2;
        joinZeroThreads = (JoinZeroThread*)
                realloc(joinZeroThreads, sizeof (JoinZeroThread) * max_join_zero_threads);
    }
}

/**
 * Adds given thread into joinZeroThreads and sets its flag to true.
 * If given thread already exists in joinZeroThreads, just sets the flag to true.
 * @param jvmti_env
 * @param thread
 */
static void addJoinZeroThread(jvmtiEnv *jvmti_env, jthread sourceThread) {
    resizeJoinZeroThreadsIfNeeded();
    int position = getJoinZeroThreadPosition(jvmti_env, sourceThread);
    if (position == -1) { // new thread
        position = numberOfJoinZeroThreads;
        numberOfJoinZeroThreads++;
    }

    jobject calledThread = NULL;
    jvmError = (*jvmti_env)->GetLocalInstance(jvmti_env, sourceThread, 0, &calledThread);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get called thread in addJoinZeroThread");

    joinZeroThreads[position].threadSourceTag = obtainTag(jvmti_env, sourceThread);
    joinZeroThreads[position].threadCalledTag = obtainTag(jvmti_env, calledThread);
    joinZeroThreads[position].enteredJoinParameterZero = true;


    if (isDebug()) {
        char * sourceThreadName = getThreadName(jvmti_env, sourceThread);
        char * calledThreadName = getThreadName(jvmti_env, calledThread);
        debug("addJoinZeroThread", "pos=%d,souThread=%s(tag=%ld),calThread=%s(tag=%ld)",
                position, sourceThreadName, (long) joinZeroThreads[position].threadSourceTag,
                calledThreadName, (long) joinZeroThreads[position].threadCalledTag);

        deallocate(jvmti_env, sourceThreadName);
        deallocate(jvmti_env, calledThreadName);
    }
}

static void removeJoinZeroThread(jvmtiEnv *jvmti_env, jthread thread) {
    int position = getJoinZeroThreadPosition(jvmti_env, thread);
    if (position != -1) {
        joinZeroThreads[position].enteredJoinParameterZero = false;
    }
}

static bool isJoinZeroThread(jvmtiEnv *jvmti_env, jthread thread) {
    int position = getJoinZeroThreadPosition(jvmti_env, thread);
    if (position == -1) {
        return false;
    } else {
        return joinZeroThreads[position].enteredJoinParameterZero;
    }
}

void JNICALL
MethodEntry(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method) {
    // raw monitor
    enterRawMonitor(jvmti_env);

    if (watchThreads && isThreadDeclaringClass(jvmti_env, method)) {
        if (isMethodAStart(jvmti_env, method)) {
            // method Thread.start has been entered
            addThreadRelationStart(jvmti_env, thread);
        } else if (isMethodAJoin(jvmti_env, method) && isJoinParameterZero(jvmti_env, thread)) {
            // method Thread.join(0) has been entered (however we still need to wait for method exit)
            addJoinZeroThread(jvmti_env, thread);
        }
    }

    // end of raw monitor
    exitRawMonitor(jvmti_env);
}

void JNICALL
MethodExit(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method,
        jboolean was_popped_by_exception, jvalue return_value) {

    // raw monitor
    enterRawMonitor(jvmti_env);

    if (watchThreads && !was_popped_by_exception && isThreadDeclaringClass(jvmti_env, method)) {
        if (isMethodAJoin(jvmti_env, method) && isJoinZeroThread(jvmti_env, thread)) {
            // method Thread.join(0) has been exited without exception
            addThreadRelationJoin(jvmti_env, thread);
            removeJoinZeroThread(jvmti_env, thread);
        }
    } else if (watchConcurrent && !was_popped_by_exception &&
            isClassOrSuperClassesImplementingALock(jvmti_env, jni_env, method)) {
        if (isMethodALock(jvmti_env, method)) {
            // method Lock.lock has been exited without exception
            addLockToThread(jvmti_env, thread);
        } else if (isMethodAnUnlock(jvmti_env, method)) {
            // method Lock.unlock has been exited without exception
            removeLockFromThread(jvmti_env, thread);
        }
    }

    // end of raw monitor
    exitRawMonitor(jvmti_env);
}

void JNICALL
VMInit(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread) {
    info("VMInit", "VM INIT");
    initMainThread(obtainTag(jvmti_env, thread));
}

static void saveOwnedMonitorsTags(jvmtiEnv *jvmti_env, int numberOfMonitorsInfo,
        jint owned_monitor_count, jobject *owned_monitors) {

    jlong * monitors = (jlong*) calloc(owned_monitor_count, sizeof (jlong));
    jint i;
    for (i = 0; i < owned_monitor_count; i++) {
        monitors[i] = obtainTag(jvmti_env, owned_monitors[i]);
    }

    ownedMonitorsInfos[numberOfMonitorsInfo].owned_monitors_tags = monitors;
}

/*
static void printMonitorInfo(OwnedMonitorsInfo p) {
    info("monitor info", "thread: %d, class: %d, object: %d, field: %d",
            (int) p.threadTag, (int) p.klassTag, (int) p.objectTag, (int) p.field);
}

static void printFieldInfo(char *prefix, jvmtiEnv *jvmti_env, jthread thread, jclass klass,
        jobject object, jfieldID field) {
    char *thread_name = getThreadName(jvmti_env, thread);
    char *class_name = getClassAdjustedSignature(jvmti_env, klass, true);
    char *field_name = getFieldName(jvmti_env, klass, field);
    int class_tag = getTag(jvmti_env, klass);
    int object_tag = getTag(jvmti_env, object);
    info(prefix, "thread: %s, class: %s (%d), object: %d, field: %s (%d)",
            thread_name, class_name, class_tag, object_tag, field_name, (int) field);
}
 */

/**
 * Compare given monitorsInfo with given thread, klass, object and field. In other words, if there
 * is record for given parameters, this method will return true. False otherwise.
 */
static bool compareMonitorsInfoToGivenParameters(jvmtiEnv* jvmti_env, OwnedMonitorsInfo monitorsInfo,
        jclass klass, jlong threadTag, jlong klassTag, jlong objectTag, jfieldID field) {

    // class and object
    if (klassTag != monitorsInfo.klassTag) {
        return false;
    }
    if (!isFieldStatic(jvmti_env, klass, field)) {
        if (objectTag != monitorsInfo.objectTag) {
            return false;
        }
    }

    // thread
    if (threadTag != monitorsInfo.threadSourceTag) {
        return false;
    }

    // thread state
    trace("compareMonitorsInfoToGivenParameters", "getting current thread state");
    int threadState = getCurrentThreadState(threadTag);
    if (threadState != monitorsInfo.threadState) {
        return false;
    }

    // field
    char* name1 = NULL;
    char* name2 = NULL;
    char* sig1 = NULL;
    char* sig2 = NULL;
    char* gen1 = NULL;
    char* gen2 = NULL;

    jvmError = (*jvmti_env)->GetFieldName(jvmti_env, klass, field, &name1, &sig1, &gen1);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain field name.");
    jvmError = (*jvmti_env)->GetFieldName(jvmti_env, klass, monitorsInfo.field, &name2, &sig2, &gen2);
    check_jvmti_error(jvmti_env, jvmError, "Unable to obtain monitors info field name.");
    /*
        if (jvmError != JVMTI_ERROR_NONE) {
            char *sig = NULL;
            char *gen = NULL;
            jvmtiError err = jvmError;
            (*jvmti_env)->GetClassSignature(jvmti_env, klass, &sig, &gen);
            warn("compareMonitorsInfoToAllInfos", "class: %s, object: %d, field: %d", sig, object_tag, (int) monitorsInfo.field);
            printFieldInfo("compareMonitorsInfoToAllInfos", jvmti_env, thread, klass, object, field);
            printMonitorInfo(monitorsInfo);
            jvmError = err;
        }
     */


    // comparing and deallocating
    bool record = true;

    trace("compareMonitorsInfoToGivenParameters", "comparing fields");
    if (!equals(name1, name2, false) || !equals(sig1, sig2, false) || !equals(gen1, gen2, true)) {
        record = false;
    }

    deallocate(jvmti_env, name1);
    deallocate(jvmti_env, name2);
    deallocate(jvmti_env, sig1);
    deallocate(jvmti_env, sig2);
    deallocate(jvmti_env, gen1);
    deallocate(jvmti_env, gen2);

    return record;
}

/**
 * Returns position of given thread and field. If there is none, returns -1
 * @param jvmti_env
 * @param thread
 * @param klass
 * @param object
 * @param field
 * @return
 */
static int findPositionOfMonitorsInfo(jvmtiEnv* jvmti_env, jthread thread,
        jclass klass, jobject object, jfieldID field) {

    bool same = false;

    jlong klassTag = getTag(jvmti_env, klass);
    jlong objectTag = getTag(jvmti_env, object);
    jlong threadTag = getTag(jvmti_env, thread);

    int pos;
    for (pos = 0; pos < numberOfMonitorsInfos; pos++) {
        same = compareMonitorsInfoToGivenParameters(jvmti_env, ownedMonitorsInfos[pos],
                klass, threadTag, klassTag, objectTag, field);
        if (same) {
            return pos;
        }
    }

    return -1;
}

/**
 * Returns line number of given method and location. If there is none, returns -1.
 * @param jvmti_env
 * @param method
 * @param location
 * @return
 */
static jint getLineNumber(jvmtiEnv* jvmti_env, jmethodID method, jlocation location) {
    jboolean is_native;
    jvmError = (*jvmti_env)->IsMethodNative(jvmti_env, method, &is_native);
    check_jvmti_error(jvmti_env, jvmError, "Unable to find out whether the method is native");
    if (is_native) {
        return -1;
    }
    jint count = 0;
    jvmtiLineNumberEntry* table = NULL;
    jvmError = (*jvmti_env)->GetLineNumberTable(jvmti_env, method, &count, &table);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get line number table");

    if (jvmError == JVMTI_ERROR_NONE) {
        if (count > 0 && table[count - 1].start_location == location) {
            return table[count - 1].line_number;
        }
        int i;
        for (i = 1; i < count; i++) { //find the closest one which is lesser or equal
            if (table[i].start_location > location && table[i - 1].start_location <= location) {
                return table[i - 1].line_number;
            }
        }
    }

    return -1;
}

/**
 * Returns stack trace or NULL. Note that returned stack trace has to be freed.
 * @param jvmti_env
 * @param thread
 * @return
 */
static char* getStackTrace(jvmtiEnv* jvmti_env, jthread thread) {
    if (!stackTraceEnabled) {
        return NULL;
    }
    trace("getStackTrace", "starting stack trace");
    char * stackTrace = NULL;
    jint max_frame_count = 3; // depth of the stack trace
    jvmtiFrameInfo * frames = (jvmtiFrameInfo*) calloc(max_frame_count, sizeof (jvmtiFrameInfo));
    jint count = -1;

    jvmError = (*jvmti_env)->GetStackTrace(jvmti_env, thread, 0,
            max_frame_count, frames, &count);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get stack trace");

    trace("getStackTrace", "before if");
    if (jvmError == JVMTI_ERROR_NONE && count >= 1) {
        char *methodName = NULL;
        char *sig = NULL;
        char *gen = NULL;
        char * classSig = NULL;
        char tmp[BUFFER_SIZE];
        stackTrace = (char*) calloc(BUFFER_SIZE * count, sizeof (char));
        int bufferNeeded = 0;
        jint lineNumber = 0;

        trace("getStackTrace", "before for");
        int i;
        for (i = 0; i < count; i++) {
            trace("getStackTrace", "on the first line of for");
            jvmError = (*jvmti_env)->GetMethodName(jvmti_env, frames[i].method,
                    &methodName, & sig, &gen);
            check_jvmti_error(jvmti_env, jvmError, "Unable to get method name");
            classSig = getDeclaringClassAdjustedSignature(jvmti_env, frames[i].method);

            trace("getStackTrace", "before inner if");
            if (methodName && classSig) {
                lineNumber = getLineNumber(jvmti_env, frames[i].method, frames[i].location);
                bufferNeeded = sprintf(tmp, "%s.%s (line:%d)", classSig, methodName,
                        (int) lineNumber);
                check_buffer_overflow(bufferNeeded);
                strcat(stackTrace, tmp);
                if (i != count - 1) { //last iteration
                    strcat(stackTrace, ", ");
                }
            }
            trace("getStackTrace", "before deallocation");
            deallocate(jvmti_env, sig);
            deallocate(jvmti_env, gen);
            trace("getStackTrace", "before classSig deallocation");
            if (classSig) {
                free(classSig);
            }
        }
        trace("getStackTrace", "after for");
    }

    free(frames);
    trace("getStackTrace", "returning stack trace");
    return stackTrace;
}

static void addNewMonitorsInfo(jvmtiEnv *jvmti_env, jthread thread, jclass klass, jobject object,
        jfieldID field, jint owned_monitor_count, jobject *owned_monitors, bool modification) {

    debug("addMonitorsInfo", "adding new monitors");
    resizeMonitorsInfosIfNeeded();

    ownedMonitorsInfos[numberOfMonitorsInfos].field = field;
    ownedMonitorsInfos[numberOfMonitorsInfos].klassTag = obtainTag(jvmti_env, klass);

    // logging
    if (isInfo()) {
        char *sig = NULL;
        char *gen = NULL;
        jvmError = (*jvmti_env)->GetClassSignature(jvmti_env, klass, &sig, &gen);
        check_jvmti_error(jvmti_env, jvmError, "Unable to obtain class signature.");
        info("addNewMonitorsInfo", "class: %s, tag: %d", sig, ownedMonitorsInfos[numberOfMonitorsInfos].klassTag);
        deallocate(jvmti_env, sig);
        deallocate(jvmti_env, gen);
    }

    // dealing with static
    if (object != NULL) { // for static fields object is null
        ownedMonitorsInfos[numberOfMonitorsInfos].objectTag = obtainTag(jvmti_env, object);
    } else {
        ownedMonitorsInfos[numberOfMonitorsInfos].objectTag = 0;
    }
    //    printMonitorInfo(ownedMonitorsInfos[numberOfMonitorsInfos]);

    ownedMonitorsInfos[numberOfMonitorsInfos].owned_monitor_count = owned_monitor_count;
    saveOwnedMonitorsTags(jvmti_env, numberOfMonitorsInfos, owned_monitor_count, owned_monitors);
    jlong threadTag = obtainTag(jvmti_env, thread);
    ownedMonitorsInfos[numberOfMonitorsInfos].threadSourceTag = threadTag;
    trace("addNewMonitorsInfo", "getting current thread state and save it to new ownedMonitorsInfo");
    ownedMonitorsInfos[numberOfMonitorsInfos].threadState = getCurrentThreadState(threadTag);
    ownedMonitorsInfos[numberOfMonitorsInfos].threadName = getThreadName(jvmti_env, thread);
    ownedMonitorsInfos[numberOfMonitorsInfos].modification = modification;

    // concurrent locks
    int lockPosition = findLockMonitorsPosition(jvmti_env, thread);

    debug("addNewMonitorsInfo", "findLockMonitorsPosition returned: %d", lockPosition);

    if (lockPosition == -1) {
        ownedMonitorsInfos[numberOfMonitorsInfos].numberOfLockTags = 0;
        ownedMonitorsInfos[numberOfMonitorsInfos].lockTags = NULL;
    } else {
        ownedMonitorsInfos[numberOfMonitorsInfos].numberOfLockTags = lockMonitors[lockPosition].numberOfLockTags;
        ownedMonitorsInfos[numberOfMonitorsInfos].lockTags = (jlong*) calloc(lockMonitors[lockPosition].numberOfLockTags,
                sizeof (jlong));

        //deep copy
        int i;
        for (i = 0; i < lockMonitors[lockPosition].numberOfLockTags; i++) {
            ownedMonitorsInfos[numberOfMonitorsInfos].lockTags[i] = lockMonitors[lockPosition].lockTags[i];
        }
    }

    // stack trace
    if (stackTraceEnabled) {
        ownedMonitorsInfos[numberOfMonitorsInfos].stackTraces = (char**) calloc(START_MAX_STACK_TRACES, sizeof (char*));
        ownedMonitorsInfos[numberOfMonitorsInfos].max_stack_traces = START_MAX_STACK_TRACES;
        ownedMonitorsInfos[numberOfMonitorsInfos].stackTraces[0] = getStackTrace(jvmti_env, thread);
        ownedMonitorsInfos[numberOfMonitorsInfos].numberOfStackTraces = 1;
    }

    //printFieldInfo("addedMonitorInfo", jvmti_env, thread, klass, object, field);
    numberOfMonitorsInfos++;
}

/**
 * Returns true if tag of given monitor is the same as given tag. False otherwise.
 * @param jvmti_env
 * @param ownedMonitor
 * @param originalMonitorTag
 * @return
 */
static bool compareMonitorToTag(jvmtiEnv *jvmti_env, jobject ownedMonitor, jlong originalMonitorTag) {
    jlong originalTag = getTag(jvmti_env, ownedMonitor);

    return (originalTag == originalMonitorTag) ? true : false;
}

static void intersectLockMonitors(jvmtiEnv *jvmti_env, jthread thread, int monitorsInfoPosition) {
    int lockMonitorsPosition = findLockMonitorsPosition(jvmti_env, thread);
    jlong * oldLockTags = ownedMonitorsInfos[monitorsInfoPosition].lockTags;
    int oldLockTagsLength = ownedMonitorsInfos[monitorsInfoPosition].numberOfLockTags;

    jlong * currentLockTags = NULL;
    int currentLockTagsLength = 0;
    if (lockMonitorsPosition != -1) {
        currentLockTags = lockMonitors[lockMonitorsPosition].lockTags;
        currentLockTagsLength = lockMonitors[lockMonitorsPosition].numberOfLockTags;
    }

    // finding new length of lock monitors
    int counter = 0;
    int i;
    for (i = 0; i < oldLockTagsLength; i++) {
        int j;
        for (j = 0; j < currentLockTagsLength; j++) {
            if (currentLockTags[j] != 0 && oldLockTags[i] == currentLockTags[j]) {
                counter++;
                break;
            }
        }
    }

    // finding lock monitors
    jlong * newLockMonitors = (jlong*) calloc(counter, sizeof (jlong));
    counter = 0;
    for (i = 0; i < oldLockTagsLength; i++) {
        int j;
        for (j = 0; j < currentLockTagsLength; j++) {
            if (currentLockTags[j] != 0 && oldLockTags[i] == currentLockTags[j]) {
                newLockMonitors[counter] = currentLockTags[j];
                counter++;
                break;
            }
        }
    }

    free(ownedMonitorsInfos[monitorsInfoPosition].lockTags);
    ownedMonitorsInfos[monitorsInfoPosition].lockTags = newLockMonitors;
    ownedMonitorsInfos[monitorsInfoPosition].numberOfLockTags = counter;
}

static void resizeStackTracesIfNeeded(int monitorPosition) {
    if (ownedMonitorsInfos[monitorPosition].numberOfStackTraces >= ownedMonitorsInfos[monitorPosition].max_stack_traces) {
        debug("resizeStackTracesIfNeeded", "resizing monitors info stack traces");

        ownedMonitorsInfos[monitorPosition].max_stack_traces *= 2;
        char ** tmp = (char**) calloc(ownedMonitorsInfos[monitorPosition].max_stack_traces, sizeof (char*));
        int i;
        for (i = 0; i < ownedMonitorsInfos[monitorPosition].numberOfStackTraces; i++) {
            tmp[i] = ownedMonitorsInfos[monitorPosition].stackTraces[i];
        }

        free(ownedMonitorsInfos[monitorPosition].stackTraces);
        ownedMonitorsInfos[monitorPosition].stackTraces = tmp;
    }
}

static bool isStackTraceAlreadyStored(int monitorPosition, char * stackTrace) {
    int i;
    for (i = 0; i < ownedMonitorsInfos[monitorPosition].numberOfStackTraces; i++) {
        if (equals(ownedMonitorsInfos[monitorPosition].stackTraces[i], stackTrace, true)) {
            return true;
        }
    }
    return false;
}

static void addStackTraceIfNeeded(jvmtiEnv *jvmti_env, int monitorPosition, jthread thread) {
    char * stackTrace = getStackTrace(jvmti_env, thread);

    if (isStackTraceAlreadyStored(monitorPosition, stackTrace)) {
        free(stackTrace);
    } else {
        resizeStackTracesIfNeeded(monitorPosition);
        int numberOfStackTraces = ownedMonitorsInfos[monitorPosition].numberOfStackTraces;
        debug("addStackTraceIfNeeded", "adding new stack trace to existing (number = %d)", numberOfStackTraces);
        ownedMonitorsInfos[monitorPosition].stackTraces[numberOfStackTraces] = stackTrace;
        ownedMonitorsInfos[monitorPosition].numberOfStackTraces++;
    }
}

/**
 * Intersect given monitors to monitors from monitorsInfo at given position and save them to that position.
 *
 * @param jvmti_env
 * @param position
 * @param modification
 * @param owned_monitor_count
 * @param owned_monitors
 */
static void intersectMonitorsInfo(jvmtiEnv *jvmti_env, jthread thread, int position, bool modification,
        jint currentMonitorsCount, jobject *currentMonitors) {

    if (modification) {
        ownedMonitorsInfos[position].modification = true;
    }

    int originalCount = ownedMonitorsInfos[position].owned_monitor_count;
    jlong* originalMonitorsTags = ownedMonitorsInfos[position].owned_monitors_tags;

    // getting tags
    jlong* currentMonitorTags = (jlong*) calloc(currentMonitorsCount, sizeof (jlong));
    int i;
    for (i = 0; i < currentMonitorsCount; i++) {
        currentMonitorTags[i] = getTag(jvmti_env, currentMonitors[i]);
    }

    // finding new length of monitors
    int counter = 0;
    for (i = 0; i < originalCount; i++) {
        int j;
        for (j = 0; j < currentMonitorsCount; j++) {
            if (originalMonitorsTags[i] == currentMonitorTags[j]) {
                counter++;
                break;
            }
        }
    }

    // finding monitors
    jlong * newMonitors = (jlong*) calloc(counter, sizeof (jlong));
    counter = 0;
    for (i = 0; i < originalCount; i++) {
        int j;
        for (j = 0; j < currentMonitorsCount; j++) {
            if (originalMonitorsTags[i] == currentMonitorTags[j]) {
                newMonitors[counter] = originalMonitorsTags[j];
                counter++;
                break;
            }
        }
    }

    free(currentMonitorTags);
    free(ownedMonitorsInfos[position].owned_monitors_tags);
    ownedMonitorsInfos[position].owned_monitors_tags = newMonitors;
    ownedMonitorsInfos[position].owned_monitor_count = counter;

    // concurrent locks intersection
    intersectLockMonitors(jvmti_env, thread, position);

    // stack trace
    if (stackTraceEnabled) {
        addStackTraceIfNeeded(jvmti_env, position, thread);
    }
}

/**
 * Add monitors info if there is not one for given thread or intersect monitors if monitors info
 * already exists.
 *
 * @param jvmti_env
 * @param thread
 * @param klass
 * @param object
 * @param field
 * @param owned_monitor_count
 * @param owned_monitors
 * @param modification
 */
static void intersectOrAddMonitorsInfo(jvmtiEnv *jvmti_env, jthread thread, jclass klass, jobject object,
        jfieldID field, jint owned_monitor_count, jobject *owned_monitors, bool modification) {

    // find position
    int position = findPositionOfMonitorsInfo(jvmti_env, thread, klass, object, field);
    debug("intersectOrAddMonitorsInfo", "position = %d", position);

    if (position == -1) { // new monitors info
        addNewMonitorsInfo(jvmti_env, thread, klass, object, field, owned_monitor_count, owned_monitors,
                modification);
    } else {
        intersectMonitorsInfo(jvmti_env, thread, position, modification, owned_monitor_count, owned_monitors);
    }
}

/**
 * Trivial implementation - every monitor of given original compare to every given owned monitor.
 * Return true if at least one monitor is same in both. False otherwise.
 *
 * @param jvmti_env
 * @param owned_monitor_count
 * @param owned_monitors
 * @param originalCount
 * @param originalMonitors
 * @return
 */
static bool findIntersectionInOneInfo(jvmtiEnv *jvmti_env, jint owned_monitor_count, jobject* owned_monitors,
        jint originalCount, jlong* originalMonitors) {

    bool intersectionExist = false;
    int i;
    for (i = 0; i < originalCount; i++) {
        int j;
        for (j = 0; j < owned_monitor_count; j++) {
            intersectionExist = compareMonitorToTag(jvmti_env, owned_monitors[j], originalMonitors[i]);
            if (intersectionExist) {
                return true;
            }
        }
    }

    return false;
}

static bool checkHappensBeforeRelation(jvmtiEnv *jvmti_env, jthread thread, int monitorsInfoPosition) {
    jlong origTag = ownedMonitorsInfos[monitorsInfoPosition].threadSourceTag;
    int origState = ownedMonitorsInfos[monitorsInfoPosition].threadState;
    jlong currentTag = getTag(jvmti_env, thread);
    return hasRelation(origTag, origState, currentTag);
}

/**
 * Trivial implementation - every given current lock monitor compared to every given original lock monitor.
 * Return true if at least one monitor is same in both. False otherwise.
 *
 * @param currentLockMonitorsCount
 * @param currentLockMonitorsTags
 * @param origLockMonitorsCount
 * @param origLockMonitorsTags
 * @return
 */
static bool findLockIntersectionInOneInfo(int currentLockMonitorsCount, jlong * currentLockMonitorsTags,
        int origLockMonitorsCount, jlong* origLockMonitorsTags) {

    int i, j;
    for (i = 0; i < currentLockMonitorsCount; i++) {
        for (j = 0; j < origLockMonitorsCount; j++) {
            if (currentLockMonitorsTags[i] != 0 && currentLockMonitorsTags[i] == origLockMonitorsTags[j]) {
                return true;
            }
        }
    }

    return false;
}

/**
 * Returns true if current field access is to same field as given ownedMonitorInfo.
 * False otherwise
 * @param field_klass
 * @param object
 * @param field
 * @return
 */
static bool isAccessToSameField(jvmtiEnv* jvmti_env, int ownedIndex,
        jclass field_klass, jobject object, jfieldID field) {

    if (ownedMonitorsInfos[ownedIndex].klassTag == getTag(jvmti_env, field_klass)
            && ownedMonitorsInfos[ownedIndex].field == field
            && (object == NULL || ownedMonitorsInfos[ownedIndex].objectTag == getTag(jvmti_env, object))) {

        return true;
    } else {
        return false;
    }
}

/**
 * Returns -1 if every field access or modification (on the same field) have at least one intersections with current
 * access or modification. If there is not an intersection, returns index of conflicted owned monitors info.
 * Also checking relations and lock monitors.
 */
static int findIntersection(jvmtiEnv *jvmti_env, jthread thread,
        jclass field_klass, jobject object, jfieldID field,
        jint owned_monitor_count, jobject* owned_monitors, bool modification) {

    // current lock monitors
    int currentLockMonitorsPosition = findLockMonitorsPosition(jvmti_env, thread);
    int currentLockMonitorsCount = 0;
    jlong * currentLockMonitorsTags = NULL;
    if (currentLockMonitorsPosition != -1) {
        currentLockMonitorsCount = lockMonitors[currentLockMonitorsPosition].numberOfLockTags;
        currentLockMonitorsTags = lockMonitors[currentLockMonitorsPosition].lockTags;
    }

    // checking all (with same field) ownedMonitorInfos
    int ownedIndex;
    for (ownedIndex = 0; ownedIndex < numberOfMonitorsInfos; ownedIndex++) {
        // exception for the same thread
        jlong threadTag = getTag(jvmti_env, thread);
        if (ownedMonitorsInfos[ownedIndex].threadSourceTag == threadTag) {
            debug("findIntersection", "access from the same thread");
            continue;
        }

        // exception if both threads are only reading
        if (modification == false && ownedMonitorsInfos[ownedIndex].modification == false) {
            debug("findIntersection", "both threads are only reading");
            continue;
        }

        if (!isAccessToSameField(jvmti_env, ownedIndex, field_klass, object, field)) {
            debug("findIntersection", "different field, skipping..");
            continue;
        }

        jint originalCount = ownedMonitorsInfos[ownedIndex].owned_monitor_count;
        jlong* originalMonitors = ownedMonitorsInfos[ownedIndex].owned_monitors_tags;

        int origLockMonitorsCount = ownedMonitorsInfos[ownedIndex].numberOfLockTags;
        jlong* origLockMonitorsTags = ownedMonitorsInfos[ownedIndex].lockTags;

        debug("findIntersection", "monitorInfo[%d]: originalCount = %d, owned_monitor_count = %d, ",
                ownedIndex, (int) originalCount, (int) owned_monitor_count);
        debug("findIntersection", "monitorInfo[%d]: origLockMonitorsCount = %d, currentLockMonitorsCount = %d, ",
                ownedIndex, (int) origLockMonitorsCount, (int) currentLockMonitorsCount);

        if (findIntersectionInOneInfo(jvmti_env, owned_monitor_count, owned_monitors, originalCount, originalMonitors)) {
            debug("findIntersection", "monitors intersection has been found");
            continue;
        }

        // java.util.concurrent control
        if (watchConcurrent && findLockIntersectionInOneInfo(currentLockMonitorsCount, currentLockMonitorsTags,
                origLockMonitorsCount, origLockMonitorsTags)) {
            debug("findIntersection", "lock monitors intersection has been found");
            continue;
        }

        // ownedMonitorsInfo has no intersection - need to check happens-before relation
        if (watchThreads && checkHappensBeforeRelation(jvmti_env, thread, ownedIndex)) {
            debug("checkFieldSynchronization", "happens-before relation exists");

            continue;
        }

        return ownedIndex;
    }

    return -1;
}

static void resizeDataRacesIfNeeded() {
    if (numberOfDataRaces >= max_data_races) {
        debug("resizeDataRacesIfNeeded", "resizing data races");
        max_data_races *= 2;
        dataRaces = (DataRace*)
                realloc(dataRaces, sizeof (DataRace) * max_data_races);
    }
}

static bool checkForRepeatedDataRace() {
    int i;
    for (i = 0; i < numberOfDataRaces; i++) {
        if (equals(dataRaces[i].fieldSignature, dataRaces[numberOfDataRaces].fieldSignature, false)
                && equals(dataRaces[i].fieldName, dataRaces[numberOfDataRaces].fieldName, false)
                && equals(dataRaces[i].classSignature, dataRaces[numberOfDataRaces].classSignature, false)
                && equals(dataRaces[i].fromThreadName, dataRaces[numberOfDataRaces].fromThreadName, false)
                && equals(dataRaces[i].stackTrace, dataRaces[numberOfDataRaces].stackTrace, true)) {

            return true;
        }
    }

    return false;
}

static void freeDataRace(jvmtiEnv *jvmti_env, int dataRaceNumber) {
    debug("freeDataRace", "freeing data race of index %d", dataRaceNumber);

    deallocate(jvmti_env, dataRaces[dataRaceNumber].classSignature);
    deallocate(jvmti_env, dataRaces[dataRaceNumber].fieldName);
    deallocate(jvmti_env, dataRaces[dataRaceNumber].fieldSignature);
    deallocate(jvmti_env, dataRaces[dataRaceNumber].fromThreadName);
    if (stackTraceEnabled) {
        free(dataRaces[dataRaceNumber].stackTrace);
    }
}

static void writeDataRaceToDebug(int dataRaceNumber) {
    debug("writeDataRaceToDebug", "%s, %ld, %s, %s, %s, %s",
            dataRaces[dataRaceNumber].classSignature, (long) dataRaces[dataRaceNumber].objectTag,
            dataRaces[dataRaceNumber].fieldName, dataRaces[dataRaceNumber].fieldSignature,
            dataRaces[dataRaceNumber].fromThreadName, dataRaces[dataRaceNumber].stackTrace);
}

static void saveAndOutputDataRace(jvmtiEnv *jvmti_env, jthread thread,
        jclass field_klass, jobject object, jfieldID field, int monitorsInfoPosition) {

    trace("saveAndOuputDataRace", "saving and outing data race");

    resizeDataRacesIfNeeded();
    fillDataRaceFieldNameAndFieldSignature(jvmti_env, field_klass, field);
    dataRaces[numberOfDataRaces].objectTag = getTag(jvmti_env, object);
    fillDataRaceThreadName(jvmti_env, thread);
    dataRaces[numberOfDataRaces].stackTrace = getStackTrace(jvmti_env, thread);
    trace("saveAndOuputDataRace", "after stack trace");

    writeDataRaceToDebug(numberOfDataRaces);

    if (checkForRepeatedDataRace()) {
        freeDataRace(jvmti_env, numberOfDataRaces);
    } else {
        trace("saveAndOuputDataRace", "else part");
        printLine();

        //info("infoFieldInformation", "field: %s, class: %s, objectTag: %ld, thread: %s",

        if (stackTraceEnabled) {
            unsigned int size = 0;
            int i;
            for (i = 0; i < ownedMonitorsInfos[monitorsInfoPosition].numberOfStackTraces; i++) {
                size += strlen(ownedMonitorsInfos[monitorsInfoPosition].stackTraces[i]);
            }
            size += 2 * ownedMonitorsInfos[monitorsInfoPosition].numberOfStackTraces + 1; // for [ ] and \0

            char * oneStringStackTraces = (char*) calloc(size, sizeof (char));
            for (i = 0; i < ownedMonitorsInfos[monitorsInfoPosition].numberOfStackTraces; i++) {
                strcat(oneStringStackTraces, "[");
                strcat(oneStringStackTraces, ownedMonitorsInfos[monitorsInfoPosition].stackTraces[i]);
                strcat(oneStringStackTraces, "]");
            }

            if (size <= strlen(oneStringStackTraces)) {
                error("saveAndOutputDataRace", "segmentation fault in stack trace output (size = %d, strlen = %d), terminating..",
                        size, strlen(oneStringStackTraces));
            }

            debug("saveAndOutputDataRace", "number of stack traces = %d",
                    ownedMonitorsInfos[monitorsInfoPosition].numberOfStackTraces);

            output("%s class: %s, objectTag: %ld, field: %s (%s), from thread: %s (%s), with thread: %s (%s)",
                    AGENT_DATA_RACE_UNIQUE_OUTPUT,
                    dataRaces[numberOfDataRaces].classSignature, (long) dataRaces[numberOfDataRaces].objectTag,
                    dataRaces[numberOfDataRaces].fieldName, dataRaces[numberOfDataRaces].fieldSignature,
                    dataRaces[numberOfDataRaces].fromThreadName, dataRaces[numberOfDataRaces].stackTrace,
                    ownedMonitorsInfos[monitorsInfoPosition].threadName, oneStringStackTraces);


            free(oneStringStackTraces);

        } else {
            output("%s class: %s, objectTag: %ld, field: %s (%s), from thread: %s, with thread: %s",
                    AGENT_DATA_RACE_UNIQUE_OUTPUT,
                    dataRaces[numberOfDataRaces].classSignature, (long) dataRaces[numberOfDataRaces].objectTag,
                    dataRaces[numberOfDataRaces].fieldName, dataRaces[numberOfDataRaces].fieldSignature,
                    dataRaces[numberOfDataRaces].fromThreadName, ownedMonitorsInfos[monitorsInfoPosition].threadName);
        }

        // first ownedMonitorInfo has no monitors
        if (ownedMonitorsInfos[monitorsInfoPosition].owned_monitor_count == 0) {
            output("Note that first access/modification from the other thread was unsynchronized.");
        }
        printLine();
        numberOfDataRaces++;
        trace("saveAndOutputDataRace", "exiting saveAndOutputDataRace");
    }
}

static void checkFieldSynchronization(jvmtiEnv *jvmti_env, jthread thread,
        jclass field_klass, jobject object, jfieldID field, bool modification) {

    jint owned_monitor_count = 0;
    jobject* owned_monitors = NULL;

    jvmError = (*jvmti_env)->
            GetOwnedMonitorInfo(jvmti_env, thread, &owned_monitor_count, &owned_monitors);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get owned monitor info.");

    intersectOrAddMonitorsInfo(jvmti_env, thread, field_klass, object, field,
            owned_monitor_count, owned_monitors, modification);

    // finding intersection
    int nonIntersectionIndex = findIntersection(jvmti_env, thread, field_klass, object, field,
            owned_monitor_count, owned_monitors, modification);

    // logging
    trace("checkFieldSynchronization", "after findIntersection");
    if (nonIntersectionIndex != -1) {
        saveAndOutputDataRace(jvmti_env, thread, field_klass, object, field, nonIntersectionIndex);
    }
    deallocate(jvmti_env, owned_monitors);
}

static void saveAndOutputDataRaceUsingTrie(jvmtiEnv *jvmti_env, jthread thread,
        jclass field_klass, jobject object, jfieldID field, Access * access, char * combinedStackTrace) {

    trace("saveAndOuputDataRace", "saving and outing data race");

    resizeDataRacesIfNeeded();
    fillDataRaceFieldNameAndFieldSignature(jvmti_env, field_klass, field);
    dataRaces[numberOfDataRaces].objectTag = getTag(jvmti_env, object);
    fillDataRaceThreadName(jvmti_env, thread);
    dataRaces[numberOfDataRaces].stackTrace = getStackTrace(jvmti_env, thread);
    trace("saveAndOuputDataRace", "after stack trace");

    writeDataRaceToDebug(numberOfDataRaces);

    if (checkForRepeatedDataRace()) {
        freeDataRace(jvmti_env, numberOfDataRaces);
    } else {
        trace("saveAndOuputDataRace", "else part");
        printLine();

        //info("infoFieldInformation", "field: %s, class: %s, objectTag: %ld, thread: %s",

        if (stackTraceEnabled) {
            output("%s class: %s, objectTag: %ld, field: %s (%s), from thread: %s (%s), with thread: %s (%s)",
                    AGENT_DATA_RACE_UNIQUE_OUTPUT,
                    dataRaces[numberOfDataRaces].classSignature, (long) dataRaces[numberOfDataRaces].objectTag,
                    dataRaces[numberOfDataRaces].fieldName, dataRaces[numberOfDataRaces].fieldSignature,
                    dataRaces[numberOfDataRaces].fromThreadName, dataRaces[numberOfDataRaces].stackTrace,
                    access->threadName, combinedStackTrace);
        } else {
            trace("saveAndOuputDataRace", "else part - NO stackTrace");
            output("%s class: %s, objectTag: %ld, field: %s (%s), from thread: %s, with thread: %s",
                    AGENT_DATA_RACE_UNIQUE_OUTPUT,
                    dataRaces[numberOfDataRaces].classSignature, (long) dataRaces[numberOfDataRaces].objectTag,
                    dataRaces[numberOfDataRaces].fieldName, dataRaces[numberOfDataRaces].fieldSignature,
                    dataRaces[numberOfDataRaces].fromThreadName, access->threadName);

            trace("saveAndOuputDataRace", "else part - NO stackTrace - after printing");
        }

        printLine();
        numberOfDataRaces++;
        trace("saveAndOutputDataRace", "exiting saveAndOutputDataRace");
    }
}

static void checkFieldSynchronizationUsingTrie(jvmtiEnv *jvmti_env, jthread thread,
        jclass field_klass, jobject object, jfieldID field, bool modification) {

    // monitors
    jint owned_monitor_count = 0;
    jobject* owned_monitors = NULL;
    jvmError = (*jvmti_env)->
            GetOwnedMonitorInfo(jvmti_env, thread, &owned_monitor_count, &owned_monitors);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get owned monitor info.");

    debug("checkFieldSynchronizationUsingTrie", "number of monitors = %d", (int) owned_monitor_count);

    // locks
    int lockPosition = findLockMonitorsPosition(jvmti_env, thread);
    debug("checkFieldSynchronizationUsingTrie", "lockPosition = %d", lockPosition);
    int numberOfLockTags = 0;
    jlong * lockTags = NULL;
    if (lockPosition != -1) {
        numberOfLockTags = lockMonitors[lockPosition].numberOfLockTags;
        lockTags = lockMonitors[lockPosition].lockTags;
    }

    // object or class
    jlong objectTag = 0;
    if (object == NULL) { // static
        objectTag = obtainTag(jvmti_env, field_klass);
    } else {
        objectTag = obtainTag(jvmti_env, object);
    }

    // thread and monitors
    jlong threadTag = obtainTag(jvmti_env, thread);
    jlong * monitorTags = (jlong*) calloc(owned_monitor_count, sizeof (jlong));
    jint i;
    for (i = 0; i < owned_monitor_count; i++) {
        monitorTags[i] = obtainTag(jvmti_env, owned_monitors[i]);
    }

    // find data races
    DataRaceAccess* dataRaceAccess = findDataRaces(objectTag, field, threadTag, modification,
            owned_monitor_count, monitorTags, numberOfLockTags, lockTags);

    // output and save data races
    int j;
    for (j = 0; j < dataRaceAccess->numberOfDataRaces; j++) {
        saveAndOutputDataRaceUsingTrie(jvmti_env, thread, field_klass, object, field,
                dataRaceAccess->dataRaces[j], dataRaceAccess->combinedStackTraces[j]);
    }

    // adding access
    debug("checkFieldSynchronizationUsingTrie", "starting to add access");
    char * currentStackTrace = getStackTrace(jvmti_env, thread);
    debug("checkFieldSynchronizationUsingTrie", "currentStackTrace = %s", currentStackTrace);
    int currentThreadSegment = getCurrentThreadState(threadTag);
    char * threadName = getThreadName(jvmti_env, thread);
    addAccess(objectTag, field, threadTag, currentThreadSegment, modification,
            owned_monitor_count, monitorTags, numberOfLockTags, lockTags,
            currentStackTrace, threadName);

    trace("checkFieldSynchronizationUsingTrie", "adding access complete");
    deallocate(jvmti_env, owned_monitors);
    free(monitorTags);
    free(dataRaceAccess->dataRaces);
    free(dataRaceAccess->combinedStackTraces);
    free(dataRaceAccess);
    if (currentStackTrace != NULL) {
        free(currentStackTrace);
    }
    if (threadName != NULL) {
        deallocate(jvmti_env, threadName);
    }
}

static void infoFieldInformation(jvmtiEnv *jvmti_env, jthread thread,
        jclass klass, jobject object, jfieldID field, bool modification) {

    char * fieldName = getFieldName(jvmti_env, klass, field);
    char * classSig = getClassAdjustedSignature(jvmti_env, klass, false);
    char * threadName = getThreadName(jvmti_env, thread);

    // object
    jlong objectTag = 0;
    if (object != NULL) {
        objectTag = getTag(jvmti_env, object);
    }

    info("infoFieldInformation", "field: %s, class: %s, objectTag: %ld, thread: %s, modification: %d",
            fieldName, classSig, (long) objectTag, threadName, modification);

    deallocate(jvmti_env, fieldName);
    deallocate(jvmti_env, classSig);
    deallocate(jvmti_env, threadName);
}

/**
 * Converts jvalue to string
 * Returned string has to be freed.
 * @param signature_type
 * @param value
 * @return
 */
/*
static char * jvalueToString(char signature_type, jvalue value) {
    char * tmp = (char*) calloc(BUFFER_SIZE, sizeof (char));
    int bufferNeeded = 0;

    switch (signature_type) {
        case 'Z': // boolean
            bufferNeeded = sprintf(tmp, "%u", (bool) value.z);
            break;
        case 'B': // byte
            bufferNeeded = sprintf(tmp, "%hu", (unsigned short) value.b);
            break;
        case 'C': // char
            bufferNeeded = sprintf(tmp, "%c", (char) value.c);
            break;
        case 'S': // short
            bufferNeeded = sprintf(tmp, "%hu", (unsigned short) value.s);
            break;
        case 'I': // int
            bufferNeeded = sprintf(tmp, "%d", (int) value.i);
            break;
        case 'J': // long
            bufferNeeded = sprintf(tmp, "%ld", (long) value.j);
            break;
        case 'F': // float
            bufferNeeded = sprintf(tmp, "%f", (float) value.f);
            break;
        case 'D': // double
            bufferNeeded = sprintf(tmp, "%lf", (double) value.d);
            break;
        case '[': // reference (array)
        case 'L': // reference (object)
            bufferNeeded = sprintf(tmp, "%s", "some object or array");
            break;
        default:
            error("jvalueToString", "Impossible state in switch, terminating..");
            abort();
    }

    check_buffer_overflow(bufferNeeded);
    return tmp;
}
*/

void JNICALL
FieldAccess(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method,
        jlocation location, jclass field_klass, jobject object, jfieldID field) {

    // raw monitor
    enterRawMonitor(jvmti_env);

    // main code
    if (isInfo()) {
        //printLine();
        //info("FieldAccess", "Field Access");
        infoFieldInformation(jvmti_env, thread, field_klass, object, field, false);
    }

    if (trie) {
        checkFieldSynchronizationUsingTrie(jvmti_env, thread, field_klass, object, field, false);
    } else {
        checkFieldSynchronization(jvmti_env, thread, field_klass, object, field, false);
    }

    // end of raw monitor
    exitRawMonitor(jvmti_env);
    trace("FieldAccess", "Field Access done");
}

void JNICALL
FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method,
        jlocation location, jclass field_klass, jobject object, jfieldID field,
        char signature_type, jvalue new_value) {

    // raw monitor
    enterRawMonitor(jvmti_env);

    // main code
    if (isInfo()) {
        //printLine();
        //info("FieldModification", "Field Modification");
        infoFieldInformation(jvmti_env, thread, field_klass, object, field, true);
        /*
                char * stringValue = jvalueToString(signature_type, new_value);
                info("FieldModification", "the new value: %s", stringValue);
                free(stringValue);
         */
    }

    if (trie) {
        checkFieldSynchronizationUsingTrie(jvmti_env, thread, field_klass, object, field, true);
    } else {
        checkFieldSynchronization(jvmti_env, thread, field_klass, object, field, true);
    }

    // end of raw monitor
    exitRawMonitor(jvmti_env);
    trace("FieldAccess", "Field Access done");
}

/**
 * Deallocate all global pointers. Should be called at the end of this jvm agent.
 *
 * @param jvmti_env
 */
static void deallocateAll(jvmtiEnv *jvmti_env) {
    info("deallocateAll", "deallocating all");

    int i;
    for (i = 0; i < numberOfClasses; i++) {
        free(classesNames[i]);
    }
    free(classesNames);

    for (i = 0; i < numberOfSuperClasses; i++) {
        free(superClassesNames[i]);
    }
    free(superClassesNames);

    for (i = 0; i < numberOfFields; i++) {
        free(fieldsNames[i].classSignature);
        free(fieldsNames[i].fieldName);
        free(fieldsNames[i].fieldSignature);
    }
    free(fieldsNames);

    for (i = 0; i < numberOfPackages; i++) {
        free(packagesNames[i]);
    }
    free(packagesNames);

    for (i = 0; i < numberOfMonitorsInfos; i++) {
        free(ownedMonitorsInfos[i].owned_monitors_tags);
        if (ownedMonitorsInfos[i].numberOfLockTags > 0) {
            free(ownedMonitorsInfos[i].lockTags);
        }
        if (stackTraceEnabled) {
            int j;
            for (j = 0; j < ownedMonitorsInfos[i].numberOfStackTraces; j++) {
                free(ownedMonitorsInfos[i].stackTraces[j]);
            }
            free(ownedMonitorsInfos[i].stackTraces);
        }
        deallocate(jvmti_env, ownedMonitorsInfos[i].threadName);
    }
    free(ownedMonitorsInfos);

    for (i = 0; i < numberOfLockMonitors; i++) {
        free(lockMonitors[i].lockTags);
        deallocate(jvmti_env, lockMonitors[i].threadName);
    }
    free(lockMonitors);

    trace("deallocateAll", "before deallocating data races");
    for (i = 0; i < numberOfDataRaces; i++) {
        deallocate(jvmti_env, dataRaces[i].classSignature);
        deallocate(jvmti_env, dataRaces[i].fieldName);
        deallocate(jvmti_env, dataRaces[i].fieldSignature);
        deallocate(jvmti_env, dataRaces[i].fromThreadName);
        free(dataRaces[i].stackTrace);
    }
    free(dataRaces);

    free(joinZeroThreads);
    freeThreadRelationControl();

    // access info
    trace("deallocateAll", "before deallocating access info");
    freeAccessInfo();
    info("deallocateAll", "deallocating all done");
}

JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm) {
    deallocateAll(global_jvmti_env);

    /*
        if (isDebug()) {
            jvmtiPhase phase;
            jvmError = (*global_jvmti_env)->GetPhase(global_jvmti_env, &phase);
            check_jvmti_error(global_jvmti_env, jvmError, "Unable to obtain phase");
            debug("Agent_OnUnload", "phase = %d", (int) phase);
        }

        // destroy raw monitor
        jvmError = (*global_jvmti_env)->DestroyRawMonitor(global_jvmti_env, rawMonitorId);
        check_jvmti_error(global_jvmti_env, jvmError, "Destroying the raw monitor have failed");
     */

    info("Agent_OnUnload", "Agent unloaded successfully.");
}

static void initFields() {
    ownedMonitorsInfos = (OwnedMonitorsInfo*) calloc(START_MAX_MONITORS_INFO, sizeof (OwnedMonitorsInfo));
    lockMonitors = (LockMonitors*) calloc(START_MAX_LOCK_MONITORS, sizeof (LockMonitors));
    dataRaces = (DataRace*) calloc(START_MAX_DATA_RACES_INFO, sizeof (DataRace));
    joinZeroThreads = (JoinZeroThread*) calloc(START_MAX_JOIN_ZERO_THREADS,
            sizeof (JoinZeroThread));

    initThreadRelationControl();
    initAccessInfo();
}

static void setCapabilities(jvmtiEnv *jvmti_env) {
    (void) memset(&capa, 0, sizeof (jvmtiCapabilities));
    capa.can_generate_field_access_events = 1;
    capa.can_generate_field_modification_events = 1;
    capa.can_get_owned_monitor_info = 1;
    capa.can_tag_objects = 1;
    capa.can_get_line_numbers = 1;

    if (watchThreads || watchConcurrent) {
        capa.can_generate_method_entry_events = 1;
        capa.can_generate_method_exit_events = 1;
        capa.can_access_local_variables = 1;
    }

    jvmError = (*jvmti_env)->AddCapabilities(jvmti_env, &capa);
    check_jvmti_error(jvmti_env, jvmError, "Unable to get necessary JVMTI capabilities.");
}

/**
 * Set given event notification
 * @param jvmti_env
 * @param event
 */
static void setEventNotification(jvmtiEnv *jvmti_env, jvmtiEvent event) {
    jvmError = (*jvmti_env)->SetEventNotificationMode(jvmti_env, JVMTI_ENABLE,
            event, (jthread) NULL);
    check_jvmti_error(jvmti_env, jvmError, "Cannot set event notification");
}

/**
 * Sets events notifications
 * @param jvmti_env
 */
static void setEventsNotification(jvmtiEnv *jvmti_env) {
    // field access and field modification
    setEventNotification(jvmti_env, JVMTI_EVENT_FIELD_ACCESS);
    setEventNotification(jvmti_env, JVMTI_EVENT_FIELD_MODIFICATION);
    setEventNotification(jvmti_env, JVMTI_EVENT_VM_INIT);
    setEventNotification(jvmti_env, JVMTI_EVENT_CLASS_PREPARE);

    // method entry and exit
    if (watchThreads || watchConcurrent) {
        setEventNotification(jvmti_env, JVMTI_EVENT_METHOD_ENTRY);
        setEventNotification(jvmti_env, JVMTI_EVENT_METHOD_EXIT);
    }
}

static void setCallbacks(jvmtiEnv *jvmti_env) {
    jvmtiEventCallbacks callbacks;
    (void) memset(&callbacks, 0, sizeof (callbacks));
    callbacks.FieldAccess = &FieldAccess;
    callbacks.FieldModification = &FieldModification;
    callbacks.VMInit = &VMInit;
    callbacks.ClassPrepare = &ClassPrepare;

    if (watchThreads || watchConcurrent) {
        callbacks.MethodEntry = &MethodEntry;
        callbacks.MethodExit = &MethodExit;
    }

    jvmError = (*jvmti_env)->SetEventCallbacks(jvmti_env, &callbacks, (jint)sizeof (callbacks));
    check_jvmti_error(jvmti_env, jvmError, "Cannot set jvmti callbacks");
}

JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    // set default logger level
    setLoggerLevel(DEFAULT_LOGGER_LEVEL);
    info("Agent_OnLoad", "Starting agent");

    // obtaining and setting global_jvmti_env
    jint res = 0;
    res = (*vm)->GetEnv(vm, (void**) &global_jvmti_env, JVMTI_VERSION_1_2);

    // creating raw monitor
    jvmError = (*global_jvmti_env)->CreateRawMonitor(global_jvmti_env, "RawMonitor", &rawMonitorId);
    check_jvmti_error(global_jvmti_env, jvmError, "Creating raw monitor failed");

    // initialization and parsing
    initFields();

    int errorCode = parseOptions(options);
    if (errorCode != 0) {
        if (errorCode != HELP_ERROR_CODE) {
            error("Agent_OnLoad", "errorCode after parsing= %d", errorCode);
            printWrongParameters();
            deallocateAll(global_jvmti_env);
            return JNI_ERR;
        } else {
            deallocateAll(global_jvmti_env);
            return JNI_ABORT;
        }
    }

    debug("Agent_OnLoad", "res = %d", (int) res);

    if (isInfo()) {
        writeParsed();
    }

    // setting agent
    setCapabilities(global_jvmti_env);
    setEventsNotification(global_jvmti_env);
    setCallbacks(global_jvmti_env);

    return JNI_OK;
}
 
 
Close
loading
Please Confirm
Close