Change Variable Value in JVM with GDB(使用 GDB 更改 JVM 中的变量值)
问题描述
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 like
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
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 中的变量值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:使用 GDB 更改 JVM 中的变量值


- Eclipse 插件更新错误日志在哪里? 2022-01-01
- 如何使用WebFilter实现授权头检查 2022-01-01
- value & 是什么意思?0xff 在 Java 中做什么? 2022-01-01
- Safepoint+stats 日志,输出 JDK12 中没有 vmop 操作 2022-01-01
- Spring Boot连接到使用仲裁器运行的MongoDB副本集 2022-01-01
- C++ 和 Java 进程之间的共享内存 2022-01-01
- 将log4j 1.2配置转换为log4j 2配置 2022-01-01
- Jersey REST 客户端:发布多部分数据 2022-01-01
- 从 finally 块返回时 Java 的奇怪行为 2022-01-01
- Java包名称中单词分隔符的约定是什么? 2022-01-01