From 9cdbad5f101bbf27d1a68aa28dfd7eac44e8dc3c Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Fri, 22 Apr 2022 17:01:24 +0300 Subject: [PATCH 01/11] add class retained_size_by_classloaders --- CMakeLists.txt | 2 + src/sizes/retained_size_by_classloaders.cpp | 81 +++++++++++++++++++++ src/sizes/retained_size_by_classloaders.h | 26 +++++++ 3 files changed, 109 insertions(+) create mode 100644 src/sizes/retained_size_by_classloaders.cpp create mode 100644 src/sizes/retained_size_by_classloaders.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef4e64a..d5c3957 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,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/sizes/retained_size_by_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp new file mode 100644 index 0000000..96ac75d --- /dev/null +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -0,0 +1,81 @@ +// 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 +#include "retained_size_by_classloaders.h" + + +const jlong GC_ROOT_TAG = 7; + +jvmtiIterationControl JNICALL heapRootCallback(jvmtiHeapRootKind root_kind, jlong class_tag, + jlong size, jlong *tag_ptr, void *user_data) { + *tag_ptr = GC_ROOT_TAG; + return JVMTI_ITERATION_IGNORE; +} + +jobjectArray RetainedSizeByClassLoadersAction::getLoadedClassesByClassLoader(jobject classLoader) { + jclass instrumentationClass = env->FindClass("java/lang/instrument/Instrumentation"); + jmethodID getAllLoadedClasses = env->GetMethodID(instrumentationClass, "getInitiatedClasses", + "(java/lang/ClassLoader)[java/lang/Class"); + return static_cast(env->CallObjectMethod(instrumentationClass, getAllLoadedClasses, classLoader)); +} + +jobjectArray RetainedSizeByClassLoadersAction::filterLoadedClassesAsRoots(jobjectArray loadedClasses) { + jvmti->IterateOverReachableObjects(heapRootCallback, NULL, NULL, NULL); + jint nroots; + jobject *roots; + jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &nroots, &roots, NULL); + std::vector rootLoadedClasses; + for (jsize i = 0; i < env->GetArrayLength(loadedClasses); i++) { + for (jsize j = 0; j < nroots; j++) { + if (env->GetObjectArrayElement(loadedClasses, i) == roots[j]) { + rootLoadedClasses.push_back(roots[i]); + } + } + } + return toJavaArray(env, rootLoadedClasses); +} + +jvmtiError RetainedSizeByClassLoadersAction::tagRootLoadedClassesByClassLoaders(jobjectArray classLoadersArray) { + debug("tag classes loaded by classloaders"); + jvmti->IterateOverReachableObjects(heapRootCallback, NULL, NULL, NULL); + jint nroots; + jobject *roots; + jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &nroots, &roots, NULL); + for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { + jobjectArray loadedClasses = getLoadedClassesByClassLoader(env->GetObjectArrayElement(classLoadersArray, i)); + jobjectArray rootLoadedClasses = filterLoadedClassesAsRoots(loadedClasses); + jvmtiError err = createTagsForClasses(this->env, this->jvmti, rootLoadedClasses); + if (err != JVMTI_ERROR_NONE) return err; + } + return IterateThroughHeap(0, nullptr, tagObjectOfTaggedClass, nullptr); +} + +RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) + : RetainedSizeAction(env, jvmti, object) { + +} + +jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, + std::vector &result) { + jvmtiError err = tagRootLoadedClassesByClassLoaders(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)); + return IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), + "calculate retained sizes"); +} + +jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { + std::vector result; + jvmtiError err = getRetainedSizeByClassLoaders(classLoadersArray, result); + if (!isOk(err)) { + handleError(jvmti, err, "Could not estimate retained size by classloaders"); + return env->NewLongArray(0); + } + return toJavaArray(env, result); +} \ No newline at end of file diff --git a/src/sizes/retained_size_by_classloaders.h b/src/sizes/retained_size_by_classloaders.h new file mode 100644 index 0000000..c2abbec --- /dev/null +++ b/src/sizes/retained_size_by_classloaders.h @@ -0,0 +1,26 @@ +// 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_CLASSLOADERS_H +#define MEMORY_AGENT_RETAINED_SIZE_BY_CLASSLOADERS_H + +#include "../memory_agent_action.h" +#include "retained_size_action.h" + + +class RetainedSizeByClassLoadersAction : public RetainedSizeAction { +public: + RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); + +private: + jlongArray executeOperation(jobjectArray classLoadersArray) override; + jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result); + jvmtiError tagRootLoadedClassesByClassLoaders(jobjectArray classLoadersArray); + jobjectArray getLoadedClassesByClassLoader(jobject classLoader); + jobjectArray filterLoadedClassesAsRoots(jobjectArray loadedClasses); +}; + +jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); + + + +#endif //MEMORY_AGENT_RETAINED_SIZE_BY_CLASSLOADERS_H From 6ce9b732a19067466d1e4c1047ac5fd2f288993e Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Fri, 6 May 2022 20:27:50 +0300 Subject: [PATCH 02/11] add new functions to proxy --- src/agent.cpp | 16 ++++++++++++++++ .../memory/agent/IdeaNativeAgentProxy.java | 4 ++++ .../com/intellij/memory/agent/MemoryAgent.java | 8 ++++++++ 3 files changed, 28 insertions(+) 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/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java b/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java index 71fe0c2..fc0fc18 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..b793a87 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. * From ebbd5613eca55ebf341d3ba57aa168941e8aec7e Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Fri, 6 May 2022 21:29:50 +0300 Subject: [PATCH 03/11] fix retained_size_by_classloaders --- src/sizes/retained_size_by_classloaders.cpp | 58 ++++++++++++--------- src/sizes/retained_size_by_classloaders.h | 3 +- src/utils.cpp | 13 +++++ src/utils.h | 4 ++ 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/sizes/retained_size_by_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp index 96ac75d..60111af 100644 --- a/src/sizes/retained_size_by_classloaders.cpp +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -4,47 +4,53 @@ #include "retained_size_by_classloaders.h" -const jlong GC_ROOT_TAG = 7; +const jlong GC_ROOT_TAG = 17; -jvmtiIterationControl JNICALL heapRootCallback(jvmtiHeapRootKind root_kind, jlong class_tag, - jlong size, jlong *tag_ptr, void *user_data) { - *tag_ptr = GC_ROOT_TAG; +jvmtiIterationControl JNICALL putTagCallback(jvmtiHeapRootKind root_kind, + jlong class_tag, + jlong size, + jlong *tag_ptr, + void *user_data) { + if (root_kind != JVMTI_HEAP_ROOT_JNI_GLOBAL && + root_kind != JVMTI_HEAP_ROOT_JNI_LOCAL) { + *tag_ptr = GC_ROOT_TAG; + } return JVMTI_ITERATION_IGNORE; } -jobjectArray RetainedSizeByClassLoadersAction::getLoadedClassesByClassLoader(jobject classLoader) { - jclass instrumentationClass = env->FindClass("java/lang/instrument/Instrumentation"); - jmethodID getAllLoadedClasses = env->GetMethodID(instrumentationClass, "getInitiatedClasses", - "(java/lang/ClassLoader)[java/lang/Class"); - return static_cast(env->CallObjectMethod(instrumentationClass, getAllLoadedClasses, classLoader)); +jvmtiIterationControl JNICALL removeTagCallback(jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) { + if (*tag_ptr == GC_ROOT_TAG) { + tag_ptr = nullptr; + } + return JVMTI_ITERATION_CONTINUE; } -jobjectArray RetainedSizeByClassLoadersAction::filterLoadedClassesAsRoots(jobjectArray loadedClasses) { - jvmti->IterateOverReachableObjects(heapRootCallback, NULL, NULL, NULL); +jobjectArray RetainedSizeByClassLoadersAction::getFilteredRoots(jobject classLoader) { + jvmti->IterateOverReachableObjects(putTagCallback, NULL, NULL, NULL); jint nroots; jobject *roots; jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &nroots, &roots, NULL); - std::vector rootLoadedClasses; - for (jsize i = 0; i < env->GetArrayLength(loadedClasses); i++) { - for (jsize j = 0; j < nroots; j++) { - if (env->GetObjectArrayElement(loadedClasses, i) == roots[j]) { - rootLoadedClasses.push_back(roots[i]); + jvmti->IterateOverReachableObjects(removeTagCallback, NULL, NULL, NULL); + removeAllTagsFromHeap(jvmti, nullptr); + + std::vector filteredRoots; + for (jsize i = 0; i < nroots; i++) { + jobject rootClassLoader = getClassLoader(env, roots[i]); + if (!env->IsSameObject(rootClassLoader, NULL)) { + if (isEqual(env, classLoader, rootClassLoader)) { + std::cout << "HEY!!" << std::endl; + filteredRoots.push_back(roots[i]); } } } - return toJavaArray(env, rootLoadedClasses); + return toJavaArray(env, filteredRoots); } jvmtiError RetainedSizeByClassLoadersAction::tagRootLoadedClassesByClassLoaders(jobjectArray classLoadersArray) { debug("tag classes loaded by classloaders"); - jvmti->IterateOverReachableObjects(heapRootCallback, NULL, NULL, NULL); - jint nroots; - jobject *roots; - jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &nroots, &roots, NULL); for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { - jobjectArray loadedClasses = getLoadedClassesByClassLoader(env->GetObjectArrayElement(classLoadersArray, i)); - jobjectArray rootLoadedClasses = filterLoadedClassesAsRoots(loadedClasses); - jvmtiError err = createTagsForClasses(this->env, this->jvmti, rootLoadedClasses); + jobjectArray filteredRoots = getFilteredRoots(env->GetObjectArrayElement(classLoadersArray, i)); + jvmtiError err = createTagsForClasses(this->env, this->jvmti, filteredRoots); if (err != JVMTI_ERROR_NONE) return err; } return IterateThroughHeap(0, nullptr, tagObjectOfTaggedClass, nullptr); @@ -56,7 +62,7 @@ RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, } jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, - std::vector &result) { + std::vector &result) { jvmtiError err = tagRootLoadedClassesByClassLoaders(classLoadersArray); if (!isOk(err)) return err; if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; @@ -71,7 +77,7 @@ jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobje } jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { - std::vector result; + std::vector result; jvmtiError err = getRetainedSizeByClassLoaders(classLoadersArray, result); if (!isOk(err)) { handleError(jvmti, err, "Could not estimate retained size by classloaders"); diff --git a/src/sizes/retained_size_by_classloaders.h b/src/sizes/retained_size_by_classloaders.h index c2abbec..f59f13a 100644 --- a/src/sizes/retained_size_by_classloaders.h +++ b/src/sizes/retained_size_by_classloaders.h @@ -15,8 +15,7 @@ class RetainedSizeByClassLoadersAction : public RetainedSizeAction { jlongArray executeOperation(jobjectArray classLoadersArray) override; jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result); jvmtiError tagRootLoadedClassesByClassLoaders(jobjectArray classLoadersArray); - jobjectArray getLoadedClassesByClassLoader(jobject classLoader); - jobjectArray filterLoadedClassesAsRoots(jobjectArray loadedClasses); + jobjectArray getFilteredRoots(jobject classLoader); }; jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); diff --git a/src/utils.cpp b/src/utils.cpp index 4626381..efa2339 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -265,6 +265,19 @@ jmethodID getIsAssignableFromMethod(JNIEnv *env) { return env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); } +jobject getClassLoader(JNIEnv *env, jobject obj) { + jclass objClass = env->GetObjectClass(obj); + jobject classObj = env->CallObjectMethod(obj, env->GetMethodID(objClass, "getClass", "()Ljava/lang/Class;")); + objClass = env->GetObjectClass(classObj); + return env->CallObjectMethod(obj, env->GetMethodID(objClass, "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); +} + std::string getToString(JNIEnv *env, jobject object) { jobject name = env->CallObjectMethod(object, env->GetMethodID(env->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;")); return jstringTostring(env, reinterpret_cast(name)); diff --git a/src/utils.h b/src/utils.h index 59f476a..5e17a0d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -73,6 +73,10 @@ std::string jstringTostring(JNIEnv *env, jstring jStr); jmethodID getIsAssignableFromMethod(JNIEnv *env); +jobject getClassLoader(JNIEnv *env, jobject obj); + +bool isEqual(JNIEnv *env, jobject obj1, jobject obj2); + std::string getToString(JNIEnv *env, jobject klass); template From c15a6afecdb5a71be72230ced0840a680087d127 Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Fri, 6 May 2022 22:02:07 +0300 Subject: [PATCH 04/11] remove unnecessary callback --- src/sizes/retained_size_by_classloaders.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/sizes/retained_size_by_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp index 60111af..4650eab 100644 --- a/src/sizes/retained_size_by_classloaders.cpp +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -11,26 +11,17 @@ jvmtiIterationControl JNICALL putTagCallback(jvmtiHeapRootKind root_kind, jlong size, jlong *tag_ptr, void *user_data) { - if (root_kind != JVMTI_HEAP_ROOT_JNI_GLOBAL && - root_kind != JVMTI_HEAP_ROOT_JNI_LOCAL) { + if (root_kind != JVMTI_HEAP_ROOT_JNI_GLOBAL && root_kind != JVMTI_HEAP_ROOT_JNI_LOCAL) { *tag_ptr = GC_ROOT_TAG; } return JVMTI_ITERATION_IGNORE; } -jvmtiIterationControl JNICALL removeTagCallback(jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) { - if (*tag_ptr == GC_ROOT_TAG) { - tag_ptr = nullptr; - } - return JVMTI_ITERATION_CONTINUE; -} - jobjectArray RetainedSizeByClassLoadersAction::getFilteredRoots(jobject classLoader) { jvmti->IterateOverReachableObjects(putTagCallback, NULL, NULL, NULL); jint nroots; jobject *roots; jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &nroots, &roots, NULL); - jvmti->IterateOverReachableObjects(removeTagCallback, NULL, NULL, NULL); removeAllTagsFromHeap(jvmti, nullptr); std::vector filteredRoots; @@ -38,7 +29,6 @@ jobjectArray RetainedSizeByClassLoadersAction::getFilteredRoots(jobject classLoa jobject rootClassLoader = getClassLoader(env, roots[i]); if (!env->IsSameObject(rootClassLoader, NULL)) { if (isEqual(env, classLoader, rootClassLoader)) { - std::cout << "HEY!!" << std::endl; filteredRoots.push_back(roots[i]); } } From 881e5b63975956c8ff9c11d48c55aecb94fab5f4 Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Fri, 13 May 2022 16:01:27 +0300 Subject: [PATCH 05/11] fix retained_size_by_classloaders --- src/sizes/retained_size_by_classloaders.cpp | 71 ++++++++++++--------- src/sizes/retained_size_by_classloaders.h | 5 +- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/sizes/retained_size_by_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp index 4650eab..4eb370e 100644 --- a/src/sizes/retained_size_by_classloaders.cpp +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -5,8 +5,9 @@ const jlong GC_ROOT_TAG = 17; +const jlong FILTERED_OBJECT = 15; -jvmtiIterationControl JNICALL putTagCallback(jvmtiHeapRootKind root_kind, +jvmtiIterationControl JNICALL tagRootsCallback(jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong *tag_ptr, @@ -17,53 +18,63 @@ jvmtiIterationControl JNICALL putTagCallback(jvmtiHeapRootKind root_kind, return JVMTI_ITERATION_IGNORE; } -jobjectArray RetainedSizeByClassLoadersAction::getFilteredRoots(jobject classLoader) { - jvmti->IterateOverReachableObjects(putTagCallback, NULL, NULL, NULL); - jint nroots; +jvmtiIterationControl JNICALL tagObjectCallback (jvmtiObjectReferenceKind reference_kind, + jlong class_tag, + jlong size, + jlong *tag_ptr, + jlong referrer_tag, + jint referrer_index, + void *user_data){ + *tag_ptr = FILTERED_OBJECT; + return JVMTI_ITERATION_CONTINUE; +} + +jvmtiError RetainedSizeByClassLoadersAction::getSizeByClassLoader(jobject classLoader, jlong *size) { + jvmtiError err = JVMTI_ERROR_NONE; + err = jvmti->IterateOverReachableObjects(tagRootsCallback, NULL, NULL, NULL); + jint rootsCount; jobject *roots; - jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &nroots, &roots, NULL); + err = jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &rootsCount, &roots, NULL); + if (!isOk(err)) return err; removeAllTagsFromHeap(jvmti, nullptr); - - std::vector filteredRoots; - for (jsize i = 0; i < nroots; i++) { + for (jsize i = 0; i < rootsCount; i++) { jobject rootClassLoader = getClassLoader(env, roots[i]); if (!env->IsSameObject(rootClassLoader, NULL)) { if (isEqual(env, classLoader, rootClassLoader)) { - filteredRoots.push_back(roots[i]); + err = jvmti->IterateOverObjectsReachableFromObject(roots[i], tagObjectCallback, NULL); + if (!isOk(err)) return err; } } } - return toJavaArray(env, filteredRoots); -} - -jvmtiError RetainedSizeByClassLoadersAction::tagRootLoadedClassesByClassLoaders(jobjectArray classLoadersArray) { - debug("tag classes loaded by classloaders"); - for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { - jobjectArray filteredRoots = getFilteredRoots(env->GetObjectArrayElement(classLoadersArray, i)); - jvmtiError err = createTagsForClasses(this->env, this->jvmti, filteredRoots); - if (err != JVMTI_ERROR_NONE) return err; + jint objectsCount; + jobject *objects; + err = jvmti->GetObjectsWithTags(1, &FILTERED_OBJECT, &objectsCount, &objects, NULL); + if (!isOk(err)) return err; + removeAllTagsFromHeap(jvmti, nullptr); + for (jsize i = 0; i < objectsCount; i++) { + jlong objectSize = 0; + err = jvmti->GetObjectSize(objects[i], &objectSize); + if (!isOk(err)) return err; + *size += objectSize; } - return IterateThroughHeap(0, nullptr, tagObjectOfTaggedClass, nullptr); + return err; } RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) - : RetainedSizeAction(env, jvmti, object) { + : RetainedSizeAction(env, jvmti, object), retainedSizeByObjects(env, jvmti, object) { } jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result) { - jvmtiError err = tagRootLoadedClassesByClassLoaders(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; - + jvmtiError err = JVMTI_ERROR_NONE; result.resize(env->GetArrayLength(classLoadersArray)); - return IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), - "calculate retained sizes"); + for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { + jlong res = 0; + jvmtiError err = getSizeByClassLoader(env->GetObjectArrayElement(classLoadersArray, i), &res); + result[i] = res; + } + return err; } jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { diff --git a/src/sizes/retained_size_by_classloaders.h b/src/sizes/retained_size_by_classloaders.h index f59f13a..bab32b9 100644 --- a/src/sizes/retained_size_by_classloaders.h +++ b/src/sizes/retained_size_by_classloaders.h @@ -5,17 +5,18 @@ #include "../memory_agent_action.h" #include "retained_size_action.h" +#include "retained_size_by_objects.h" class RetainedSizeByClassLoadersAction : public RetainedSizeAction { + RetainedSizeByObjectsAction retainedSizeByObjects; public: RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); private: jlongArray executeOperation(jobjectArray classLoadersArray) override; jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result); - jvmtiError tagRootLoadedClassesByClassLoaders(jobjectArray classLoadersArray); - jobjectArray getFilteredRoots(jobject classLoader); + jvmtiError getSizeByClassLoader(jobject classLoader, jlong *size); }; jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); From 3338af14ce4910ded0f9ee708961d8a6f5bf6a5d Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Sat, 14 May 2022 00:25:26 +0300 Subject: [PATCH 06/11] fix roots for classloaders --- src/sizes/retained_size_by_classloaders.cpp | 29 ++++++++------------- src/sizes/retained_size_by_classloaders.h | 4 +-- src/utils.cpp | 16 ++++++++---- src/utils.h | 4 ++- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/sizes/retained_size_by_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp index 4eb370e..d3267e0 100644 --- a/src/sizes/retained_size_by_classloaders.cpp +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -32,36 +32,29 @@ jvmtiIterationControl JNICALL tagObjectCallback (jvmtiObjectReferenceKind refere jvmtiError RetainedSizeByClassLoadersAction::getSizeByClassLoader(jobject classLoader, jlong *size) { jvmtiError err = JVMTI_ERROR_NONE; err = jvmti->IterateOverReachableObjects(tagRootsCallback, NULL, NULL, NULL); - jint rootsCount; - jobject *roots; - err = jvmti->GetObjectsWithTags(1, &GC_ROOT_TAG, &rootsCount, &roots, NULL); + jclass *classes; + jint cnt; + err = jvmti->GetLoadedClasses(&cnt, &classes); if (!isOk(err)) return err; removeAllTagsFromHeap(jvmti, nullptr); - for (jsize i = 0; i < rootsCount; i++) { - jobject rootClassLoader = getClassLoader(env, roots[i]); + 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)) { - err = jvmti->IterateOverObjectsReachableFromObject(roots[i], tagObjectCallback, NULL); - if (!isOk(err)) return err; + filteredClasses.emplace_back(classes[i]); } } } - jint objectsCount; - jobject *objects; - err = jvmti->GetObjectsWithTags(1, &FILTERED_OBJECT, &objectsCount, &objects, NULL); - if (!isOk(err)) return err; - removeAllTagsFromHeap(jvmti, nullptr); - for (jsize i = 0; i < objectsCount; i++) { - jlong objectSize = 0; - err = jvmti->GetObjectSize(objects[i], &objectSize); - if (!isOk(err)) return err; - *size += objectSize; + auto sizes = retainedSizeByClasses.run(toJavaArray(env, filteredClasses)); + for (jsize i = 0; i < env->GetArrayLength(sizes); i++) { + *size += reinterpret_cast(env->GetObjectArrayElement(sizes, i)); } return err; } RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) - : RetainedSizeAction(env, jvmti, object), retainedSizeByObjects(env, jvmti, object) { + : RetainedSizeAction(env, jvmti, object), retainedSizeByClasses(env, jvmti, object) { } diff --git a/src/sizes/retained_size_by_classloaders.h b/src/sizes/retained_size_by_classloaders.h index bab32b9..b46536d 100644 --- a/src/sizes/retained_size_by_classloaders.h +++ b/src/sizes/retained_size_by_classloaders.h @@ -5,11 +5,11 @@ #include "../memory_agent_action.h" #include "retained_size_action.h" -#include "retained_size_by_objects.h" +#include "retained_size_by_classes.h" class RetainedSizeByClassLoadersAction : public RetainedSizeAction { - RetainedSizeByObjectsAction retainedSizeByObjects; + RetainedSizeByClassesAction retainedSizeByClasses; public: RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); diff --git a/src/utils.cpp b/src/utils.cpp index efa2339..9a8ea2c 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); @@ -265,11 +274,8 @@ jmethodID getIsAssignableFromMethod(JNIEnv *env) { return env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); } -jobject getClassLoader(JNIEnv *env, jobject obj) { - jclass objClass = env->GetObjectClass(obj); - jobject classObj = env->CallObjectMethod(obj, env->GetMethodID(objClass, "getClass", "()Ljava/lang/Class;")); - objClass = env->GetObjectClass(classObj); - return env->CallObjectMethod(obj, env->GetMethodID(objClass, "getClassLoader", "()Ljava/lang/ClassLoader;")); +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){ diff --git a/src/utils.h b/src/utils.h index 5e17a0d..0b3e8df 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); @@ -73,7 +75,7 @@ std::string jstringTostring(JNIEnv *env, jstring jStr); jmethodID getIsAssignableFromMethod(JNIEnv *env); -jobject getClassLoader(JNIEnv *env, jobject obj); +jobject getClassLoader(JNIEnv *env, jclass obj); bool isEqual(JNIEnv *env, jobject obj1, jobject obj2); From a33d6b0053f886176beae6c2e9724860c4b47898 Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Sat, 21 May 2022 12:20:16 +0300 Subject: [PATCH 07/11] fix sizes --- src/sizes/retained_size_action.h | 28 +++ src/sizes/retained_size_by_classloaders.cpp | 36 +++- src/sizes/retained_size_by_classloaders.h | 3 +- src/utils.cpp | 191 +++++++++++------- src/utils.h | 2 + .../memory/agent/IdeaNativeAgentProxy.java | 2 +- .../intellij/memory/agent/MemoryAgent.java | 4 +- 7 files changed, 180 insertions(+), 86 deletions(-) diff --git a/src/sizes/retained_size_action.h b/src/sizes/retained_size_action.h index b142e53..0d826de 100644 --- a/src/sizes/retained_size_action.h +++ b/src/sizes/retained_size_action.h @@ -72,6 +72,26 @@ 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 = tagClassAndItsInheritors(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; + }); + std::cout << "TAGGED"<env, this->jvmti, classesArray); @@ -80,6 +100,14 @@ 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 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_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp index d3267e0..abc82f1 100644 --- a/src/sizes/retained_size_by_classloaders.cpp +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -29,7 +29,7 @@ jvmtiIterationControl JNICALL tagObjectCallback (jvmtiObjectReferenceKind refere return JVMTI_ITERATION_CONTINUE; } -jvmtiError RetainedSizeByClassLoadersAction::getSizeByClassLoader(jobject classLoader, jlong *size) { +jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jobject classLoader, jsize classLoaderIndex) { jvmtiError err = JVMTI_ERROR_NONE; err = jvmti->IterateOverReachableObjects(tagRootsCallback, NULL, NULL, NULL); jclass *classes; @@ -37,6 +37,7 @@ jvmtiError RetainedSizeByClassLoadersAction::getSizeByClassLoader(jobject classL err = jvmti->GetLoadedClasses(&cnt, &classes); if (!isOk(err)) return err; removeAllTagsFromHeap(jvmti, nullptr); + std::vector filteredClasses; for (jsize i = 0; i < cnt; i++) { jobject rootClassLoader = getClassLoader(env, classes[i]); @@ -46,35 +47,48 @@ jvmtiError RetainedSizeByClassLoadersAction::getSizeByClassLoader(jobject classL } } } - auto sizes = retainedSizeByClasses.run(toJavaArray(env, filteredClasses)); - for (jsize i = 0; i < env->GetArrayLength(sizes); i++) { - *size += reinterpret_cast(env->GetObjectArrayElement(sizes, i)); - } + removeAllTagsFromHeap(jvmti, nullptr); + std::cout << "GET CLASSES" << std::endl; + + err = tagObjectsOfClassLoaderClasses(toJavaArray(env, filteredClasses), classLoaderIndex); + std::cout << "TAGGED CLASSES" << std::endl; + if (!isOk(err)) return err; + if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; return err; } RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) - : RetainedSizeAction(env, jvmti, object), retainedSizeByClasses(env, jvmti, object) { + : RetainedSizeAction(env, jvmti, object) { } jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result) { jvmtiError err = JVMTI_ERROR_NONE; - result.resize(env->GetArrayLength(classLoadersArray)); for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { - jlong res = 0; - jvmtiError err = getSizeByClassLoader(env->GetObjectArrayElement(classLoadersArray, i), &res); - result[i] = res; + jvmtiError err = tagObjectsByClassLoader(env->GetObjectArrayElement(classLoadersArray, i), i); } + 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; + std::cout << "TAGGED HEAP" << std::endl; + + result.resize(env->GetArrayLength(classLoadersArray)); + return IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), + "calculate retained sizes"); return err; } jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { std::vector result; jvmtiError err = getRetainedSizeByClassLoaders(classLoadersArray, result); + std::cout << "GET SIZES" << std::endl; + removeAllTagsFromHeap(jvmti, nullptr); if (!isOk(err)) { - handleError(jvmti, err, "Could not estimate retained size by classloaders"); + handleError(jvmti, err, "Could not estimate retained size by classLoaders"); return env->NewLongArray(0); } return toJavaArray(env, result); diff --git a/src/sizes/retained_size_by_classloaders.h b/src/sizes/retained_size_by_classloaders.h index b46536d..9504174 100644 --- a/src/sizes/retained_size_by_classloaders.h +++ b/src/sizes/retained_size_by_classloaders.h @@ -9,14 +9,13 @@ class RetainedSizeByClassLoadersAction : public RetainedSizeAction { - RetainedSizeByClassesAction retainedSizeByClasses; public: RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); private: jlongArray executeOperation(jobjectArray classLoadersArray) override; jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result); - jvmtiError getSizeByClassLoader(jobject classLoader, jlong *size); + jvmtiError tagObjectsByClassLoader(jobject classLoader, jsize classLoaderIndex); }; jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); diff --git a/src/utils.cpp b/src/utils.cpp index 9a8ea2c..d23899f 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -9,38 +9,39 @@ #include "memory_agent_action.h" #include "log.h" -const char *getReferenceTypeDescription(jvmtiHeapReferenceKind kind) { - if (kind == JVMTI_HEAP_REFERENCE_CLASS) return "Reference from an object to its class."; - if (kind == JVMTI_HEAP_REFERENCE_FIELD) - return "Reference from an object to the value of one of its instance fields."; - if (kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) return "Reference from an array to one of its elements."; - if (kind == JVMTI_HEAP_REFERENCE_CLASS_LOADER) return "Reference from a class to its class loader."; - if (kind == JVMTI_HEAP_REFERENCE_SIGNERS) return "Reference from a class to its signers array."; - if (kind == JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN) return "Reference from a class to its protection domain."; - if (kind == JVMTI_HEAP_REFERENCE_INTERFACE) - return "Reference from a class to one of its interfaces. Note: interfaces are defined via a constant pool " - "reference, so the referenced interfaces may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_" - "POOL reference kind."; - if (kind == JVMTI_HEAP_REFERENCE_STATIC_FIELD) - return "Reference from a class to the value of one of its static fields."; - if (kind == JVMTI_HEAP_REFERENCE_CONSTANT_POOL) - return "Reference from a class to a resolved entry in the constant pool."; - if (kind == JVMTI_HEAP_REFERENCE_SUPERCLASS) - return "Reference from a class to its superclass. A callback is bot sent if the superclass is " - "java.lang.Object. Note: loaded classes define superclasses via a constant pool reference, " - "so the referenced superclass may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_POOL " - "reference kind."; - if (kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL) return "Heap root reference: JNI global reference."; - if (kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) return "Heap root reference: System class."; - if (kind == JVMTI_HEAP_REFERENCE_MONITOR) return "Heap root reference: monitor."; - if (kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL) return "Heap root reference: local variable on the stack."; - if (kind == JVMTI_HEAP_REFERENCE_JNI_LOCAL) return "Heap root reference: JNI local reference."; - if (kind == JVMTI_HEAP_REFERENCE_THREAD) return "Heap root reference: Thread."; - if (kind == JVMTI_HEAP_REFERENCE_OTHER) return "Heap root reference: other heap root reference."; - return "Unknown reference kind"; -} - -jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { +const char *getReferenceTypeDescription(jvmtiHeapReferenceKind +kind) { +if (kind == JVMTI_HEAP_REFERENCE_CLASS) return "Reference from an object to its class."; +if (kind == JVMTI_HEAP_REFERENCE_FIELD) +return "Reference from an object to the value of one of its instance fields."; +if (kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) return "Reference from an array to one of its elements."; +if (kind == JVMTI_HEAP_REFERENCE_CLASS_LOADER) return "Reference from a class to its class loader."; +if (kind == JVMTI_HEAP_REFERENCE_SIGNERS) return "Reference from a class to its signers array."; +if (kind == JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN) return "Reference from a class to its protection domain."; +if (kind == JVMTI_HEAP_REFERENCE_INTERFACE) +return "Reference from a class to one of its interfaces. Note: interfaces are defined via a constant pool " +"reference, so the referenced interfaces may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_" +"POOL reference kind."; +if (kind == JVMTI_HEAP_REFERENCE_STATIC_FIELD) +return "Reference from a class to the value of one of its static fields."; +if (kind == JVMTI_HEAP_REFERENCE_CONSTANT_POOL) +return "Reference from a class to a resolved entry in the constant pool."; +if (kind == JVMTI_HEAP_REFERENCE_SUPERCLASS) +return "Reference from a class to its superclass. A callback is bot sent if the superclass is " +"java.lang.Object. Note: loaded classes define superclasses via a constant pool reference, " +"so the referenced superclass may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_POOL " +"reference kind."; +if (kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL) return "Heap root reference: JNI global reference."; +if (kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) return "Heap root reference: System class."; +if (kind == JVMTI_HEAP_REFERENCE_MONITOR) return "Heap root reference: monitor."; +if (kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL) return "Heap root reference: local variable on the stack."; +if (kind == JVMTI_HEAP_REFERENCE_JNI_LOCAL) return "Heap root reference: JNI local reference."; +if (kind == JVMTI_HEAP_REFERENCE_THREAD) return "Heap root reference: Thread."; +if (kind == JVMTI_HEAP_REFERENCE_OTHER) return "Heap root reference: other heap root reference."; +return "Unknown reference kind"; +} + +jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { auto count = static_cast(objects.size()); jobjectArray res = env->NewObjectArray(count, env->FindClass("java/lang/Object"), nullptr); for (auto i = 0; i < count; ++i) { @@ -49,7 +50,7 @@ jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { return res; } -jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { +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) { @@ -58,21 +59,21 @@ jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { return res; } -jlongArray toJavaArray(JNIEnv *env, std::vector &items) { +jlongArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jlongArray result = env->NewLongArray(count); env->SetLongArrayRegion(result, 0, count, items.data()); return result; } -jintArray toJavaArray(JNIEnv *env, std::vector &items) { +jintArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jintArray result = env->NewIntArray(count); env->SetIntArrayRegion(result, 0, count, items.data()); return result; } -jbooleanArray toJavaArray(JNIEnv *env, std::vector &items) { +jbooleanArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jbooleanArray result = env->NewBooleanArray(count); env->SetBooleanArrayRegion(result, 0, count, items.data()); @@ -80,12 +81,12 @@ jbooleanArray toJavaArray(JNIEnv *env, std::vector &items) { } jintArray toJavaArray(JNIEnv *env, jint value) { - std::vector vector = {value}; + std::vector vector = {value}; return toJavaArray(env, vector); } jlongArray toJavaArray(JNIEnv *env, jlong value) { - std::vector vector = {value}; + std::vector vector = {value}; return toJavaArray(env, vector); } @@ -100,7 +101,7 @@ bool isOk(jvmtiError error) { return error == JVMTI_ERROR_NONE; } -void fromJavaArray(JNIEnv *env, jobjectArray javaArray, std::vector &result) { +void fromJavaArray(JNIEnv *env, jobjectArray javaArray, std::vector &result) { auto arrayLength = static_cast(env->GetArrayLength(javaArray)); result.resize(arrayLength); for (jsize i = 0; i < arrayLength; ++i) { @@ -108,8 +109,8 @@ void fromJavaArray(JNIEnv *env, jobjectArray javaArray, std::vector &re } } -std::vector fromJavaArray(JNIEnv *env, jobjectArray javaArray) { - std::vector result; +std::vector fromJavaArray(JNIEnv *env, jobjectArray javaArray) { + std::vector result; fromJavaArray(env, javaArray, result); return result; } @@ -131,24 +132,41 @@ void handleError(jvmtiEnv *jvmti, jvmtiError err, const char *message) { } } -typedef std::pair *, tagReleasedCallback> iterationInfo; +typedef std::pair *, tagReleasedCallback> +iterationInfo; -static jint JNICALL freeObjectCallback(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData) { - auto info = reinterpret_cast(userData); - jlong tagValue = *tagPtr; - *tagPtr = 0; - if (info->first->find(tagValue) == info->first->end() && info->second) { - info->second(tagValue); - } +static jint JNICALL +freeObjectCallback(jlong +classTag, +jlong size, jlong +*tagPtr, +jint length, +void *userData +) { +auto info = reinterpret_cast(userData); +jlong tagValue = *tagPtr; +* +tagPtr = 0; +if (info->first-> +find(tagValue) +== info->first-> + +end() && + +info->second) { +info-> +second(tagValue); +} - return JVMTI_ITERATION_CONTINUE; +return +JVMTI_ITERATION_CONTINUE; } -jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, tagReleasedCallback callback) { +jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, tagReleasedCallback callback) { jvmtiHeapCallbacks cb; std::memset(&cb, 0, sizeof(jvmtiHeapCallbacks)); cb.heap_iteration_callback = freeObjectCallback; - std::set ignoredSet(ignoredTags.begin(), ignoredTags.end()); + std::set ignoredSet(ignoredTags.begin(), ignoredTags.end()); iterationInfo userData(&ignoredSet, callback); debug("remove tags"); jvmtiError err = jvmti->IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, &cb, &userData); @@ -156,7 +174,9 @@ jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, tag return err; } -static jvmtiError collectObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, jint &objectsCount, jobject **objects, jlong **objectsTags) { +static jvmtiError +collectObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, jint &objectsCount, jobject **objects, + jlong **objectsTags) { auto tagsCount = static_cast(tags.size()); debug("call GetObjectsWithTags"); @@ -175,10 +195,10 @@ static jvmtiError deallocateArrays(jvmtiEnv *jvmti, jobject *objects, jlong *obj return err; } -jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector &result) { +jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector &result) { jint objectsCount; jobject *objects; - jlong *objectsTags; + jlong * objectsTags; jvmtiError err = collectObjectsByTags(jvmti, tags, objectsCount, &objects, &objectsTags); if (!isOk(err)) return err; @@ -190,14 +210,15 @@ jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vect return deallocateArrays(jvmti, objects, objectsTags); } -jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector &result) { +jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector &result) { return getObjectsByTags(jvmti, tags, result); } -jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector> &result) { +jvmtiError +getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector > &result) { jint objectsCount = 0; jobject *objects; - jlong *objectsTags; + jlong * objectsTags; jvmtiError err = collectObjectsByTags(jvmti, tags, objectsCount, &objects, &objectsTags); if (!isOk(err)) return err; @@ -209,13 +230,14 @@ jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vect return deallocateArrays(jvmti, objects, objectsTags); } -jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector> &result) { +jvmtiError +getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector > &result) { return getObjectsByTags(jvmti, tags, result); } -jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, - std::vector> &result, tagReleasedCallback callback) { - std::set uniqueTags(tags.begin(), tags.end()); +jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, + std::vector > &result, tagReleasedCallback callback) { + std::set uniqueTags(tags.begin(), tags.end()); removeTagsFromHeap(jvmti, uniqueTags, callback); tags.assign(uniqueTags.begin(), uniqueTags.end()); @@ -223,7 +245,7 @@ jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tag } jvmtiError removeAllTagsFromHeap(jvmtiEnv *jvmti, tagReleasedCallback callback) { - std::set ignored; + std::set ignored; return removeTagsFromHeap(jvmti, ignored, callback); } @@ -243,28 +265,55 @@ std::string jstringTostring(JNIEnv *env, jstring jStr) { } -jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag) { +jvmtiError +tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag) { jclass *classes; jint cnt; jvmtiError err = jvmti->GetLoadedClasses(&cnt, &classes); + std::cout << "get loaded classes" << std::endl; if (err != JVMTI_ERROR_NONE) return err; jclass langClass = env->FindClass("java/lang/Class"); jmethodID isAssignableFrom = env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); + std::cout << "get method: " << isAssignableFrom << " " << classObject /*<< " " << getToString(env, classObject)*/ + << std::endl; for (int i = 0; i < cnt; i++) { - if (env->CallBooleanMethod(classObject, isAssignableFrom, classes[i])) { + if (classes[i] != NULL && env->CallBooleanMethod(classObject, isAssignableFrom, classes[i])) { + std::cout << "true" << std::endl; jlong oldTag; err = jvmti->GetTag(classes[i], &oldTag); + std::cout << "get tag" << std::endl; if (err != JVMTI_ERROR_NONE) return err; jlong newTag = createTag(oldTag); - if (newTag != 0) { + std::cout << "create tag" << std::endl; + if (newTag != 0) { err = jvmti->SetTag(classes[i], newTag); + std::cout << "set tag" << std::endl; if (err != JVMTI_ERROR_NONE) return err; } } } + std::cout << "finish" << std::endl; + + return err; +} + +jvmtiError tagClassAndItsInheritorsSimple(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject) { + jclass *classes; + jint cnt; + jvmtiError err = jvmti->GetLoadedClasses(&cnt, &classes); + if (err != JVMTI_ERROR_NONE) return err; + + jclass langClass = env->FindClass("java/lang/Class"); + jmethodID isAssignableFrom = env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); + + for (int i = 0; i < cnt; i++) { + if (env->CallBooleanMethod(classObject, isAssignableFrom, classes[i])) { + err = jvmti->SetTag(classes[i], 25); + } + } return err; } @@ -275,17 +324,19 @@ jmethodID getIsAssignableFromMethod(JNIEnv *env) { } jobject getClassLoader(JNIEnv *env, jclass clazz) { - return env->CallObjectMethod(clazz, env->GetMethodID(env->GetObjectClass(clazz), "getClassLoader", "()Ljava/lang/ClassLoader;")); + return env->CallObjectMethod(clazz, env->GetMethodID(env->GetObjectClass(clazz), "getClassLoader", + "()Ljava/lang/ClassLoader;")); } -bool isEqual(JNIEnv *env, jobject obj, jobject otherObj){ +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); } std::string getToString(JNIEnv *env, jobject object) { - jobject name = env->CallObjectMethod(object, env->GetMethodID(env->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;")); + jobject name = env->CallObjectMethod(object, env->GetMethodID(env->FindClass("java/lang/Object"), "toString", + "()Ljava/lang/String;")); return jstringTostring(env, reinterpret_cast(name)); } @@ -330,7 +381,7 @@ ThreadSuspender::ThreadSuspender(jvmtiEnv *jvmti) : jvmti(jvmti) { } ThreadSuspender::~ThreadSuspender() { - for (jthread thread : suspendedThreads) { + for (jthread thread: suspendedThreads) { jvmtiError err = jvmti->ResumeThread(thread); if (!isOk(err)) { handleError(jvmti, err, "Failed to resume thread"); diff --git a/src/utils.h b/src/utils.h index 0b3e8df..554bd9d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -67,6 +67,8 @@ jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tag jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag); +jvmtiError tagClassAndItsInheritorsSimple(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject); + bool isOk(jvmtiError error); bool fileExists(const std::string &fileName); 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 fc0fc18..dd57b3a 100644 --- a/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java +++ b/test_data/proxy/src/com/intellij/memory/agent/IdeaNativeAgentProxy.java @@ -40,7 +40,7 @@ public IdeaNativeAgentProxy(Object cancellationFileName, Object progressFileName public native Object[] getRetainedSizeByClasses(Object[] classes); - public native Object[] getRetainedSizeByClassLoaders(ClassLoader[] classLoaders); + public native Object[] getRetainedSizeByClassLoaders(ClassLoader[] classloaders); public native Object[] getShallowAndRetainedSizeByClasses(Object[] classes); 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 b793a87..ac79663 100644 --- a/test_data/proxy/src/com/intellij/memory/agent/MemoryAgent.java +++ b/test_data/proxy/src/com/intellij/memory/agent/MemoryAgent.java @@ -101,8 +101,8 @@ public synchronized T[] getAllReachableObjects(Object startObject, Class * TODO */ @SuppressWarnings("unchecked") - public synchronized long[] getRetainedSizeByClassloaders(ClassLoader[] classLoaders) throws MemoryAgentExecutionException { - return (long [])getResult(callProxyMethod(() -> proxy.getRetainedSizeByClassLoaders(classLoaders))); + public synchronized long[] getRetainedSizeByClassLoaders(ClassLoader[] classloaders) throws MemoryAgentExecutionException { + return (long [])getResult(callProxyMethod(() -> proxy.getRetainedSizeByClassLoaders(classloaders))); } /** From 17a208e8e5dad997eb7be5c159d19a0da43d91be Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Sun, 22 May 2022 22:57:43 +0300 Subject: [PATCH 08/11] fix calculating --- src/sizes/retained_size_action.h | 4 +- src/sizes/retained_size_by_classloaders.cpp | 34 -------- src/utils.cpp | 87 ++++----------------- src/utils.h | 2 +- 4 files changed, 17 insertions(+), 110 deletions(-) diff --git a/src/sizes/retained_size_action.h b/src/sizes/retained_size_action.h index 0d826de..86d0a0c 100644 --- a/src/sizes/retained_size_action.h +++ b/src/sizes/retained_size_action.h @@ -75,7 +75,7 @@ class RetainedSizeAction : public MemoryAgentAction { 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 = tagClassAndItsInheritors(env, jvmti, classObject, [classLoaderIndex](jlong oldTag) -> jlong { + jvmtiError err = tagClass(env, jvmti, classObject, [classLoaderIndex](jlong oldTag) -> jlong { ClassTag *classTag = tagToClassTagPointer(oldTag); if (classTag != nullptr) { classTag->ids.push_back(classLoaderIndex); @@ -85,10 +85,8 @@ class RetainedSizeAction : public MemoryAgentAction { return 0; }); - std::cout << "TAGGED"<IterateOverReachableObjects(tagRootsCallback, NULL, NULL, NULL); jclass *classes; jint cnt; err = jvmti->GetLoadedClasses(&cnt, &classes); if (!isOk(err)) return err; - removeAllTagsFromHeap(jvmti, nullptr); std::vector filteredClasses; for (jsize i = 0; i < cnt; i++) { @@ -47,11 +20,8 @@ jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jobject cla } } } - removeAllTagsFromHeap(jvmti, nullptr); - std::cout << "GET CLASSES" << std::endl; err = tagObjectsOfClassLoaderClasses(toJavaArray(env, filteredClasses), classLoaderIndex); - std::cout << "TAGGED CLASSES" << std::endl; if (!isOk(err)) return err; if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; return err; @@ -74,19 +44,15 @@ jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobje err = tagHeap(); if (!isOk(err)) return err; if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; - std::cout << "TAGGED HEAP" << std::endl; result.resize(env->GetArrayLength(classLoadersArray)); return IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), "calculate retained sizes"); - return err; } jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { std::vector result; jvmtiError err = getRetainedSizeByClassLoaders(classLoadersArray, result); - std::cout << "GET SIZES" << std::endl; - removeAllTagsFromHeap(jvmti, nullptr); if (!isOk(err)) { handleError(jvmti, err, "Could not estimate retained size by classLoaders"); return env->NewLongArray(0); diff --git a/src/utils.cpp b/src/utils.cpp index d23899f..c001ec1 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -135,31 +135,14 @@ void handleError(jvmtiEnv *jvmti, jvmtiError err, const char *message) { typedef std::pair *, tagReleasedCallback> iterationInfo; -static jint JNICALL -freeObjectCallback(jlong -classTag, -jlong size, jlong -*tagPtr, -jint length, -void *userData -) { -auto info = reinterpret_cast(userData); -jlong tagValue = *tagPtr; -* -tagPtr = 0; -if (info->first-> -find(tagValue) -== info->first-> - -end() && - -info->second) { -info-> -second(tagValue); -} - -return -JVMTI_ITERATION_CONTINUE; +static jint JNICALL freeObjectCallback(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData) { + auto info = reinterpret_cast(userData); + jlong tagValue = *tagPtr; + *tagPtr = 0; + if (info->first->find(tagValue)== info->first->end() &&info->second) { + info->second(tagValue); + } + return JVMTI_ITERATION_CONTINUE; } jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, tagReleasedCallback callback) { @@ -265,56 +248,16 @@ std::string jstringTostring(JNIEnv *env, jstring jStr) { } -jvmtiError -tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag) { - jclass *classes; - jint cnt; - jvmtiError err = jvmti->GetLoadedClasses(&cnt, &classes); - std::cout << "get loaded classes" << std::endl; - if (err != JVMTI_ERROR_NONE) return err; - - jclass langClass = env->FindClass("java/lang/Class"); - jmethodID isAssignableFrom = env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); - std::cout << "get method: " << isAssignableFrom << " " << classObject /*<< " " << getToString(env, classObject)*/ - << std::endl; - - for (int i = 0; i < cnt; i++) { - if (classes[i] != NULL && env->CallBooleanMethod(classObject, isAssignableFrom, classes[i])) { - std::cout << "true" << std::endl; - jlong oldTag; - err = jvmti->GetTag(classes[i], &oldTag); - std::cout << "get tag" << std::endl; - if (err != JVMTI_ERROR_NONE) return err; - - jlong newTag = createTag(oldTag); - std::cout << "create tag" << std::endl; - if (newTag != 0) { - err = jvmti->SetTag(classes[i], newTag); - std::cout << "set tag" << std::endl; - if (err != JVMTI_ERROR_NONE) return err; - } - } - } - std::cout << "finish" << std::endl; - - return err; -} - -jvmtiError tagClassAndItsInheritorsSimple(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject) { - jclass *classes; - jint cnt; - jvmtiError err = jvmti->GetLoadedClasses(&cnt, &classes); +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; - jclass langClass = env->FindClass("java/lang/Class"); - jmethodID isAssignableFrom = env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); - - for (int i = 0; i < cnt; i++) { - if (env->CallBooleanMethod(classObject, isAssignableFrom, classes[i])) { - err = jvmti->SetTag(classes[i], 25); - } + jlong newTag = createTag(oldTag); + if (newTag != 0) { + err = jvmti->SetTag(classObject, newTag); + if (err != JVMTI_ERROR_NONE) return err; } - return err; } diff --git a/src/utils.h b/src/utils.h index 554bd9d..f94dcba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -67,7 +67,7 @@ jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tag jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag); -jvmtiError tagClassAndItsInheritorsSimple(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject); +jvmtiError tagClass(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag); bool isOk(jvmtiError error); From a99104a48316f88b6d306afd2e4f3eec4e2aea3b Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Sun, 22 May 2022 23:04:54 +0300 Subject: [PATCH 09/11] fix formating --- src/utils.cpp | 157 ++++++++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 69 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index c001ec1..91dc317 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -9,39 +9,38 @@ #include "memory_agent_action.h" #include "log.h" -const char *getReferenceTypeDescription(jvmtiHeapReferenceKind -kind) { -if (kind == JVMTI_HEAP_REFERENCE_CLASS) return "Reference from an object to its class."; -if (kind == JVMTI_HEAP_REFERENCE_FIELD) -return "Reference from an object to the value of one of its instance fields."; -if (kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) return "Reference from an array to one of its elements."; -if (kind == JVMTI_HEAP_REFERENCE_CLASS_LOADER) return "Reference from a class to its class loader."; -if (kind == JVMTI_HEAP_REFERENCE_SIGNERS) return "Reference from a class to its signers array."; -if (kind == JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN) return "Reference from a class to its protection domain."; -if (kind == JVMTI_HEAP_REFERENCE_INTERFACE) -return "Reference from a class to one of its interfaces. Note: interfaces are defined via a constant pool " -"reference, so the referenced interfaces may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_" -"POOL reference kind."; -if (kind == JVMTI_HEAP_REFERENCE_STATIC_FIELD) -return "Reference from a class to the value of one of its static fields."; -if (kind == JVMTI_HEAP_REFERENCE_CONSTANT_POOL) -return "Reference from a class to a resolved entry in the constant pool."; -if (kind == JVMTI_HEAP_REFERENCE_SUPERCLASS) -return "Reference from a class to its superclass. A callback is bot sent if the superclass is " -"java.lang.Object. Note: loaded classes define superclasses via a constant pool reference, " -"so the referenced superclass may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_POOL " -"reference kind."; -if (kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL) return "Heap root reference: JNI global reference."; -if (kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) return "Heap root reference: System class."; -if (kind == JVMTI_HEAP_REFERENCE_MONITOR) return "Heap root reference: monitor."; -if (kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL) return "Heap root reference: local variable on the stack."; -if (kind == JVMTI_HEAP_REFERENCE_JNI_LOCAL) return "Heap root reference: JNI local reference."; -if (kind == JVMTI_HEAP_REFERENCE_THREAD) return "Heap root reference: Thread."; -if (kind == JVMTI_HEAP_REFERENCE_OTHER) return "Heap root reference: other heap root reference."; -return "Unknown reference kind"; -} - -jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { +const char *getReferenceTypeDescription(jvmtiHeapReferenceKind kind) { + if (kind == JVMTI_HEAP_REFERENCE_CLASS) return "Reference from an object to its class."; + if (kind == JVMTI_HEAP_REFERENCE_FIELD) + return "Reference from an object to the value of one of its instance fields."; + if (kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) return "Reference from an array to one of its elements."; + if (kind == JVMTI_HEAP_REFERENCE_CLASS_LOADER) return "Reference from a class to its class loader."; + if (kind == JVMTI_HEAP_REFERENCE_SIGNERS) return "Reference from a class to its signers array."; + if (kind == JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN) return "Reference from a class to its protection domain."; + if (kind == JVMTI_HEAP_REFERENCE_INTERFACE) + return "Reference from a class to one of its interfaces. Note: interfaces are defined via a constant pool " + "reference, so the referenced interfaces may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_" + "POOL reference kind."; + if (kind == JVMTI_HEAP_REFERENCE_STATIC_FIELD) + return "Reference from a class to the value of one of its static fields."; + if (kind == JVMTI_HEAP_REFERENCE_CONSTANT_POOL) + return "Reference from a class to a resolved entry in the constant pool."; + if (kind == JVMTI_HEAP_REFERENCE_SUPERCLASS) + return "Reference from a class to its superclass. A callback is bot sent if the superclass is " + "java.lang.Object. Note: loaded classes define superclasses via a constant pool reference, " + "so the referenced superclass may also be reported with a JVMTI_HEAP_REFERENCE_CONSTANT_POOL " + "reference kind."; + if (kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL) return "Heap root reference: JNI global reference."; + if (kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) return "Heap root reference: System class."; + if (kind == JVMTI_HEAP_REFERENCE_MONITOR) return "Heap root reference: monitor."; + if (kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL) return "Heap root reference: local variable on the stack."; + if (kind == JVMTI_HEAP_REFERENCE_JNI_LOCAL) return "Heap root reference: JNI local reference."; + if (kind == JVMTI_HEAP_REFERENCE_THREAD) return "Heap root reference: Thread."; + if (kind == JVMTI_HEAP_REFERENCE_OTHER) return "Heap root reference: other heap root reference."; + return "Unknown reference kind"; +} + +jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { auto count = static_cast(objects.size()); jobjectArray res = env->NewObjectArray(count, env->FindClass("java/lang/Object"), nullptr); for (auto i = 0; i < count; ++i) { @@ -59,21 +58,21 @@ jobjectArray toJavaArray(JNIEnv *env, std::vector &objects) { return res; } -jlongArray toJavaArray(JNIEnv *env, std::vector &items) { +jlongArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jlongArray result = env->NewLongArray(count); env->SetLongArrayRegion(result, 0, count, items.data()); return result; } -jintArray toJavaArray(JNIEnv *env, std::vector &items) { +jintArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jintArray result = env->NewIntArray(count); env->SetIntArrayRegion(result, 0, count, items.data()); return result; } -jbooleanArray toJavaArray(JNIEnv *env, std::vector &items) { +jbooleanArray toJavaArray(JNIEnv *env, std::vector &items) { auto count = static_cast(items.size()); jbooleanArray result = env->NewBooleanArray(count); env->SetBooleanArrayRegion(result, 0, count, items.data()); @@ -81,12 +80,12 @@ jbooleanArray toJavaArray(JNIEnv *env, std::vector &items) { } jintArray toJavaArray(JNIEnv *env, jint value) { - std::vector vector = {value}; + std::vector vector = {value}; return toJavaArray(env, vector); } jlongArray toJavaArray(JNIEnv *env, jlong value) { - std::vector vector = {value}; + std::vector vector = {value}; return toJavaArray(env, vector); } @@ -101,7 +100,7 @@ bool isOk(jvmtiError error) { return error == JVMTI_ERROR_NONE; } -void fromJavaArray(JNIEnv *env, jobjectArray javaArray, std::vector &result) { +void fromJavaArray(JNIEnv *env, jobjectArray javaArray, std::vector &result) { auto arrayLength = static_cast(env->GetArrayLength(javaArray)); result.resize(arrayLength); for (jsize i = 0; i < arrayLength; ++i) { @@ -109,8 +108,8 @@ void fromJavaArray(JNIEnv *env, jobjectArray javaArray, std::vector &r } } -std::vector fromJavaArray(JNIEnv *env, jobjectArray javaArray) { - std::vector result; +std::vector fromJavaArray(JNIEnv *env, jobjectArray javaArray) { + std::vector result; fromJavaArray(env, javaArray, result); return result; } @@ -132,24 +131,24 @@ void handleError(jvmtiEnv *jvmti, jvmtiError err, const char *message) { } } -typedef std::pair *, tagReleasedCallback> -iterationInfo; +typedef std::pair *, tagReleasedCallback> iterationInfo; static jint JNICALL freeObjectCallback(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData) { auto info = reinterpret_cast(userData); jlong tagValue = *tagPtr; *tagPtr = 0; - if (info->first->find(tagValue)== info->first->end() &&info->second) { + if (info->first->find(tagValue) == info->first->end() && info->second) { info->second(tagValue); } + return JVMTI_ITERATION_CONTINUE; } -jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, tagReleasedCallback callback) { +jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, tagReleasedCallback callback) { jvmtiHeapCallbacks cb; std::memset(&cb, 0, sizeof(jvmtiHeapCallbacks)); cb.heap_iteration_callback = freeObjectCallback; - std::set ignoredSet(ignoredTags.begin(), ignoredTags.end()); + std::set ignoredSet(ignoredTags.begin(), ignoredTags.end()); iterationInfo userData(&ignoredSet, callback); debug("remove tags"); jvmtiError err = jvmti->IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, &cb, &userData); @@ -157,9 +156,7 @@ jvmtiError removeTagsFromHeap(jvmtiEnv *jvmti, std::set &ignoredTags, ta return err; } -static jvmtiError -collectObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, jint &objectsCount, jobject **objects, - jlong **objectsTags) { +static jvmtiError collectObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, jint &objectsCount, jobject **objects, jlong **objectsTags) { auto tagsCount = static_cast(tags.size()); debug("call GetObjectsWithTags"); @@ -178,10 +175,10 @@ static jvmtiError deallocateArrays(jvmtiEnv *jvmti, jobject *objects, jlong *obj return err; } -jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector &result) { +jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector &result) { jint objectsCount; jobject *objects; - jlong * objectsTags; + jlong *objectsTags; jvmtiError err = collectObjectsByTags(jvmti, tags, objectsCount, &objects, &objectsTags); if (!isOk(err)) return err; @@ -193,15 +190,14 @@ jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vec return deallocateArrays(jvmti, objects, objectsTags); } -jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector &result) { +jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector &result) { return getObjectsByTags(jvmti, tags, result); } -jvmtiError -getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector > &result) { +jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector> &result) { jint objectsCount = 0; jobject *objects; - jlong * objectsTags; + jlong *objectsTags; jvmtiError err = collectObjectsByTags(jvmti, tags, objectsCount, &objects, &objectsTags); if (!isOk(err)) return err; @@ -213,14 +209,13 @@ getObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, std::vector &&tags, std::vector > &result) { +jvmtiError getObjectsByTags(jvmtiEnv *jvmti, std::vector &&tags, std::vector> &result) { return getObjectsByTags(jvmti, tags, result); } -jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, - std::vector > &result, tagReleasedCallback callback) { - std::set uniqueTags(tags.begin(), tags.end()); +jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &tags, + std::vector> &result, tagReleasedCallback callback) { + std::set uniqueTags(tags.begin(), tags.end()); removeTagsFromHeap(jvmti, uniqueTags, callback); tags.assign(uniqueTags.begin(), uniqueTags.end()); @@ -228,7 +223,7 @@ jvmtiError cleanHeapAndGetObjectsByTags(jvmtiEnv *jvmti, std::vector &ta } jvmtiError removeAllTagsFromHeap(jvmtiEnv *jvmti, tagReleasedCallback callback) { - std::set ignored; + std::set ignored; return removeTagsFromHeap(jvmti, ignored, callback); } @@ -248,11 +243,36 @@ std::string jstringTostring(JNIEnv *env, jstring jStr) { } +jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function &&createTag) { + jclass *classes; + jint cnt; + jvmtiError err = jvmti->GetLoadedClasses(&cnt, &classes); + if (err != JVMTI_ERROR_NONE) return err; + + jclass langClass = env->FindClass("java/lang/Class"); + jmethodID isAssignableFrom = env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); + + for (int i = 0; i < cnt; i++) { + if (env->CallBooleanMethod(classObject, isAssignableFrom, classes[i])) { + jlong oldTag; + err = jvmti->GetTag(classes[i], &oldTag); + if (err != JVMTI_ERROR_NONE) return err; + + jlong newTag = createTag(oldTag); + if (newTag != 0) { + err = jvmti->SetTag(classes[i], newTag); + if (err != JVMTI_ERROR_NONE) return err; + } + } + } + + 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); @@ -266,6 +286,11 @@ jmethodID getIsAssignableFromMethod(JNIEnv *env) { return env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); } +std::string getToString(JNIEnv *env, jobject object) { + jobject name = env->CallObjectMethod(object, env->GetMethodID(env->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;")); + 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;")); @@ -277,12 +302,6 @@ bool isEqual(JNIEnv *env, jobject obj, jobject otherObj) { return env->CallBooleanMethod(obj, equalsID, otherObj); } -std::string getToString(JNIEnv *env, jobject object) { - jobject name = env->CallObjectMethod(object, env->GetMethodID(env->FindClass("java/lang/Object"), "toString", - "()Ljava/lang/String;")); - return jstringTostring(env, reinterpret_cast(name)); -} - ThreadSuspender::ThreadSuspender(jvmtiEnv *jvmti) : jvmti(jvmti) { jint threadCnt; jthread *threads; @@ -324,7 +343,7 @@ ThreadSuspender::ThreadSuspender(jvmtiEnv *jvmti) : jvmti(jvmti) { } ThreadSuspender::~ThreadSuspender() { - for (jthread thread: suspendedThreads) { + for (jthread thread : suspendedThreads) { jvmtiError err = jvmti->ResumeThread(thread); if (!isOk(err)) { handleError(jvmti, err, "Failed to resume thread"); From 000ef331543088ef0b68ecd4492d2bd22f6bd68b Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Sat, 28 May 2022 12:38:23 +0300 Subject: [PATCH 10/11] add count of other classes size --- src/sizes/retained_size_by_classes.cpp | 5 ++ src/sizes/retained_size_by_classes.h | 1 + src/sizes/retained_size_by_classloaders.cpp | 63 +++++++++++++++++---- src/sizes/retained_size_by_classloaders.h | 4 +- 4 files changed, 61 insertions(+), 12 deletions(-) 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(jobject classLoader, jsize classLoaderIndex) { +jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jclass *classes, jint* cnt, jobject classLoader, jsize classLoaderIndex) { jvmtiError err = JVMTI_ERROR_NONE; - jclass *classes; - jint cnt; - err = jvmti->GetLoadedClasses(&cnt, &classes); - if (!isOk(err)) return err; - std::vector filteredClasses; - for (jsize i = 0; i < cnt; i++) { + for (jsize i = 0; i < *cnt; i++) { jobject rootClassLoader = getClassLoader(env, classes[i]); if (!env->IsSameObject(rootClassLoader, NULL)) { if (isEqual(env, classLoader, rootClassLoader)) { @@ -27,6 +24,39 @@ jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jobject cla 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) { @@ -35,9 +65,15 @@ RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result) { jvmtiError err = JVMTI_ERROR_NONE; + jclass *classes; + jint cnt; + err = jvmti->GetLoadedClasses(&cnt, &classes); + std::cout << cnt << std::endl; for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { - jvmtiError err = tagObjectsByClassLoader(env->GetObjectArrayElement(classLoadersArray, i), 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; @@ -45,9 +81,10 @@ jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobje if (!isOk(err)) return err; if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; - result.resize(env->GetArrayLength(classLoadersArray)); - return IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), - "calculate retained sizes"); + result.resize(env->GetArrayLength(classLoadersArray) + 1); + err = IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), + "calculate retained size"); + return err; } jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { @@ -57,5 +94,9 @@ jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray class 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: " << std::accumulate(result.begin(), result.end()-1, 0) < { private: jlongArray executeOperation(jobjectArray classLoadersArray) override; jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector &result); - jvmtiError tagObjectsByClassLoader(jobject classLoader, jsize classLoaderIndex); + jvmtiError tagObjectsByClassLoader(jclass *classes, jint* cnt, jobject classLoader, jsize classLoaderIndex); + jlong getHeapSize(); + jlong tagOtherObjects(jclass *classes, jint* cnt, jobjectArray classLoadersArray); }; jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); From 29289a05175e481f133de05025c87bf967819635 Mon Sep 17 00:00:00 2001 From: Karina5005 Date: Mon, 20 Jun 2022 12:22:43 +0300 Subject: [PATCH 11/11] add checker --- CMakeLists.txt | 2 + src/sizes/retained_size_action.cpp | 10 ++ src/sizes/retained_size_action.h | 20 ++++ src/sizes/retained_size_by_classloaders.cpp | 64 ++++++++++- src/sizes/retained_size_by_classloaders.h | 6 + src/sizes/retained_size_by_objects_merged.cpp | 104 ++++++++++++++++++ src/sizes/retained_size_by_objects_merged.h | 27 +++++ 7 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 src/sizes/retained_size_by_objects_merged.cpp create mode 100644 src/sizes/retained_size_by_objects_merged.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d5c3957..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 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 86d0a0c..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); @@ -90,6 +91,17 @@ class RetainedSizeAction : public MemoryAgentAction { 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); @@ -106,6 +118,14 @@ class RetainedSizeAction : public MemoryAgentAction { 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_classloaders.cpp b/src/sizes/retained_size_by_classloaders.cpp index c480f5a..acf0e8c 100644 --- a/src/sizes/retained_size_by_classloaders.cpp +++ b/src/sizes/retained_size_by_classloaders.cpp @@ -24,6 +24,28 @@ jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jclass *cla 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; @@ -58,7 +80,7 @@ jlong RetainedSizeByClassLoadersAction::getHeapSize(){ } RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) - : RetainedSizeAction(env, jvmti, object) { + : RetainedSizeAction(env, jvmti, object), retainedSizeByObjectsMergedAction(env, jvmti, object), retainedSizeByObjectsAction(env, jvmti, object) { } @@ -68,7 +90,6 @@ jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobje jclass *classes; jint cnt; err = jvmti->GetLoadedClasses(&cnt, &classes); - std::cout << cnt << std::endl; for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { jvmtiError err = tagObjectsByClassLoader(classes, &cnt, env->GetObjectArrayElement(classLoadersArray, i), i); } @@ -87,15 +108,50 @@ jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobje 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: " << std::accumulate(result.begin(), result.end()-1, 0) < { 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); }; 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