كيفية الحصول على قيم المعلمة في رد اتصال Motherny
-
04-10-2019 - |
سؤال
لدي رمز جافا التالي
public class Test {
public void sayHello(String msg) {
System.out.println(msg);
}
}
new Test().sayHello("Bonjour");
لدي وكيل JVMTI متصل بـ Java حيث يمكنني التقاط مكالمات الوظائف. أريد الحصول على قيمة المعلمة التي تم تمريرها إلى طريقتي (على سبيل المثال "Bonjour")
static void JNICALL cbMethodEntry(jvmtiEnv *jvmti,
JNIEnv* jni_env, jthread thread, jmethodID method) {
// here I want to get a parameter value "Bonjour"
// which was passed to my method sayHello("Bonjour")
}
jvmtiEventCallbacks callbacks;
callbacks.MethodEntry = &cbMethodEntry;
في رد الاتصال نفسه ، لدي معرف خيط ومعرف الطريقة.
بالنظر إلى رأس JVMTI.H ، وجدت فقط هذا الهيكل الذي يتعامل مع المعلمات ولكن لا توجد قيم.
typedef struct {
char* name;
jvmtiParamKind kind;
jvmtiParamTypes base_type;
jboolean null_ok;
} jvmtiParamInfo;
كيف يمكنني الحصول على قيم المعلمات من رد الاتصال الخاص بي؟
المحلول
أنا أعمل على مهام مماثلة. فيما يلي مثالان رمزان مكتوبان في C ++. يوضح مثال 1 كيفية الحصول على المتغيرات المحلية في رد اتصال MethodEntry باستخدام getlocalvariaBletable و getLocalObject. يوضح مثال 2 كيفية تكوين هذا باستخدام BCI (أجهزة BYTECODE).
مثال 1:
HandleMethodentry هي طريقة إعادة الاتصال لحدث MethodEntry. يقوم بتسجيل بعض المعلومات حول معلمات الطريقة. GetLocalVariableTable يسترجع المعلومات المتغيرة المحلية ، والتي تستخدمها getLocalObject. الإطار في Depth Zero هو الإطار الحالي ، المعلمة الأولى هي في الفتحة 0. بالنسبة للإطارات غير القتالية ، تحتوي الفتحة 0 على كائن "هذا". لاسترداد كائن "هذا" من إطارات الطريقة الأصلية ، يجب استخدامك getlocalinstance بدلاً من getLocalObject.
أول شار من التوقيع هو نوع القيمة. هذا المثال ببساطة يتحقق من علامة الوظيفة. لقيم السلسلة ، يمكنك استخدام GetStringutfchars. يمكن العثور على مثال هنا.
void JNICALL MethodTraceAgent::HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID method)
{
try {
jvmtiError error;
jclass clazz;
char* name;
char* signature;
// get declaring class of the method
error = m_jvmti->GetMethodDeclaringClass(method, &clazz);
Errors::Check(error);
// get the signature of the class
error = m_jvmti->GetClassSignature(clazz, &signature, 0);
Errors::Check(error);
// get method name
error = m_jvmti->GetMethodName(method, &name, NULL, NULL);
Errors::Check(error);
char tmp[1024];
sprintf(tmp, "%s%s", signature, name);
if(pFilter->Match("method", tmp)) { // intrested method?
char out[1024];
jint param_size = 0;
error = m_jvmti->GetArgumentsSize(method, ¶m_size);
int line_len = sprintf(out, "method_entry: %s%s%, param_size:%d", signature, name, param_size);
// visit local variable
jint entry_count = 0;
jvmtiLocalVariableEntry *table_ptr = NULL;
jlocation cur_loc;
// this call may return JVMTI_ERROR_ABSENT_INFORMATION, this error is avoided by initialize entry_count to 0 to escape the following for loop
error = m_jvmti->GetLocalVariableTable(method, &entry_count, &table_ptr);
error = m_jvmti->GetFrameLocation(thread, 0, NULL, &cur_loc);
for(int j=0; j<min(param_size, entry_count); j++) {
if(table_ptr[j].start_location > cur_loc) break;
if(table_ptr[j].signature[0] == 'L') { // fully-qualified-class
jobject param_obj;
jlong param_obj_tag = 0;
error = m_jvmti->GetLocalObject(thread, 0, table_ptr[j].slot, ¶m_obj); // frame at depth zero is the current frame
m_jvmti->GetTag(param_obj, ¶m_obj_tag);
if(param_obj_tag == 0) {
m_jvmti->SetTag(param_obj, theTag);
param_obj_tag = theTag;
++theTag;
}
line_len += sprintf(out + line_len, ", param_obj_tag: %ld", param_obj_tag);
//line_len += sprintf(out+line_len, ", slot:%d, start:%ld, cur:%ld, param:%s%s", table_ptr[j].slot, table_ptr[j].start_location, cur_loc, table_ptr[j].signature, table_ptr[j].name);
jni->DeleteLocalRef(param_obj);
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].signature));
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].name));
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].generic_signature));
}
}
error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr));
Errors::Check(error);
// put to log list
logList.push_back(out);
printf("\r%-10d", logList.size());
}
// release resources
error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(name));
Errors::Check(error);
error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
Errors::Check(error);
} catch (AgentException& e) {
cout << "Error when enter HandleMethodEntry: " << e.what() << " [" << e.ErrCode() << "]" << endl;
}
}
مثال 2:
كما ذكر في إجابة سؤال مماثل, ، والتعامل معها في MethodEntry ، حتى أن رد الاتصال قد يواجه مشكلة قبل الشكل. يمكنك النظر في نهج BCI. mtrace_native_entry هي طريقة أصلية يتم حقنها لبداية كل استدعاء طريقة. يسمى من طريقة Mtrace's Method_entry.
في mtrace_native_entry ، تحتاج إلى تتبع مرة أخرى إلى الطريقة المعقدة في الإطار 2 (الإطار الحالي للطريقة الأصلية المنفذة في الإطار 0). يمكن العثور على مثال مماثل على Param Trace في مشروع آخر Stackparam في Github. ومع ذلك ، لم يتم اختبار الاختلافات في أداء هاتين الطريقتين.
يمكن العثور على رمز غير محدد لهذا المثال في مستند JDK DIR DEMO/JVMTI/MTRACE. تتمثل الخطوة الأساسية في حقن mother_entry في رد الاتصال على الحدث classFileLoadHook باستخدام Java_crw_demo.
يوضح هذا المثال أيضًا كيفية الحصول على قيمة حقل كائن Param.
void JNICALL MethodTraceAgent::MTRACE_native_entry(JNIEnv *jni, jclass klass, jthread thread, jint cnum, jint mnum)
{
/* It's possible we get here right after VmDeath event, be careful */
if ( !pTheAgent->vmInitialized || pTheAgent->vmDead || thread == NULL)
return;
jvmtiError error;
char out[1024];
int line_len = 0;
jvmtiFrameInfo frames[3];
jint cframe;
error = m_jvmti->GetStackTrace(thread, 0, 3, frames, &cframe);
Errors::Check(error);
if(cframe < 3)
return;
jmethodID method = frames[2].method;
jclass dec_cls;
char *mtd_name, *dec_cls_sig;
m_jvmti->GetMethodDeclaringClass(method, &dec_cls);
m_jvmti->GetClassSignature(dec_cls, &dec_cls_sig, NULL);
m_jvmti->GetMethodName(method, &mtd_name, NULL, NULL);
jboolean isNative = false;
m_jvmti->IsMethodNative(method, &isNative);
if(isNative)
return;
line_len += sprintf(out + line_len, "m_en: %s%s", dec_cls_sig, mtd_name);
// operate tags
jint param_size = 0;
jint entry_count = 0;
jvmtiLocalVariableEntry *table_ptr = NULL;
error = m_jvmti->GetArgumentsSize(method, ¶m_size);
error = m_jvmti->GetLocalVariableTable(method, &entry_count, &table_ptr);
Errors::Check(error);
line_len += sprintf(out + line_len, ", %d, %d", param_size, entry_count);
for(int j=0; j<min(param_size, entry_count); j++) {
jobject param_obj = 0;
if(j==0 && strcmp(table_ptr[0].name, "this") == 0) { // this instance
error = m_jvmti->GetLocalInstance(thread, 2, ¶m_obj);
if(thiso == 0) thiso = param_obj;
else {
line_len += sprintf(out + line_len, ", same_this: %d", jni->IsSameObject(thiso, param_obj));
}
jfieldID field = jni->GetFieldID(dec_cls, "a", "I");
jint a = jni->GetIntField(param_obj, field);
line_len += sprintf(out + line_len, ", a: %d", a);
Errors::Check(error);
}
else if(table_ptr[j].signature[0] == 'L') { // object
error = m_jvmti->GetLocalObject(thread, 2, table_ptr[j].slot, ¶m_obj); // frame at depth zero is the current frame
Errors::Check(error);
}
if(param_obj != 0) {
//line_len += sprintf(out + line_len, ", modi: %d, this: %d, same: %d", modied, param_obj, jni->IsSameObject(param_obj, modied));
jlong param_obj_tag = 0;
m_jvmti->GetTag(param_obj, ¶m_obj_tag);
if(param_obj_tag == 0) {
error = m_jvmti->SetTag(param_obj, pTheAgent->ctag);
Errors::Check(error);
param_obj_tag = pTheAgent->ctag;
++pTheAgent->ctag;
}
line_len += sprintf(out + line_len, ", param_obj_tag: %ld", param_obj_tag);
//line_len += sprintf(out+line_len, ", slot:%d, start:%ld, cur:%ld, param:%s%s", table_ptr[j].slot, table_ptr[j].start_location, cur_loc, table_ptr[j].signature, table_ptr[j].name);
jni->DeleteLocalRef(param_obj);
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].signature));
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].name));
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr[j].generic_signature));
}
}
error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(table_ptr));
Errors::Check(error);
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(dec_cls_sig));
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(mtd_name));
logList.push_back(out);
}
طريقة الفصل المستخدمة للحقن:
public class MTrace {
private static int engaged = 0;
/* At the very beginning of every method, a call to method_entry()
* is injected.
*/
private static native void _method_entry(Object thread, int cnum, int mnum);
public static void method_entry(int cnum, int mnum)
{
if ( engaged != 0 ) {
_method_entry(Thread.currentThread(), cnum, mnum);
}
}
/* Before any of the return bytecodes, a call to method_exit()
* is injected.
*/
private static native void _method_exit(Object thread, int cnum, int mnum);
public static void method_exit(int cnum, int mnum)
{
if ( engaged != 0 ) {
_method_exit(Thread.currentThread(), cnum, mnum);
}
}
}
لاحظ أن هذين المثالين مكتوبان لغرض الاختبار ، وليس كل قيمة إرجاع وظائف JVMTI. قد توجد بعض المشكلات الأخرى أيضًا.
نصائح أخرى
ستحتاج إلى البدء باستخدام getLocalObject. في هذا الصدد ، تمكنت من العثور على ما يلي مثال يجب أن يساعدك ذلك في الحصول على الاتجاه الصحيح.