`
caoxudong818
  • 浏览: 44402 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Serviceability 简介 —— tools

    博客分类:
  • sa
阅读更多

前文中提到,$JAVA_HOME/bin下有一些工具也是通过SA实现的,本文就对SA中tools包下的工具做简单介绍。

 

tools包下一个主要的类是sun.jvm.hotspot.tools.Tool。使用SA实现的工具类大部分都是继承自此类。子类通过覆盖run方法来实现自定义的功能。例如,类sun.jvm.hotspot.tools.JInfo在run方法中确定是打印系统属性还是打印虚拟机参数:

 

public void run() {
        Tool tool = null;
        switch (mode) {
        case MODE_FLAGS:
            printVMFlags();
            return;
        case MODE_SYSPROPS:
            tool = new SysPropsDumper();
            break;
        case MODE_BOTH: {
            tool = new Tool() {
                    public void run() {
                        Tool sysProps = new SysPropsDumper();
                        sysProps.setAgent(getAgent());
                        System.out.println("Java System Properties:");
                        System.out.println();
                        sysProps.run();
                        System.out.println();
                        System.out.println("VM Flags:");
                        printVMFlags();
                        System.out.println();
                    }
                };
            }
            break;

        default:
            usage();
            break;
        }
        tool.setAgent(getAgent());
        tool.run();
    }

 

 

在使用的时候,一般会是如下情况:

  1. 启动Java,调用子类的main方法;
  2. 子类做一些初步的处理;
  3. 调用sun.jvm.hotspot.tools.Tool类的start方法,传入相应的命令行参数;
  4. 在start方法中根据接收的参数判断要使用何种调式类型;
  5. 根据不同的调式类型使用不同的attach(不知道还用什么词比较好)策略;
  6. 调用子类实现的run方法。
  7. 调用 sun.jvm.hotspot.tools.Tool类的 方法,一般用于从目标JVM中detach

所以,如果想自己编写调试工具的话,只需要继承Tool类,实现run方法,再调用类似下面的代码(以FinalizerInfo类为例)即可:

 

public static void main(String[] args) {
        FinalizerInfo finfo = new FinalizerInfo();
        finfo.start(args);
        finfo.stop();
    }

 

下面看一下Tool类是如何进行调试的。

在Tool类中,定义了3中调试类型,分别是:

 

   // debugeeType is one of constants below
   protected static final int DEBUGEE_PID    = 0;
   protected static final int DEBUGEE_CORE   = 1;
   protected static final int DEBUGEE_REMOTE = 2;
 

针对不同的调式类型所使用的attach代码:

switch (debugeeType) {
    case DEBUGEE_PID:
        err.println("Attaching to process ID " + pid + ", please wait...");
        agent.attach(pid);
        break;

    case DEBUGEE_CORE:
         err.println("Attaching to core " + coreFileName + " from executable " + executableName + ", please wait...");
         agent.attach(executableName, coreFileName);
         break;

     case DEBUGEE_REMOTE:
         err.println("Attaching to remote server " + remoteServer + ", please wait...");
         agent.attach(remoteServer);
         break;
}

 

 

由上面的代码中可以看到,调式功能主要是通过agent进行的,该变量是一个sun.jvm.hotspot.bugspot.BugSpotAgent类的实例。

 

OpenJDK 写道
This class wraps the basic functionality for connecting to the target process or debug server. It makes it simple to start up the debugging system.

This agent (as compared to the HotSpotAgent) can connect to and interact with arbitrary processes. If the target process happens to be a HotSpot JVM, the Java debugging features of the Serviceability Agent are enabled.

Further, if the Serviceability Agent's JVMDI module is loaded into the target VM, interaction with the live Java program is possible, specifically the catching of exceptions and setting of breakpoints.

The BugSpot debugger requires that the underlying Debugger support C/C++ debugging via the CDebugger interface.

 

该类的主要功能是简化对JVM的调试。它可以对任意目标进行调试,若目标进行是HotSpot JVM,则启用SA。若目标JVM载入了JVMDI模块,则可以与Java程序进行交互。

 

在该类中,调试功能(如加断点、挂起线程、恢复线程运行、挂起目标JVM等)都是通过sun.jvm.hotspot.livejvm.ServiceabilityAgentJVMDIModule类完成的。当然,使用ServiceabilityAgentJVMDIModule类的前提是目标JVM是HotSpot,并且启用了SA的JVMDI模块。例如下面添加断点的代码:

 

/** Toggle a Java breakpoint at the given location. */
    public synchronized ServiceabilityAgentJVMDIModule.BreakpointToggleResult
    toggleJavaBreakpoint(String srcFileName, String pkgName, int lineNo) {
        if (!canInteractWithJava()) {
            throw new DebuggerException("Could not connect to SA's JVMDI module; can not toggle Java breakpoints");
        }
        return jvmdi.toggleBreakpoint(srcFileName, pkgName, lineNo);
    }

 

 

ServiceabilityAgentJVMDIModule类是一个Java语言级的交互式调试工具。

 

/** Provides Java programming language-level interaction with a live
    Java HotSpot VM via the use of the SA's JVMDI module. This is an
    experimental mechanism. The BugSpot debugger should be converted
    to use the JVMDI/JDWP-based JDI implementation for live process
    interaction once the JDI binding for the SA is complete. */

 

其中,VM与SA交互部分的数据有:

 

// Values in target process
  // Events sent from VM to SA
  private CIntegerAccessor saAttached;
  private CIntegerAccessor saEventPending;
  private CIntegerAccessor saEventKind;
  // Exception events
  private JNIHandleAccessor saExceptionThread;
  private JNIHandleAccessor saExceptionClass;
  private JNIid             saExceptionMethod;
  private CIntegerAccessor  saExceptionLocation;
  private JNIHandleAccessor saExceptionException;
  private JNIHandleAccessor saExceptionCatchClass;
  private JNIid             saExceptionCatchMethod;
  private CIntegerAccessor  saExceptionCatchLocation;
  // Breakpoint events
  private JNIHandleAccessor saBreakpointThread;
  private JNIHandleAccessor saBreakpointClass;
  private JNIid             saBreakpointMethod;
  private CIntegerAccessor  saBreakpointLocation;
  // Commands sent by the SA to the VM
  private int               SA_CMD_SUSPEND_ALL;
  private int               SA_CMD_RESUME_ALL;
  private int               SA_CMD_TOGGLE_BREAKPOINT;
  private int               SA_CMD_BUF_SIZE;
  private CIntegerAccessor  saCmdPending;
  private CIntegerAccessor  saCmdType;
  private CIntegerAccessor  saCmdResult;
  private CStringAccessor   saCmdResultErrMsg;
  // Toggle breakpoint command arguments
  private CStringAccessor   saCmdBkptSrcFileName;
  private CStringAccessor   saCmdBkptPkgName;
  private CIntegerAccessor  saCmdBkptLineNumber;
  private CIntegerAccessor  saCmdBkptResWasError;
  private CIntegerAccessor  saCmdBkptResLineNumber;
  private CIntegerAccessor  saCmdBkptResBCI;
  private CIntegerAccessor  saCmdBkptResWasSet;
  private CStringAccessor   saCmdBkptResMethodName;
  private CStringAccessor   saCmdBkptResMethodSig;
 

在继续对ServiceabilityAgentJVMDIModule类进行介绍之前,先说一个这里常用到的类sun.jvm.hotspot.debugger.Address。该类是在调试过程中对地址进行访问的底层抽象接口,简单的理解话,就当作是指针地址好了。你可以在使用CLHSDB的过程中看到很多数据后面都会跟着一个类似于“0x32bf4978”这样的数,这个就是地址,Address类对这个进行了封装,便于在Java中进行访问和操作,提供了对地址的加、减、比较和位运算,以及获取、设置指定地址内容等操作。例如:

 

public long       getCIntegerAt      (long offset, long numBytes, boolean isUnsigned)
    throws UnmappedAddressException, UnalignedAddressException;
/** Sets a C integer numBytes in size at the specified offset. Note
      that there is no "unsigned" flag for the accessor since the
      value will not be sign-extended; the number of bytes are simply
      copied from the value into the target address space. */
  public void setCIntegerAt(long offset, long numBytes, long value);
/** This throws an UnsupportedOperationException if this address happens
      to actually be an OopHandle, because interior object pointers
      are not allowed. Negative offsets are allowed and handle the
      subtraction case. */
  public Address    addOffsetTo        (long offset) throws UnsupportedOperationException;
/** This throws an UnsupportedOperationException if this address happens
      to actually be an OopHandle. Performs a logical "and" operation
      of the bits of the address and the mask (least significant bits
      of the Address and the mask are aligned) and returns the result
      as an Address. Returns null if the result was zero. */
  public Address    andWithMask(long mask) throws UnsupportedOperationException;

 

当然,在使用Address过程中,会有一些需要注意的地方:

 

  • 地址是不可变的(immutable),但其指向的内容是可变的,可以理解为const void* point;
  • 如果当前的调试目标是一个核心转储文件(core dump),则不可以修改地址指向的内容;
  • C/C++中的地址一般由无符号整数表示,而Java中不存在无符号原生类型,所以以原始地址数据(例如0x32bf4978)为参数的方法中,都是以long型代替。相应的,对地址进行运行时,也需要注意类型的处理。

下面以Linux平台为例进行说明。在Linux上,Address类是实现类是sun.jvm.hotspot.debugger.linux.LinuxAddress。在该类中,对地址指向的数据的读取操作是通过sun.jvm.hotspot.debugger.linux.LinuxDebugger类完成的,而写操作目前还未实现,调用方法时会抛异常。对sun.jvm.hotspot.debugger.linux.LinuxDebugger类的说明并不是本文的目的,就此打住,后续的文章中再谈。

 

现在回到ServiceabilityAgentJVMDIModule类,在前面提到的SA与VM交互的数据中,其类型基本上都是对Address的封装。这里有个奇怪的问题,在挂起VM线程的suspend方法中,会调用sun.jvm.hotspot.livejvm.CIntegerAccessor类的setValue方法,而这个方法实现是通过调用Address类的setCIntegerAt方法完成的。从目前看到的Address的实现类(包括LinuxAddressWindbgAddressDbxAddressDummyAddressRemoteAddressProcAddress)看,setValue方法都会抛出异常(Win32Address类除外)。

 

ServiceabilityAgentJVMDIModule类的suspend方法:

 

/** Suspend all Java threads in the target VM. Throws
      DebuggerException if the VM disconnected. */
  public void suspend() {
    saCmdType.setValue(SA_CMD_SUSPEND_ALL);
    saCmdPending.setValue(1);
    waitForCommandCompletion();
    suspended = true;
  }

 

CIntegerAccessor类的setValue方法:

 

void setValue(long value) {
    addr.setCIntegerAt(0, numBytes, value);
  }
 

 

LinuxAddress类setCIntegerAt方法:

 

// Mutators -- not implemented for now (FIXME)
  public void setCIntegerAt(long offset, long numBytes, long value) {
    throw new DebuggerException("Unimplemented");
  }

 

只有Win32Address类中,通过Win32Debugger接口提供对setCIntegerAt方法的实现,在该接口的实现类Win32DebuggerLocal的父类DebuggerBase类中的writeCInteger方法中会做相应的检查,包括字节对齐和相应成员对象是否存在的检查。在最终实现上,是通过Win32DebuggerLocalwriteBytesToProcess方法完成的。

 

Win32Address类的setCIntegerAt方法:

 

public void setCIntegerAt(long offset, long numBytes, long value) {
    debugger.writeCInteger(addr + offset, numBytes, value);
  }

 

 

DebuggerBase类的writeCInteger方法:

 

public void writeCInteger(long address, long numBytes, long value)
    throws UnmappedAddressException, UnalignedAddressException {
    checkConfigured();
    utils.checkAlignment(address, numBytes);
    byte[] data = utils.cIntegerToData(numBytes, value);
    writeBytes(address, numBytes, data);
  }

 

 

Win32DebuggerLocal类的writeBytesToProcess方法:

 

public synchronized void writeBytesToProcess(long startAddress, long numBytes, byte[] data)
    throws UnmappedAddressException, DebuggerException {
    try {
      printToOutput("poke 0x" + Long.toHexString(startAddress) +
                    " |");
      writeIntToOutput((int) numBytes);
      writeToOutput(data, 0, (int) numBytes);
      printlnToOutput("");
      if (!in.parseBoolean()) {
        throw new UnmappedAddressException(startAddress);
      }
    } catch (IOException e) {
      throw new DebuggerException(e);
    }
  }

 

到这里就是将内容发送到输出中,至于内容中“poke”是个啥玩意,还不清楚,需要后续跟进一些调试器的知识。这里的输出应该是与本地程序之间建立的socket连接:

 

 /** Connects to the debug server, setting up out and in streams. */
  private void connectToDebugServer() throws IOException {
    // Try for a short period of time to connect to debug server; time out
    // with failure if didn't succeed
    debuggerSocket = null;
    long endTime = System.currentTimeMillis() + SHORT_TIMEOUT;

    while ((debuggerSocket == null) && (System.currentTimeMillis() < endTime)) {
      try {
        // FIXME: this does not work if we are on a DHCP machine which
        // did not get an IP address this session. It appears to use
        // an old cached address and the connection does not actually
        // succeed. Must file a bug.
        // debuggerSocket = new Socket(InetAddress.getLocalHost(), PORT);
        debuggerSocket = new Socket(InetAddress.getByName("127.0.0.1"), PORT);
        debuggerSocket.setTcpNoDelay(true);
      }
      catch (IOException e) {
        // Swallow IO exceptions while attempting connection
        debuggerSocket = null;
        try {
          // Don't swamp the CPU
          Thread.sleep(750);
        }
        catch (InterruptedException ex) {
        }
      }
    }

    if (debuggerSocket == null) {
      // Failed to connect because of timeout
      throw new DebuggerException("Timed out while attempting to connect to debug server (please start SwDbgSrv.exe)");
    }

    out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(debuggerSocket.getOutputStream(), "US-ASCII")), true);
    rawOut = new DataOutputStream(new BufferedOutputStream(debuggerSocket.getOutputStream()));
    in = new InputLexer(new BufferedInputStream(debuggerSocket.getInputStream()));
  }
 

说到这里,有个问题,难道只有win32下可以进行调试么?那么,这个方法会不会有问题呢?到底有没有调用ServiceabilityAgentJVMDIModule类的suspend方法呢?现在手边没有开发环境,等配置好后,再通过btrace查一下。

 

to be continued......

分享到:
评论

相关推荐

    鸿蒙关于js与serviceAbility交互的Demo讲解

    鸿蒙系统

    Reliability, Availability and Serviceability on Linux.pdf

    Reliability, Availability and Serviceability on Linux.pdf

    Service实现通知,判断通知栏是否已打开

    Service实现通知,判断通知栏是否已打开

    Java.Performance.Companion.0133796825

    Using HotSpot VM Serviceability Agent to analyze, triage, and resolve diverse HotSpot VM issues Troubleshooting out of memory errors, Java level deadlocks, and HotSpot VM crashes Extending the ...

    Java Performance Companion(Addison,2016)

    Using HotSpot VM Serviceability Agent to analyze, triage, and resolve diverse HotSpot VM issues Troubleshooting out of memory errors, Java level deadlocks, and HotSpot VM crashes Extending the ...

    jdk-9.0.1_doc-all 最新版

    Defines the implementation of the HotSpot Serviceability Agent. jdk.httpserver Defines the JDK-specific HTTP server API. jdk.incubator.httpclient Defines the high-level HTTP and WebSocket API. jdk....

    DDI0587D_b_RAS_Supplement.pdf

    Reliability, Availability, and Serviceability (RAS), for Armv8-A

    BSEN1993-1-12-2007Eurocode3—designofsteelstructures

    It complies with the principles and requirements for the safety and serviceability of structures, the basis of their design and verification that are given in EN 1990 – Basis of structural design....

    linux集群 heartbeat应用

    Linux-HA的全称是High-Availability Linux,这个开源项目的目标是:通过社区开发者的共同努力,提供一个增强linux可靠性(reliability)、可用性(availability)和可服务性(serviceability)(RAS)的群集解决方案...

    ArmARM v8-A Supplement - RAS

    ARM® Reliability, Availability, and Serviceability (RAS) Specification ARMv8, for the ARMv8-A architecture profile

    华为E9000 服务器 V100R001

    Availability, and Serviceability)、计算密度、节能减排、背板带宽、智能管控与服务、 计算与存储的弹性配置和灵活扩展、网络低时延和加速方面具有领先的竞争力: l 提供与小型机相当的品质和服务能力,为电信运营...

    Structural Health Monitoring of Long-Span Suspension Bridges

    In recent decades, structural health monitoring systems have been developed to measure the loading environment and responses of these bridges in order to assess serviceability and safety while ...

    visualVm1.4.2分析工具.rar

    VisualVM 是一款免费的性能分析工具。它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。

    PICMG3.0 R3.0 AdvancedTCA Base Specification

    Provide high levels of service availability (99.999% and greater) through the integration of features for resiliency, redundancy, serviceability and manageability Support appropriate scalability of ...

    服务器与小型机的比较.docx

    小型机与PC服务器的简单比较 小型机的最主要的三个优势(Reliability, Availability, Serviceability 高可靠性、高可用性、高服务性) 小型机 1,小型机是封闭专用的计算机系统,CPU一般采用RISC技术即简单指令集...

    服务器系统概述(全文).doc

    它负责将电脑的核心——微处 理器和机器的其他部分相连接,是决定主板级别的重要部件。以往,芯片组由多颗芯片 组成,慢慢的简化为两颗芯片。在计算机领域,"芯片组"术语通常是特指计算机主板或 扩展卡上的芯片。 在...

    VisualVM1_3_9多语言版本(visualvm_139-ml.zip)及离线插件包(plugins for 1.3.9.rar)

    VisualVM1.3.9(2017年11月从官网下载并安装插件验证通过)通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更...

    VisualVM_139 for mac

    它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,...

    工业电子中的图解CompactPCI的优点

     CompactPCI本身所具有的高可热插拔、抗震性强、高可用性(Availability)、易使用性(Serviceability)、可扩展性(Scalability)等特性完全适合工业级的应用。所以采用CompactPCI架构的工业PC,相对商业PC的架构...

    图解CompactPCI的优点

     CompactPCI本身所具有的高可热插拔、抗震性强、高可用性(Availability)、易使用性(Serviceability)、可扩展性(Scalability)等特性完全适合工业级的应用。所以采用CompactPCI架构的工业PC,相对商业PC的架构...

Global site tag (gtag.js) - Google Analytics