使用GDB更改JVM中的变量值 [英] Change Variable Value in JVM with GDB

查看:123
本文介绍了使用GDB更改JVM中的变量值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我有一个简单的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 to test (it is not an object and just an instance of class Z)
  • jstack <pid> can get the tid of main thread (0x7fa412002000) and jhat hex has the object of the java.lang.Thread of main (0x76ab05c40)
  • java.lang.Thread has a native method start0 which invokes hotspot method of JVM_StartThread (hotspot/src/share/vm/prims/jvm.cpp), there is a class JavaThread may contain the memory structure for local variables in thread stack.
  • if private static boolean test = true;; then JNI_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中的变量值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆