diff --git a/CMakeLists.txt b/CMakeLists.txt index ef4e64a..5802dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ endif () list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") find_package(JNI11) include_directories(${JNI_INCLUDE_DIRS}) +include_directories("/usr/lib/jvm/java-1.11.0-openjdk-amd64/include") +include_directories("/usr/lib/jvm/java-1.11.0-openjdk-amd64/include/linux") add_library(memory_agent SHARED src/agent.cpp @@ -36,6 +38,8 @@ add_library(memory_agent SHARED src/sizes/tag_info_array.cpp src/sizes/retained_size_by_classes.h src/sizes/retained_size_by_classes.cpp + src/sizes/retained_size_by_classloaders.h + src/sizes/retained_size_by_classloaders.cpp src/sizes/retained_size_action.h src/sizes/retained_size_by_objects.h src/sizes/retained_size_by_objects.cpp diff --git a/src/agent.cpp b/src/agent.cpp index d864808..3fbe714 100644 --- a/src/agent.cpp +++ b/src/agent.cpp @@ -14,6 +14,7 @@ #include "sizes/retained_size_and_held_objects.h" #include "sizes/retained_size_by_classes.h" #include "sizes/retained_size_by_objects.h" +#include "sizes/retained_size_by_classloaders.h" #include "allocation_sampling.h" #pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" @@ -140,6 +141,13 @@ JNIEXPORT jboolean JNICALL Java_com_intellij_memory_agent_IdeaNativeAgentProxy_c return (jboolean) 1; } +extern "C" +JNIEXPORT jboolean JNICALL Java_com_intellij_memory_agent_IdeaNativeAgentProxy_canGetRetainedSizeByClassLoaders( + JNIEnv *env, + jobject thisObject) { + return (jboolean) 1; +} + extern "C" JNIEXPORT jboolean JNICALL Java_com_intellij_memory_agent_IdeaNativeAgentProxy_canGetShallowSizeByClasses( JNIEnv *env, @@ -196,6 +204,14 @@ JNIEXPORT jobjectArray JNICALL Java_com_intellij_memory_agent_IdeaNativeAgentPro return RetainedSizeByClassesAction(env, gdata->jvmti, thisObject).run(classesArray); } +extern "C" +JNIEXPORT jobjectArray JNICALL Java_com_intellij_memory_agent_IdeaNativeAgentProxy_getRetainedSizeByClassLoaders( + JNIEnv *env, + jobject thisObject, + jobjectArray classLoadersArray) { + return RetainedSizeByClassLoadersAction(env, gdata->jvmti, thisObject).run(classLoadersArray); +} + extern "C" JNIEXPORT jobjectArray JNICALL Java_com_intellij_memory_agent_IdeaNativeAgentProxy_getShallowAndRetainedSizeByClasses( JNIEnv *env, diff --git a/src/sizes/retained_size_action.cpp b/src/sizes/retained_size_action.cpp index e4307b2..1cacb8d 100644 --- a/src/sizes/retained_size_action.cpp +++ b/src/sizes/retained_size_action.cpp @@ -98,6 +98,10 @@ jint JNICALL clearTag(jlong classTag, jlong size, jlong *tagPtr, jint length, vo if (*tagPtr == 0) { return JVMTI_ITERATION_CONTINUE; } +// if (*tagPtr == 13) { +// *tagPtr = 0; +// return JVMTI_ITERATION_CONTINUE; +// } tagToPointer(*tagPtr)->unref(); *tagPtr = 0; @@ -120,7 +124,13 @@ jint JNICALL tagObjectOfTaggedClass(jlong classTag, jlong size, jlong *tagPtr, j if (pClassTag && *tagPtr == 0) { *tagPtr = pointerToTag(pClassTag->createStartTag()); } + return JVMTI_ITERATION_CONTINUE; +} +jint JNICALL tagObjectOfTaggedClassSimple(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData) { + if (classTag == 13) { + *tagPtr = 13; + } return JVMTI_ITERATION_CONTINUE; } diff --git a/src/sizes/retained_size_action.h b/src/sizes/retained_size_action.h index b142e53..680f1d1 100644 --- a/src/sizes/retained_size_action.h +++ b/src/sizes/retained_size_action.h @@ -28,6 +28,7 @@ jint JNICALL clearTag (jlong classTag, jlong size, jlong *tagPtr, jint JNICALL retagStartObjects (jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); jint JNICALL tagObjectOfTaggedClass (jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); +jint JNICALL tagObjectOfTaggedClassSimple (jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); jvmtiError walkHeapFromObjects (jvmtiEnv *jvmti, const std::vector &objects, const CancellationChecker &cancellationChecker); @@ -72,6 +73,35 @@ class RetainedSizeAction : public MemoryAgentAction { return JVMTI_ERROR_NONE; } + jvmtiError createTagsForClassLoadersClasses(JNIEnv *env, jvmtiEnv *jvmti, jobjectArray classesArray, jsize classLoaderIndex) { + for (jsize i = 0; i < this->env->GetArrayLength(classesArray); i++) { + jobject classObject = this->env->GetObjectArrayElement(classesArray, i); + jvmtiError err = tagClass(env, jvmti, classObject, [classLoaderIndex](jlong oldTag) -> jlong { + ClassTag *classTag = tagToClassTagPointer(oldTag); + if (classTag != nullptr) { + classTag->ids.push_back(classLoaderIndex); + } else { + return pointerToTag(ClassTag::create(static_cast(classLoaderIndex))); + } + + return 0; + }); + if (err != JVMTI_ERROR_NONE) return err; + } + return JVMTI_ERROR_NONE; + } + + jvmtiError createTagsForClassLoadersClassesSimple(JNIEnv *env, jvmtiEnv *jvmti, jobjectArray classesArray) { + for (jsize i = 0; i < this->env->GetArrayLength(classesArray); i++) { + jobject classObject = this->env->GetObjectArrayElement(classesArray, i); + jvmtiError err = tagClass(env, jvmti, classObject, [](jlong oldTag) -> jlong { + return 13; + }); + if (err != JVMTI_ERROR_NONE) return err; + } + return JVMTI_ERROR_NONE; + } + jvmtiError tagObjectsOfClasses(jobjectArray classesArray) { debug("tag objects of classes"); jvmtiError err = createTagsForClasses(this->env, this->jvmti, classesArray); @@ -80,6 +110,22 @@ class RetainedSizeAction : public MemoryAgentAction { return this->IterateThroughHeap(0, nullptr, tagObjectOfTaggedClass, nullptr); } + jvmtiError tagObjectsOfClassLoaderClasses(jobjectArray classesArray, jsize classLoaderIndex) { + debug("tag objects of classes"); + jvmtiError err = createTagsForClassLoadersClasses(this->env, this->jvmti, classesArray, classLoaderIndex); + if (err != JVMTI_ERROR_NONE) return err; + + return this->IterateThroughHeap(0, nullptr, tagObjectOfTaggedClass, nullptr); + } + + jvmtiError tagObjectsOfClassLoaderClassesSimple(jobjectArray classesArray) { + debug("tag objects of classes"); + jvmtiError err = createTagsForClassLoadersClassesSimple(this->env, this->jvmti, classesArray); + if (err != JVMTI_ERROR_NONE) return err; + + return this->IterateThroughHeap(0, nullptr, tagObjectOfTaggedClassSimple, nullptr); + } + jvmtiError tagHeap() { jvmtiError err = this->FollowReferences(0, nullptr, nullptr, getTagsWithNewInfo, nullptr, "find objects with new info"); if (err != JVMTI_ERROR_NONE) return err; diff --git a/src/sizes/retained_size_by_classes.cpp b/src/sizes/retained_size_by_classes.cpp index 28e2e7e..9e8660e 100644 --- a/src/sizes/retained_size_by_classes.cpp +++ b/src/sizes/retained_size_by_classes.cpp @@ -21,6 +21,11 @@ jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, return JVMTI_ITERATION_CONTINUE; } +jint JNICALL countHeapSize(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData) { + *reinterpret_cast(userData) += size; + return JVMTI_ITERATION_CONTINUE; +} + jint JNICALL visitObjectForShallowAndRetainedSize(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData) { if (*tagPtr == 0) { diff --git a/src/sizes/retained_size_by_classes.h b/src/sizes/retained_size_by_classes.h index 0bda99b..ed83fde 100644 --- a/src/sizes/retained_size_by_classes.h +++ b/src/sizes/retained_size_by_classes.h @@ -26,5 +26,6 @@ class RetainedAndShallowSizeByClassesAction : public RetainedSizeAction +#include "retained_size_by_classloaders.h" +#include +#include + + +jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jclass *classes, jint* cnt, jobject classLoader, jsize classLoaderIndex) { + jvmtiError err = JVMTI_ERROR_NONE; + std::vector filteredClasses; + for (jsize i = 0; i < *cnt; i++) { + jobject rootClassLoader = getClassLoader(env, classes[i]); + if (!env->IsSameObject(rootClassLoader, NULL)) { + if (isEqual(env, classLoader, rootClassLoader)) { + filteredClasses.emplace_back(classes[i]); + } + } + } + + err = tagObjectsOfClassLoaderClasses(toJavaArray(env, filteredClasses), classLoaderIndex); + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + return err; +} + +jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoaderSimple(jclass *classes, jint* cnt, jobject classLoader) { + jvmtiError err = JVMTI_ERROR_NONE; + std::vector filteredClasses; + for (jsize i = 0; i < *cnt; i++) { + jobject rootClassLoader = getClassLoader(env, classes[i]); + if (!env->IsSameObject(rootClassLoader, NULL)) { + if (isEqual(env, classLoader, rootClassLoader)) { + filteredClasses.emplace_back(classes[i]); + } + } + } + + debug("tag objects of classes"); + err = createTagsForClassLoadersClassesSimple(this->env, this->jvmti, toJavaArray(env, filteredClasses)); + if (err != JVMTI_ERROR_NONE) return err; + + err = this->IterateThroughHeap(0, nullptr, tagObjectOfTaggedClassSimple, nullptr); + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + return err; +} + +jlong RetainedSizeByClassLoadersAction::tagOtherObjects(jclass *classes, jint* cnt, jobjectArray classLoadersArray) { + jvmtiError err = JVMTI_ERROR_NONE; + std::vector filteredClasses; + for (jsize i = 0; i < *cnt; i++) { + jobject rootClassLoader = getClassLoader(env, classes[i]); + if (!env->IsSameObject(rootClassLoader, NULL)) { + for (jsize j = 0; j < env->GetArrayLength(classLoadersArray); j++) { + if (isEqual(env, env->GetObjectArrayElement(classLoadersArray, j), rootClassLoader)) { + filteredClasses.emplace_back(classes[i]); + break; + } + } + } + } + + std::vector nonfilteredClasses; + for (jsize i = 0; i < *cnt; i++) { + if (std::find(filteredClasses.begin(), filteredClasses.end(), classes[i]) == filteredClasses.end()) { + nonfilteredClasses.emplace_back(classes[i]); + } + } + std::cout << "SIZE: " << nonfilteredClasses.size() << std::endl; + + err = tagObjectsOfClassLoaderClasses(toJavaArray(env, nonfilteredClasses), env->GetArrayLength(classLoadersArray)); +} + +jlong RetainedSizeByClassLoadersAction::getHeapSize(){ + jlong totalSize = 0; + IterateThroughHeap(0, nullptr, countHeapSize, &totalSize, + "calculate heap size"); + return totalSize; +} + +RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) + : RetainedSizeAction(env, jvmti, object), retainedSizeByObjectsMergedAction(env, jvmti, object), retainedSizeByObjectsAction(env, jvmti, object) { + +} + +jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, + std::vector &result) { + jvmtiError err = JVMTI_ERROR_NONE; + jclass *classes; + jint cnt; + err = jvmti->GetLoadedClasses(&cnt, &classes); + for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { + jvmtiError err = tagObjectsByClassLoader(classes, &cnt, env->GetObjectArrayElement(classLoadersArray, i), i); + } + tagOtherObjects(classes, &cnt, classLoadersArray); + + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + + err = tagHeap(); + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + + result.resize(env->GetArrayLength(classLoadersArray) + 1); + err = IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), + "calculate retained size"); + return err; +} + +jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaderSimple(jobject classLoader, + jlong* result) { + jvmtiError err = JVMTI_ERROR_NONE; + jclass *classes; + jint cnt; + err = jvmti->GetLoadedClasses(&cnt, &classes); + err = tagObjectsByClassLoaderSimple(classes, &cnt, classLoader); + std::cout << "tagged" << std::endl; + + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + + std::vector objects; + err = getObjectsByTags(this->jvmti, std::vector{13}, objects); + std::cout << "get objects" << std::endl; + if (err != JVMTI_ERROR_NONE) return err; + if (this->shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + removeAllTagsFromHeap(jvmti, nullptr); + + std::cout << "remove tags" << std::endl; + std::cout << objects.size() << std::endl; + auto r = retainedSizeByObjectsMergedAction.run(toJavaArray(env, objects)); + result = reinterpret_cast(env->GetObjectArrayElement(r, 0)); + std::cout << result << " " << *result << std::endl; + removeAllTagsFromHeap(jvmti, nullptr); + return err; +} + +jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { + std::vector result; + jvmtiError err = getRetainedSizeByClassLoaders(classLoadersArray, result); + std::cout << "CLASSLOADERS CLASSES: " << result[0] < otherRes; + for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { + jlong res; + jvmtiError err = getRetainedSizeByClassLoaderSimple(env->GetObjectArrayElement(classLoadersArray, i), &res); + otherRes.push_back(res); + } + if (!isOk(err)) { + handleError(jvmti, err, "Could not estimate retained size by classLoaders"); + return env->NewLongArray(0); + } + //std::cout << "HEAP SIZE: " << getHeapSize() << std::endl; + std::cout << "CLASSLOADERS CLASSES CHECK: " << std::accumulate(otherRes.begin(), otherRes.end(), 0l) < { +public: + RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); + RetainedSizeByObjectsMergedAction retainedSizeByObjectsMergedAction; + RetainedSizeByObjectsAction retainedSizeByObjectsAction; + +private: + jlongArray executeOperation(jobjectArray classLoadersArray) override; + jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result); + jvmtiError getRetainedSizeByClassLoaderSimple(jobject classLoader, jlong* result); + jvmtiError tagObjectsByClassLoader(jclass *classes, jint* cnt, jobject classLoader, jsize classLoaderIndex); + jvmtiError tagObjectsByClassLoaderSimple(jclass *classes, jint* cnt, jobject classLoader); + jlong getHeapSize(); + jlong tagOtherObjects(jclass *classes, jint* cnt, jobjectArray classLoadersArray); +}; + +jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); + + + +#endif //MEMORY_AGENT_RETAINED_SIZE_BY_CLASSLOADERS_H diff --git a/src/sizes/retained_size_by_objects_merged.cpp b/src/sizes/retained_size_by_objects_merged.cpp new file mode 100644 index 0000000..a5ccffe --- /dev/null +++ b/src/sizes/retained_size_by_objects_merged.cpp @@ -0,0 +1,104 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +#include "retained_size_by_objects_merged.h" +#include "sizes_tags.h" +#include "retained_size_by_classes.h" + +#define START_TAG 1 +#define VISITED_TAG 2 + +jint JNICALL firstTravers(jvmtiHeapReferenceKind refKind, const jvmtiHeapReferenceInfo *refInfo, jlong classTag, + jlong referrerClassTag, jlong size, jlong *tagPtr, + jlong *referrerTagPtr, jint length, void *userData) { + if (refKind == JVMTI_HEAP_REFERENCE_JNI_LOCAL || refKind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL) { + return 0; + } else if (*tagPtr == START_TAG) { + return 0; + } else { + *tagPtr = VISITED_TAG; + } + return JVMTI_VISIT_OBJECTS; +} + +jint JNICALL secondTravers(jvmtiHeapReferenceKind refKind, const jvmtiHeapReferenceInfo *refInfo, jlong classTag, + jlong referrerClassTag, jlong size, jlong *tagPtr, + jlong *referrerTagPtr, jint length, void *userData) { + if (refKind == JVMTI_HEAP_REFERENCE_JNI_LOCAL || refKind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL) { + return 0; + } else if (*tagPtr == VISITED_TAG){ + return 0; + } else if (*tagPtr != VISITED_TAG) { + // *reinterpret_cast(userData) += size; + *tagPtr = START_TAG; + } + return JVMTI_VISIT_OBJECTS; +} + +RetainedSizeByObjectsMergedAction::RetainedSizeByObjectsMergedAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) : MemoryAgentAction(env, jvmti, object) { + +} + +jvmtiError RetainedSizeByObjectsMergedAction::traverseHeapForTheFirstTime(std::vector &objects) { + for(const auto& object: objects) { + jvmtiError err = jvmti->SetTag(object, START_TAG); + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + } + progressManager.updateProgress(10, "Traversing heap for the first time..."); + return FollowReferences(0, nullptr, nullptr, firstTravers, nullptr, "tag heap"); +} + +jvmtiError RetainedSizeByObjectsMergedAction::traverseHeapFromStartObject(std::vector &objects) { + progressManager.updateProgress(45, "Traversing heap for the second time..."); + for(const auto& object: objects) { + jvmtiError err = FollowReferences(0, nullptr, object, secondTravers, nullptr, "tag heap"); + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + } + return JVMTI_ERROR_NONE; +} + +jvmtiError RetainedSizeByObjectsMergedAction::countRetainedSize(jlong &retainedSize){ + std::vector objects; + retainedSize = 0; + jvmtiError err = getObjectsByTags(this->jvmti, std::vector{START_TAG}, objects); + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + for (const auto& object:objects){ + jlong startObjectSize = 0; + err = jvmti->GetObjectSize(object, &startObjectSize); + if (!isOk(err)) return err; + retainedSize += startObjectSize; + } + return err; +} + +jvmtiError RetainedSizeByObjectsMergedAction::estimateObjectsSize(std::vector &objects, jlong &retainedSize) { + jvmtiError err = traverseHeapForTheFirstTime(objects); + std::cout << "finish first traversal" << std::endl; + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + err = traverseHeapFromStartObject(objects); + std::cout << "finish second traversal" << std::endl; + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; + return countRetainedSize(retainedSize); +} + +jlongArray RetainedSizeByObjectsMergedAction::executeOperation(jobjectArray objects) { + std::vector objects_v; + fromJavaArray(env, objects, objects_v); + jlong retainedSize; + jvmtiError err = estimateObjectsSize(objects_v, retainedSize); + std::cout << "got res " << retainedSize << std::endl; + if (!isOk(err)) { + handleError(jvmti, err, "Could not estimate object size"); + } + std::vector res; + res.push_back(retainedSize); + return toJavaArray(env, res); +} + +jvmtiError RetainedSizeByObjectsMergedAction::cleanHeap() { + return removeAllTagsFromHeap(jvmti, nullptr); +} diff --git a/src/sizes/retained_size_by_objects_merged.h b/src/sizes/retained_size_by_objects_merged.h new file mode 100644 index 0000000..fa031b4 --- /dev/null +++ b/src/sizes/retained_size_by_objects_merged.h @@ -0,0 +1,27 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +#ifndef MEMORY_AGENT_RETAINED_SIZE_BY_OBJECTS_MERGED_H +#define MEMORY_AGENT_RETAINED_SIZE_BY_OBJECTS_MERGED_H + +#include +#include "../memory_agent_action.h" + +class RetainedSizeByObjectsMergedAction : public MemoryAgentAction { +public: + RetainedSizeByObjectsMergedAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); + +private: + jlongArray executeOperation(jobjectArray objects) override; + jvmtiError cleanHeap() override; + + jvmtiError estimateObjectsSize(std::vector &objects, jlong &retainedSize); + + jvmtiError traverseHeapForTheFirstTime(std::vector &objects); + + jvmtiError traverseHeapFromStartObject(std::vector &objects); + jvmtiError countRetainedSize(jlong &retainedSize); + +}; + + +#endif //MEMORY_AGENT_RETAINED_SIZE_BY_OBJECTS_MERGED_H diff --git a/src/utils.cpp b/src/utils.cpp index 4626381..91dc317 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -49,6 +49,15 @@ jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { return res; } +jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { + auto count = static_cast(objects.size()); + jobjectArray res = env->NewObjectArray(count, env->FindClass("java/lang/Class"), nullptr); + for (auto i = 0; i < count; ++i) { + env->SetObjectArrayElement(res, i, objects[i]); + } + return res; +} + jlongArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jlongArray result = env->NewLongArray(count); @@ -260,6 +269,18 @@ jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classO return err; } +jvmtiError tagClass(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag) { + jlong oldTag; + jvmtiError err = jvmti->GetTag(classObject, &oldTag); + if (err != JVMTI_ERROR_NONE) return err; + jlong newTag = createTag(oldTag); + if (newTag != 0) { + err = jvmti->SetTag(classObject, newTag); + if (err != JVMTI_ERROR_NONE) return err; + } + return err; +} + jmethodID getIsAssignableFromMethod(JNIEnv *env) { jclass langClass = env->FindClass("java/lang/Class"); return env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); @@ -270,6 +291,17 @@ std::string getToString(JNIEnv *env, jobject object) { return jstringTostring(env, reinterpret_cast(name)); } +jobject getClassLoader(JNIEnv *env, jclass clazz) { + return env->CallObjectMethod(clazz, env->GetMethodID(env->GetObjectClass(clazz), "getClassLoader", + "()Ljava/lang/ClassLoader;")); +} + +bool isEqual(JNIEnv *env, jobject obj, jobject otherObj) { + jclass objClass = env->GetObjectClass(obj); + jmethodID equalsID = env->GetMethodID(objClass, "equals", "(Ljava/lang/Object;)Z"); + return env->CallBooleanMethod(obj, equalsID, otherObj); +} + ThreadSuspender::ThreadSuspender(jvmtiEnv *jvmti) : jvmti(jvmti) { jint threadCnt; jthread *threads; diff --git a/src/utils.h b/src/utils.h index 59f476a..f94dcba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -27,6 +27,8 @@ jbooleanArray toJavaArray(JNIEnv *env, std::vector &items); jobjectArray toJavaArray(JNIEnv *env, std::vector &objects); +jobjectArray toJavaArray(JNIEnv *env, std::vector &objects); + jintArray toJavaArray(JNIEnv *env, std::vector &items); jlongArray toJavaArray(JNIEnv *env, std::vector &items); @@ -65,6 +67,8 @@ jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tag jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag); +jvmtiError tagClass(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag); + bool isOk(jvmtiError error); bool fileExists(const std::string &fileName); @@ -73,6 +77,10 @@ std::string jstringTostring(JNIEnv *env, jstring jStr); jmethodID getIsAssignableFromMethod(JNIEnv *env); +jobject getClassLoader(JNIEnv *env, jclass obj); + +bool isEqual(JNIEnv *env, jobject obj1, jobject obj2); + std::string getToString(JNIEnv *env, jobject klass); template diff --git a/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java b/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java index 71fe0c2..dd57b3a 100644 --- a/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java +++ b/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java @@ -28,6 +28,8 @@ public IdeaNativeAgentProxy(Object cancellationFileName, Object progressFileName public native boolean canGetRetainedSizeByClasses(); + public native boolean canGetRetainedSizeByClassLoaders(); + public native boolean canGetShallowSizeByClasses(); public native boolean canEstimateObjectsSizes(); @@ -38,6 +40,8 @@ public IdeaNativeAgentProxy(Object cancellationFileName, Object progressFileName public native Object[] getRetainedSizeByClasses(Object[] classes); + public native Object[] getRetainedSizeByClassLoaders(ClassLoader[] classloaders); + public native Object[] getShallowAndRetainedSizeByClasses(Object[] classes); public native Object[] findPathsToClosestGcRoots(Object object, int pathsNumber, int objectsNumber); diff --git a/test_data/proxy/src/com/intellij/memory/agent/MemoryAgent.java b/test_data/proxy/src/com/intellij/memory/agent/MemoryAgent.java index 8b44c97..ac79663 100644 --- a/test_data/proxy/src/com/intellij/memory/agent/MemoryAgent.java +++ b/test_data/proxy/src/com/intellij/memory/agent/MemoryAgent.java @@ -97,6 +97,14 @@ public synchronized T[] getAllReachableObjects(Object startObject, Class return resultArray; } + /** + * TODO + */ + @SuppressWarnings("unchecked") + public synchronized long[] getRetainedSizeByClassLoaders(ClassLoader[] classloaders) throws MemoryAgentExecutionException { + return (long [])getResult(callProxyMethod(() -> proxy.getRetainedSizeByClassLoaders(classloaders))); + } + /** * Adds an allocation listener that catches allocation sampling events for specified classes. *