使用GDB更改JVM中的变量值 [英] Change Variable Value in JVM with GDB
问题描述
目前我有一个简单的Java程序:
public class Test {
;然后
public static void main(String [ ] args){
boolean test = true;
while(test){
System.out.println(Hello World);
尝试{Thread.sleep(1000); } catch(Exception e){}
}
System.out.println(Bye-bye);
$ / code $ / pre
$ b $ p它每秒打印一次Hello World。我希望使用gdb附加到进程并创建一个内存补丁,以打印Bye-bye来停止它。
我知道GDB可以创建虚拟机JNI_GetCreatedVMs),env对象也可以通过GetEnv的API访问。 我如何在JVM中找到
test
变量地址并将其设置为false(这是可选的)使程序正常退出? 不知道API像AttachCurrentThread,像HotSpotVirtualMachine这样的类,像jmap或jstack这样的工具可以提供帮助吗?
并且没有调试选项,假设运行简单的程序在生产中使用
java -cp。测试
。
预先感谢您的任何指导。 :)
附加信息(跟踪状态)
jmap -dump:file = hex< pid> &安培;&安培; jhat hex
并在 http:// localhost:7000 中浏览;找不到任何对test
的引用(它不是一个对象,而只是class Z
的一个实例)
jstack< pid>
可以获得主线程(0x7fa412002000)的tid和jhat hex
具有main(java.lang.Thread的对象)(0x76ab05c40)
java.lang.Thread
具有本地方法start0
,它调用JVM_StartThread
(hotspot / src / share / vm / prims / jvm.cpp)的hotspot方法,类JavaThread
可能包含线程堆栈中局部变量的内存结构。 = true;JNI_GetCreatedJavaVMs ==> jvm
,jvm-> jvm_api-> AttachCurrentThread ==> env
,env-> env_api->(FindClass,GetStaticFieldID,SetStaticBooleanField)==>测试[true ==> false]
解决方案在某些情况下,使用HotSpot可维护性代理的本地变量地址。我制作了一个样本代理,用本地变量info打印扩展堆栈跟踪:
import sun.jvm.hotspot.code。位置;
import sun.jvm.hotspot.code.LocationValue;
导入sun.jvm.hotspot.code.NMethod;
导入sun.jvm.hotspot.code.ScopeValue;
导入sun.jvm.hotspot.code.VMRegImpl;
导入sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.debugger.OopHandle;
import sun.jvm.hotspot.interpreter.OopMapCacheEntry;
导入sun.jvm.hotspot.oops.Method;
导入sun.jvm.hotspot.oops.Oop;
导入sun.jvm.hotspot.runtime.CompiledVFrame;
import sun.jvm.hotspot.runtime.InterpretedVFrame;
导入sun.jvm.hotspot.runtime.JavaThread;
导入sun.jvm.hotspot.runtime.JavaVFrame;
导入sun.jvm.hotspot.runtime.VM;
导入sun.jvm.hotspot.runtime.VMReg;
导入sun.jvm.hotspot.tools.Tool;
import java.util.List;
public class Frames extends Tool {
@Override
public void run(){
for(JavaThread thread = VM.getVM()。getThreads ().first(); thread!= null; thread = thread.next()){
System.out.println(thread.getThreadName()+,id =+ thread.getOSThread()。threadId ()); ();
for(JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf!= null; vf = vf.javaSender()){
dumpFrame(vf);
}
System.out.println();
private void dumpFrame(JavaVFrame vf){
Method method = vf.getMethod();
String className = method.getMethodHolder()。getName()。asString()。replace('/','。');
String methodName = method.getName()。asString()+ method.getSignature()。asString();
System.out.println(#+ className +'。'+ methodName +@+ vf.getBCI()); $(b)b
if(vf.isCompiledFrame()){
dumpCompiledFrame(((CompiledVFrame)vf));
} else {
dumpInterpretedFrame(((InterpretedVFrame)vf));
private void dumpCompiledFrame(CompiledVFrame vf){
if(vf.getScope()== null){
return;
}
NMethod nm = vf.getCode();
System.out.println(* code = [+ nm.codeBegin()+ - + nm.codeEnd()+],pc =+ vf.getFrame()。getPC()) ;
List locals = vf.getScope()。getLocals();
for(int i = 0; i< locals.size(); i ++){
ScopeValue sv =(ScopeValue)locals.get(i);
if(!sv.isLocation())继续;
位置loc =((LocationValue)sv).getLocation();
地址addr = null;
String regName =;
if(loc.isRegister()){
int reg = loc.getRegisterNumber();
addr = vf.getRegisterMap()。getLocation(new VMReg(reg));
regName =:+ VMRegImpl.getRegisterName(reg); ()函数返回一个函数,如果函数返回true,则返回true,否则返回true,否则返回true。
} else if(loc.isStack()&&!loc.isIllegal()){
addr = vf.getFrame()。getUnextendedSP()。addOffsetTo(loc.getStackOffset());
}
字符串值= getValue(addr,loc.getType());
System.out.println([+ i +]+ addr + regName +=+ value);
private void dumpInterpretedFrame(InterpretedVFrame vf){
Method method = vf.getMethod();
int locals =(int)(method.isNative()?method.getSizeOfParameters():method.getMaxLocals());
OopMapCacheEntry oopMask = method.getMaskFor(vf.getBCI());
for(int i = 0; i< locals; i ++){
Address addr = vf.getFrame()。addressOfInterpreterFrameLocal(i);
String value = getValue(addr,oopMask.isOop(i)?Location.Type.OOP:Location.Type.NORMAL);
System.out.println([+ i +]+ addr +=+ value);
private String getValue(Address addr,Location.Type type){
if(type == Location.Type.INVALID || addr == null){
return(invalid);
} else if(type == Location.Type.OOP){
return(oop)+ getOopName(addr.getOopHandleAt(0));
} else if(type == Location.Type.NARROWOOP){
return(narrow_oop)+ getOopName(addr.getCompOopHandleAt(0));
} else if(type == Location.Type.NORMAL){
return(int)0x+ Integer.toHexString(addr.getJIntAt(0));
} else {
return(+ type +)0x+ Long.toHexString(addr.getJLongAt(0));
private String getOopName(OopHandle hadle){
if(hadle == null){
returnnull;
}
Oop oop = VM.getVM()。getObjectHeap()。newOop(hadle);
return oop.getKlass()。getName()。asString();
public static void main(String [] args)throws Exception {
new Frames()。execute(args);
运行它:
java -cp $ JAVA_HOME / lib / sa-jdi.jar :.框架PID
这将附加到Java进程
PID
并打印堆栈跟踪,如
main,id = 30920
#java.lang.Thread.sleep(J) V @ 0
#Test.main([Ljava / lang / String;)V @ 15
[0] 0x00007f075a857918 =(oop)[Ljava / lang / String;
[1] 0x00007f075a857910 =(int)0x1
[2] 0x00007f075a857908 =(int)0x0
这里
main
是Java线程名称;30920
是本地线程ID;@ 15
是字节码索引。
$ b $[1] 0x00007f075a857910 =(int) 0x1
表示局部变量#1位于地址0x00007f075a857910并且值为1.这正是您感兴趣的变量。
局部变量信息对于解释方法是可靠的,但并非总是用于编译方法。然而,编译的方法会在代码中添加一行代码,因此您可以在gdb中反汇编并检查它。
Currently I have a simple Java program:
public class Test { public static void main(String[] args) { boolean test = true; while (test) { System.out.println("Hello World"); try { Thread.sleep(1000); } catch (Exception e) {} } System.out.println("Bye-bye"); } }
It prints "Hello World" every second. I would like to use gdb to attach to the process and make a memory patch to stop it with "Bye-bye" printed.
I know GDB can get created VMs (JNI_GetCreatedVMs) from its console, the env object is also available via the API of GetEnv. How can I find the
test
variable address in JVM and set it to false (this is optional) to make the program exit normally? Not sure if API like AttachCurrentThread, class like HotSpotVirtualMachine, tools like jmap or jstack can help?And there is no debug option, assume the simple program running in production with
java -cp . Test
.Thanks in advance for any guidance. :)
additional info (track state)
jmap -dump:file=hex <pid> && jhat hex
and browse at http://localhost:7000; cannot find any reference totest
(it is not an object and just an instance ofclass Z
)jstack <pid>
can get the tid of main thread (0x7fa412002000) andjhat hex
has the object of the java.lang.Thread of main (0x76ab05c40)java.lang.Thread
has a native methodstart0
which invokes hotspot method ofJVM_StartThread
(hotspot/src/share/vm/prims/jvm.cpp), there is a classJavaThread
may contain the memory structure for local variables in thread stack.- if
private static boolean test = true;
; thenJNI_GetCreatedJavaVMs ==> jvm
,jvm->jvm_api->AttachCurrentThread ==> env
,env->env_api->(FindClass, GetStaticFieldID, SetStaticBooleanField) ==> test[true ==> false]
解决方案In some cases it is possible to get local variable addresses using HotSpot Serviceability Agent. I've made a sample agent that prints extended stack traces with local variable info:
import sun.jvm.hotspot.code.Location; import sun.jvm.hotspot.code.LocationValue; import sun.jvm.hotspot.code.NMethod; import sun.jvm.hotspot.code.ScopeValue; import sun.jvm.hotspot.code.VMRegImpl; import sun.jvm.hotspot.debugger.Address; import sun.jvm.hotspot.debugger.OopHandle; import sun.jvm.hotspot.interpreter.OopMapCacheEntry; import sun.jvm.hotspot.oops.Method; import sun.jvm.hotspot.oops.Oop; import sun.jvm.hotspot.runtime.CompiledVFrame; import sun.jvm.hotspot.runtime.InterpretedVFrame; import sun.jvm.hotspot.runtime.JavaThread; import sun.jvm.hotspot.runtime.JavaVFrame; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.runtime.VMReg; import sun.jvm.hotspot.tools.Tool; import java.util.List; public class Frames extends Tool { @Override public void run() { for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) { System.out.println(thread.getThreadName() + ", id = " + thread.getOSThread().threadId()); for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { dumpFrame(vf); } System.out.println(); } } private void dumpFrame(JavaVFrame vf) { Method method = vf.getMethod(); String className = method.getMethodHolder().getName().asString().replace('/', '.'); String methodName = method.getName().asString() + method.getSignature().asString(); System.out.println(" # " + className + '.' + methodName + " @ " + vf.getBCI()); if (vf.isCompiledFrame()) { dumpCompiledFrame(((CompiledVFrame) vf)); } else { dumpInterpretedFrame(((InterpretedVFrame) vf)); } } private void dumpCompiledFrame(CompiledVFrame vf) { if (vf.getScope() == null) { return; } NMethod nm = vf.getCode(); System.out.println(" * code=[" + nm.codeBegin() + "-" + nm.codeEnd() + "], pc=" + vf.getFrame().getPC()); List locals = vf.getScope().getLocals(); for (int i = 0; i < locals.size(); i++) { ScopeValue sv = (ScopeValue) locals.get(i); if (!sv.isLocation()) continue; Location loc = ((LocationValue) sv).getLocation(); Address addr = null; String regName = ""; if (loc.isRegister()) { int reg = loc.getRegisterNumber(); addr = vf.getRegisterMap().getLocation(new VMReg(reg)); regName = ":" + VMRegImpl.getRegisterName(reg); } else if (loc.isStack() && !loc.isIllegal()) { addr = vf.getFrame().getUnextendedSP().addOffsetTo(loc.getStackOffset()); } String value = getValue(addr, loc.getType()); System.out.println(" [" + i + "] " + addr + regName + " = " + value); } } private void dumpInterpretedFrame(InterpretedVFrame vf) { Method method = vf.getMethod(); int locals = (int) (method.isNative() ? method.getSizeOfParameters() : method.getMaxLocals()); OopMapCacheEntry oopMask = method.getMaskFor(vf.getBCI()); for (int i = 0; i < locals; i++) { Address addr = vf.getFrame().addressOfInterpreterFrameLocal(i); String value = getValue(addr, oopMask.isOop(i) ? Location.Type.OOP : Location.Type.NORMAL); System.out.println(" [" + i + "] " + addr + " = " + value); } } private String getValue(Address addr, Location.Type type) { if (type == Location.Type.INVALID || addr == null) { return "(invalid)"; } else if (type == Location.Type.OOP) { return "(oop) " + getOopName(addr.getOopHandleAt(0)); } else if (type == Location.Type.NARROWOOP) { return "(narrow_oop) " + getOopName(addr.getCompOopHandleAt(0)); } else if (type == Location.Type.NORMAL) { return "(int) 0x" + Integer.toHexString(addr.getJIntAt(0)); } else { return "(" + type + ") 0x" + Long.toHexString(addr.getJLongAt(0)); } } private String getOopName(OopHandle hadle) { if (hadle == null) { return "null"; } Oop oop = VM.getVM().getObjectHeap().newOop(hadle); return oop.getKlass().getName().asString(); } public static void main(String[] args) throws Exception { new Frames().execute(args); } }
To run it:
java -cp $JAVA_HOME/lib/sa-jdi.jar:. Frames PID
This will attach to Java process
PID
and print the stacktraces likemain, id = 30920 # java.lang.Thread.sleep(J)V @ 0 # Test.main([Ljava/lang/String;)V @ 15 [0] 0x00007f075a857918 = (oop) [Ljava/lang/String; [1] 0x00007f075a857910 = (int) 0x1 [2] 0x00007f075a857908 = (int) 0x0
Here
main
is Java thread name;30920
is native thread ID;@ 15
is bytecode index.The line
[1] 0x00007f075a857910 = (int) 0x1
means that the local variable #1 is located at address 0x00007f075a857910 and has the value 1. This is exactly the variable you are interested in.The local variable information is reliable for interpreted methods, but not always for compiled methods. However, compiled methods will have an extra line with an address of the code, so you can disassemble and inspect it in gdb.
这篇关于使用GDB更改JVM中的变量值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!