diff --git a/build.gradle b/build.gradle index c2cb0fd..6e5ab81 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,7 @@ dependencies { implementation 'org.ow2.asm:asm-analysis:9.7.1' implementation 'org.ow2.asm:asm-util:9.7.1' implementation 'org.ow2.asm:asm-tree:9.7.1' + implementation 'net.bytebuddy:byte-buddy:1.17.6' implementation 'org.jsoup:jsoup:1.17.2' diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/.suo b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/.suo new file mode 100644 index 0000000..0d29bf9 Binary files /dev/null and b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/.suo differ diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db new file mode 100644 index 0000000..895bd0a Binary files /dev/null and b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db differ diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db-shm b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db-shm new file mode 100644 index 0000000..e02962a Binary files /dev/null and b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db-shm differ diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db-wal b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.db-wal new file mode 100644 index 0000000..e69de29 diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.opendb b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.opendb new file mode 100644 index 0000000..5d9f9da Binary files /dev/null and b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/Browse.VC.opendb differ diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/DocumentLayout.json b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/DocumentLayout.json new file mode 100644 index 0000000..302e03c --- /dev/null +++ b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/DocumentLayout.json @@ -0,0 +1,91 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{3C9F4D1A-8483-4E0A-94FB-7E75617957A9}|GetInstance\\GetInstance.vcxproj|C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\org_tzd_debug_GetInstance.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}", + "RelativeMoniker": "D:0:0:{3C9F4D1A-8483-4E0A-94FB-7E75617957A9}|GetInstance\\GetInstance.vcxproj|solutionrelative:GetInstance\\org_tzd_debug_GetInstance.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}" + }, + { + "AbsoluteMoniker": "D:0:0:{3C9F4D1A-8483-4E0A-94FB-7E75617957A9}|GetInstance\\GetInstance.vcxproj|C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\org_tzd_debug_GetInstance.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}", + "RelativeMoniker": "D:0:0:{3C9F4D1A-8483-4E0A-94FB-7E75617957A9}|GetInstance\\GetInstance.vcxproj|solutionrelative:GetInstance\\org_tzd_debug_GetInstance.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}" + }, + { + "AbsoluteMoniker": "D:0:0:{3C9F4D1A-8483-4E0A-94FB-7E75617957A9}|GetInstance\\GetInstance.vcxproj|C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\dllmain.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}", + "RelativeMoniker": "D:0:0:{3C9F4D1A-8483-4E0A-94FB-7E75617957A9}|GetInstance\\GetInstance.vcxproj|solutionrelative:GetInstance\\dllmain.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 6, + "Children": [ + { + "$type": "Bookmark", + "Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{1c64b9c2-e352-428e-a56d-0ace190b99a6}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}" + }, + { + "$type": "Bookmark", + "Name": "ST:1:0:{e8b06f52-6d01-11d2-aa7d-00c04f990343}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{d3750d8a-574b-4fb3-b7e2-aa8af40e8231}" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "org_tzd_debug_GetInstance.h", + "DocumentMoniker": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\org_tzd_debug_GetInstance.h", + "RelativeDocumentMoniker": "GetInstance\\org_tzd_debug_GetInstance.h", + "ToolTip": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\org_tzd_debug_GetInstance.h", + "RelativeToolTip": "GetInstance\\org_tzd_debug_GetInstance.h", + "ViewState": "AQIAAAEAAAAAAAAAAAAcwA4AAAAxAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|", + "WhenOpened": "2025-06-26T12:47:16.975Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "org_tzd_debug_GetInstance.cpp", + "DocumentMoniker": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\org_tzd_debug_GetInstance.cpp", + "RelativeDocumentMoniker": "GetInstance\\org_tzd_debug_GetInstance.cpp", + "ToolTip": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\org_tzd_debug_GetInstance.cpp*", + "RelativeToolTip": "GetInstance\\org_tzd_debug_GetInstance.cpp*", + "ViewState": "AQIAAAAAAAAAAAAAAAAAAA0AAAALAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|", + "WhenOpened": "2025-06-26T12:42:02.251Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "dllmain.cpp", + "DocumentMoniker": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\dllmain.cpp", + "RelativeDocumentMoniker": "GetInstance\\dllmain.cpp", + "ToolTip": "C:\\Users\\Administrator\\source\\repos\\GetInstance\\GetInstance\\dllmain.cpp", + "RelativeToolTip": "GetInstance\\dllmain.cpp", + "ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|", + "WhenOpened": "2025-06-26T12:39:18.259Z", + "EditorCaption": "" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/Cpp/GetInstance/.vs/GetInstance/v17/ipch/71f1c34df6139ca1.ipch b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/ipch/71f1c34df6139ca1.ipch new file mode 100644 index 0000000..187fe26 Binary files /dev/null and b/src/main/Cpp/GetInstance/.vs/GetInstance/v17/ipch/71f1c34df6139ca1.ipch differ diff --git a/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj b/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj new file mode 100644 index 0000000..a15e3fb --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {3c9f4d1a-8483-4e0a-94fb-7e75617957a9} + GetInstance + 10.0.20348.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + C:\Users\Administrator\.jdks\corretto-20.0.2.1\include\win32;C:\Users\Administrator\.jdks\corretto-20.0.2.1\include;$(IncludePath) + + + + Level3 + true + WIN32;_DEBUG;GETINSTANCE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;GETINSTANCE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;GETINSTANCE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;GETINSTANCE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj.filters b/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj.filters new file mode 100644 index 0000000..32f20b0 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj.user b/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj.user new file mode 100644 index 0000000..5df420f --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/GetInstance.vcxproj.user @@ -0,0 +1,6 @@ + + + + false + + \ No newline at end of file diff --git a/src/main/Cpp/GetInstance/GetInstance/dllmain.cpp b/src/main/Cpp/GetInstance/GetInstance/dllmain.cpp new file mode 100644 index 0000000..daed8c8 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : 定义 DLL 应用程序的入口点。 +#include "pch.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/src/main/Cpp/GetInstance/GetInstance/framework.h b/src/main/Cpp/GetInstance/GetInstance/framework.h new file mode 100644 index 0000000..80cbbc9 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 +// Windows 头文件 +#include diff --git a/src/main/Cpp/GetInstance/GetInstance/org_tzd_debug_GetInstance.cpp b/src/main/Cpp/GetInstance/GetInstance/org_tzd_debug_GetInstance.cpp new file mode 100644 index 0000000..12bb17e --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/org_tzd_debug_GetInstance.cpp @@ -0,0 +1,85 @@ +#include "pch.h" + +#include +#include +#include + +// JVMTIصΪÿʵñǩ +static jvmtiIterationControl JNICALL +objectInstanceCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) { + *tag_ptr = 1; // Ϊʵù̶ǩ1 + return JVMTI_ITERATION_CONTINUE; +} + +extern "C" JNIEXPORT jobjectArray JNICALL +Java_org_tzd_debug_GetInstance_getInstance(JNIEnv* env, jclass clazz, jclass targetClazz) { + // ȡJVM + JavaVM* vm; + env->GetJavaVM(&vm); + + // ȡJVMTI + jvmtiEnv* jvmti = nullptr; + jint result = vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0); + if (result != JNI_OK || jvmti == nullptr) { + env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), + "Failed to get JVMTI environment"); + return nullptr; + } + + // ӱǩ + jvmtiCapabilities capabilities = { 0 }; + capabilities.can_tag_objects = 1; + jvmtiError error = jvmti->AddCapabilities(&capabilities); + if (error != JVMTI_ERROR_NONE) { + env->ThrowNew(env->FindClass("java/lang/UnsupportedOperationException"), + "JVM doesn't support object tagging"); + return nullptr; + } + + // ʵñǩ + error = jvmti->IterateOverInstancesOfClass( + targetClazz, + JVMTI_HEAP_OBJECT_EITHER, + objectInstanceCallback, + nullptr + ); + if (error != JVMTI_ERROR_NONE) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "IterateOverInstancesOfClass failed"); + return nullptr; + } + + // ȡǩĶ + jlong tag = 1; + jint count = 0; + jobject* instances = nullptr; + error = jvmti->GetObjectsWithTags( + 1, + &tag, + &count, + &instances, + nullptr + ); + if (error != JVMTI_ERROR_NONE) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "GetObjectsWithTags failed"); + return nullptr; + } + + // + jobjectArray resultArray = env->NewObjectArray(count, targetClazz, nullptr); + if (resultArray == nullptr) { + jvmti->Deallocate((unsigned char*)instances); + return nullptr; + } + + // + for (int i = 0; i < count; i++) { + env->SetObjectArrayElement(resultArray, i, instances[i]); + } + + // ͷJVMTIڴ + jvmti->Deallocate((unsigned char*)instances); + + return resultArray; +} \ No newline at end of file diff --git a/src/main/Cpp/GetInstance/GetInstance/org_tzd_debug_GetInstance.h b/src/main/Cpp/GetInstance/GetInstance/org_tzd_debug_GetInstance.h new file mode 100644 index 0000000..54c7fc1 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/org_tzd_debug_GetInstance.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_tzd_debug_GetInstance */ + +#ifndef _Included_org_tzd_debug_GetInstance +#define _Included_org_tzd_debug_GetInstance +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_tzd_debug_GetInstance + * Method: getInstance + * Signature: (Ljava/lang/Class;)[Ljava/lang/Object; + */ +JNIEXPORT jobjectArray JNICALL Java_org_tzd_debug_GetInstance_getInstance + (JNIEnv *, jclass, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/main/Cpp/GetInstance/GetInstance/pch.cpp b/src/main/Cpp/GetInstance/GetInstance/pch.cpp new file mode 100644 index 0000000..b6fb8f4 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: 与预编译标头对应的源文件 + +#include "pch.h" + +// 当使用预编译的头时,需要使用此源文件,编译才能成功。 diff --git a/src/main/Cpp/GetInstance/GetInstance/pch.h b/src/main/Cpp/GetInstance/GetInstance/pch.h new file mode 100644 index 0000000..9660927 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/pch.h @@ -0,0 +1,13 @@ +// pch.h: 这是预编译标头文件。 +// 下方列出的文件仅编译一次,提高了将来生成的生成性能。 +// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 +// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 +// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 + +#ifndef PCH_H +#define PCH_H + +// 添加要在此处预编译的标头 +#include "framework.h" + +#endif //PCH_H diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.dll.recipe b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.dll.recipe new file mode 100644 index 0000000..5c342db --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.dll.recipe @@ -0,0 +1,11 @@ + + + + + C:\Users\Administrator\source\repos\GetInstance\x64\Release\GetInstance.dll + + + + + + \ No newline at end of file diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.iobj b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.iobj new file mode 100644 index 0000000..c0d8820 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.iobj differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.ipdb b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.ipdb new file mode 100644 index 0000000..1d5530c Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.ipdb differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.log b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.log new file mode 100644 index 0000000..6d35435 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.log @@ -0,0 +1,7 @@ + org_tzd_debug_GetInstance.cpp + 正在创建库 C:\Users\Administrator\source\repos\GetInstance\x64\Release\GetInstance.lib 和对象 C:\Users\Administrator\source\repos\GetInstance\x64\Release\GetInstance.exp + 正在生成代码 + Previous IPDB and IOBJ mismatch, fall back to full compilation. + All 13 functions were compiled because no usable IPDB/IOBJ from previous compilation was found. + 已完成代码的生成 + GetInstance.vcxproj -> C:\Users\Administrator\source\repos\GetInstance\x64\Release\GetInstance.dll diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.pch b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.pch new file mode 100644 index 0000000..a3a7d94 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.pch differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.command.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.command.1.tlog new file mode 100644 index 0000000..e6c7be8 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.command.1.tlog differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.read.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.read.1.tlog new file mode 100644 index 0000000..53e8335 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.read.1.tlog differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.write.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.write.1.tlog new file mode 100644 index 0000000..f92149c Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/CL.write.1.tlog differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/Cl.items.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/Cl.items.tlog new file mode 100644 index 0000000..468b70f --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/Cl.items.tlog @@ -0,0 +1,3 @@ +C:\Users\Administrator\source\repos\GetInstance\GetInstance\dllmain.cpp;C:\Users\Administrator\source\repos\GetInstance\GetInstance\x64\Release\dllmain.obj +C:\Users\Administrator\source\repos\GetInstance\GetInstance\org_tzd_debug_GetInstance.cpp;C:\Users\Administrator\source\repos\GetInstance\GetInstance\x64\Release\org_tzd_debug_GetInstance.obj +C:\Users\Administrator\source\repos\GetInstance\GetInstance\pch.cpp;C:\Users\Administrator\source\repos\GetInstance\GetInstance\x64\Release\pch.obj diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/GetInstance.lastbuildstate b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/GetInstance.lastbuildstate new file mode 100644 index 0000000..7c748f5 --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/GetInstance.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.40.33807:TargetPlatformVersion=10.0.20348.0: +Release|x64|C:\Users\Administrator\source\repos\GetInstance\| diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.command.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.command.1.tlog new file mode 100644 index 0000000..efcc79f Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.command.1.tlog differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.read.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.read.1.tlog new file mode 100644 index 0000000..3dcd982 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.read.1.tlog differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.secondary.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.secondary.1.tlog new file mode 100644 index 0000000..29675df --- /dev/null +++ b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.secondary.1.tlog @@ -0,0 +1,5 @@ +^C:\USERS\ADMINISTRATOR\SOURCE\REPOS\GETINSTANCE\GETINSTANCE\X64\RELEASE\DLLMAIN.OBJ|C:\USERS\ADMINISTRATOR\SOURCE\REPOS\GETINSTANCE\GETINSTANCE\X64\RELEASE\ORG_TZD_DEBUG_GETINSTANCE.OBJ|C:\USERS\ADMINISTRATOR\SOURCE\REPOS\GETINSTANCE\GETINSTANCE\X64\RELEASE\PCH.OBJ +C:\Users\Administrator\source\repos\GetInstance\x64\Release\GetInstance.lib +C:\Users\Administrator\source\repos\GetInstance\x64\Release\GetInstance.EXP +C:\Users\Administrator\source\repos\GetInstance\GetInstance\x64\Release\GetInstance.IPDB +C:\Users\Administrator\source\repos\GetInstance\GetInstance\x64\Release\GetInstance.iobj diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.write.1.tlog b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.write.1.tlog new file mode 100644 index 0000000..0d90297 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/GetInstance.tlog/link.write.1.tlog differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/dllmain.obj b/src/main/Cpp/GetInstance/GetInstance/x64/Release/dllmain.obj new file mode 100644 index 0000000..7a58883 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/dllmain.obj differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/org_tzd_debug_GetInstance.obj b/src/main/Cpp/GetInstance/GetInstance/x64/Release/org_tzd_debug_GetInstance.obj new file mode 100644 index 0000000..fde85d9 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/org_tzd_debug_GetInstance.obj differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/pch.obj b/src/main/Cpp/GetInstance/GetInstance/x64/Release/pch.obj new file mode 100644 index 0000000..10f7302 Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/pch.obj differ diff --git a/src/main/Cpp/GetInstance/GetInstance/x64/Release/vc143.pdb b/src/main/Cpp/GetInstance/GetInstance/x64/Release/vc143.pdb new file mode 100644 index 0000000..d51983d Binary files /dev/null and b/src/main/Cpp/GetInstance/GetInstance/x64/Release/vc143.pdb differ diff --git a/src/main/Cpp/GetInstance/x64/Release/GetInstance.dll b/src/main/Cpp/GetInstance/x64/Release/GetInstance.dll new file mode 100644 index 0000000..0379cc8 Binary files /dev/null and b/src/main/Cpp/GetInstance/x64/Release/GetInstance.dll differ diff --git a/src/main/Cpp/GetInstance/x64/Release/GetInstance.exp b/src/main/Cpp/GetInstance/x64/Release/GetInstance.exp new file mode 100644 index 0000000..9949376 Binary files /dev/null and b/src/main/Cpp/GetInstance/x64/Release/GetInstance.exp differ diff --git a/src/main/Cpp/GetInstance/x64/Release/GetInstance.lib b/src/main/Cpp/GetInstance/x64/Release/GetInstance.lib new file mode 100644 index 0000000..33c4e8d Binary files /dev/null and b/src/main/Cpp/GetInstance/x64/Release/GetInstance.lib differ diff --git a/src/main/Cpp/GetInstance/x64/Release/GetInstance.pdb b/src/main/Cpp/GetInstance/x64/Release/GetInstance.pdb new file mode 100644 index 0000000..d19e54b Binary files /dev/null and b/src/main/Cpp/GetInstance/x64/Release/GetInstance.pdb differ diff --git a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java index 86916d5..ecda107 100644 --- a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java +++ b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java @@ -1,6 +1,5 @@ package com.axis.innovators.box; -import com.axis.innovators.box.decompilation.gui.ModernJarViewer; import com.axis.innovators.box.events.GlobalEventBus; import com.axis.innovators.box.events.OpenFileEvents; import com.axis.innovators.box.events.StartupEvent; @@ -8,10 +7,10 @@ import com.axis.innovators.box.gui.*; import com.axis.innovators.box.plugins.PluginDescriptor; import com.axis.innovators.box.plugins.PluginLoader; import com.axis.innovators.box.plugins.PluginPyLoader; +import com.axis.innovators.box.register.LanguageManager; import com.axis.innovators.box.register.RegistrationSettingsItem; import com.axis.innovators.box.register.RegistrationTool; import com.axis.innovators.box.register.RegistrationTopic; -import com.axis.innovators.box.register.LanguageManager; import com.axis.innovators.box.tools.*; import com.axis.innovators.box.util.PythonResult; import com.axis.innovators.box.util.Tray; @@ -27,19 +26,20 @@ import org.apache.logging.log4j.core.appender.RollingFileAppender; import org.apache.logging.log4j.core.config.Configuration; import org.api.dog.agent.VirtualMachine; import org.jetbrains.annotations.NotNull; -import org.tzd.lm.LM; import javax.swing.*; import java.awt.*; -import java.awt.event.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.*; import java.lang.instrument.Instrumentation; import java.lang.management.*; -import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; -import java.util.*; import java.util.List; +import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -72,9 +72,20 @@ public class AxisInnovatorsBox { private final List windowsJDialogList = new ArrayList<>(); private final StateManager stateManager = new StateManager(); private UserTags userTags; + private final boolean isDebug; + private static DebugWindow debugWindow; - public AxisInnovatorsBox(String[] args) { + public AxisInnovatorsBox(String[] args, boolean isDebug) { this.args = args; + this.isDebug = isDebug; + } + + /** + * 获取调试状态 + * @return 调试状态 + */ + public boolean isDebugEnvironment(){ + return isDebug; } @@ -82,7 +93,7 @@ public class AxisInnovatorsBox { try { LibraryLoad.loadLibrary("FridaNative"); LibraryLoad.loadLibrary("ThrowSafely"); - LibraryLoad.loadLibrary("DogAgent"); + LibraryLoad.loadLibrary("DogAgent"); } catch (Exception e) { logger.error("Failed to load the 'FridaNative' library", e); } @@ -325,21 +336,8 @@ public class AxisInnovatorsBox { // 播放错误音效的方法 private void playErrorSound() { try { - // 使用系统默认的错误音效 Toolkit.getDefaultToolkit().beep(); - - // 或者播放自定义音效 - /* - File soundFile = new File("error_sound.wav"); - if (soundFile.exists()) { - AudioInputStream audioIn = AudioSystem.getAudioInputStream(soundFile); - Clip clip = AudioSystem.getClip(); - clip.open(audioIn); - clip.start(); - } - */ - } catch (Exception ex) { - // 忽略音效播放错误 + } catch (Exception ignored) { } } @@ -357,10 +355,8 @@ public class AxisInnovatorsBox { private void processAppender(ZipOutputStream zos, Set addedFiles, Appender appender) throws IOException { - if (appender instanceof FileAppender) { - FileAppender fileAppender = (FileAppender) appender; + if (appender instanceof FileAppender fileAppender) { String fileName = fileAppender.getFileName(); - if (fileName != null && !addedFiles.contains(fileName)) { addFileToZip(zos, new File(fileName), "logs/"); addedFiles.add(fileName); @@ -410,7 +406,9 @@ public class AxisInnovatorsBox { filePattern = filePattern.substring(lastSlash + 1); } int patternStart = filePattern.indexOf('%'); - if (patternStart == -1) patternStart = filePattern.indexOf('$'); + if (patternStart == -1) { + patternStart = filePattern.indexOf('$'); + } if (patternStart > 0) { baseName = filePattern.substring(0, patternStart); @@ -531,7 +529,9 @@ public class AxisInnovatorsBox { } private String formatMemory(long bytes) { - if (bytes < 1024) return bytes + " B"; + if (bytes < 1024) { + return bytes + " B"; + } int exp = (int) (Math.log(bytes) / Math.log(1024)); char unit = "KMGTPE".charAt(exp - 1); return String.format("%.1f %sB", bytes / Math.pow(1024, exp), unit); @@ -601,22 +601,10 @@ public class AxisInnovatorsBox { return tempFile; } - // 添加目录到ZIP(递归) - private void addDirectoryToZip(ZipOutputStream zos, File dir, String basePath) throws IOException { - if (!dir.exists() || !dir.isDirectory()) return; - - for (File file : dir.listFiles()) { - String entryPath = basePath + "/" + file.getName(); - if (file.isDirectory()) { - addDirectoryToZip(zos, file, entryPath); - } else { - addFileToZip(zos, file, entryPath); - } - } - } - private void addFileToZip(ZipOutputStream zos, File file, String entryPath) throws IOException { - if (!file.exists()) return; + if (!file.exists()) { + return; + } String entryName = entryPath + file.getName(); ZipEntry zipEntry = new ZipEntry(entryName); @@ -755,7 +743,7 @@ public class AxisInnovatorsBox { * @param windowsJDialog 窗口 */ public boolean isWindowStartup(WindowsJDialog windowsJDialog) { - return windowsJDialogList.contains(windowsJDialog); + return !windowsJDialogList.contains(windowsJDialog); } /** @@ -790,8 +778,34 @@ public class AxisInnovatorsBox { isWindow = true; } - public static void run(String[] args) { - main = new AxisInnovatorsBox(args); + private void createDebugWindow() { + debugWindow = new DebugWindow(); + + // 关键配置:设置为独立窗口,不受模态对话框影响 + //debugWindow.setAlwaysOnTop(true); + //debugWindow.setFocusableWindowState(true); + //debugWindow.setFocusable(true); + + // 当主窗口关闭时关闭调试窗口 + ex.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + debugWindow.dispose(); + } + }); + + ex.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + debugWindow.setVisible(true); + } + }); + + debugWindow.setVisible(true); + } + + public static void run(String[] args, boolean isDebug) { + main = new AxisInnovatorsBox(args,isDebug); try { main.initLog4j2(); main.setTopic(); @@ -828,7 +842,15 @@ public class AxisInnovatorsBox { SwingUtilities.invokeLater(() -> { try { - main.ex = new MainWindow(); + main.ex = new MainWindow(){ + @Override + public void setVisible(boolean b) { + if (debugWindow != null) { + debugWindow.setVisible(b); + } + super.setVisible(b); + } + }; GlobalEventBus.EVENT_BUS.post(new StartupEvent(main)); PluginPyLoader.getLoadedPlugins().forEach((pluginId, pyPluginDescriptor) -> { if (pyPluginDescriptor.getLastResult().getInterpreter() != null){ @@ -843,8 +865,9 @@ public class AxisInnovatorsBox { main.runWindow(); } catch (Exception e) { logger.error("There was a problem starting the main thread", e); - if (main.ex != null) + if (main.ex != null) { main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } main.organizingCrashReports(e); throw new RuntimeException(e); } @@ -860,8 +883,9 @@ public class AxisInnovatorsBox { } catch (Exception e) { logger.error("Failed to load plugins", e); - if (main.ex != null) + if (main.ex != null) { main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } main.organizingCrashReports(e); throw new RuntimeException(e); } @@ -870,8 +894,9 @@ public class AxisInnovatorsBox { main.thread.start(); } catch (Exception e) { logger.error("In unexpected errors", e); - if (main.ex != null) + if (main.ex != null) { main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } main.organizingCrashReports(e); throw new RuntimeException(e); } @@ -894,6 +919,10 @@ public class AxisInnovatorsBox { ex.initUI(); isWindow = true; ex.setVisible(true); + + if (isDebug) { + SwingUtilities.invokeLater(this::createDebugWindow); + } } /** diff --git a/src/main/java/com/axis/innovators/box/Main.java b/src/main/java/com/axis/innovators/box/Main.java index 21c9533..41c87b4 100644 --- a/src/main/java/com/axis/innovators/box/Main.java +++ b/src/main/java/com/axis/innovators/box/Main.java @@ -1,12 +1,11 @@ package com.axis.innovators.box; import com.axis.innovators.box.browser.MainApplication; -import com.axis.innovators.box.browser.WindowRegistry; import com.axis.innovators.box.decompilation.gui.ModernJarViewer; +import com.axis.innovators.box.register.LanguageManager; import com.axis.innovators.box.tools.ArgsParser; import com.axis.innovators.box.tools.FolderCleaner; import com.axis.innovators.box.tools.FolderCreator; -import com.axis.innovators.box.register.LanguageManager; import javax.swing.*; import java.io.File; @@ -46,6 +45,20 @@ public class Main { LanguageManager.loadLanguage("system:zh_CN"); } + // 检查是否包含调试控制台参数 + boolean debugWindowEnabled = false; + for (int i = 0; i < args.length; i++) { + if ("-debugControlWindow-on".equals(args[i])) { + debugWindowEnabled = true; + // 移除此参数避免干扰后续处理 + String[] newArgs = new String[args.length - 1]; + System.arraycopy(args, 0, newArgs, 0, i); + System.arraycopy(args, i + 1, newArgs, i, args.length - i - 1); + args = newArgs; + break; + } + } + List> validFiles = ArgsParser.parseArgs(args); for (Map fileInfo : validFiles) { String extension = fileInfo.get("extension"); @@ -66,12 +79,12 @@ public class Main { if (".html".equals(extension)) { MainApplication.popupHTMLWindow(path); - releaseLock(); // 释放锁(窗口模式) + releaseLock(); return; } } - AxisInnovatorsBox.run(args); + AxisInnovatorsBox.run(args, debugWindowEnabled); } /** diff --git a/src/main/java/com/axis/innovators/box/gui/DebugWindow.java b/src/main/java/com/axis/innovators/box/gui/DebugWindow.java new file mode 100644 index 0000000..be16a25 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/DebugWindow.java @@ -0,0 +1,1986 @@ +package com.axis.innovators.box.gui; + +import com.axis.innovators.box.AxisInnovatorsBox; +import org.tzd.debug.ClassDebug; +import org.tzd.debug.MethodDebug; +import org.tzd.debug.MethodDebug.MethodMonitorCallback; + +import javax.swing.Timer; +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeWillExpandListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableRowSorter; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; +import java.awt.*; +import java.awt.event.*; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author tzdwindows 7 + */ +public class DebugWindow extends JFrame { + static final Font MAIN_FONT = getPreferredFont(Font.SANS_SERIF, "Microsoft YaHei UI", "SimSun"); + static final Font MONOSPACE_FONT = getPreferredFont(Font.MONOSPACED, "Microsoft YaHei Mono", "SimSun"); + + // 颜色方案保持不变 + private static final Color BACKGROUND = new Color(45, 45, 48); + private static final Color FOREGROUND = new Color(241, 241, 241); + private static final Color ACCENT = new Color(0, 122, 204); + private static final Color TABLE_HEADER = new Color(37, 37, 38); + private static final Color TABLE_ROW = new Color(30, 30, 30); + private static final Color TABLE_ALT_ROW = new Color(35, 35, 35); + + // 组件声明 + private final JTabbedPane tabbedPane = new JTabbedPane(); + private final ClassTreePanel classTreePanel = new ClassTreePanel(); + private final MethodTablePanel methodTablePanel = new MethodTablePanel(); + private final ThreadMonitorPanel threadMonitorPanel = new ThreadMonitorPanel(); + private final MemoryAnalysisPanel memoryAnalysisPanel = new MemoryAnalysisPanel(); + private final ConsolePanel consolePanel = new ConsolePanel(); + private final Map methodDetailCache = new HashMap<>(); + + // 获取最佳支持中文的字体 + private static Font getPreferredFont(String... fontNames) { + for (String name : fontNames) { + Font font = new Font(name, Font.PLAIN, 13); + if (font.canDisplayUpTo("中文") == -1) { + return font; + } + } + return new Font(Font.SANS_SERIF, Font.PLAIN, 13); + } + + public DebugWindow() { + super("高级调试器"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + configureUI(); + setupTabs(); + setupMonitoring(); + setSize(1000, 700); + setLocationRelativeTo(null); + setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); + } + + private void configureUI() { + UIManager.put("TabbedPane.background", BACKGROUND); + UIManager.put("TabbedPane.foreground", FOREGROUND); + UIManager.put("TabbedPane.selected", ACCENT); + UIManager.put("TabbedPane.unselectedBackground", TABLE_HEADER); + UIManager.put("TabbedPane.borderHightlightColor", ACCENT); + + JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setBackground(BACKGROUND); + contentPane.setBorder(new EmptyBorder(10, 10, 10, 10)); + setContentPane(contentPane); + } + + private void setupTabs() { + tabbedPane.setFont(MAIN_FONT); + tabbedPane.addTab("类监控", classTreePanel); + tabbedPane.addTab("方法追踪", methodTablePanel); + tabbedPane.addTab("线程监控", threadMonitorPanel); + tabbedPane.addTab("内存分析", memoryAnalysisPanel); + tabbedPane.addTab("控制台", consolePanel); + getContentPane().add(tabbedPane, BorderLayout.CENTER); + } + + private void setupMonitoring() { + // 启动类加载监控(使用后台线程) + //new Thread(() -> { + // try { + // ClassDebug.monitoringLoading(new ClassDebug.LoadingMonitor() { + // @Override + // public void load(ClassLoader loader, String className, Class classBeingRedefined, + // ProtectionDomain protectionDomain, byte[] classfileBuffer) { + // // 添加安全检查 + // if (className == null || className.isEmpty() || className.startsWith("java/") || + // className.startsWith("sun/") || className.startsWith("jdk/")) { + // return; + // } +// + // SwingUtilities.invokeLater(() -> { + // classTreePanel.addClass(className); + // }); + // } + // }); + // } catch (Throwable t) { + // System.err.println("类监控初始化失败: " + t.getMessage()); + // t.printStackTrace(); + // } + //}, "ClassMonitor-Thread").start(); + + // 启动方法监控(使用后台线程) + new Thread(() -> { + try { + MethodDebug.addCallback(new MethodMonitorCallback() { + @Override + public void onMethodEnter(String className, String methodName, Object[] args) { + // 过滤系统方法 + //if (className.startsWith("java.") || className.startsWith("sun.") || + // className.startsWith("jdk.")) { + // return; + //} + methodTablePanel.addMethodCall(className, methodName, args, true, null, null, 0); + } + + @Override + public void onMethodExit(String className, String methodName, Object returnValue, + Throwable exception, long durationNanos) { + // 过滤系统方法 + //if (className.startsWith("java.") || className.startsWith("sun.") || + // className.startsWith("jdk.")) { + // return; + //} + methodTablePanel.addMethodCall(className, methodName, null, false, returnValue, exception, durationNanos); + } + }); + + } catch (Throwable t) { + System.err.println("方法监控初始化失败: " + t.getMessage()); + //t.printStackTrace(); + } + }, "MethodMonitor-Thread").start(); + + MethodDebug.load(); + } + + // 类树面板(添加延迟加载) + class ClassTreePanel extends JPanel { + private final JTree classTree; + private final DefaultMutableTreeNode rootNode; + private final Map packageNodes = new HashMap<>(); + private final JTextField searchField = new JTextField(20); + private final JProgressBar progressBar = new JProgressBar(); + private final Map classDetailCache = new HashMap<>(); + + public ClassTreePanel() { + super(new BorderLayout()); + setBackground(BACKGROUND); + + rootNode = new DefaultMutableTreeNode("已加载的类"); + classTree = new JTree(rootNode); + classTree.setFont(MAIN_FONT); + classTree.setBackground(BACKGROUND); + classTree.setForeground(FOREGROUND); + classTree.setRowHeight(25); + classTree.setCellRenderer(new ClassTreeCellRenderer()); + + // 添加树展开监听器实现延迟加载 + classTree.addTreeWillExpandListener(new TreeWillExpandListener() { + @Override + public void treeWillExpand(TreeExpansionEvent event) { + Object node = event.getPath().getLastPathComponent(); + if (node instanceof DefaultMutableTreeNode treeNode) { + // 检查并移除虚拟节点 + if (treeNode.getChildCount() == 1) { + DefaultMutableTreeNode firstChild = (DefaultMutableTreeNode) treeNode.getFirstChild(); + if ("加载中...".equals(firstChild.getUserObject())) { + treeNode.remove(0); + } + } + // 加载实际类 + if (treeNode.getChildCount() == 0 && treeNode.getUserObject() instanceof String packageName) { + loadPackageClasses(packageName, treeNode); + } + } + } + + @Override + public void treeWillCollapse(TreeExpansionEvent event) {} + }); + + JScrollPane scrollPane = new JScrollPane(classTree); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + scrollPane.getViewport().setBackground(BACKGROUND); + + // 搜索面板 + JPanel searchPanel = new JPanel(new BorderLayout(10, 0)); + searchPanel.setBackground(TABLE_HEADER); + searchPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + searchPanel.add(new JLabel("搜索类:"), BorderLayout.WEST); + searchPanel.add(searchField, BorderLayout.CENTER); + + JPanel rightPanel = new JPanel(new BorderLayout()); + rightPanel.add(progressBar, BorderLayout.CENTER); + JButton refreshBtn = new JButton("刷新"); + rightPanel.add(refreshBtn, BorderLayout.EAST); + searchPanel.add(rightPanel, BorderLayout.EAST); + + add(searchPanel, BorderLayout.NORTH); + add(scrollPane, BorderLayout.CENTER); + + // 事件监听 + classTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + TreePath path = classTree.getPathForLocation(e.getX(), e.getY()); + if (path != null) { + Object node = path.getLastPathComponent(); + if (node instanceof DefaultMutableTreeNode) { + Object userObject = ((DefaultMutableTreeNode) node).getUserObject(); + if (userObject instanceof String) { + openClassDetails((String) userObject); + } + } + } + } + } + }); + + searchField.getDocument().addDocumentListener(new DocumentListener() { + @Override public void insertUpdate(DocumentEvent e) { filterTree(); } + @Override public void removeUpdate(DocumentEvent e) { filterTree(); } + @Override public void changedUpdate(DocumentEvent e) { filterTree(); } + }); + + refreshBtn.addActionListener(e -> refreshClassTree()); + refreshBtn.setBackground(ACCENT); + refreshBtn.setForeground(FOREGROUND); + refreshBtn.setFocusPainted(false); + + progressBar.setVisible(false); + progressBar.setIndeterminate(true); + + // 创建后立即自动刷新 + refreshClassTree(); + } + + // 延迟加载包中的类 + private void loadPackageClasses(String packageName, DefaultMutableTreeNode packageNode) { + new SwingWorker() { + @Override + protected Void doInBackground() { + try { + Class[] classes = ClassDebug.getLoadedClasses(); + Arrays.stream(classes) + .map(Class::getName) + .filter(name -> name.startsWith(packageName + ".")) + .sorted() + .forEach(this::publish); + } catch (Throwable t) { + System.err.println("加载包类失败: " + t.getMessage()); + t.printStackTrace(); + } + return null; + } + + @Override + protected void process(List chunks) { + for (String className : chunks) { + DefaultMutableTreeNode classNode = new DefaultMutableTreeNode(className); + packageNode.add(classNode); + } + ((DefaultTreeModel) classTree.getModel()).reload(packageNode); + } + }.execute(); + } + + private void refreshClassTree() { + if (progressBar.isVisible()) { + return; + } + + progressBar.setVisible(true); + new SwingWorker() { + @Override + protected Void doInBackground() { + SwingUtilities.invokeLater(() -> { + rootNode.removeAllChildren(); + packageNodes.clear(); + ((DefaultTreeModel) classTree.getModel()).reload(); + }); + + try { + Class[] classes = ClassDebug.getLoadedClasses(); + Map> packageMap = new HashMap<>(); + + // 按包名分组 + for (Class cls : classes) { + try { + String className = cls.getName(); + // 过滤系统类 + if (className.startsWith("java.") || className.startsWith("sun.") || + className.startsWith("jdk.")) { + continue; + } + + String packageName = className.contains(".") + ? className.substring(0, className.lastIndexOf('.')) + : "(default)"; + + packageMap.computeIfAbsent(packageName, k -> new ArrayList<>()).add(className); + } catch (Throwable t) { + System.err.println("处理类时出错: " + t.getMessage()); + t.printStackTrace(); + } + } + + // 添加到树节点 + SwingUtilities.invokeLater(() -> { + packageMap.keySet().stream() + .sorted() + .forEach(pkg -> { + DefaultMutableTreeNode pkgNode = new DefaultMutableTreeNode(pkg); + packageNodes.put(pkg, pkgNode); + rootNode.add(pkgNode); + + // 添加一个虚拟子节点用于延迟加载 + pkgNode.add(new DefaultMutableTreeNode("加载中...")); + }); + + ((DefaultTreeModel) classTree.getModel()).reload(); + progressBar.setVisible(false); + }); + } catch (Throwable t) { + System.err.println("刷新类树失败: " + t.getMessage()); + t.printStackTrace(); + SwingUtilities.invokeLater(() -> progressBar.setVisible(false)); + } + return null; + } + }.execute(); + } + + private void expandAllNodes() { + try { + for (int i = 0; i < classTree.getRowCount(); i++) { + classTree.expandRow(i); + } + } catch (Throwable t) { + System.err.println("展开节点时出错: " + t.getMessage()); + t.printStackTrace(); + } + } + + private void filterTree() { + String filter = searchField.getText().toLowerCase().trim(); + if (filter.isEmpty()) { + // 清空过滤,恢复完整树 + refreshClassTree(); + return; + } + + if (progressBar.isVisible()) { + return; + } + + progressBar.setVisible(true); + new SwingWorker() { + @Override + protected Void doInBackground() { + try { + DefaultMutableTreeNode filteredRoot = new DefaultMutableTreeNode("搜索结果"); + Map filteredPackages = new HashMap<>(); + + Class[] classes = ClassDebug.getLoadedClasses(); + for (Class cls : classes) { + String className = cls.getName(); + if (className.toLowerCase().contains(filter)) { + // 过滤系统类 + if (className.startsWith("java.") || className.startsWith("sun.") || + className.startsWith("jdk.")) { + continue; + } + + String packageName = className.contains(".") + ? className.substring(0, className.lastIndexOf('.')) + : "(default)"; + + DefaultMutableTreeNode packageNode = filteredPackages.get(packageName); + if (packageNode == null) { + packageNode = new DefaultMutableTreeNode(packageName); + filteredPackages.put(packageName, packageNode); + filteredRoot.add(packageNode); + } + + DefaultMutableTreeNode classNode = new DefaultMutableTreeNode(className); + packageNode.add(classNode); + } + } + + // 更新UI + SwingUtilities.invokeLater(() -> { + rootNode.removeAllChildren(); + packageNodes.clear(); + + if (filteredRoot.getChildCount() > 0) { + Enumeration children = filteredRoot.children(); + while (children.hasMoreElements()) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) children.nextElement(); + rootNode.add(node); + packageNodes.put((String) node.getUserObject(), node); + } + } else { + rootNode.add(new DefaultMutableTreeNode("未找到匹配的类")); + } + + ((DefaultTreeModel) classTree.getModel()).reload(); + expandAllNodes(); + progressBar.setVisible(false); + }); + } catch (Throwable t) { + System.err.println("过滤树失败: " + t.getMessage()); + t.printStackTrace(); + SwingUtilities.invokeLater(() -> progressBar.setVisible(false)); + } + return null; + } + }.execute(); + } + + private void openClassDetails(String className) { + try { + // 检查类是否存在 + boolean classExists = false; + try { + Class.forName(className); + classExists = true; + } catch (ClassNotFoundException e) { + System.err.println("类不存在: " + className); + } + + if (!classExists) { + JOptionPane.showMessageDialog(DebugWindow.this, + "类不存在: " + className, + "加载错误", + JOptionPane.ERROR_MESSAGE); + return; + } + + ClassDetailPanel panel = classDetailCache.computeIfAbsent(className, k -> { + ClassDetailPanel p = new ClassDetailPanel(className); + p.setPreferredSize(new Dimension(800, 600)); + return p; + }); + + JDialog dialog = new JDialog(DebugWindow.this, "类详情: " + className, true); + dialog.setContentPane(panel); + dialog.pack(); + dialog.setLocationRelativeTo(DebugWindow.this); + dialog.setVisible(true); + } catch (Throwable t) { + System.err.println("打开类详情时出错: " + className); + t.printStackTrace(); + JOptionPane.showMessageDialog(DebugWindow.this, + "无法加载类详情: " + className + "\n原因: " + t.getMessage(), + "错误", + JOptionPane.ERROR_MESSAGE); + } + } + } + + // 类树单元格渲染器 + static class ClassTreeCellRenderer extends DefaultTreeCellRenderer { + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, + boolean expanded, boolean leaf, int row, boolean hasFocus) { + super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + setBackgroundNonSelectionColor(BACKGROUND); + setBackgroundSelectionColor(ACCENT); + setForeground(FOREGROUND); + setTextSelectionColor(FOREGROUND); + setTextNonSelectionColor(FOREGROUND); + + if (leaf) { + setIcon(UIManager.getIcon("FileView.fileIcon")); + } else { + setIcon(UIManager.getIcon("FileView.directoryIcon")); + } + return this; + } + } + + // ====================== 线程监控面板 ====================== + class ThreadMonitorPanel extends JPanel { + private final DefaultTableModel tableModel = new DefaultTableModel( + new Object[]{"ID", "名称", "状态", "CPU时间(ms)", "用户时间(ms)", "阻塞计数", "等待计数", "锁名称", "锁拥有者", "堆栈深度"}, 0 + ) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + @Override + public Class getColumnClass(int columnIndex) { + if (columnIndex == 0 || columnIndex == 3 || columnIndex == 4 || + columnIndex == 5 || columnIndex == 6 || columnIndex == 9) { + return Long.class; + } + return String.class; + } + }; + + private final JTable threadTable = new JTable(tableModel); + private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + private final AtomicLong lastUpdateTime = new AtomicLong(System.nanoTime()); + private final Map threadDataCache = new ConcurrentHashMap<>(); + + public ThreadMonitorPanel() { + super(new BorderLayout()); + setBackground(BACKGROUND); + + // 配置表格 + threadTable.setFont(MONOSPACE_FONT); + threadTable.setBackground(BACKGROUND); + threadTable.setForeground(FOREGROUND); + threadTable.setGridColor(new Color(60, 60, 60)); + threadTable.setRowHeight(25); + threadTable.setSelectionBackground(ACCENT); + threadTable.setSelectionForeground(FOREGROUND); + threadTable.setAutoCreateRowSorter(true); + + // 表格头样式 + JTableHeader header = threadTable.getTableHeader(); + header.setBackground(TABLE_HEADER); + header.setForeground(FOREGROUND); + header.setFont(MAIN_FONT.deriveFont(Font.BOLD)); + + // 自定义渲染器 + threadTable.setDefaultRenderer(Object.class, new ThreadTableCellRenderer()); + + // 创建排序器 + TableRowSorter sorter = new TableRowSorter<>(tableModel); + threadTable.setRowSorter(sorter); + + JScrollPane scrollPane = new JScrollPane(threadTable); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + // 控制面板 + JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10)); + controlPanel.setBackground(TABLE_HEADER); + controlPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + // 配置按钮 + JButton refreshBtn = new JButton("刷新"); + configureButton(refreshBtn, this::refreshThreadData); + JButton dumpStackBtn = new JButton("转储堆栈"); + configureButton(dumpStackBtn, this::dumpSelectedThreadStack); + JButton interruptBtn = new JButton("中断线程"); + configureButton(interruptBtn, this::interruptSelectedThread); + + controlPanel.add(refreshBtn); + controlPanel.add(dumpStackBtn); + controlPanel.add(interruptBtn); + + add(controlPanel, BorderLayout.NORTH); + add(scrollPane, BorderLayout.CENTER); + + // 初始化定时器 + Timer updateTimer = new Timer(2000, e -> updateThreadData()); + updateTimer.start(); + + // 初始刷新 + refreshThreadData(null); + } + + private void configureButton(JButton button, ActionListener listener) { + button.setBackground(ACCENT); + button.setForeground(FOREGROUND); + button.setFocusPainted(false); + button.addActionListener(listener); + } + + private void refreshThreadData(ActionEvent actionEvent) { + threadDataCache.clear(); + updateThreadData(); + } + + private void updateThreadData() { + long currentTime = System.nanoTime(); + long elapsedNanos = currentTime - lastUpdateTime.getAndSet(currentTime); + double elapsedSeconds = elapsedNanos / 1_000_000_000.0; + + long[] threadIds = threadMXBean.getAllThreadIds(); + Map currentThreads = new HashMap<>(); + + // 获取线程信息 + for (ThreadInfo info : threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), Integer.MAX_VALUE)) { + if (info == null) { + continue; + } + + long threadId = info.getThreadId(); + ThreadData data = threadDataCache.computeIfAbsent(threadId, id -> new ThreadData()); + + // 更新线程信息 + data.threadId = threadId; + data.threadName = info.getThreadName(); + data.threadState = info.getThreadState().toString(); + data.blockedCount = info.getBlockedCount(); + data.waitedCount = info.getWaitedCount(); + data.lockName = info.getLockName(); + data.lockOwnerId = info.getLockOwnerId(); + data.stackDepth = info.getStackTrace().length; + + // 计算CPU使用率 + long cpuTime = threadMXBean.getThreadCpuTime(threadId); + long userTime = threadMXBean.getThreadUserTime(threadId); + + if (cpuTime != -1 && data.lastCpuTime != -1) { + long cpuDelta = cpuTime - data.lastCpuTime; + data.cpuUsage = (cpuDelta / elapsedSeconds) / 1_000_000.0; // 转换为毫秒/秒 + } + + data.lastCpuTime = cpuTime; + data.cpuTime = cpuTime / 1_000_000; // 转换为毫秒 + data.userTime = userTime / 1_000_000; // 转换为毫秒 + + currentThreads.put(threadId, data); + } + + // 移除不存在的线程 + threadDataCache.keySet().retainAll(currentThreads.keySet()); + + // 更新表格 + SwingUtilities.invokeLater(() -> { + tableModel.setRowCount(0); + + for (ThreadData data : threadDataCache.values()) { + String lockOwner = data.lockOwnerId != -1 ? + threadMXBean.getThreadInfo(data.lockOwnerId).getThreadName() : "N/A"; + + tableModel.addRow(new Object[]{ + data.threadId, + data.threadName, + data.threadState, + data.cpuTime, + data.userTime, + data.blockedCount, + data.waitedCount, + data.lockName, + lockOwner, + data.stackDepth + }); + } + + // 自动调整列宽 + for (int i = 0; i < threadTable.getColumnCount(); i++) { + threadTable.getColumnModel().getColumn(i).setPreferredWidth(120); + } + }); + } + + private ThreadData getSelectedThread() { + int selectedRow = threadTable.getSelectedRow(); + if (selectedRow == -1) return null; + + int modelRow = threadTable.convertRowIndexToModel(selectedRow); + long threadId = (Long) tableModel.getValueAt(modelRow, 0); + return threadDataCache.get(threadId); + } + + private void dumpSelectedThreadStack(ActionEvent actionEvent) { + ThreadData data = getSelectedThread(); + if (data == null) { + JOptionPane.showMessageDialog(DebugWindow.this, + "请先选择一个线程", + "警告", + JOptionPane.WARNING_MESSAGE); + return; + } + + ThreadInfo info = threadMXBean.getThreadInfo(data.threadId, Integer.MAX_VALUE); + if (info == null) return; + + StringBuilder sb = new StringBuilder(); + sb.append("线程: ").append(info.getThreadName()).append(" (ID: ").append(data.threadId).append(")\n"); + sb.append("状态: ").append(info.getThreadState()).append("\n\n"); + sb.append("堆栈跟踪:\n"); + + for (StackTraceElement element : info.getStackTrace()) { + sb.append("\tat ").append(element).append("\n"); + } + + JTextArea textArea = new JTextArea(sb.toString()); + textArea.setFont(MONOSPACE_FONT); + textArea.setEditable(false); + + JScrollPane scrollPane = new JScrollPane(textArea); + scrollPane.setPreferredSize(new Dimension(800, 500)); + + JOptionPane.showMessageDialog(DebugWindow.this, + scrollPane, + "线程堆栈: " + info.getThreadName(), + JOptionPane.INFORMATION_MESSAGE); + } + + private void interruptSelectedThread(ActionEvent actionEvent) { + ThreadData data = getSelectedThread(); + if (data == null) { + JOptionPane.showMessageDialog(DebugWindow.this, + "请先选择一个线程", + "警告", + JOptionPane.WARNING_MESSAGE); + return; + } + + // 查找线程对象 + Thread thread = findThreadById(data.threadId); + if (thread == null) { + JOptionPane.showMessageDialog(DebugWindow.this, + "找不到线程对象", + "错误", + JOptionPane.ERROR_MESSAGE); + return; + } + + // 确认对话框 + int result = JOptionPane.showConfirmDialog(DebugWindow.this, + "确定要中断线程: " + thread.getName() + "?", + "确认中断", + JOptionPane.YES_NO_OPTION); + + if (result == JOptionPane.YES_OPTION) { + thread.interrupt(); + JOptionPane.showMessageDialog(DebugWindow.this, + "已发送中断信号到线程: " + thread.getName(), + "中断已发送", + JOptionPane.INFORMATION_MESSAGE); + } + } + + private Thread findThreadById(long threadId) { + ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + + Thread[] threads = new Thread[rootGroup.activeCount()]; + while (rootGroup.enumerate(threads, true) == threads.length) { + threads = new Thread[threads.length * 2]; + } + + for (Thread thread : threads) { + if (thread != null && thread.threadId() == threadId) { + return thread; + } + } + return null; + } + } + + // 线程表格单元格渲染器 + static class ThreadTableCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + c.setBackground(row % 2 == 0 ? DebugWindow.TABLE_ROW : DebugWindow.TABLE_ALT_ROW); + c.setForeground(DebugWindow.FOREGROUND); + + // 状态列特殊着色 + if (column == 2 && value != null) { + String state = value.toString(); + if (state.contains("RUNNABLE")) { + c.setForeground(Color.GREEN); + } else if (state.contains("BLOCKED")) { + c.setForeground(Color.RED); + } else if (state.contains("WAITING") || state.contains("TIMED_WAITING")) { + c.setForeground(Color.ORANGE); + } + } + + // CPU时间列特殊着色 + if (column == 3 && value instanceof Long) { + long cpuTime = (Long) value; + if (cpuTime > 10000) { + c.setForeground(Color.RED); + } else if (cpuTime > 1000) { + c.setForeground(Color.ORANGE); + } + } + + return c; + } + } + + // 线程数据容器 + static class ThreadData { + long threadId; + String threadName; + String threadState; + long cpuTime = -1; + long userTime = -1; + long blockedCount; + long waitedCount; + String lockName; + long lockOwnerId = -1; + int stackDepth; + long lastCpuTime = -1; + double cpuUsage; // CPU使用率 (%) + } + + // 类详情面板 + static class ClassDetailPanel extends JPanel { + private final String className; + private final JTextArea detailArea = new JTextArea(); + + public ClassDetailPanel(String className) { + super(new BorderLayout()); + this.className = className; + setBackground(BACKGROUND); + + detailArea.setFont(MONOSPACE_FONT); + detailArea.setForeground(FOREGROUND); + detailArea.setBackground(BACKGROUND); + detailArea.setEditable(false); + detailArea.setBorder(new EmptyBorder(10, 10, 10, 10)); + + JScrollPane scrollPane = new JScrollPane(detailArea); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + add(scrollPane, BorderLayout.CENTER); + + loadClassDetails(); + } + + private void loadClassDetails() { + new SwingWorker() { + @Override + protected Void doInBackground() { + try { + Class clazz = Class.forName(className); + StringBuilder sb = new StringBuilder(); + + // 类基本信息 + sb.append("类名: ").append(clazz.getName()).append("\n"); + sb.append("包名: ").append(clazz.getPackage().getName()).append("\n"); + sb.append("类加载器: ").append(clazz.getClassLoader()).append("\n\n"); + + // 获取类文件位置 + String location = getClassLocation(clazz); + sb.append("位置: ").append(location).append("\n\n"); + + // 字段信息 + sb.append("字段:\n"); + for (Field field : clazz.getDeclaredFields()) { + sb.append(" ").append(field.toString()).append("\n"); + } + + // 方法信息 + sb.append("\n方法:\n"); + for (Method method : clazz.getDeclaredMethods()) { + sb.append(" ").append(method.toString()).append("\n"); + } + + SwingUtilities.invokeLater(() -> + detailArea.setText(sb.toString())); + } catch (ClassNotFoundException e) { + SwingUtilities.invokeLater(() -> + detailArea.setText("无法加载类详情: " + e.getMessage())); + } catch (Throwable t) { + SwingUtilities.invokeLater(() -> + detailArea.setText("加载类详情时出错: " + t.getMessage())); + } + return null; + } + }.execute(); + } + + private String getClassLocation(Class clazz) { + try { + URL url = clazz.getProtectionDomain().getCodeSource().getLocation(); + Path path = Paths.get(url.toURI()); + return path.toString(); + } catch (Exception e) { + return "未知位置"; + } + } + } + + class MethodTablePanel extends JPanel { + private final DefaultTableModel tableModel = new DefaultTableModel( + new Object[]{"时间", "类名", "方法名", "状态", "耗时 (ms)"}, 0 + ) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + + private final JTable methodTable = new JTable(tableModel); + private final Map activeCalls = new HashMap<>(); + private final List callHistory = new ArrayList<>(); + private final ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue<>(); + private final Timer updateTimer; + private final JLabel counterLabel = new JLabel("调用次数: 0"); + private boolean autoScrollEnabled = true; // 自动滚轮开关状态 + private final List filters = new CopyOnWriteArrayList<>(); + private final JButton filterBtn = new JButton("过滤设置"); + private String currentAllowlistText = ""; + private String currentBlocklistText = ""; + + public MethodTablePanel() { + super(new BorderLayout()); + setBackground(BACKGROUND); + + // 配置表格 + methodTable.setFont(MONOSPACE_FONT); + methodTable.setBackground(BACKGROUND); + methodTable.setForeground(FOREGROUND); + methodTable.setGridColor(new Color(60, 60, 60)); + methodTable.setRowHeight(25); + methodTable.setSelectionBackground(ACCENT); + methodTable.setSelectionForeground(FOREGROUND); + methodTable.setAutoCreateRowSorter(true); + + // 表格头样式 + JTableHeader header = methodTable.getTableHeader(); + header.setBackground(TABLE_HEADER); + header.setForeground(FOREGROUND); + header.setFont(MAIN_FONT.deriveFont(Font.BOLD)); + + // 自定义渲染器 + methodTable.setDefaultRenderer(Object.class, new MethodTableCellRenderer()); + + JScrollPane scrollPane = new JScrollPane(methodTable); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + // 控制按钮 + JPanel controlPanel = new JPanel(new BorderLayout()); + controlPanel.setBackground(TABLE_HEADER); + controlPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + // 添加工具栏 + JToolBar toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setBackground(TABLE_HEADER); + toolBar.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); + + filterBtn.setBackground(ACCENT); + filterBtn.setForeground(FOREGROUND); + filterBtn.setFocusPainted(false); + filterBtn.addActionListener(e -> openFilterSettings()); + toolBar.add(filterBtn); // 添加到现有的工具栏 + + // 自动滚轮开关按钮 + JToggleButton autoScrollBtn = new JToggleButton("自动滚轮"); + autoScrollBtn.setSelected(autoScrollEnabled); + autoScrollBtn.setBackground(autoScrollEnabled ? ACCENT : TABLE_HEADER); + autoScrollBtn.setForeground(FOREGROUND); + autoScrollBtn.setFocusPainted(false); + autoScrollBtn.addActionListener(e -> { + autoScrollEnabled = autoScrollBtn.isSelected(); + autoScrollBtn.setBackground(autoScrollEnabled ? ACCENT : TABLE_HEADER); + if (autoScrollEnabled) { + scrollToBottom(); // 切换开启时立即滚动到底部 + } + }); + + toolBar.add(autoScrollBtn); + controlPanel.add(toolBar, BorderLayout.WEST); + + // 右侧面板(刷新按钮和计数器) + JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + rightPanel.setBackground(TABLE_HEADER); + + JButton clearBtn = new JButton("清空记录"); + clearBtn.setBackground(ACCENT); + clearBtn.setForeground(FOREGROUND); + clearBtn.setFocusPainted(false); + clearBtn.addActionListener(e -> clearRecords()); + + rightPanel.add(counterLabel); + rightPanel.add(clearBtn); + + controlPanel.add(rightPanel, BorderLayout.EAST); + + add(controlPanel, BorderLayout.NORTH); + add(scrollPane, BorderLayout.CENTER); + + // 初始化批处理定时器 + updateTimer = new Timer(100, e -> { + int batchSize = 0; + while (!eventQueue.isEmpty() && batchSize < 50) { + Runnable task = eventQueue.poll(); + if (task != null) { + task.run(); + batchSize++; + } + } + counterLabel.setText("调用次数: " + tableModel.getRowCount()); + }); + updateTimer.start(); + + // 双击打开方法详情 + methodTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + int row = methodTable.rowAtPoint(e.getPoint()); + if (row >= 0) { + String className = (String) tableModel.getValueAt(row, 1); + String methodName = (String) tableModel.getValueAt(row, 2); + openMethodDetails(className, methodName); + } + } + } + }); + } + + public interface MethodFilter { + boolean shouldShow(String className, String methodName); + } + + private static class NameBasedFilter implements MethodFilter { + final Set packages = new HashSet<>(); + final Set classes = new HashSet<>(); + final Set methods = new HashSet<>(); + final boolean isAllowlist; + + NameBasedFilter(boolean isAllowlist) { + this.isAllowlist = isAllowlist; + } + + void addPackage(String pkg) { + packages.add(pkg); + } + + void addClass(String cls) { + classes.add(cls); + } + + void addMethod(String method) { + methods.add(method); + } + + @Override + public boolean shouldShow(String className, String methodName) { + boolean matches = false; + + // 检查包名匹配 + for (String pkg : packages) { + if (className.startsWith(pkg)) { + matches = true; + break; + } + } + + // 检查类名匹配 + matches = matches || classes.contains(className); + + // 检查方法名匹配 + matches = matches || methods.contains(methodName); + + // 检查类+方法组合 + matches = matches || methods.contains(className + "#" + methodName); + + return isAllowlist ? matches : !matches; + } + } + + // 过滤设置对话框 + private void openFilterSettings() { + JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(this), "过滤设置"); + dialog.setLayout(new BorderLayout()); + dialog.setSize(500, 400); + + JTabbedPane tabbedPane = new JTabbedPane(); + + // 白名单面板 - 加载当前保存的规则 + JPanel allowPanel = createFilterPanel(true, currentAllowlistText); + tabbedPane.addTab("白名单", allowPanel); + + // 黑名单面板 - 加载当前保存的规则 + JPanel blockPanel = createFilterPanel(false, currentBlocklistText); + tabbedPane.addTab("黑名单", blockPanel); + + dialog.add(tabbedPane, BorderLayout.CENTER); + + JButton applyBtn = new JButton("应用"); + JButton cancelBtn = new JButton("取消"); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); + buttonPanel.add(cancelBtn); + buttonPanel.add(applyBtn); + + applyBtn.addActionListener(e -> { + // 保存用户输入的规则 + currentAllowlistText = getTextFromPanel(allowPanel); + currentBlocklistText = getTextFromPanel(blockPanel); + applyFilters(); + dialog.dispose(); + }); + + cancelBtn.addActionListener(e -> dialog.dispose()); + + dialog.add(buttonPanel, BorderLayout.SOUTH); + dialog.setLocationRelativeTo(this); + dialog.setVisible(true); + } + + private String getTextFromPanel(JPanel panel) { + for (Component comp : panel.getComponents()) { + if (comp instanceof JScrollPane scrollPane) { + JViewport viewport = scrollPane.getViewport(); + Component view = viewport.getView(); + if (view instanceof JTextArea) { + return ((JTextArea) view).getText(); + } + } + } + return ""; + } + private JPanel createFilterPanel(boolean isAllowlist, String initialText) { + JPanel panel = new JPanel(new BorderLayout()); + + JTextArea area = new JTextArea(15, 40); + area.setFont(MONOSPACE_FONT); + area.setText(initialText); // 设置初始文本 + JScrollPane scrollPane = new JScrollPane(area); + + JLabel hint = new JLabel("格式:
" + + "• 包: com.example.*
" + + "• 类: com.example.MyClass
" + + "• 方法: myMethod 或 MyClass#myMethod"); + + panel.add(hint, BorderLayout.NORTH); + panel.add(scrollPane, BorderLayout.CENTER); + + return panel; + } + + private void applyFilters() { + filters.clear(); + + // 处理白名单 + if (!currentAllowlistText.trim().isEmpty()) { + NameBasedFilter allowFilter = parseFilter(currentAllowlistText, true); + if (!allowFilter.packages.isEmpty() || + !allowFilter.classes.isEmpty() || + !allowFilter.methods.isEmpty()) { + filters.add(allowFilter); + } + } + + // 处理黑名单 + if (!currentBlocklistText.trim().isEmpty()) { + NameBasedFilter blockFilter = parseFilter(currentBlocklistText, false); + if (!blockFilter.packages.isEmpty() || + !blockFilter.classes.isEmpty() || + !blockFilter.methods.isEmpty()) { + filters.add(blockFilter); + } + } + + // 刷新表格显示 + refreshTable(); + } + + private NameBasedFilter parseFilter(String text, boolean isAllowlist) { + NameBasedFilter filter = new NameBasedFilter(isAllowlist); + String[] lines = text.split("\\n"); + + for (String line : lines) { + line = line.trim(); + if (line.isEmpty()) continue; + + if (line.endsWith(".*")) { + // 包名过滤 + filter.addPackage(line.substring(0, line.length() - 1)); + } else if (line.contains("#")) { + // 类#方法 格式 + filter.addMethod(line); + } else if (line.contains(".")) { + // 类名格式 + filter.addClass(line); + } else { + // 方法名 + filter.addMethod(line); + } + } + return filter; + } + + private JTextArea findTextArea(Container container) { + for (Component comp : container.getComponents()) { + if (comp instanceof JTextArea) { + return (JTextArea) comp; + } else if (comp instanceof Container) { + JTextArea result = findTextArea((Container) comp); + if (result != null) return result; + } + } + return null; + } + + private void refreshTable() { + tableModel.setRowCount(0); + + for (MethodCallRecord record : callHistory) { + if (shouldShow(record.className, record.methodName)) { + addRecordToTable(record); + } + } + + counterLabel.setText("调用次数: " + tableModel.getRowCount()); + } + + private boolean shouldShow(String className, String methodName) { + if (filters.isEmpty()) return true; + + for (MethodFilter filter : filters) { + if (!filter.shouldShow(className, methodName)) { + return false; + } + } + return true; + } + + private void addRecordToTable(MethodCallRecord record) { + String status = record.exception != null ? "失败" : "完成"; + double durationMs = record.durationNanos / 1_000_000.0; + + tableModel.addRow(new Object[]{ + record.timestamp, + record.className, + record.methodName, + "进入", + "—" + }); + + tableModel.addRow(new Object[]{ + record.timestamp + record.durationNanos / 1_000_000, + record.className, + record.methodName, + status, + String.format("%.3f", durationMs) + }); + } + + public void addMethodCall(String className, String methodName, Object[] args, + boolean isEnter, Object returnValue, Throwable exception, long durationNanos) { + eventQueue.offer(() -> { + String callId = className + "#" + methodName + "@" + Thread.currentThread().getId(); + MethodCallRecord record = activeCalls.get(callId); + if (shouldShow(className, methodName)) { + if (isEnter) { + record = new MethodCallRecord(className, methodName, args); + activeCalls.put(callId, record); + + // 添加进入记录 + tableModel.addRow(new Object[]{ + System.currentTimeMillis(), + className, + methodName, + "进入", + "—" + }); + } else if (record != null) { + activeCalls.remove(callId); + record.complete(returnValue, exception, durationNanos); + callHistory.add(record); + + // 添加退出记录 + String status = exception != null ? "失败" : "完成"; + double durationMs = durationNanos / 1_000_000.0; + tableModel.addRow(new Object[]{ + System.currentTimeMillis(), + className, + methodName, + status, + String.format("%.3f", durationMs) + }); + } + } + + // 添加行后滚动到底部(如果启用) + if (autoScrollEnabled) { + scrollToBottom(); + } + }); + } + + // 滚动到表格底部 + private void scrollToBottom() { + SwingUtilities.invokeLater(() -> { + int lastRow = tableModel.getRowCount() - 1; + if (lastRow >= 0) { + methodTable.scrollRectToVisible(methodTable.getCellRect(lastRow, 0, true)); + } + }); + } + + private void clearRecords() { + tableModel.setRowCount(0); + activeCalls.clear(); + callHistory.clear(); + counterLabel.setText("调用次数: 0"); + } + + private void openMethodDetails(String className, String methodName) { + String key = className + "#" + methodName; + MethodDetailPanel panel = methodDetailCache.computeIfAbsent(key, k -> { + MethodDetailPanel p = new MethodDetailPanel(className, methodName); + p.setPreferredSize(new Dimension(800, 500)); + return p; + }); + + JDialog dialog = new JDialog(DebugWindow.this, "方法详情: " + methodName, true); + dialog.setContentPane(panel); + dialog.pack(); + dialog.setLocationRelativeTo(DebugWindow.this); + dialog.setVisible(true); + } + } + + // 方法表格单元格渲染器 + static class MethodTableCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + c.setBackground(row % 2 == 0 ? DebugWindow.TABLE_ROW : DebugWindow.TABLE_ALT_ROW); + c.setForeground(DebugWindow.FOREGROUND); + + // 状态列特殊着色 + if (column == 3 && value != null) { + String status = value.toString(); + if ("进入".equals(status)) { + c.setForeground(Color.CYAN); + } else if ("完成".equals(status)) { + c.setForeground(Color.GREEN); + } else if ("失败".equals(status)) { + c.setForeground(Color.ORANGE); + } + } + + // 耗时列特殊着色 + if (column == 4 && value != null && !"—".equals(value)) { + try { + double ms = Double.parseDouble(value.toString()); + if (ms > 100) { + c.setForeground(Color.ORANGE); + } else if (ms > 10) { + c.setForeground(Color.YELLOW); + } + } catch (NumberFormatException ignored) {} + } + + return c; + } + } + + // 方法调用记录 + static class MethodCallRecord { + final long timestamp; + final String className; + final String methodName; + final Object[] args; + Object returnValue; + Throwable exception; + long durationNanos; + boolean completed; + + public MethodCallRecord(String className, String methodName, Object[] args) { + this.timestamp = System.currentTimeMillis(); + this.className = className; + this.methodName = methodName; + this.args = args; + } + + public void complete(Object returnValue, Throwable exception, long durationNanos) { + this.returnValue = returnValue; + this.exception = exception; + this.durationNanos = durationNanos; + this.completed = true; + } + } + + // 方法详情面板 + class MethodDetailPanel extends JPanel { + private final JTextArea detailArea = new JTextArea(); + + public MethodDetailPanel(String className, String methodName) { + super(new BorderLayout()); + setBackground(BACKGROUND); + + detailArea.setFont(MONOSPACE_FONT); + detailArea.setForeground(FOREGROUND); + detailArea.setBackground(BACKGROUND); + detailArea.setEditable(false); + detailArea.setBorder(new EmptyBorder(10, 10, 10, 10)); + + JScrollPane scrollPane = new JScrollPane(detailArea); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + add(scrollPane, BorderLayout.CENTER); + + // 类名链接 + JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + headerPanel.setBackground(TABLE_HEADER); + headerPanel.setBorder(new EmptyBorder(5, 10, 5, 10)); + + JLabel methodLabel = new JLabel(methodName); + methodLabel.setForeground(FOREGROUND); + methodLabel.setFont(MAIN_FONT.deriveFont(Font.BOLD, 14)); + + JLabel classLabel = new JLabel("在类: " + className); + classLabel.setForeground(ACCENT); + classLabel.setFont(MAIN_FONT.deriveFont(Font.PLAIN, 12)); + classLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + classLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + classTreePanel.openClassDetails(className); + ((Window) SwingUtilities.getRoot(MethodDetailPanel.this)).dispose(); + } + }); + + headerPanel.add(methodLabel); + headerPanel.add(classLabel); + + add(headerPanel, BorderLayout.NORTH); + + loadMethodDetails(className, methodName); + } + + private void loadMethodDetails(String className, String methodName) { + new SwingWorker() { + @Override + protected Void doInBackground() { + try { + Class clazz = Class.forName(className); + StringBuilder sb = new StringBuilder(); + + // 查找匹配的方法 + for (Method method : clazz.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + // 方法签名 + sb.append("方法签名: ").append(method).append("\n\n"); + + // 方法参数 + sb.append("参数类型:\n"); + for (Class paramType : method.getParameterTypes()) { + sb.append(" ").append(paramType.getName()).append("\n"); + } + + // 返回类型 + sb.append("\n返回类型: ").append(method.getReturnType().getName()).append("\n"); + break; + } + } + + SwingUtilities.invokeLater(() -> + detailArea.setText(sb.toString())); + } catch (ClassNotFoundException e) { + SwingUtilities.invokeLater(() -> + detailArea.setText("无法加载方法详情: " + e.getMessage())); + } catch (Throwable t) { + SwingUtilities.invokeLater(() -> + detailArea.setText("加载方法详情时出错: " + t.getMessage())); + } + return null; + } + }.execute(); + } + + } + + // 控制台面板(添加中文支持) + class ConsolePanel extends JPanel { + private final JTextArea consoleOutput = new JTextArea(); + private final JTextField commandInput = new JTextField(); + private final JLabel statusLabel = new JLabel("就绪"); + private final AtomicLong executionId = new AtomicLong(); + private boolean autoScrollEnabled = true; + private final List commandHistory = new ArrayList<>(); + private int historyIndex = -1; + private final JPopupMenu contextMenu = new JPopupMenu(); + private final List autoCompleteList = new ArrayList<>(); + private JList suggestionList; + private JWindow suggestionWindow; + private final DefaultListModel suggestionModel = new DefaultListModel<>(); + private int lastCaretPosition = -1; + private String currentPartialWord = ""; + + // 颜色常量 + private static final Color BACKGROUND = new Color(30, 30, 30); + private static final Color FOREGROUND = new Color(220, 220, 220); + private static final Color TABLE_ROW = new Color(50, 50, 50); + private static final Color TABLE_HEADER = new Color(40, 40, 40); + private static final Color ACCENT = new Color(0, 120, 215); + private static final Font MONOSPACE_FONT = new Font("Monospaced", Font.PLAIN, 14); + + public ConsolePanel() { + super(new BorderLayout()); + setBackground(BACKGROUND); + + // 输出区域 - 类似终端样式 + consoleOutput.setFont(MONOSPACE_FONT); + consoleOutput.setForeground(FOREGROUND); + consoleOutput.setBackground(BACKGROUND); + consoleOutput.setEditable(false); + consoleOutput.setComponentPopupMenu(contextMenu); // 添加右键菜单 + + // 右键菜单 + JMenuItem clearItem = new JMenuItem("清除控制台"); + clearItem.addActionListener(e -> consoleOutput.setText("")); + contextMenu.add(clearItem); + + JScrollPane outputScroll = new JScrollPane(consoleOutput); + outputScroll.setBorder(null); // 移除边框 + + // 输入区域 - 单行输入框 + commandInput.setFont(MONOSPACE_FONT); + commandInput.setForeground(FOREGROUND); + commandInput.setBackground(TABLE_ROW); + commandInput.setCaretColor(FOREGROUND); + commandInput.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY), + BorderFactory.createEmptyBorder(5, 5, 5, 5) + )); + + // 初始化自动补全列表 + initAutoCompleteList(); + + // 创建建议窗口(无边框弹出窗口) + suggestionList = new JList<>(suggestionModel); + suggestionList.setFont(MONOSPACE_FONT); + suggestionList.setBackground(TABLE_ROW); + suggestionList.setForeground(FOREGROUND); + suggestionList.setSelectionBackground(ACCENT); + suggestionList.setSelectionForeground(Color.WHITE); + + // 自定义渲染器显示提示文本 + suggestionList.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value != null) { + String text = value.toString(); + if (text.startsWith("print")) { + label.setText("print → System.out.println(...)"); + label.setForeground(Color.GRAY); + } else if (text.startsWith("global.")) { + label.setForeground(new Color(0, 180, 0)); + } + } + return label; + } + }); + + JScrollPane scrollPane = new JScrollPane(suggestionList); + scrollPane.setBorder(BorderFactory.createLineBorder(Color.GRAY)); + scrollPane.setPreferredSize(new Dimension(500, 120)); + + suggestionWindow = new JWindow(); + suggestionWindow.setFocusable(false); + suggestionWindow.getContentPane().add(scrollPane); + suggestionWindow.pack(); + + // 输入框事件处理 + commandInput.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + if (suggestionWindow.isVisible()) { + applySuggestion(); + e.consume(); + } else if (!commandInput.getText().trim().isEmpty()) { + executeCode(); + } + } + else if (e.getKeyCode() == KeyEvent.VK_TAB) { + if (suggestionWindow.isVisible()) { + applySuggestion(); + e.consume(); + } + } + else if (e.getKeyCode() == KeyEvent.VK_UP) { + if (suggestionWindow.isVisible()) { + navigateSuggestions(-1); + e.consume(); + } else { + navigateHistory(true); + } + } + else if (e.getKeyCode() == KeyEvent.VK_DOWN) { + if (suggestionWindow.isVisible()) { + navigateSuggestions(1); + e.consume(); + } else { + navigateHistory(false); + } + } + else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + hideSuggestions(); + e.consume(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + // 在输入变化后更新建议 + if (e.getKeyCode() != KeyEvent.VK_ESCAPE && + e.getKeyCode() != KeyEvent.VK_UP && + e.getKeyCode() != KeyEvent.VK_DOWN) { + updateSuggestions(); + } + } + }); + + // 建议列表鼠标点击处理 + suggestionList.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 1) { + applySuggestion(); + } + } + }); + + // 当输入框失去焦点时隐藏建议 + commandInput.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + // 延迟隐藏,避免点击建议时立即关闭 + SwingUtilities.invokeLater(() -> { + if (!suggestionList.isFocusOwner() && !suggestionWindow.isFocusOwner()) { + hideSuggestions(); + } + }); + } + }); + + // 状态栏 + JPanel statusPanel = new JPanel(new BorderLayout()); + statusPanel.setBackground(TABLE_HEADER); + statusPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5)); + statusLabel.setForeground(FOREGROUND); + statusPanel.add(statusLabel, BorderLayout.WEST); + + JToggleButton autoScrollBtn = new JToggleButton("自动滚轮"); + autoScrollBtn.setSelected(autoScrollEnabled); + autoScrollBtn.setBackground(autoScrollEnabled ? ACCENT : TABLE_HEADER); + autoScrollBtn.setForeground(FOREGROUND); + autoScrollBtn.setFocusPainted(false); + autoScrollBtn.addActionListener(e -> { + autoScrollEnabled = autoScrollBtn.isSelected(); + autoScrollBtn.setBackground(autoScrollEnabled ? ACCENT : TABLE_HEADER); + if (autoScrollEnabled) scrollToBottom(); + }); + statusPanel.add(autoScrollBtn, BorderLayout.EAST); + + // 文档监听器(自动滚动) + consoleOutput.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { + if (autoScrollEnabled) scrollToBottom(); + } + public void removeUpdate(DocumentEvent e) {} + public void changedUpdate(DocumentEvent e) {} + }); + + // 布局 + add(outputScroll, BorderLayout.CENTER); + add(commandInput, BorderLayout.SOUTH); + add(statusPanel, BorderLayout.NORTH); // 状态栏放在顶部 + } + + // 初始化自动补全列表 + private void initAutoCompleteList() { + // 基础命令 + autoCompleteList.add("print"); + autoCompleteList.add("printf"); + autoCompleteList.add("System.out.println"); + autoCompleteList.add("System.out.print"); + autoCompleteList.add("System.out.printf"); + + // global对象相关 + autoCompleteList.add("global"); + autoCompleteList.add("global.instrumentation"); + autoCompleteList.add("global.axisInnovatorsBox"); + + // 添加instrumentation的方法 + addMethodSuggestions(Instrumentation.class, "global.instrumentation"); + addMethodSuggestions(AxisInnovatorsBox.class, "global.axisInnovatorsBox"); + + addFieldSuggestions(Instrumentation.class, "global.instrumentation"); + addFieldSuggestions(AxisInnovatorsBox.class, "global.axisInnovatorsBox"); + } + + // 添加方法建议 + private void addMethodSuggestions(Class clazz, String prefix) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPublic(method.getModifiers())) { + StringBuilder sb = new StringBuilder(prefix).append(".").append(method.getName()).append("("); + + Class[] params = method.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + sb.append(getSimpleTypeName(params[i])); + if (i < params.length - 1) sb.append(", "); + } + sb.append(")"); + autoCompleteList.add(sb.toString()); + } + } + } + + // 添加字段建议 + private void addFieldSuggestions(Class clazz, String prefix) { + for (Field field : clazz.getDeclaredFields()) { + if (java.lang.reflect.Modifier.isPublic(field.getModifiers())) { + String sb = prefix + "." + field.getName(); + autoCompleteList.add(sb); + } + } + } + + // 获取简化类型名 + private String getSimpleTypeName(Class type) { + if (type.isArray()) { + return getSimpleTypeName(type.getComponentType()) + "[]"; + } + return type.getSimpleName(); + } + + // 更新建议列表 + private void updateSuggestions() { + String text = commandInput.getText(); + int caretPos = commandInput.getCaretPosition(); + + // 查找当前单词的起始位置 + int start = caretPos - 1; + while (start >= 0 && Character.isJavaIdentifierPart(text.charAt(start))) { + start--; + } + start++; + + String currentWord = text.substring(start, caretPos); + currentPartialWord = currentWord; + lastCaretPosition = start; + + if (currentWord.isEmpty()) { + hideSuggestions(); + return; + } + + // 查找匹配的补全项 + List matches = new ArrayList<>(); + for (String item : autoCompleteList) { + if (item.toLowerCase().contains(currentWord.toLowerCase())) { + matches.add(item); + } + } + + if (matches.isEmpty()) { + hideSuggestions(); + return; + } + + // 排序匹配项(短的优先) + matches.sort((a, b) -> { + int lenDiff = a.length() - b.length(); + if (lenDiff != 0) return lenDiff; + return a.compareTo(b); + }); + + // 更新建议模型 + suggestionModel.clear(); + for (String match : matches) { + suggestionModel.addElement(match); + } + + // 显示建议窗口 + showSuggestions(); + + // 默认选择第一个 + if (suggestionModel.getSize() > 0) { + suggestionList.setSelectedIndex(0); + } + } + + // 显示建议窗口 + private void showSuggestions() { + if (suggestionModel.isEmpty()) { + hideSuggestions(); + return; + } + + // 设置建议窗口位置 + Point location = commandInput.getLocationOnScreen(); + location.y -= suggestionWindow.getPreferredSize().height; + suggestionWindow.setLocation(location); + + // 调整宽度匹配输入框 + Dimension preferredSize = suggestionWindow.getPreferredSize(); + preferredSize.width = commandInput.getWidth(); + suggestionWindow.setSize(preferredSize); + + suggestionWindow.setVisible(true); + } + + // 隐藏建议窗口 + private void hideSuggestions() { + suggestionWindow.setVisible(false); + } + + // 应用当前选中的建议 + private void applySuggestion() { + if (suggestionList.getSelectedValue() == null) return; + + String selected = suggestionList.getSelectedValue().toString(); + String text = commandInput.getText(); + + // 替换当前单词 + String newText = text.substring(0, lastCaretPosition) + + selected + + text.substring(lastCaretPosition + currentPartialWord.length()); + + commandInput.setText(newText); + + // 设置光标位置在替换后的单词末尾 + int newCaretPos = lastCaretPosition + selected.length(); + commandInput.setCaretPosition(newCaretPos); + + hideSuggestions(); + } + + // 在建议列表中导航 + private void navigateSuggestions(int direction) { + int selectedIndex = suggestionList.getSelectedIndex(); + int newIndex = selectedIndex + direction; + + if (newIndex >= 0 && newIndex < suggestionModel.getSize()) { + suggestionList.setSelectedIndex(newIndex); + suggestionList.ensureIndexIsVisible(newIndex); + } + } + + private void navigateHistory(boolean up) { + if (commandHistory.isEmpty()) return; + + if (up) { + if (historyIndex == -1) { + historyIndex = commandHistory.size() - 1; + } else if (historyIndex > 0) { + historyIndex--; + } + } else { + if (historyIndex != -1 && historyIndex < commandHistory.size() - 1) { + historyIndex++; + } else { + historyIndex = -1; + commandInput.setText(""); + return; + } + } + commandInput.setText(commandHistory.get(historyIndex)); + } + + private void scrollToBottom() { + SwingUtilities.invokeLater(() -> { + consoleOutput.setCaretPosition(consoleOutput.getDocument().getLength()); + }); + } + + private void executeCode() { + long id = executionId.incrementAndGet(); + String code = commandInput.getText().trim(); + commandHistory.add(code); + historyIndex = -1; // 重置历史索引 + + // 显示执行的命令 + consoleOutput.append("> " + code + "\n"); + commandInput.setText(""); + statusLabel.setText("执行中..."); + + // 创建用于捕获输出的流 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // 异步执行 + new SwingWorker() { + @Override + protected String doInBackground() throws Exception { + // 保存原始输出流 + PrintStream originalOut = System.out; + PrintStream originalErr = System.err; + + try { + // 重定向输出到我们的流 + System.setOut(new PrintStream(baos, true, StandardCharsets.UTF_8)); + System.setErr(new PrintStream(baos, true, StandardCharsets.UTF_8)); + + // 执行代码并返回结果 + return ClassDebug.executeCode(code); + } finally { + // 恢复原始输出流 + System.setOut(originalOut); + System.setErr(originalErr); + } + } + + @Override + protected void done() { + try { + // 获取执行结果 + String result = get(); + + // 获取捕获的输出 + String output = ""; + try { + output = baos.toString(StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + output = "输出解码错误: " + e.getMessage(); + } + + // 显示执行结果 + if (!output.isEmpty()) { + consoleOutput.append(output); + if (!output.endsWith("\n")) { + consoleOutput.append("\n"); + } + } + + // 显示执行状态 + if (result.isEmpty()) { + consoleOutput.append("← 执行成功\n\n"); + } else { + consoleOutput.append("← " + result + "\n\n"); + } + + statusLabel.setText("执行完成"); + } catch (Exception e) { + consoleOutput.append("← 执行错误: " + e.getMessage() + "\n\n"); + statusLabel.setText("执行失败"); + } + } + }.execute(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/gui/MainWindow.java b/src/main/java/com/axis/innovators/box/gui/MainWindow.java index 77e161d..545984e 100644 --- a/src/main/java/com/axis/innovators/box/gui/MainWindow.java +++ b/src/main/java/com/axis/innovators/box/gui/MainWindow.java @@ -1,30 +1,27 @@ package com.axis.innovators.box.gui; import com.axis.innovators.box.AxisInnovatorsBox; -import com.axis.innovators.box.decompilation.gui.ModernJarViewer; import com.axis.innovators.box.events.*; -import com.axis.innovators.box.register.RegistrationSettingsItem; import com.axis.innovators.box.register.LanguageManager; +import com.axis.innovators.box.register.RegistrationSettingsItem; import com.formdev.flatlaf.FlatClientProperties; -import com.formdev.flatlaf.FlatLaf; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import javax.swing.*; import javax.swing.Timer; +import javax.swing.*; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.PanelUI; import javax.swing.plaf.basic.BasicScrollBarUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; import java.awt.*; import java.awt.event.*; -import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; -import java.util.*; import java.util.List; +import java.util.*; /** * 显示窗口(显示的主窗口) @@ -350,7 +347,7 @@ public class MainWindow extends JFrame { private WindowsJDialog dialog; public void showSettings() { - if (dialog == null || !AxisInnovatorsBox.getMain().isWindowStartup(dialog)) { + if (dialog == null || AxisInnovatorsBox.getMain().isWindowStartup(dialog)) { dialog = new WindowsJDialog(this, LanguageManager.getLoadedLanguages().getText("mainWindow.settings.title"), true); } @@ -373,7 +370,7 @@ public class MainWindow extends JFrame { GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, content)); dialog.add(content); dialog.revalidate(); - if (!AxisInnovatorsBox.getMain().isWindowStartup(dialog)) { + if (AxisInnovatorsBox.getMain().isWindowStartup(dialog)) { AxisInnovatorsBox.getMain().popupWindow(dialog); } } diff --git a/src/main/java/com/axis/innovators/box/gui/MemoryAnalysisPanel.java b/src/main/java/com/axis/innovators/box/gui/MemoryAnalysisPanel.java new file mode 100644 index 0000000..71bc1ac --- /dev/null +++ b/src/main/java/com/axis/innovators/box/gui/MemoryAnalysisPanel.java @@ -0,0 +1,1578 @@ +package com.axis.innovators.box.gui; + +import org.tzd.debug.ClassDebug; +import org.tzd.debug.GetInstance; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableRowSorter; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Rectangle2D; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.text.DecimalFormat; +import java.util.List; +import java.util.Timer; +import java.util.*; +import java.util.concurrent.CancellationException; + +public class MemoryAnalysisPanel extends JPanel { + private static final DecimalFormat MB_FORMAT = new DecimalFormat("#,##0.00"); + private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("0.0%"); + + // 颜色方案 + private static final Color BACKGROUND = new Color(45, 45, 48); + private static final Color FOREGROUND = new Color(241, 241, 241); + private static final Color ACCENT = new Color(0, 122, 204); + private static final Color TABLE_HEADER = new Color(37, 37, 38); + private static final Color TABLE_ROW = new Color(30, 30, 30); + private static final Color TABLE_ALT_ROW = new Color(35, 35, 35); + + // 内存使用状态颜色 + private static final Color MEMORY_SAFE = new Color(0, 180, 0); // 绿色 (<70%) + private static final Color MEMORY_WARNING = new Color(255, 165, 0); // 橙色 (70-85%) + private static final Color MEMORY_CRITICAL = new Color(255, 0, 0); // 红色 (>85%) + + // 占比可视化颜色 + private static final Color PERCENT_LOW = new Color(100, 200, 100); // 浅绿 (低占比) + private static final Color PERCENT_MEDIUM = new Color(255, 200, 100); // 浅橙 (中占比) + private static final Color PERCENT_HIGH = new Color(255, 100, 100); // 浅红 (高占比) + + // 组件 + private final JTabbedPane tabbedPane = new JTabbedPane(); + private final JTable memoryPoolTable; + private final DefaultTableModel memoryPoolModel; + private final JTable objectMemoryTable; + private final DefaultTableModel objectMemoryModel; + private final TableRowSorter objectMemorySorter; + private final JProgressBar heapUsageBar = new JProgressBar(); + private final JLabel heapUsageLabel = new JLabel(); + private final JProgressBar nonHeapUsageBar = new JProgressBar(); + private final JLabel nonHeapUsageLabel = new JLabel(); + private final JTextField searchField = new JTextField(20); + + // 内存数据 + private final MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean(); + private final List memoryPoolBeans = ManagementFactory.getMemoryPoolMXBeans(); + private final Instrumentation instrumentation; + + private JDialog loadingDialog; + private JLabel loadingLabel; + private JProgressBar loadingProgressBar; + private SpinnerAnimation spinner; + + // 添加标志位,记录对象内存数据是否已加载 + private boolean objectMemoryDataLoaded = false; + + // 跟踪当前的SwingWorker + private SwingWorker currentWorker; + + // 存储对象内存数据 + private List currentObjectMemoryData = new ArrayList<>(); + + private final JTextField specificClassField = new JTextField(30); + private final JButton analyzeSpecificClassBtn = new JButton("分析"); + private final JTextArea classInfoArea = new JTextArea(); + private final JTable specificClassInstanceTable; + private final DefaultTableModel specificClassInstanceModel; + + // 特定类分析面板组件 + private JPanel visualizationPanel; + private JLabel ratioLabel; + private JProgressBar ratioBar; + private JTextField instanceSearchField; + private TableRowSorter instanceSorter; + + + public MemoryAnalysisPanel() { + super(new BorderLayout()); + setBackground(BACKGROUND); + + // 获取 Instrumentation 实例 + instrumentation = ClassDebug.getInstrumentation(); + + // 创建内存池表格 + memoryPoolModel = new DefaultTableModel( + new Object[]{"内存池", "类型", "总内存 (MB)", "已用内存 (MB)", "使用率"}, 0 + ) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + + memoryPoolTable = new JTable(memoryPoolModel); + configureTable(memoryPoolTable); + + // 创建对象内存表格 + objectMemoryModel = new DefaultTableModel( + new Object[]{"类名", "实例大小 (bytes)", "实例数量", "总内存 (MB)", "占比"}, 0 + ) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + @Override + public Class getColumnClass(int columnIndex) { + if (columnIndex == 1 || columnIndex == 2) { + return Long.class; + } + return String.class; + } + }; + + objectMemoryTable = new JTable(objectMemoryModel); + objectMemorySorter = new TableRowSorter<>(objectMemoryModel); + objectMemoryTable.setRowSorter(objectMemorySorter); + configureTable(objectMemoryTable); + + // 添加双击事件监听器 + objectMemoryTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + int viewRow = objectMemoryTable.rowAtPoint(e.getPoint()); + if (viewRow >= 0) { + int modelRow = objectMemoryTable.convertRowIndexToModel(viewRow); + if (modelRow < currentObjectMemoryData.size()) { + ClassMemoryData data = currentObjectMemoryData.get(modelRow); + showClassDetailsDialog(data); + } + } + } + } + }); + + // 配置标签页 + JScrollPane poolScrollPane = new JScrollPane(memoryPoolTable); + poolScrollPane.setBorder(new EmptyBorder(10, 10, 10, 10)); + + // 对象内存面板添加搜索框 + JPanel objectPanel = new JPanel(new BorderLayout()); + JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5)); + searchPanel.setBackground(TABLE_HEADER); + searchPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + + JLabel searchLabel = new JLabel("搜索类名:"); + searchLabel.setForeground(FOREGROUND); + searchLabel.setFont(DebugWindow.MAIN_FONT); + + searchField.setFont(DebugWindow.MAIN_FONT); + searchField.setBackground(new Color(60, 60, 60)); + searchField.setForeground(FOREGROUND); + searchField.setCaretColor(FOREGROUND); + searchField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + BorderFactory.createEmptyBorder(5, 5, 5, 5) + )); + + // 添加搜索监听器 + searchField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + filterTable(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + filterTable(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + filterTable(); + } + }); + + JButton clearButton = new JButton("清除"); + configureButton(clearButton, e -> { + searchField.setText(""); + filterTable(); + }); + + searchPanel.add(searchLabel); + searchPanel.add(searchField); + searchPanel.add(clearButton); + + JScrollPane objectScrollPane = new JScrollPane(objectMemoryTable); + objectScrollPane.setBorder(new EmptyBorder(0, 10, 10, 10)); + + objectPanel.add(searchPanel, BorderLayout.NORTH); + objectPanel.add(objectScrollPane, BorderLayout.CENTER); + + tabbedPane.addTab("内存池", poolScrollPane); + tabbedPane.addTab("对象内存", objectPanel); + + // 添加标签切换监听器 + tabbedPane.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + // 当切换到"对象内存"标签页且数据未加载时,加载数据 + if (tabbedPane.getSelectedIndex() == 1 && !objectMemoryDataLoaded) { + refreshObjectMemoryData(); + } + } + }); + + // 配置堆内存使用面板 + JPanel heapPanel = createMemoryUsagePanel("堆内存使用:", heapUsageBar, heapUsageLabel); + JPanel nonHeapPanel = createMemoryUsagePanel("非堆内存使用:", nonHeapUsageBar, nonHeapUsageLabel); + + JPanel usagePanel = new JPanel(new GridLayout(2, 1, 0, 10)); + usagePanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + usagePanel.setBackground(TABLE_HEADER); + usagePanel.add(heapPanel); + usagePanel.add(nonHeapPanel); + + // 控制按钮 + JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); + controlPanel.setBackground(TABLE_HEADER); + controlPanel.setBorder(new EmptyBorder(5, 10, 5, 10)); + + JButton refreshBtn = new JButton("刷新内存数据"); + configureButton(refreshBtn, e -> refreshMemoryData()); + controlPanel.add(refreshBtn); + + specificClassInstanceModel = new DefaultTableModel( + new Object[]{"实例ID", "ToString()", "内存大小 (bytes)"}, 0 + ) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + + specificClassInstanceTable = new JTable(specificClassInstanceModel); + configureTable(specificClassInstanceTable); + + // 创建特定类分析面板 + JPanel specificClassPanel = createSpecificClassAnalysisPanel(); + tabbedPane.addTab("特定类分析", specificClassPanel); + + // 创建加载对话框 + createLoadingDialog(); + + JPanel topPanel = new JPanel(new BorderLayout()); + topPanel.setBackground(TABLE_HEADER); + topPanel.add(usagePanel, BorderLayout.CENTER); + topPanel.add(controlPanel, BorderLayout.EAST); + + add(topPanel, BorderLayout.NORTH); + add(tabbedPane, BorderLayout.CENTER); + + // 初始刷新 - 只加载内存使用和内存池数据 + showLoadingDialog("正在初始化内存数据..."); + refreshInitialData(); + } + + private JPanel createSpecificClassAnalysisPanel() { + JPanel panel = new JPanel(new BorderLayout(10, 10)); + panel.setBorder(new EmptyBorder(15, 15, 15, 15)); + panel.setBackground(BACKGROUND); + + // 输入面板 + JPanel inputPanel = new JPanel(new BorderLayout(10, 10)); + inputPanel.setBackground(TABLE_HEADER); + inputPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + JLabel classLabel = new JLabel("输入完整类名:"); + classLabel.setForeground(FOREGROUND); + classLabel.setFont(DebugWindow.MAIN_FONT); + + specificClassField.setFont(DebugWindow.MONOSPACE_FONT); + specificClassField.setBackground(new Color(60, 60, 60)); + specificClassField.setForeground(FOREGROUND); + specificClassField.setCaretColor(FOREGROUND); + specificClassField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + BorderFactory.createEmptyBorder(5, 5, 5, 5) + )); + + configureButton(analyzeSpecificClassBtn, e -> analyzeSpecificClass()); + analyzeSpecificClassBtn.setFont(DebugWindow.MAIN_FONT); + + JPanel fieldPanel = new JPanel(new BorderLayout(10, 0)); + fieldPanel.setBackground(TABLE_HEADER); + fieldPanel.add(specificClassField, BorderLayout.CENTER); + fieldPanel.add(analyzeSpecificClassBtn, BorderLayout.EAST); + + inputPanel.add(classLabel, BorderLayout.WEST); + inputPanel.add(fieldPanel, BorderLayout.CENTER); + + // 主内容面板 (使用网格袋布局) + JPanel mainContentPanel = new JPanel(new GridBagLayout()); + mainContentPanel.setBackground(BACKGROUND); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.BOTH; + gbc.weightx = 1.0; + gbc.insets = new Insets(5, 5, 5, 5); + + // 类信息面板 + JPanel infoPanel = createClassInfoPanel(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.weighty = 0.4; + mainContentPanel.add(infoPanel, gbc); + + // 可视化面板 + visualizationPanel = createVisualizationPanel(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 1; + gbc.weighty = 0.1; + mainContentPanel.add(visualizationPanel, gbc); + + // 实例列表面板 + JPanel instancesPanel = createInstancesPanel(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.gridwidth = 1; + gbc.weighty = 0.5; + mainContentPanel.add(instancesPanel, gbc); + + // 添加主内容面板到中心 + panel.add(inputPanel, BorderLayout.NORTH); + panel.add(mainContentPanel, BorderLayout.CENTER); + + return panel; + } + + private JPanel createClassInfoPanel() { + JPanel infoPanel = new JPanel(new BorderLayout()); + infoPanel.setBackground(TABLE_HEADER); + infoPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + "类信息", + javax.swing.border.TitledBorder.LEFT, + javax.swing.border.TitledBorder.TOP, + DebugWindow.MAIN_FONT, + FOREGROUND + )); + + classInfoArea.setEditable(false); + classInfoArea.setFont(DebugWindow.MONOSPACE_FONT); + classInfoArea.setBackground(new Color(50, 50, 50)); + classInfoArea.setForeground(FOREGROUND); + classInfoArea.setLineWrap(true); + classInfoArea.setWrapStyleWord(true); + classInfoArea.setBorder(new EmptyBorder(10, 10, 10, 10)); + + JScrollPane infoScrollPane = new JScrollPane(classInfoArea); + infoScrollPane.setBorder(null); + infoPanel.add(infoScrollPane, BorderLayout.CENTER); + + return infoPanel; + } + + private JPanel createVisualizationPanel() { + JPanel panel = new JPanel(new BorderLayout(5, 5)); + panel.setBackground(TABLE_HEADER); + panel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + "内存占比", + javax.swing.border.TitledBorder.LEFT, + javax.swing.border.TitledBorder.TOP, + DebugWindow.MAIN_FONT, + FOREGROUND + )); + + JLabel ratioTitle = new JLabel("堆内存占比:"); + ratioTitle.setForeground(FOREGROUND); + ratioTitle.setFont(DebugWindow.MAIN_FONT); + + ratioBar = new JProgressBar(0, 100); + ratioBar.setStringPainted(true); + ratioBar.setFont(DebugWindow.MAIN_FONT); + ratioBar.setBackground(new Color(60, 60, 60)); + ratioBar.setForeground(PERCENT_LOW); + ratioBar.setBorder(BorderFactory.createEmptyBorder()); + + ratioLabel = new JLabel("0.0%"); + ratioLabel.setForeground(FOREGROUND); + ratioLabel.setFont(DebugWindow.MAIN_FONT); + ratioLabel.setHorizontalAlignment(SwingConstants.RIGHT); + ratioLabel.setPreferredSize(new Dimension(80, 20)); + + JPanel barPanel = new JPanel(new BorderLayout(10, 0)); + barPanel.setBackground(TABLE_HEADER); + barPanel.add(ratioBar, BorderLayout.CENTER); + barPanel.add(ratioLabel, BorderLayout.EAST); + + panel.add(ratioTitle, BorderLayout.WEST); + panel.add(barPanel, BorderLayout.CENTER); + + return panel; + } + + private JPanel createInstancesPanel() { + JPanel instancesPanel = new JPanel(new BorderLayout()); + instancesPanel.setBackground(TABLE_HEADER); + instancesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + "实例列表 (最多100个)", + javax.swing.border.TitledBorder.LEFT, + javax.swing.border.TitledBorder.TOP, + DebugWindow.MAIN_FONT, + FOREGROUND + )); + + // 添加搜索面板 + JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5)); + searchPanel.setBackground(TABLE_HEADER); + searchPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + + JLabel searchLabel = new JLabel("搜索实例:"); + searchLabel.setForeground(FOREGROUND); + searchLabel.setFont(DebugWindow.MAIN_FONT); + + instanceSearchField = new JTextField(20); + instanceSearchField.setFont(DebugWindow.MAIN_FONT); + instanceSearchField.setBackground(new Color(60, 60, 60)); + instanceSearchField.setForeground(FOREGROUND); + instanceSearchField.setCaretColor(FOREGROUND); + instanceSearchField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + BorderFactory.createEmptyBorder(5, 5, 5, 5) + )); + + // 设置实例表格的排序器 + instanceSorter = new TableRowSorter<>(specificClassInstanceModel); + specificClassInstanceTable.setRowSorter(instanceSorter); + + // 添加搜索监听器 + instanceSearchField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + filterInstanceTable(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + filterInstanceTable(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + filterInstanceTable(); + } + }); + + JButton clearButton = new JButton("清除"); + configureButton(clearButton, e -> { + instanceSearchField.setText(""); + filterInstanceTable(); + }); + + searchPanel.add(searchLabel); + searchPanel.add(instanceSearchField); + searchPanel.add(clearButton); + + // 添加双击事件监听器 + specificClassInstanceTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + int viewRow = specificClassInstanceTable.rowAtPoint(e.getPoint()); + if (viewRow >= 0) { + int modelRow = specificClassInstanceTable.convertRowIndexToModel(viewRow); + Object instance = specificClassInstanceModel.getValueAt(modelRow, 1); + if (instance != null) { + showInstanceDetailsDialog(instance); + } + } + } + } + }); + + JScrollPane instanceScrollPane = new JScrollPane(specificClassInstanceTable); + instanceScrollPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + + instancesPanel.add(searchPanel, BorderLayout.NORTH); + instancesPanel.add(instanceScrollPane, BorderLayout.CENTER); + + return instancesPanel; + } + + private void filterInstanceTable() { + String text = instanceSearchField.getText().trim(); + if (text.isEmpty()) { + instanceSorter.setRowFilter(null); + } else { + // 在第二列(ToString)中搜索 + instanceSorter.setRowFilter(RowFilter.regexFilter("(?i)" + text, 1)); + } + } + + private void analyzeSpecificClass() { + String className = specificClassField.getText().trim(); + if (className.isEmpty()) { + JOptionPane.showMessageDialog(this, "请输入完整类名", "错误", JOptionPane.WARNING_MESSAGE); + return; + } + + showLoadingDialog("正在分析类: " + className + "..."); + + currentWorker = new SpecificClassAnalysisWorker(className); + currentWorker.addPropertyChangeListener(evt -> { + if (evt.getPropertyName().equals("state") && + evt.getNewValue() == SwingWorker.StateValue.DONE) { + + try { + // 更新类信息 + updateClassInfo(); + + // 更新实例表格 + updateInstanceTable(); + + // 更新内存占比可视化 + updateVisualization(); + + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(MemoryAnalysisPanel.this, + "分析失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); + } finally { + hideLoadingDialog(); + } + } + }); + currentWorker.execute(); + } + + private void updateVisualization() { + if (currentWorker instanceof SpecificClassAnalysisWorker) { + SpecificClassAnalysisWorker worker = (SpecificClassAnalysisWorker) currentWorker; + ClassMemoryData data = worker.getMemoryData(); + if (data != null) { + int percent = (int) (data.ratio * 100); + ratioBar.setValue(percent); + ratioLabel.setText(PERCENT_FORMAT.format(data.ratio)); + + // 根据占比设置颜色 + if (percent > 10) { + ratioBar.setForeground(PERCENT_HIGH); + } else if (percent > 5) { + ratioBar.setForeground(PERCENT_MEDIUM); + } else { + ratioBar.setForeground(PERCENT_LOW); + } + } + } + } + + // 查找类 + private Class findClassByName(String className) { + for (Class clazz : instrumentation.getAllLoadedClasses()) { + if (clazz.getName().equals(className)) { + return clazz; + } + } + return null; + } + + // 计算类内存数据 + private ClassMemoryData calculateClassMemoryData(Class clazz) { + Object[] instances = GetInstance.getInstance(clazz); + if (instances == null || instances.length == 0) { + return new ClassMemoryData(clazz, 0, 0, 0, 0); + } + + long objectSize = instrumentation.getObjectSize(clazz); + long instanceCount = instances.length; + long totalSize = objectSize * instanceCount; + + // 计算占比(基于堆内存使用) + MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage(); + long totalHeapUsed = heapUsage.getUsed(); + double ratio = totalHeapUsed > 0 ? (double) totalSize / totalHeapUsed : 0; + + return new ClassMemoryData(clazz, objectSize, instanceCount, totalSize, ratio); + } + + // 更新类信息 + private void updateClassInfo() { + if (currentWorker == null || currentWorker.isCancelled()) return; + + if (currentWorker instanceof SpecificClassAnalysisWorker) { + SpecificClassAnalysisWorker worker = (SpecificClassAnalysisWorker) currentWorker; + ClassMemoryData data = worker.getMemoryData(); + if (data == null) return; + + StringBuilder info = new StringBuilder(); + Class clazz = data.clazz; + + // 基本类信息 + info.append("类名: ").append(clazz.getName()).append("\n\n"); + info.append("修饰符: ").append(Modifier.toString(clazz.getModifiers())).append("\n\n"); + info.append("父类: ").append(clazz.getSuperclass() != null ? + clazz.getSuperclass().getName() : "无").append("\n\n"); + + // 接口 + Class[] interfaces = clazz.getInterfaces(); + if (interfaces.length > 0) { + info.append("实现的接口:\n"); + for (Class iface : interfaces) { + info.append(" - ").append(iface.getName()).append("\n"); + } + info.append("\n"); + } + + // 字段信息 + Field[] fields = clazz.getDeclaredFields(); + info.append("字段 (").append(fields.length).append("):\n"); + for (Field field : fields) { + info.append(" ") + .append(Modifier.toString(field.getModifiers())).append(" ") + .append(field.getType().getSimpleName()).append(" ") + .append(field.getName()).append("\n"); + } + info.append("\n"); + + // 内存信息 + info.append("内存使用:\n"); + info.append(" 实例大小: ").append(formatBytes(data.objectSize)).append("\n"); + info.append(" 实例数量: ").append(String.format("%,d", data.instanceCount)).append("\n"); + info.append(" 总内存: ").append(MB_FORMAT.format(data.totalSize / (1024.0 * 1024.0))).append(" MB\n"); + info.append(" 堆内存占比: ").append(PERCENT_FORMAT.format(data.ratio)).append("\n"); + + classInfoArea.setText(info.toString()); + } + } + + + // 更新实例表格 + private void updateInstanceTable() { + if (currentWorker == null || currentWorker.isCancelled()) return; + + specificClassInstanceModel.setRowCount(0); + + // 检查当前 worker 是否是分析特定类的任务 + if (currentWorker instanceof SpecificClassAnalysisWorker) { + SpecificClassAnalysisWorker worker = (SpecificClassAnalysisWorker) currentWorker; + List instances = worker.getInstances(); + List instanceSizes = worker.getInstanceSizes(); + + if (instances != null && !instances.isEmpty()) { + for (int i = 0; i < instances.size(); i++) { + Object instance = instances.get(i); + specificClassInstanceModel.addRow(new Object[]{ + i + 1, + instanceToString(instance), + instanceSizes.get(i) + }); + } + } + } + } + + + private class SpecificClassAnalysisWorker extends SwingWorker { + private final Class targetClass; + private ClassMemoryData memoryData; + private List instances = new ArrayList<>(); + private List instanceSizes = new ArrayList<>(); + private final String className; + + public SpecificClassAnalysisWorker(String className) { + // 查找类 + this.className = className; + this.targetClass = findClassByName(className); + } + + @Override + protected Void doInBackground() { + if (targetClass == null) { + throw new RuntimeException("未找到类: " + className); + } + + // 获取内存数据 + memoryData = calculateClassMemoryData(targetClass); + + // 获取实例 + Object[] allInstances = GetInstance.getInstance(targetClass); + if (allInstances == null || allInstances.length == 0) { + return null; + } + + // 计算实例大小并排序(按内存大小降序) + for (int i = 0; i < allInstances.length; i++) { + Object instance = allInstances[i]; + instances.add(instance); + instanceSizes.add(instrumentation.getObjectSize(instance)); + } + + // 按内存大小排序(降序) + sortInstancesBySize(); + + return null; + } + + private void sortInstancesBySize() { + // 使用索引数组排序避免复制大对象 + Integer[] indices = new Integer[instances.size()]; + for (int i = 0; i < indices.length; i++) { + indices[i] = i; + } + + Arrays.sort(indices, (i1, i2) -> + Long.compare(instanceSizes.get(i2), instanceSizes.get(i1))); + + // 重新排列实例和大小 + List sortedInstances = new ArrayList<>(); + List sortedSizes = new ArrayList<>(); + + for (int index : indices) { + sortedInstances.add(instances.get(index)); + sortedSizes.add(instanceSizes.get(index)); + } + + instances = sortedInstances; + instanceSizes = sortedSizes; + } + + public ClassMemoryData getMemoryData() { + return memoryData; + } + + public List getInstances() { + return instances; + } + + public List getInstanceSizes() { + return instanceSizes; + } + } + + private void showInstanceDetailsDialog(Object instance) { + JDialog dialog = new JDialog((Window) SwingUtilities.getWindowAncestor(this), + "实例详情", Dialog.ModalityType.APPLICATION_MODAL); + dialog.setSize(500, 350); + dialog.setLayout(new BorderLayout()); + dialog.setLocationRelativeTo(this); + + JPanel contentPanel = new JPanel(new BorderLayout(10, 10)); + contentPanel.setBorder(new EmptyBorder(15, 15, 15, 15)); + contentPanel.setBackground(BACKGROUND); + + // 基本信息面板 + JPanel infoPanel = new JPanel(new GridBagLayout()); + infoPanel.setBackground(TABLE_HEADER); + infoPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 10); + gbc.gridx = 0; + gbc.gridy = 0; + + // 获取实例信息 + Class clazz = instance.getClass(); + ClassLoader classLoader = clazz.getClassLoader(); + String classLoaderName = classLoader != null ? classLoader.toString() : "Bootstrap ClassLoader"; + + // 获取类位置 + String classLocation = "未知"; + try { + URL classUrl = clazz.getResource('/' + clazz.getName().replace('.', '/') + ".class"); + if (classUrl != null) { + classLocation = classUrl.toString(); + // 如果是jar文件,提取jar路径 + if (classLocation.startsWith("jar:")) { + classLocation = classLocation.substring(4, classLocation.indexOf("!")); + } + } + } catch (Exception e) { + classLocation = "无法获取: " + e.getMessage(); + } + + // 获取实例大小 + long instanceSize = instrumentation.getObjectSize(instance); + + // 添加信息行 + addInfoRow(infoPanel, gbc, "实例ToString:", instanceToString(instance)); + gbc.gridy++; + addInfoRow(infoPanel, gbc, "类名:", clazz.getName()); + gbc.gridy++; + addInfoRow(infoPanel, gbc, "实例大小:", formatBytes(instanceSize)); + gbc.gridy++; + addInfoRow(infoPanel, gbc, "类加载器:", classLoaderName); + gbc.gridy++; + addInfoRow(infoPanel, gbc, "类位置:", classLocation); + + // 添加关闭按钮 + JButton closeButton = new JButton("关闭"); + configureButton(closeButton, e -> dialog.dispose()); + closeButton.setPreferredSize(new Dimension(100, 30)); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.setBackground(TABLE_HEADER); + buttonPanel.add(closeButton); + + contentPanel.add(infoPanel, BorderLayout.CENTER); + contentPanel.add(buttonPanel, BorderLayout.SOUTH); + + dialog.add(contentPanel, BorderLayout.CENTER); + dialog.setVisible(true); + } + + private void addInfoRow(JPanel panel, GridBagConstraints gbc, String label, String value) { + JLabel lbl = new JLabel(label); + lbl.setForeground(FOREGROUND); + lbl.setFont(DebugWindow.MAIN_FONT); + panel.add(lbl, gbc); + + gbc.gridx++; + JTextArea val = new JTextArea(value); + val.setEditable(false); + val.setLineWrap(true); + val.setWrapStyleWord(true); + val.setBackground(TABLE_HEADER); + val.setForeground(ACCENT); + val.setFont(DebugWindow.MONOSPACE_FONT); + val.setBorder(null); + panel.add(val, gbc); + + gbc.gridx = 0; + gbc.gridy++; + } + + private void filterTable() { + String text = searchField.getText(); + if (text.trim().isEmpty()) { + objectMemorySorter.setRowFilter(null); + } else { + objectMemorySorter.setRowFilter(RowFilter.regexFilter("(?i)" + text, 0)); + } + } + + private void showClassDetailsDialog(ClassMemoryData data) { + JDialog dialog = new JDialog((Window) SwingUtilities.getWindowAncestor(this), "类详情: " + data.clazz.getName(), Dialog.ModalityType.APPLICATION_MODAL); + dialog.setSize(600, 400); + dialog.setLayout(new BorderLayout()); + dialog.setLocationRelativeTo(this); + + JPanel contentPanel = new JPanel(new BorderLayout(10, 10)); + contentPanel.setBorder(new EmptyBorder(15, 15, 15, 15)); + contentPanel.setBackground(BACKGROUND); + + // 基本信息面板 + JPanel infoPanel = new JPanel(new GridLayout(0, 2, 10, 10)); + infoPanel.setBackground(TABLE_HEADER); + infoPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + addInfoRow(infoPanel, "类名:", data.clazz.getName()); + addInfoRow(infoPanel, "实例大小:", formatBytes(data.objectSize)); + addInfoRow(infoPanel, "实例数量:", String.format("%,d", data.instanceCount)); + addInfoRow(infoPanel, "总内存:", MB_FORMAT.format(data.totalSize / (1024.0 * 1024.0)) + " MB"); + + // 实例列表 + JPanel instancesPanel = new JPanel(new BorderLayout()); + instancesPanel.setBackground(TABLE_HEADER); + instancesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(new Color(80, 80, 80)), + "实例列表 (最多100个)", + javax.swing.border.TitledBorder.LEFT, + javax.swing.border.TitledBorder.TOP, + DebugWindow.MAIN_FONT, + FOREGROUND + )); + + DefaultListModel listModel = new DefaultListModel<>(); + JList instancesList = new JList<>(listModel); + instancesList.setBackground(new Color(50, 50, 50)); + instancesList.setForeground(FOREGROUND); + instancesList.setFont(DebugWindow.MONOSPACE_FONT); + instancesList.setSelectionBackground(ACCENT); + + JScrollPane listScrollPane = new JScrollPane(instancesList); + listScrollPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + instancesPanel.add(listScrollPane, BorderLayout.CENTER); + + // 加载实例数据 + showLoadingDialog("正在加载实例数据..."); + + new SwingWorker() { + @Override + protected Void doInBackground() { + try { + // 获取实例 + Object[] instances = GetInstance.getInstance(data.clazz); + if (instances == null) { + publish("无法获取实例"); + return null; + } + + int max = Math.min(100, instances.length); + for (int i = 0; i < max; i++) { + Object instance = instances[i]; + publish(String.format("[%d] %s", i + 1, instanceToString(instance))); + } + } catch (Exception e) { + publish("加载实例时出错: " + e.getMessage()); + } + return null; + } + + @Override + protected void process(List chunks) { + for (String item : chunks) { + listModel.addElement(item); + } + } + + @Override + protected void done() { + hideLoadingDialog(); + } + }.execute(); + + contentPanel.add(infoPanel, BorderLayout.NORTH); + contentPanel.add(instancesPanel, BorderLayout.CENTER); + + dialog.add(contentPanel, BorderLayout.CENTER); + dialog.setVisible(true); + } + + private String instanceToString(Object instance) { + try { + // 尝试调用toString方法 + return instance.toString(); + } catch (Exception e) { + // 如果出错,使用默认表示 + return instance.getClass().getName() + "@" + Integer.toHexString(instance.hashCode()); + } + } + + private void addInfoRow(JPanel panel, String label, String value) { + JLabel lbl = new JLabel(label); + lbl.setForeground(FOREGROUND); + lbl.setFont(DebugWindow.MAIN_FONT); + + JLabel val = new JLabel(value); + val.setForeground(ACCENT); + val.setFont(DebugWindow.MAIN_FONT); + + panel.add(lbl); + panel.add(val); + } + + private String formatBytes(long bytes) { + if (bytes < 1024) { + return bytes + " bytes"; + } else if (bytes < 1024 * 1024) { + return String.format("%.2f KB", bytes / 1024.0); + } else { + return String.format("%.2f MB", bytes / (1024.0 * 1024.0)); + } + } + + private void createLoadingDialog() { + // 使用非模态对话框,避免阻塞主线程 + loadingDialog = new JDialog((Window) SwingUtilities.getWindowAncestor(this), "加载中", Dialog.ModalityType.MODELESS); + loadingDialog.setSize(300, 180); + loadingDialog.setLayout(new BorderLayout()); + loadingDialog.setLocationRelativeTo(this); + loadingDialog.setResizable(false); + loadingDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); // 防止用户关闭 + + JPanel contentPanel = new JPanel(new BorderLayout(10, 10)); + contentPanel.setBorder(new EmptyBorder(20, 20, 20, 20)); + contentPanel.setBackground(BACKGROUND); + + // 创建动画面板 + spinner = new SpinnerAnimation(); + spinner.setPreferredSize(new Dimension(80, 80)); + + JPanel spinnerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + spinnerPanel.setBackground(BACKGROUND); + spinnerPanel.add(spinner); + + loadingLabel = new JLabel("正在加载数据...", SwingConstants.CENTER); + loadingLabel.setForeground(FOREGROUND); + loadingLabel.setFont(DebugWindow.MAIN_FONT); + + loadingProgressBar = new JProgressBar(); + loadingProgressBar.setIndeterminate(true); // 不确定进度模式 + loadingProgressBar.setPreferredSize(new Dimension(200, 10)); + loadingProgressBar.setBorderPainted(false); + loadingProgressBar.setStringPainted(false); + + // 添加取消按钮 + JButton cancelButton = new JButton("取消"); + cancelButton.setBackground(new Color(180, 0, 0)); + cancelButton.setForeground(FOREGROUND); + cancelButton.setFocusPainted(false); + cancelButton.addActionListener(e -> { + if (currentWorker != null) { + currentWorker.cancel(true); + } + hideLoadingDialog(); + }); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setBackground(BACKGROUND); + buttonPanel.add(cancelButton); + + contentPanel.add(spinnerPanel, BorderLayout.NORTH); + contentPanel.add(loadingLabel, BorderLayout.CENTER); + contentPanel.add(loadingProgressBar, BorderLayout.SOUTH); + contentPanel.add(buttonPanel, BorderLayout.SOUTH); + + loadingDialog.add(contentPanel, BorderLayout.CENTER); + } + + private void showLoadingDialog(String message) { + loadingLabel.setText(message); + loadingProgressBar.setIndeterminate(true); + spinner.startAnimation(); + loadingDialog.pack(); + loadingDialog.setLocationRelativeTo(SwingUtilities.getWindowAncestor(this)); + loadingDialog.setVisible(true); + } + + private void hideLoadingDialog() { + spinner.stopAnimation(); + loadingDialog.setVisible(false); + } + + // 初始加载:只加载内存使用和内存池数据 + private void refreshInitialData() { + currentWorker = new SwingWorker() { + @Override + protected Void doInBackground() { + updateMemoryUsage(); + updateMemoryPools(); + return null; + } + + @Override + protected void done() { + try { + get(); // 检查是否有异常 + } catch (CancellationException e) { + // 任务被取消,不做处理 + } catch (Exception e) { + e.printStackTrace(); + } finally { + hideLoadingDialog(); + currentWorker = null; + + // 设置定时刷新 + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + SwingUtilities.invokeLater(() -> { + updateMemoryUsage(); + updateMemoryPools(); + }); + } + }, 5000, 5000); + } + } + }; + currentWorker.execute(); + } + + // 刷新对象内存数据 + private void refreshObjectMemoryData() { + if (objectMemoryDataLoaded) return; + + showLoadingDialog("正在加载对象内存数据..."); + + currentWorker = new SwingWorker() { + private int totalClasses; + private int processed; + + @Override + protected Void doInBackground() { + if (instrumentation == null) { + throw new RuntimeException("无法获取内存分析数据: Instrumentation 不可用"); + } + + Class[] loadedClasses = instrumentation.getAllLoadedClasses(); + totalClasses = loadedClasses.length; + processed = 0; + + // 获取堆内存使用量作为总基准 + MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage(); + long totalHeapUsed = heapUsage.getUsed(); + + // 存储类内存数据 + Map, ClassMemoryData> classMemoryMap = new HashMap<>(); + currentObjectMemoryData.clear(); + + for (Class clazz : loadedClasses) { + if (isCancelled()) { + return null; + } + + try { + if (clazz.isInterface() || clazz.isPrimitive() || clazz.isArray() || + clazz.isAnnotation() || clazz == void.class + || clazz.getName().startsWith("java.") + || clazz.getName().startsWith("org.apache.") + || clazz.getName().startsWith("jdk.")) { + processed++; + continue; + } + + // 获取实际实例数组 + Object[] instances = null; + try { + instances = GetInstance.getInstance(clazz); + if (instances == null) { + processed++; + continue; + } + } catch (Exception e) { + processed++; + continue; + } + + long instanceCount = instances.length; + if (instanceCount == 0) { + processed++; + continue; + } + + // 计算实例总大小 + long objectSize = instrumentation.getObjectSize(clazz); + long totalSize = objectSize * instanceCount; + + ClassMemoryData data = new ClassMemoryData( + clazz, + objectSize, + instanceCount, + totalSize, + totalHeapUsed > 0 ? (double) totalSize / totalHeapUsed : 0 + ); + + classMemoryMap.put(clazz, data); + + } catch (Exception e) { + // 忽略异常类 + } + + processed++; + int progress = (int) ((double) processed / totalClasses * 100); + publish(progress); + } + + // 转换为列表并排序 + currentObjectMemoryData = new ArrayList<>(classMemoryMap.values()); + currentObjectMemoryData.sort((a, b) -> Long.compare(b.totalSize, a.totalSize)); // 降序 + + // 更新表格 + SwingUtilities.invokeLater(() -> { + objectMemoryModel.setRowCount(0); + + // 显示前1000个结果 + int count = Math.min(1000, currentObjectMemoryData.size()); + for (int i = 0; i < count; i++) { + ClassMemoryData data = currentObjectMemoryData.get(i); + double memoryMB = data.totalSize / (1024.0 * 1024.0); + + objectMemoryModel.addRow(new Object[]{ + data.clazz.getName(), + data.objectSize, + data.instanceCount, + MB_FORMAT.format(memoryMB), + PERCENT_FORMAT.format(data.ratio) + }); + } + }); + + return null; + } + + @Override + protected void process(List chunks) { + if (!chunks.isEmpty()) { + int progress = chunks.get(chunks.size() - 1); + loadingProgressBar.setIndeterminate(false); + loadingProgressBar.setValue(progress); + loadingProgressBar.setString(progress + "%"); + } + } + + @Override + protected void done() { + try { + get(); // 检查是否有异常 + objectMemoryDataLoaded = true; + } catch (CancellationException e) { + // 任务被取消,不做处理 + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(MemoryAnalysisPanel.this, + "加载对象内存数据失败: " + e.getMessage(), + "错误", + JOptionPane.ERROR_MESSAGE); + } finally { + hideLoadingDialog(); + currentWorker = null; + } + } + }; + currentWorker.execute(); + } + + private void refreshMemoryData() { + showLoadingDialog("正在刷新内存数据..."); + + currentWorker = new SwingWorker() { + @Override + protected Void doInBackground() { + updateMemoryUsage(); + updateMemoryPools(); + + // 如果当前在对象内存标签页或数据已加载过,刷新对象内存 + if (tabbedPane.getSelectedIndex() == 1 || objectMemoryDataLoaded) { + refreshObjectMemoryData(); + } + return null; + } + + @Override + protected void done() { + try { + get(); // 检查是否有异常 + objectMemoryDataLoaded = true; + } catch (CancellationException e) { + // 任务被取消,不做处理 + } catch (Exception e) { + e.printStackTrace(); + } finally { + hideLoadingDialog(); + currentWorker = null; + } + } + }; + currentWorker.execute(); + } + + private void configureTable(JTable table) { + table.setFont(DebugWindow.MONOSPACE_FONT); + table.setBackground(BACKGROUND); + table.setForeground(FOREGROUND); + table.setGridColor(new Color(60, 60, 60)); + table.setRowHeight(25); + table.setSelectionBackground(ACCENT); + table.setSelectionForeground(FOREGROUND); + + // 表格头样式 + JTableHeader header = table.getTableHeader(); + header.setBackground(TABLE_HEADER); + header.setForeground(FOREGROUND); + header.setFont(DebugWindow.MAIN_FONT.deriveFont(Font.BOLD)); + + // 自定义渲染器 + table.setDefaultRenderer(Object.class, new MemoryTableCellRenderer()); + } + + private JPanel createMemoryUsagePanel(String title, JProgressBar progressBar, JLabel label) { + JPanel panel = new JPanel(new BorderLayout(10, 0)); + panel.setBackground(TABLE_HEADER); + + JLabel titleLabel = new JLabel(title); + titleLabel.setForeground(FOREGROUND); + titleLabel.setFont(DebugWindow.MAIN_FONT); + + // 配置进度条 + progressBar.setStringPainted(true); + progressBar.setForeground(MEMORY_SAFE); + progressBar.setBackground(new Color(60, 60, 60)); + progressBar.setFont(DebugWindow.MAIN_FONT); + + // 配置标签 + label.setForeground(FOREGROUND); + label.setFont(DebugWindow.MAIN_FONT); + label.setHorizontalAlignment(SwingConstants.RIGHT); + label.setPreferredSize(new Dimension(150, 20)); + + panel.add(titleLabel, BorderLayout.WEST); + panel.add(progressBar, BorderLayout.CENTER); + panel.add(label, BorderLayout.EAST); + + return panel; + } + + private void configureButton(JButton button, ActionListener listener) { + button.setBackground(ACCENT); + button.setForeground(FOREGROUND); + button.setFocusPainted(false); + button.addActionListener(e -> { + if (listener != null) { + listener.actionPerformed(e); + } + }); + } + + private void updateMemoryUsage() { + // 更新堆内存使用情况 + MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage(); + updateMemoryUsageBar(heapUsageBar, heapUsageLabel, heapUsage, "堆"); + + // 更新非堆内存使用情况 + MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage(); + updateMemoryUsageBar(nonHeapUsageBar, nonHeapUsageLabel, nonHeapUsage, "非堆"); + } + + private void updateMemoryUsageBar(JProgressBar bar, JLabel label, MemoryUsage usage, String type) { + long used = usage.getUsed(); + long max = usage.getMax(); + double usageRatio = max > 0 ? (double) used / max : 0; + + // 设置进度条值 + int percent = (int) (usageRatio * 100); + bar.setValue(percent); + + // 根据使用率设置颜色 + if (percent > 85) { + bar.setForeground(MEMORY_CRITICAL); + } else if (percent > 70) { + bar.setForeground(MEMORY_WARNING); + } else { + bar.setForeground(MEMORY_SAFE); + } + + // 设置标签文本 + String usedMB = MB_FORMAT.format(used / (1024.0 * 1024.0)); + String maxMB = max == -1 ? "无限制" : MB_FORMAT.format(max / (1024.0 * 1024.0)); + String ratio = PERCENT_FORMAT.format(usageRatio); + label.setText(String.format("%s / %s (%s)", usedMB, maxMB, ratio)); + } + + private void updateMemoryPools() { + SwingUtilities.invokeLater(() -> { + memoryPoolModel.setRowCount(0); + + for (MemoryPoolMXBean pool : memoryPoolBeans) { + MemoryUsage usage = pool.getUsage(); + long used = usage.getUsed(); + long max = usage.getMax(); + double usageRatio = max > 0 ? (double) used / max : 0; + + String poolName = pool.getName(); + String type = pool.getType().toString(); + String totalMB = max == -1 ? "无限制" : MB_FORMAT.format(max / (1024.0 * 1024.0)); + String usedMB = MB_FORMAT.format(used / (1024.0 * 1024.0)); + String ratio = PERCENT_FORMAT.format(usageRatio); + + memoryPoolModel.addRow(new Object[]{ + poolName, type, totalMB, usedMB, ratio + }); + } + }); + } + + private static class ClassMemoryData { + final Class clazz; + final long objectSize; + final long instanceCount; + final long totalSize; + final double ratio; + + ClassMemoryData(Class clazz, long objectSize, long instanceCount, long totalSize, double ratio) { + this.clazz = clazz; + this.objectSize = objectSize; + this.instanceCount = instanceCount; + this.totalSize = totalSize; + this.ratio = ratio; + } + } + + // 自定义动画组件 + static class SpinnerAnimation extends JComponent implements Runnable { + private volatile boolean running = false; + private double angle = 0; + private static final int FPS = 24; + private static final int DELAY = 1000 / FPS; + private static final double ANGLE_INCREMENT = Math.PI / 12; + + public SpinnerAnimation() { + setPreferredSize(new Dimension(80, 80)); + } + + public void startAnimation() { + if (!running) { + running = true; + new Thread(this).start(); + } + } + + public void stopAnimation() { + running = false; + } + + @Override + public void run() { + while (running) { + angle += ANGLE_INCREMENT; + if (angle > Math.PI * 2) { + angle -= Math.PI * 2; + } + repaint(); + try { + Thread.sleep(DELAY); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g.create(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + int width = getWidth(); + int height = getHeight(); + int size = Math.min(width, height) - 20; + int x = (width - size) / 2; + int y = (height - size) / 2; + + // 绘制背景圆 + g2d.setColor(new Color(60, 60, 60)); + g2d.fillOval(x, y, size, size); + + // 绘制旋转的弧 + g2d.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + g2d.setColor(new Color(0, 122, 204)); + + int arcSize = size - 10; + int arcX = x + 5; + int arcY = y + 5; + + double start = angle; + double extent = Math.PI * 1.5; + + for (int i = 0; i < 8; i++) { + double alpha = (8 - i) * 0.1; + g2d.setColor(new Color(0, 122, 204, (int)(alpha * 255))); + g2d.drawArc(arcX, arcY, arcSize, arcSize, + (int) Math.toDegrees(start), + (int) Math.toDegrees(extent)); + start += Math.PI / 4; + } + + g2d.dispose(); + } + } + + // 内存表格单元格渲染器 + static class MemoryTableCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + c.setBackground(row % 2 == 0 ? TABLE_ROW : TABLE_ALT_ROW); + c.setForeground(FOREGROUND); + + // 设置对齐方式 + if (column == 0) { // 类名列左对齐 + ((JLabel) c).setHorizontalAlignment(SwingConstants.LEFT); + } else if (column == 2 || column == 3 || column == 4) { // 数值列右对齐 + ((JLabel) c).setHorizontalAlignment(SwingConstants.RIGHT); + } + + // 使用率列特殊着色和可视化 + if (column == 4 && value instanceof String) { + String ratioStr = (String) value; + try { + double ratio = Double.parseDouble(ratioStr.replace("%", "")) / 100.0; + + // 设置文本颜色 + if (ratio > 0.05) { + c.setForeground(MEMORY_CRITICAL); + } else if (ratio > 0.01) { + c.setForeground(MEMORY_WARNING); + } else { + c.setForeground(MEMORY_SAFE); + } + + // 添加占比可视化 + if (table.getModel() instanceof DefaultTableModel) { + JLabel label = (JLabel) c; + label.setText(""); // 清除文本,我们将在自定义绘制中绘制 + + return new JComponent() { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g.create(); + + // 绘制背景 + g2d.setColor(getBackground()); + g2d.fillRect(0, 0, getWidth(), getHeight()); + + // 绘制文本 + g2d.setColor(c.getForeground()); + g2d.setFont(c.getFont()); + FontMetrics fm = g2d.getFontMetrics(); + Rectangle2D textBounds = fm.getStringBounds(ratioStr, g2d); + int textX = getWidth() - (int) textBounds.getWidth() - 5; + int textY = (getHeight() - fm.getHeight()) / 2 + fm.getAscent(); + g2d.drawString(ratioStr, textX, textY); + + // 绘制占比条 + int barWidth = (int) (getWidth() * 0.6); + int barHeight = 8; + int barX = 5; + int barY = (getHeight() - barHeight) / 2; + + // 背景条 + g2d.setColor(new Color(60, 60, 60)); + g2d.fillRect(barX, barY, barWidth, barHeight); + + // 前景条 + Color barColor; + if (ratio > 0.05) { + barColor = PERCENT_HIGH; + } else if (ratio > 0.01) { + barColor = PERCENT_MEDIUM; + } else { + barColor = PERCENT_LOW; + } + g2d.setColor(barColor); + int fillWidth = (int) (barWidth * Math.min(1.0, ratio * 5)); // 放大5倍以便可视化 + g2d.fillRect(barX, barY, fillWidth, barHeight); + + g2d.dispose(); + } + + @Override + public Dimension getPreferredSize() { + return c.getPreferredSize(); + } + }; + } + } catch (NumberFormatException ignored) {} + } + + return c; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/tools/FolderCreator.java b/src/main/java/com/axis/innovators/box/tools/FolderCreator.java index 60b8cc6..9fa7983 100644 --- a/src/main/java/com/axis/innovators/box/tools/FolderCreator.java +++ b/src/main/java/com/axis/innovators/box/tools/FolderCreator.java @@ -2,8 +2,10 @@ package com.axis.innovators.box.tools; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.api.dog.agent.VirtualMachine; import java.io.File; +import java.lang.management.ManagementFactory; /** * 在当前jar下创建文件夹 @@ -85,6 +87,15 @@ public class FolderCreator { return folder; } + public static void main(String[] args) throws Exception { + String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + String pid = jvmName.split("@")[0]; + System.load("C:\\Users\\Administrator\\MCreatorWorkspaces\\mineralluminescence\\DogAgent.dll"); + VirtualMachine vm = VirtualMachine.getVirtualMachine(Float.parseFloat(pid), true); + //vm.getInstrumentation(); + vm.getInstrumentation(); + } + public static String getLibraryFolder() { String folder = createFolder(LIBRARY_NAME); if (folder == null) { diff --git a/src/main/java/org/tzd/debug/ClassDebug.java b/src/main/java/org/tzd/debug/ClassDebug.java new file mode 100644 index 0000000..e2bb0e4 --- /dev/null +++ b/src/main/java/org/tzd/debug/ClassDebug.java @@ -0,0 +1,227 @@ +package org.tzd.debug; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.api.dog.agent.VirtualMachine; +import org.tzd.debug.util.GlobalObjects; + +import javax.tools.*; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Locale; +import java.util.stream.Collectors; + +/** + * 对类进行监控和debug操作 + * @author tzdwindows 7 + */ +public class ClassDebug { + private static final Logger logger = LogManager.getLogger(ClassDebug.class); + private static Instrumentation instrumentation; + + /** + * 监控类加载 + * @param monitor 监控器 + */ + public static void monitoringLoading(LoadingMonitor monitor) { + if (instrumentation != null) { + instrumentation.addTransformer(new ClassFileTransformer() { + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + monitor.load(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + return classfileBuffer; + } + }); + } else { + logger.error("Instrumentation is null"); + try { + VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); + instrumentation = vm.getInstrumentation(); + } catch (Exception e) { + logger.error("Failed to attach to VM: {}", e.getMessage()); + } + monitoringLoading(monitor); + } + } + + public static Instrumentation getInstrumentation() { + if (instrumentation == null) { + try { + VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); + instrumentation = vm.getInstrumentation(); + } catch (Exception e) { + logger.error("Failed to attach to VM: {}", e.getMessage()); + } + } + return instrumentation; + } + + /** + * 执行动态代码 + * @param code 要执行的代码 + * @return 执行结果(空字符串表示成功,否则为错误信息) + */ + public static String executeCode(String code) { + GlobalObjects globalObjects = new GlobalObjects(); + globalObjects.instrumentation = instrumentation; + + + // 1. 预处理代码:替换print/printf为System.out + String processedCode = code.trim(); + if (processedCode.startsWith("print(") && processedCode.endsWith(");")) { + processedCode = "System.out.println" + processedCode.substring("print".length()); + } else if (processedCode.startsWith("printf(") && processedCode.endsWith(");")) { + processedCode = "System.out.printf" + processedCode.substring("printf".length()); + } + + // 2. 构建完整类代码 + String className = "DynamicExecutedCode"; + String fullCode = "package dynamic;\n" + // 添加包声明避免冲突 + "import org.tzd.debug.util.GlobalObjects;import java.util.*;\n" + + "public class " + className + " {\n" + + " public static GlobalObjects global;\n" + // 静态字段接收全局对象 + " public static void run() {\n" + + " try {\n" + // 添加try-catch捕获用户代码异常 + processedCode + "\n" + + " } catch (Throwable t) {\n" + + " throw new RuntimeException(t);\n" + // 包装异常以保持堆栈 + " }\n" + + " }\n" + + "}"; + + Path tempDir = null; + try { + // 3. 创建临时目录和源文件 + tempDir = Files.createTempDirectory("dynamicCode"); + Path sourceDir = tempDir.resolve("dynamic"); + Files.createDirectories(sourceDir); + Path sourceFile = sourceDir.resolve(className + ".java"); + Files.write(sourceFile, fullCode.getBytes(StandardCharsets.UTF_8)); + + // 4. 编译代码 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + return "错误:找不到Java编译器。请使用JDK运行此程序。"; + } + + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) { + String[] compileOptions = {"-d", tempDir.toString(), "-cp", System.getProperty("java.class.path")}; + Iterable compilationUnits = fileManager.getJavaFileObjects(sourceFile); + + JavaCompiler.CompilationTask task = compiler.getTask( + null, fileManager, diagnostics, + Arrays.asList(compileOptions), null, compilationUnits + ); + + boolean success = task.call(); + if (!success) { + StringBuilder errorMsg = new StringBuilder("编译错误:\n"); + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + errorMsg.append(String.format( + "Line %d: %s\n", + diagnostic.getLineNumber(), + diagnostic.getMessage(Locale.getDefault()) + )); + } + return errorMsg.toString(); + } + } + + // 5. 加载并执行 + URLClassLoader classLoader = new URLClassLoader( + new URL[]{tempDir.toUri().toURL()}, + ClassDebug.class.getClassLoader() + ); + Class loadedClass = classLoader.loadClass("dynamic." + className); + + // 注入全局对象 + Field globalField = loadedClass.getDeclaredField("global"); + globalField.set(null, globalObjects); + + Method runMethod = loadedClass.getMethod("run"); + runMethod.invoke(null); + return ""; + } catch (Throwable e) { + // 6. 异常处理 + Throwable cause = e; + while (cause.getCause() != null) { + cause = cause.getCause(); + } + + StringWriter sw = new StringWriter(); + cause.printStackTrace(new PrintWriter(sw)); + return "运行时错误:" + cause.getMessage() + "\n堆栈跟踪:\n" + sw.toString(); + } finally { + // 7. 清理资源 + if (tempDir != null) { + try { + Files.walk(tempDir) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } catch (IOException ignored) {} + } + } + } + + public static void main(String[] args) { + executeCode("JOptionPane.showMessageDialog(null, \"普通对话框\");"); + } + + /** + * 获取已加载的类 + * @return 已加载的类 + */ + public static Class[] getLoadedClasses() { + if (instrumentation == null){ + try { + VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); + instrumentation = vm.getInstrumentation(); + } catch (Exception e) { + logger.error("Failed to attach to VM: {}", e.getMessage()); + } + } + return instrumentation.getAllLoadedClasses(); + } + + /** + * 获取类的方法与字段 + * @param clazz 类名 + * @return 类的方法与字段 + */ + public static String getMethodAndField(String clazz) { + try { + Class aClass = Class.forName(clazz); + String methods = Arrays.stream(aClass.getDeclaredMethods()) + .map(Method::toString) + .collect(Collectors.joining("\n")); + String fields = Arrays.stream(aClass.getDeclaredFields()) + .map(Field::toString) + .collect(Collectors.joining("\n")); + return "方法:\n" + methods + "\n\n字段:\n" + fields; + } catch (ClassNotFoundException e) { + throw new RuntimeException("Class not found: " + clazz, e); + } + } + + public interface LoadingMonitor { + void load(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer); + } +} diff --git a/src/main/java/org/tzd/debug/GetInstance.java b/src/main/java/org/tzd/debug/GetInstance.java new file mode 100644 index 0000000..7c003d3 --- /dev/null +++ b/src/main/java/org/tzd/debug/GetInstance.java @@ -0,0 +1,18 @@ +package org.tzd.debug; + +/** + * @author tzdwindows 7 + */ +public class GetInstance { + + static { + System.load("C:\\Users\\Administrator\\source\\repos\\GetInstance\\x64\\Release\\GetInstance.dll"); + } + + /** + * 获取类的所有实例 + * @param clazz 类 + * @return 类的所有实例 + */ + public static native Object[] getInstance(Class clazz); +} diff --git a/src/main/java/org/tzd/debug/MethodDebug.java b/src/main/java/org/tzd/debug/MethodDebug.java new file mode 100644 index 0000000..f3df57e --- /dev/null +++ b/src/main/java/org/tzd/debug/MethodDebug.java @@ -0,0 +1,217 @@ +package org.tzd.debug; + +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.api.dog.agent.VirtualMachine; + +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CopyOnWriteArrayList; + +public class MethodDebug { + private static final Logger logger = LogManager.getLogger(MethodDebug.class); + private static Instrumentation instrumentation; + private static final List callbacks = new CopyOnWriteArrayList<>(); + + static { + try { + VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); + instrumentation = vm.getInstrumentation(); + } catch (Exception e) { + logger.error("Failed to attach to VM: {}", e.getMessage()); + } + } + + public static void load() { + try { + new AgentBuilder.Default() + .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) + // 只拦截你的应用代码,排除系统类 + .ignore(ElementMatchers.nameStartsWith("java.") + .or(ElementMatchers.nameStartsWith("javax.")) + .or(ElementMatchers.nameStartsWith("sun.")) + .or(ElementMatchers.nameStartsWith("com.sun.")) + .or(ElementMatchers.nameStartsWith("jdk.")) + .or(ElementMatchers.nameStartsWith("org.tzd.debug."))) + .or(ElementMatchers.nameStartsWith("com.axis.innovators.box.tools.RegisterTray")) + .or(ElementMatchers.nameStartsWith("com.formdev.flatlaf.")) + .or(ElementMatchers.nameStartsWith("com.axis.innovators.box.gui.")) + .or(ElementMatchers.nameStartsWith("org.apache.")) + .or(ElementMatchers.nameStartsWith("jdk.")) + .or(ElementMatchers.nameStartsWith("org.fife.")) + .or(ElementMatchers.nameStartsWith("java.awt.")) + .or(ElementMatchers.nameStartsWith("javax.swing.")) + .type(ElementMatchers.any()) + .transform((builder, type, classLoader, module, pd) -> + builder.method(ElementMatchers.any(). + and(ElementMatchers.not(ElementMatchers.isNative())) + .and(ElementMatchers.not(ElementMatchers.isAbstract())) + ) + .intercept(MethodDelegation.to( + MethodInterceptor.class)) + ).installOn(instrumentation); + } catch (Exception e) { + logger.error("Agent installation failed", e); + } + } + + public static void addCallback(MethodMonitorCallback callback) { + callbacks.add(callback); + } + + public static void removeCallback(MethodMonitorCallback callback) { + callbacks.remove(callback); + } + + public static void main(String[] args) { + logger.info("Starting MethodDebug main method"); + + // 先添加回调 + addCallback(new MethodMonitorCallback() { + @Override + public void onMethodEnter(String className, String methodName, Object[] args) { + logger.info("ENTER: {}.{}", className, methodName); + } + + @Override + public void onMethodExit(String className, String methodName, Object returnValue, Throwable exception, long durationNanos) { + String status = exception != null ? "FAILED" : "SUCCESS"; + logger.info("EXIT: {}.{} - {} ({} ns)", className, methodName, status, durationNanos); + } + }); + + // 然后安装Agent + load(); + + // 测试监控 + logger.info("Starting test loop..."); + while (true) { + try { + logger.info("Calling ds.ada()..."); + //ds.ada(); + Thread.sleep(1000); // 添加延迟避免过度占用CPU + } catch (Exception e) { + logger.error("Error in test loop", e); + } + } + } + + public interface MethodMonitorCallback { + void onMethodEnter(String className, String methodName, Object[] args); + void onMethodExit(String className, String methodName, Object returnValue, Throwable exception, long durationNanos); + } + + public static class MethodInterceptor { + @RuntimeType + public static Object intercept(@Origin Method method, + @SuperCall Callable callable, + @AllArguments Object[] args) throws Exception { + String className = method.getDeclaringClass().getName(); + String methodName = method.getName(); + + // 通知进入 + for (MethodMonitorCallback callback : callbacks) { + try { + callback.onMethodEnter(className, methodName, args); + } catch (Throwable t) { + logger.error("Callback error in onMethodEnter", t); + } + } + + long start = System.nanoTime(); + Object result = null; + Throwable exception = null; + + try { + if (callable != null) { + result = callable.call(); + } + return result; + } catch (Throwable t) { + exception = t; + throw t; + } finally { + long duration = System.nanoTime() - start; + + // 通知退出 + for (MethodMonitorCallback callback : callbacks) { + try { + callback.onMethodExit(className, methodName, result, exception, duration); + } catch (Throwable t) { + logger.error("Callback error in onMethodExit", t); + } + } + } + } + } + + public static class MethodTimerAdvice { + @Advice.OnMethodEnter + static MethodContext enter(@Advice.Origin Method method, + @Advice.AllArguments Object[] args) { + System.out.println("MethodTimerAdvice.enter"); + if (callbacks.isEmpty()) return null; + + MethodContext context = new MethodContext(); + context.startTime = System.nanoTime(); + context.className = method.getDeclaringClass().getName(); + context.methodName = method.getName(); + context.args = args; + + // 通知所有回调 + for (MethodMonitorCallback callback : callbacks) { + try { + callback.onMethodEnter(context.className, context.methodName, args); + } catch (Throwable t) { + logger.error("Callback execution error in onMethodEnter", t); + } + } + + return context; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + static void exit(@Advice.Enter MethodContext context, + @Advice.Thrown Throwable exception, + @Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue) { + System.out.println("MethodTimerAdvice.exit"); + if (context == null || callbacks.isEmpty()) return; + + long duration = System.nanoTime() - context.startTime; + + // 通知所有回调 + for (MethodMonitorCallback callback : callbacks) { + try { + callback.onMethodExit( + context.className, + context.methodName, + returnValue, + exception, + duration + ); + } catch (Throwable t) { + logger.error("Callback execution error in onMethodExit", t); + } + } + } + + // 上下文对象,用于在方法进入和退出之间传递数据 + static class MethodContext { + long startTime; + String className; + String methodName; + Object[] args; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/tzd/debug/util/GlobalObjects.java b/src/main/java/org/tzd/debug/util/GlobalObjects.java new file mode 100644 index 0000000..a2a5e29 --- /dev/null +++ b/src/main/java/org/tzd/debug/util/GlobalObjects.java @@ -0,0 +1,18 @@ +package org.tzd.debug.util; + +import com.axis.innovators.box.AxisInnovatorsBox; + +import java.lang.instrument.Instrumentation; + +/** + * 调试控制台的全局对象 + * @author tzdwindows 7 + */ +public class GlobalObjects { + /** + * Instrumentation对象 + */ + public Instrumentation instrumentation; + + public AxisInnovatorsBox axisInnovatorsBox = AxisInnovatorsBox.getMain(); +} diff --git a/src/main/java/org/tzd/lm/LMApi.java b/src/main/java/org/tzd/lm/LMApi.java index 9506b09..2b244ba 100644 --- a/src/main/java/org/tzd/lm/LMApi.java +++ b/src/main/java/org/tzd/lm/LMApi.java @@ -1,6 +1,12 @@ package org.tzd.lm; -import java.io.*; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; @@ -9,9 +15,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - /** * 使用AI接口获取回复 * @author tzdwindows 7 @@ -35,6 +38,7 @@ public class LMApi { public void setContent(String content) { this.content = content; } } + /** * 调用AI接口获取回复 * @param messages 消息列表