मैं अपने जावा एजेंट को गतिशील रूप से जावा प्रक्रिया से जोड़ रहा हूं जो कोड को यंत्रित करता है। मूल रूप से यह विधि की प्रत्येक शुरुआत में एक स्थिर कॉल जोड़ता है:
//method start
AgentClass.staticMethod();
//method body
AgentClass
एजेंट के .jar
में निहित है। लेकिन इंस्ट्रुमेंटेशन के बाद, प्रक्रिया नए कोड को निष्पादित करना शुरू कर देती है और यह एक NoClassDefFoundError
फेंकता है, यह AgentClass
नहीं ढूंढ सकता है। मैंने कोशिश-पकड़ ब्लॉक को शामिल करने और AgentClass
को forName
के साथ लोड करने के लिए कक्षाओं को इस तरह से व्यवस्थित करने का प्रयास किया:
try {
AgentClass.staticMethod();
} catch(NoClassDefFoundError e) {
Class.forName("AgentClass");
}
लेकिन फिर मुझे स्टैक फ्रेम के पुनर्गणना से संबंधित कई त्रुटियां मिलीं जैसे: Caused by: java.lang.VerifyError: Inconsistent stackmap frames at branch target 20
मैंने इसे visitMaxs()
(मैं ASM
लाइब्रेरी का उपयोग कर रहा हूं) का उपयोग करके हल किया। तब मुझे यह मिला: StackMapTable error: bad offset
। इसे रिटर्न के बजाय गोटो का उपयोग करके हल किया गया था लेकिन फिर मुझे मिला: ClassFormatError: Illegal local variable table in method
।
क्या मेरी प्रारंभिक NoClassDefFoundError
त्रुटि को हल करने का कोई आसान तरीका है?
अद्यतन करें: मेरी एजेंट कक्षाएं एप्लिकेशन क्लासलोडर (sun.misc.Launcher$AppClassLoader
) से भरी हुई हैं, और जिस प्रक्रिया को मैं इंस्ट्रूमेंट करना चाहता था वह कस्टम यूआरएल क्लासलोडर के साथ लोड करता है।
अद्यतन2: यही वह है जिसे मैं बाइटकोड में बदलना चाहता था:
try {
AgentClass agent = AgentClass.staticMethod();
} catch (Throwable e) {
try {
Class.forName("AgentClass");
} catch (ClassNotFoundException ex) {
}
}
मेरा MethodVisitor
(मैं बाइटकोड में बहुत अच्छा नहीं हूं, इसलिए एएसएम द्वारा TraceClassVisitor
का उपयोग करके बाइटकोड स्वचालित रूप से जेनरेट किया गया था।):
protected MethodVisitor createVisitor(MethodVisitor mv,final String name,final String desc,int access,String signature,String[]exceptions){
int variablesCount = (8 & access) != 0 ? 0 : 1;
Type[]args=Type.getArgumentTypes(desc);
for(int i=0;i<args.length; ++i){
Type arg=args[i];
variablesCount+=arg.getSize();
}
final int varCount=variablesCount;
return new MethodVisitor(458752,mv){
public void visitCode(){
Label label0=new Label();
Label label1=new Label();
Label label2=new Label();
this.mv.visitTryCatchBlock(label0,label1,label2,"java/lang/Throwable");
Label label3=new Label();
Label label4=new Label();
Label label5=new Label();
this.mv.visitTryCatchBlock(label3,label4,label5,"java/lang/ClassNotFoundException");
this.mv.visitLabel(label0);
this.mv.visitLineNumber(42,label0);
this.mv.visitMethodInsn(Opcodes.INVOKESTATIC,"AgentClass","staticMethod","()LAgentClass;",false);
this.mv.visitVarInsn(Opcodes.ASTORE,varCount);
this.mv.visitLabel(label1);
this.mv.visitLineNumber(48,label1);
Label label6=new Label();
this.mv.visitJumpInsn(Opcodes.GOTO,label6);
this.mv.visitLabel(label2);
this.mv.visitLineNumber(43,label2);
this.mv.visitFrame(Opcodes.F_SAME1,0,null,1,new Object[]{"java/lang/Throwable"});
this.mv.visitVarInsn(Opcodes.ASTORE,0);
this.mv.visitLabel(label3);
this.mv.visitLineNumber(45,label3);
this.mv.visitLdcInsn("AgentClass");
this.mv.visitMethodInsn(Opcodes.INVOKESTATIC,"java/lang/Class","forName","(Ljava/lang/String;)Ljava/lang/Class;",false);
this.mv.visitInsn(Opcodes.POP);
this.mv.visitLabel(label4);
this.mv.visitLineNumber(47,label4);
this.mv.visitJumpInsn(Opcodes.GOTO,label6);
this.mv.visitLabel(label5);
this.mv.visitLineNumber(46,label5);
this.mv.visitFrame(Opcodes.F_FULL,1,new Object[]{"java/lang/Throwable"},1,new Object[]{"java/lang/ClassNotFoundException"});
this.mv.visitVarInsn(Opcodes.ASTORE,1);
this.mv.visitLabel(label6);
this.mv.visitLineNumber(49,label6);
this.mv.visitFrame(Opcodes.F_CHOP,1,null,0,null);
this.mv.visitInsn(Opcodes.RETURN);
this.mv.visitLocalVariable("e","Ljava/lang/Throwable;",null,label3,label6,0);
this.mv.visitMaxs(1, 2);
super.visitCode();
}
...
}
}
3 अपडेट करें रनटाइम के दौरान मैं अपने एजेंट को इस प्रकार संलग्न करता हूं:
final VirtualMachine attachedVm = VirtualMachine.attach(String.valueOf(processID));
attachedVm.loadAgent(pathOfAgent, argStr);
attachedVm.detach();
1 उत्तर
अभी के लिए मेरा अनुमान है कि आपका वर्ग लोडर पदानुक्रम कुछ ऐसा है:
boot class loader
platform class loader
system/application class loader
custom URL class loader
या हो सकता है:
boot class loader
platform class loader
system/application class loader
custom URL class loader
अर्थात। एप्लिकेशन क्लास लोडर और कस्टम यूआरएल क्लास लोडर भाई बहन हैं या किसी अन्य तरीके से क्लास लोडर पदानुक्रम के विभिन्न हिस्सों में हैं, यानी उनमें से एक में लोड की गई कक्षाएं दूसरे के लिए अज्ञात हैं।
इसे हल करने का तरीका एक सामान्य पूर्वज को ढूंढना होगा और यह सुनिश्चित करना होगा कि आपकी इंस्ट्रूमेंटेशन स्कीम के लिए आवश्यक कक्षाएं वहां भरी हुई हैं। मैं आमतौर पर बूटस्ट्रैप क्लास लोडर का उपयोग करता हूं। इससे पहले कि मैं आपको समझाऊं कि बूटस्ट्रैप क्लास लोडर में प्रोग्रामेटिक रूप से कक्षाओं को कैसे जोड़ा जाता है, कृपया अपने एजेंट JAR को बूटस्ट्रैप क्लास पथ में मैन्युअल रूप से -Xbootclasspath/a:/path/to/your/agent.jar
के माध्यम से जावा कमांड लाइन पर जोड़ने का प्रयास करें और देखें कि कस्टम URL क्लास लोडर तब पाता है या नहीं कक्षा। मुझे बहुत आश्चर्य होगा अगर यह काम नहीं करेगा। फिर कृपया वापस रिपोर्ट करें और हम जारी रख सकते हैं।
कृपया यह भी बताएं कि आप इंस्ट्रूमेंटेशन एजेंट को कैसे संलग्न करते हैं:
-javaagent:/path/to/your/agent.jar
या . के माध्यम से- रनटाइम के दौरान हॉट-अटैचमेंट के माध्यम से (यदि हां, तो कृपया कोड दिखाएं)
ओपी की कुछ स्पष्ट टिप्पणियों के बाद अपडेट करें:
Instrumentation.appendToBootstrapClassLoaderSearch(JarFile)
. आपके एजेंट के premain
या (हॉट-अटैचमेंट के लिए) agentmain
विधियों में JVM आपको एक Instrumentation
उदाहरण देता है जिसका उपयोग आप उस उद्देश्य के लिए कर सकते हैं।
चेतावनी: बूटस्ट्रैप क्लासपाथ पर आपके द्वारा आवश्यक किसी भी वर्ग को आयात करने या अन्य, पहले से लोड की गई कक्षाओं (एजेंट वर्ग सहित) द्वारा उपयोग किए जाने से पहले आपको JAR जोड़ने की आवश्यकता है। इसलिए यदि आपके मामले में सिबलिंग क्लास लोडर में अन्य वर्ग द्वारा बुलाई गई AgentClass
विधि उसी वर्ग के अंदर रहती है जिसमें premain
और agentmain
विधियां हैं, तो आप उस विधि को कारक बनाना चाहते हैं (और अन्य सभी जिन्हें बाहर से बुलाया जा सकता है) किसी अन्य उपयोगिता वर्ग में। साथ ही, एजेंट मुख्य वर्ग से उस वर्ग को सीधे संदर्भित न करें, बल्कि पहले एजेंट को बूट क्लास पथ में अपना स्वयं का जार जोड़ें और फिर एजेंट मुख्य वर्ग से सीधे प्रतिबिंब के माध्यम से वहां किसी भी तरीके को कॉल करें। एजेंट मुख्य वर्ग ने अपना काम करने के बाद, अन्य वर्ग उन वर्गों को संदर्भित कर सकते हैं जो अब सीधे बूटस्ट्रैप वर्ग पथ पर हैं, समस्या हल हो गई है।
एक समस्या बनी हुई है, हालांकि: एजेंट बूटस्ट्रैप वर्ग पथ में जोड़ने के लिए JAR पथ का पता कैसे लगाता है? यह आप पर निर्भर है। आप कमांड लाइन पर एक सिस्टम प्रॉपर्टी सेट कर सकते हैं, एक फ़ाइल से पथ पढ़ सकते हैं, हार्ड-कोड, इसे एक एजेंट कॉन्फ़िगरेशन स्ट्रिंग के रूप में सौंप सकते हैं जो premain/agentmain
को attachedVm.loadAgent(agentPath, configString)
के माध्यम से पास किया गया है (इस मामले में configString
एजेंट पथ फिर से युक्त) या जो भी हो। वैकल्पिक रूप से, मुख्य एजेंट JAR के अंदर एक संसाधन के रूप में एक आंतरिक JAR बनाएं, जिसमें बूटस्ट्रैप क्लास लोडर पर रखी जाने वाली कक्षाएं हों। एजेंट संसाधन लोड कर सकता है, इसे एक अस्थायी फ़ाइल में सहेज सकता है और फिर बूटस्ट्रैप क्लास पथ में अस्थायी फ़ाइल पथ जोड़ सकता है। यह थोड़ा जटिल है, लेकिन साफ है और इसलिए एजेंट डेवलपर्स के बीच काफी लोकप्रिय है। कभी-कभी इस योजना को "ट्रैम्पोलिन एजेंट" दृष्टिकोण के रूप में जाना जाता है।
संबंधित सवाल
नए सवाल
java
जावा एक उच्च स्तरीय प्रोग्रामिंग भाषा है। इस टैग का उपयोग तब करें जब आपको भाषा का उपयोग करने या समझने में समस्या हो। इस टैग का उपयोग शायद ही कभी किया जाता है और इसका उपयोग अक्सर [वसंत], [वसंत-बूट], [जकार्ता-ई], [Android], [javafx], [हडूप], [श्रेणी] और [मावेन] के साथ किया जाता है।