Add a "find what writes" to java
This commit is contained in:
parent
1ccde5a07e
commit
f8c96e7e70
@ -26,6 +26,12 @@ void JNICALL DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* name, const v
|
||||
{
|
||||
if (eventserver)
|
||||
eventserver->DynamicCodeGenerated(jvmti_env, name, address,length);
|
||||
}
|
||||
|
||||
void JNICALL FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location, jclass field_klass, jobject object, jfieldID field, char signature_type, jvalue new_value)
|
||||
{
|
||||
if (eventserver)
|
||||
eventserver->FieldModification(jvmti_env, jni_env, thread, method, location, field_klass, object, field, signature_type, new_value);
|
||||
|
||||
}
|
||||
|
||||
@ -76,6 +82,7 @@ CJavaEventServer::CJavaEventServer(jvmtiEnv *jvmti_env)
|
||||
callbacks.CompiledMethodLoad=::MethodLoad;
|
||||
callbacks.CompiledMethodUnload=::MethodUnload;
|
||||
callbacks.DynamicCodeGenerated=::DynamicCodeGenerated;
|
||||
callbacks.FieldModification=::FieldModification;
|
||||
|
||||
error=jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
|
||||
|
||||
@ -83,7 +90,7 @@ CJavaEventServer::CJavaEventServer(jvmtiEnv *jvmti_env)
|
||||
{
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
|
||||
error=jvmti_env->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
|
||||
error=jvmti_env->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
|
||||
}
|
||||
@ -151,7 +158,7 @@ OutputDebugStringA("MethodLoad");
|
||||
|
||||
if (classsig)
|
||||
{
|
||||
len=strlen(classsig);
|
||||
len=(WORD)strlen(classsig);
|
||||
WriteWord(len);
|
||||
if (len)
|
||||
Write(classsig, len);
|
||||
@ -168,7 +175,7 @@ OutputDebugStringA("MethodLoad");
|
||||
|
||||
if (name)
|
||||
{
|
||||
len=strlen(name);
|
||||
len=(WORD)strlen(name);
|
||||
WriteWord(len);
|
||||
if (len)
|
||||
Write(name, len);
|
||||
@ -180,7 +187,7 @@ OutputDebugStringA("MethodLoad");
|
||||
|
||||
if (sig)
|
||||
{
|
||||
len=strlen(sig);
|
||||
len=(WORD)strlen(sig);
|
||||
WriteWord(len);
|
||||
if (len)
|
||||
Write(sig, len);
|
||||
@ -231,8 +238,8 @@ void CJavaEventServer::DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* nam
|
||||
WriteByte(EVENTCMD_DYNAMICCODEGENERATED);
|
||||
WriteQword((UINT_PTR)address);
|
||||
WriteDword(length);
|
||||
WriteWord(strlen(name));
|
||||
Write((void *)name, strlen(name));
|
||||
WriteWord((WORD)strlen(name));
|
||||
Write((void *)name, (int)strlen(name));
|
||||
|
||||
}
|
||||
catch (char *e)
|
||||
@ -274,3 +281,103 @@ void CJavaEventServer::Terminate(void)
|
||||
Sleep(500);
|
||||
}
|
||||
|
||||
void CJavaEventServer::FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location, jclass field_klass, jobject object, jfieldID field, char signature_type, jvalue new_value)
|
||||
{
|
||||
unsigned int i;
|
||||
//go through the list and check which one caused this
|
||||
|
||||
|
||||
for (i=0; i<FindWhatWritesList.size(); i++)
|
||||
{
|
||||
PFindWhatWritesEntry e=FindWhatWritesList[i];
|
||||
if (e)
|
||||
{
|
||||
if ((e->fieldid==field) && ((object==NULL) || (jni_env->IsSameObject(e->object, object))))
|
||||
{
|
||||
//signal ce
|
||||
Lock();
|
||||
try
|
||||
{
|
||||
jvmtiFrameInfo frames[10];
|
||||
jint count;
|
||||
|
||||
WriteByte(EVENTCMD_FIELDMODIFICATION);
|
||||
WriteDword(i); //id
|
||||
WriteQword((UINT_PTR)method);
|
||||
WriteQword((UINT_PTR)location);
|
||||
|
||||
//and a stack snapshot as well
|
||||
|
||||
if (jvmti_env->GetStackTrace(thread, 0, 10, frames, &count)==JVMTI_ERROR_NONE)
|
||||
{
|
||||
int i;
|
||||
WriteByte((BYTE)count);
|
||||
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
WriteQword((UINT_PTR)frames[i].method);
|
||||
WriteQword((UINT_PTR)frames[i].location);
|
||||
}
|
||||
}
|
||||
else
|
||||
WriteByte(0);
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
OutputDebugStringA(e);
|
||||
//no connection yet
|
||||
}
|
||||
Unlock();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int CJavaEventServer::RegisterFindWhatWrites(jobject object, jclass klass, jfieldID fieldid)
|
||||
{
|
||||
int id=-1;
|
||||
|
||||
//find an usable entry in the FindWhatWritesList
|
||||
unsigned int i;
|
||||
|
||||
for (i=0; i<FindWhatWritesList.size(); i++)
|
||||
{
|
||||
if (FindWhatWritesList[i]==NULL)
|
||||
{
|
||||
id=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id==-1)
|
||||
{
|
||||
PFindWhatWritesEntry newentry=(PFindWhatWritesEntry)malloc(sizeof(FindWhatWritesEntry));
|
||||
newentry->klass=klass;
|
||||
newentry->fieldid=fieldid;
|
||||
newentry->object=object;
|
||||
|
||||
FindWhatWritesList.push_back(newentry);
|
||||
|
||||
id=FindWhatWritesList.size()-1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, NULL);
|
||||
|
||||
if (jvmti_env->SetFieldModificationWatch(klass, fieldid)!=JVMTI_ERROR_NONE)
|
||||
UnregisterFindWhatWrites(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void CJavaEventServer::UnregisterFindWhatWrites(int id)
|
||||
{
|
||||
if (FindWhatWritesList[id])
|
||||
{
|
||||
free(FindWhatWritesList[id]);
|
||||
FindWhatWritesList[id]=NULL;
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,17 @@
|
||||
#define EVENTCMD_METHODLOAD 0
|
||||
#define EVENTCMD_METHODUNLOAD 1
|
||||
#define EVENTCMD_DYNAMICCODEGENERATED 2
|
||||
#define EVENTCMD_FIELDMODIFICATION 3
|
||||
#define EVENTCMD_TERMINATED 255
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
jfieldID fieldid;
|
||||
jclass klass;
|
||||
jobject object;
|
||||
} FindWhatWritesEntry, *PFindWhatWritesEntry;
|
||||
|
||||
class CJavaEventServer :
|
||||
public Pipe
|
||||
@ -15,6 +24,7 @@ class CJavaEventServer :
|
||||
private:
|
||||
wchar_t pipename[256];
|
||||
jvmtiEnv *jvmti_env;
|
||||
vector<PFindWhatWritesEntry> FindWhatWritesList;
|
||||
|
||||
public:
|
||||
CJavaEventServer(jvmtiEnv *jvmti_env);
|
||||
@ -23,7 +33,9 @@ public:
|
||||
void MethodLoad(jvmtiEnv *jvmti_env, jmethodID method, jint code_size, const void* code_addr);
|
||||
void MethodUnload(jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr);
|
||||
void DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* name, const void* address, jint length);
|
||||
|
||||
void FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location, jclass field_klass, jobject object, jfieldID field, char signature_type, jvalue new_value);
|
||||
int RegisterFindWhatWrites(jobject object, jclass klass, jfieldID fieldid);
|
||||
void UnregisterFindWhatWrites(int id);
|
||||
void Terminate(void);
|
||||
};
|
||||
|
||||
|
@ -1771,6 +1771,36 @@ void CJavaServer::GetScanResults(void)
|
||||
WriteQword(0); //end of the list
|
||||
}
|
||||
|
||||
void CJavaServer::FindWhatWrites(void)
|
||||
{
|
||||
int id=-1;
|
||||
jobject object=(jobject)ReadQword();
|
||||
jfieldID fieldid=(jfieldID)ReadQword();
|
||||
|
||||
|
||||
//register a watch on the given field id
|
||||
if (eventserver)
|
||||
{
|
||||
jclass _klass=jni->GetObjectClass(object);
|
||||
jclass klass=(jclass)jni->NewGlobalRef(_klass);//don't forget to destroy this once unregistered
|
||||
|
||||
jni->DeleteLocalRef(_klass);
|
||||
|
||||
if (klass)
|
||||
id=eventserver->RegisterFindWhatWrites(object, klass, fieldid);
|
||||
|
||||
}
|
||||
|
||||
WriteDword(id);
|
||||
|
||||
}
|
||||
|
||||
void CJavaServer::StopFindWhatWrites(void)
|
||||
{
|
||||
if (eventserver)
|
||||
eventserver->UnregisterFindWhatWrites(ReadDword());
|
||||
}
|
||||
|
||||
|
||||
void CJavaServer::Start(void)
|
||||
{
|
||||
@ -1906,6 +1936,14 @@ void CJavaServer::Start(void)
|
||||
GetScanResults();
|
||||
break;
|
||||
|
||||
case JAVACMD_FINDWHATWRITES:
|
||||
FindWhatWrites();
|
||||
break;
|
||||
|
||||
case JAVACMD_STOPFINDWHATWRITES:
|
||||
StopFindWhatWrites();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw("Unexpected command\n");
|
||||
break;
|
||||
|
@ -32,6 +32,8 @@
|
||||
#define JAVACMD_STARTSCAN 27
|
||||
#define JAVACMD_REFINESCANRESULTS 28
|
||||
#define JAVACMD_GETSCANRESULTS 29
|
||||
#define JAVACMD_FINDWHATWRITES 30
|
||||
#define JAVACMD_STOPFINDWHATWRITES 31
|
||||
|
||||
|
||||
|
||||
@ -91,4 +93,7 @@ public:
|
||||
void RefineScanResults(void);
|
||||
void GetScanResults(void);
|
||||
|
||||
void FindWhatWrites(void);
|
||||
void StopFindWhatWrites(void);
|
||||
|
||||
};
|
||||
|
@ -29,7 +29,8 @@ JAVACMD_SETFIELD=26
|
||||
JAVACMD_STARTSCAN=27
|
||||
JAVACMD_REFINESCANRESULTS=28
|
||||
JAVACMD_GETSCANRESULTS=29
|
||||
|
||||
JAVACMD_FINDWHATWRITES=30
|
||||
JAVACMD_STOPFINDWHATWRITES=31
|
||||
|
||||
|
||||
|
||||
@ -432,6 +433,7 @@ function JavaEventListener(thread)
|
||||
local EVENTCMD_METHODLOAD=0
|
||||
local EVENTCMD_METHODUNLOAD=1
|
||||
local EVENTCMD_DYNAMICCODEGENERATED=2
|
||||
local EVENTCMD_FIELDMODIFICATION=3
|
||||
|
||||
local JavaEventPipe
|
||||
|
||||
@ -510,11 +512,78 @@ function JavaEventListener(thread)
|
||||
|
||||
|
||||
--
|
||||
elseif command==EVENTCMD_FIELDMODIFICATION then
|
||||
|
||||
print("EVENTCMD_FIELDMODIFICATION")
|
||||
local id=JavaEventPipe.readDword()
|
||||
local entry={}
|
||||
|
||||
print("id=="..id)
|
||||
|
||||
entry.methodid=JavaEventPipe.readQword()
|
||||
entry.location=JavaEventPipe.readQword()
|
||||
|
||||
local stackcount=JavaEventPipe.readByte()
|
||||
local i
|
||||
local stack={}
|
||||
|
||||
print("stackcount=="..stackcount)
|
||||
|
||||
for i=1, stackcount do
|
||||
stack[i]={}
|
||||
stack[i].methodid=JavaEventPipe.readQword()
|
||||
stack[i].location=JavaEventPipe.readQword()
|
||||
end
|
||||
|
||||
entry.stack=stack
|
||||
|
||||
|
||||
|
||||
if java.findwhatwriteslist~=nil then
|
||||
local fcd=java.findwhatwriteslist[id]
|
||||
|
||||
if fcd~=nil then
|
||||
--check if this entry is already in the list
|
||||
local found=false
|
||||
|
||||
for i=1, #fcd.entries do
|
||||
if (fcd.entries[i].methodid==entry.methodid) and (fcd.entries[i].location==entry.location) then
|
||||
found=true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
--if not, add it
|
||||
print("adding to the list")
|
||||
|
||||
tventry=fcd.lv.items.add()
|
||||
local mname=java_getMethodName(entry.methodid)
|
||||
tventry.Caption='...'
|
||||
|
||||
tventry.SubItems.add(string.format('%x: %s', entry.methodid, mname))
|
||||
tventry.SubItems.add(entry.position)
|
||||
|
||||
table.insert(fcd.entries, entry)
|
||||
|
||||
|
||||
else
|
||||
print("Already in the list")
|
||||
end
|
||||
else
|
||||
print("fcd==nil")
|
||||
|
||||
end
|
||||
else
|
||||
print("java.findwhatwriteslist==nil")
|
||||
end
|
||||
|
||||
print("done")
|
||||
elseif command==JAVACODECMD_TERMINATED then
|
||||
--print("eventserver terminated")
|
||||
print("eventserver terminated")
|
||||
break
|
||||
elseif command==nil then
|
||||
--print("Disconnected")
|
||||
print("Disconnected")
|
||||
break
|
||||
else
|
||||
print("Unexpected event received") --synchronize isn't necesary for print as that function is designed to synchronize internally
|
||||
@ -522,6 +591,7 @@ function JavaEventListener(thread)
|
||||
end
|
||||
end
|
||||
|
||||
print("Event handler terminating")
|
||||
JavaEventPipe.destroy();
|
||||
end
|
||||
|
||||
@ -530,21 +600,21 @@ function java_getCapabilities()
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_GETCAPABILITIES)
|
||||
|
||||
result.can_access_local_variables=javapipe.readByte()
|
||||
result.can_generate_all_class_hook_events=javapipe.readByte()
|
||||
result.can_generate_breakpoint_events=javapipe.readByte()
|
||||
result.can_generate_compiled_method_load_events=javapipe.readByte()
|
||||
result.can_generate_field_access_events=javapipe.readByte()
|
||||
result.can_generate_field_modification_events=javapipe.readByte()
|
||||
result.can_generate_single_step_events=javapipe.readByte()
|
||||
result.can_get_bytecodes=javapipe.readByte()
|
||||
result.can_get_constant_pool=javapipe.readByte()
|
||||
result.can_maintain_original_method_order=javapipe.readByte()
|
||||
result.can_redefine_any_class=javapipe.readByte()
|
||||
result.can_redefine_classes=javapipe.readByte()
|
||||
result.can_retransform_any_class=javapipe.readByte()
|
||||
result.can_retransform_classes=javapipe.readByte()
|
||||
result.can_tag_objects=javapipe.readByte()
|
||||
result.can_access_local_variables=javapipe.readByte()==1
|
||||
result.can_generate_all_class_hook_events=javapipe.readByte()==1
|
||||
result.can_generate_breakpoint_events=javapipe.readByte()==1
|
||||
result.can_generate_compiled_method_load_events=javapipe.readByte()==1
|
||||
result.can_generate_field_access_events=javapipe.readByte()==1
|
||||
result.can_generate_field_modification_events=javapipe.readByte()==1
|
||||
result.can_generate_single_step_events=javapipe.readByte()==1
|
||||
result.can_get_bytecodes=javapipe.readByte()==1
|
||||
result.can_get_constant_pool=javapipe.readByte()==1
|
||||
result.can_maintain_original_method_order=javapipe.readByte()==1
|
||||
result.can_redefine_any_class=javapipe.readByte()==1
|
||||
result.can_redefine_classes=javapipe.readByte()==1
|
||||
result.can_retransform_any_class=javapipe.readByte()==1
|
||||
result.can_retransform_classes=javapipe.readByte()==1
|
||||
result.can_tag_objects=javapipe.readByte()==1
|
||||
|
||||
javapipe.unlock()
|
||||
|
||||
@ -1267,6 +1337,73 @@ function java_search_finish()
|
||||
end
|
||||
|
||||
|
||||
function java_find_what_writes(object, fieldid)
|
||||
local id=nil
|
||||
if java.capabilities.can_generate_field_modification_events then
|
||||
--spawn a window to receive the data
|
||||
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_FINDWHATWRITES)
|
||||
javapipe.writeQword(object)
|
||||
javapipe.writeQword(fieldid)
|
||||
|
||||
id=javapipe.readDword()
|
||||
|
||||
print("id="..id)
|
||||
|
||||
javapipe.unlock()
|
||||
|
||||
|
||||
local fcd={} --found code dialog
|
||||
fcd.form=createForm()
|
||||
fcd.form.width=400
|
||||
fcd.form.height=300
|
||||
|
||||
|
||||
fcd.form.Position=poScreenCenter
|
||||
fcd.form.BorderStyle=bsSizeable
|
||||
fcd.form.caption='The following methods accessed the given variable'
|
||||
fcd.lv=createListView(fcd.form)
|
||||
fcd.lv.Name='results';
|
||||
fcd.lv.Align=alClient
|
||||
fcd.lv.ViewStyle=vsReport
|
||||
fcd.lv.ReadOnly=true
|
||||
fcd.lv.RowSelect=true
|
||||
|
||||
local c=fcd.lv.Columns.add()
|
||||
c.caption='Class'
|
||||
c.width=100
|
||||
|
||||
c=fcd.lv.Columns.add()
|
||||
c.caption='Method'
|
||||
c.width=100
|
||||
|
||||
c=fcd.lv.Columns.add()
|
||||
c.caption='Position'
|
||||
c.autosize=true
|
||||
|
||||
fcd.entries={}
|
||||
|
||||
|
||||
if java.findwhatwriteslist==nil then
|
||||
java.findwhatwriteslist={}
|
||||
end
|
||||
|
||||
java.findwhatwriteslist[id]=fcd
|
||||
else
|
||||
error('java_find_what_writes only works when the jvmti agent is launched at start')
|
||||
end
|
||||
|
||||
return id
|
||||
end
|
||||
|
||||
function java_stop_find_what_writes(id)
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_STOPFINDWHATWRITES)
|
||||
javapipe.writeDword(id)
|
||||
javapipe.unlock()
|
||||
end
|
||||
|
||||
|
||||
function java_getObjectHandleToAddress(address)
|
||||
local result=0
|
||||
|
Loading…
Reference in New Issue
Block a user