使用GDB在JVM中更改变量值

剂量帕帕

目前,我有一个简单的Java程序:

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");
  }
}

它每秒打印一次“ Hello World”。我想使用gdb附加到该进程,并制作一个内存补丁以使其停止并显示“ Bye-bye”。

我知道GDB可以从其控制台获取创建的VM(JNI_GetCreatedVMs),env对象也可以通过GetEnv的API获得。如何test在JVM中找到变量地址并将其设置为false(这是可选的),以使程序正常退出?不确定AttachCurrentThread之类的API,HotSpotVirtualMachine之类的类,jmap或jstack之类的工具是否可以帮助您?

并且没有调试选项,假设简单的程序在生产中运行java -cp . Test

在此先感谢您的指导。:)


附加信息(跟踪状态)

  • jmap -dump:file=hex <pid> && jhat hex并浏览http:// localhost:7000 ; 找不到任何引用test(不是的对象,而只是的实例class Z
  • jstack <pid>可以获取主线程(0x7fa412002000)的tid,并jhat hex具有主线程(0x76ab05c40)的java.lang.Thread对象
  • java.lang.Thread有一个本地方法start0调用hotspot方法JVM_StartThread(hotspot / src / share / vm / prims / jvm.cpp),还有一个类JavaThread可能包含线程堆栈中局部变量的内存结构。
  • 如果private static boolean test = true;; 那么JNI_GetCreatedJavaVMs ==> jvmjvm->jvm_api->AttachCurrentThread ==> envenv->env_api->(FindClass, GetStaticFieldID, SetStaticBooleanField) ==> test[true ==> false]
脂蛋白

在某些情况下,可以使用HotSpot Serviceability Agent获取局部变量地址。我已经制作了一个样本代理,该代理使用本地变量信息打印扩展的堆栈跟踪:

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);
    }
}

要运行它:

java -cp $JAVA_HOME/lib/sa-jdi.jar:. Frames 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是字节码索引。

该行[1] 0x00007f075a857910 = (int) 0x1表示本地变量#1位于地址0x00007f075a857910且值为1。这正是您感兴趣的变量。

局部变量信息对于解释方法是可靠的,但对于编译方法并不总是可靠的。但是,已编译的方法将在代码的地址后加一行,因此您可以在gdb中反汇编并检查它。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何使用if条件在python中更改变量值

来自分类Dev

如何使用if条件在python中更改变量值

来自分类Dev

如何使用if条件在python中更改变量值

来自分类Dev

如何使用if条件在python中更改变量值

来自分类Dev

使用函数中的内容更改变量值

来自分类Dev

在PHP中的变量内更改变量值

来自分类Dev

在Swift中更改变量值时执行方法

来自分类Dev

在julia中声明和更改变量值的问题

来自分类Dev

如何在JasperReports中按需更改变量值

来自分类Dev

在ColdFusion中的请求之间更改变量值

来自分类Dev

从 Haskell 中的列表更改变量值

来自分类Dev

使用dplyr按组更改变量值

来自分类Dev

使用sed命令更改变量值

来自分类Dev

使用Checkable ListView更改变量值

来自分类Dev

使用ref和this来更改变量值

来自分类Dev

在 linux 中使用 sed 命令更改变量值

来自分类Dev

使用sed命令在Script shell中更改变量值;错误语法

来自分类Dev

std :: cout更改变量值

来自分类Dev

从对象字段更改变量值

来自分类Dev

常量更改变量值

来自分类Dev

用循环更改变量值

来自分类Dev

Java if语句更改变量值

来自分类Dev

无法更改变量值javascript

来自分类Dev

AngularJS-在范围更改时使用if else语句更改变量值

来自分类Dev

在AS3中的动画片段中更改变量值

来自分类Dev

如何在Lisp中的函数内全局更改变量值

来自分类Dev

在Java中更改变量值的最常规方法是什么?

来自分类Dev

在各种getinterval中纠缠在一起时,如何更改变量值?

来自分类Dev

(Javascript)如何根据下拉列表中的选定选项更改变量值?