Eclipse通过JDWP调试Dalvik

Eclipse发送各种JDWP命令包,DalvikVM收到命令包(command package),响应并发送回复包(reply package),通过缓冲区交换数据

 

JDWP:调试协议

 

(一)Android Dalvik实现的JDWP响应:

static const JdwpHandlerMap gHandlerMap[] = {
    /* VirtualMachine command set (1) */
    { 1,    1,  handleVM_Version,       "VirtualMachine.Version" },
    { 1,    2,  handleVM_ClassesBySignature,
                                        "VirtualMachine.ClassesBySignature" },
    //1,    3,  VirtualMachine.AllClasses
    { 1,    4,  handleVM_AllThreads,    "VirtualMachine.AllThreads" },
    { 1,    5,  handleVM_TopLevelThreadGroups,
                                        "VirtualMachine.TopLevelThreadGroups" },
    { 1,    6,  handleVM_Dispose,       "VirtualMachine.Dispose" },
    { 1,    7,  handleVM_IDSizes,       "VirtualMachine.IDSizes" },
    { 1,    8,  handleVM_Suspend,       "VirtualMachine.Suspend" },
    { 1,    9,  handleVM_Resume,        "VirtualMachine.Resume" },
    { 1,    10, handleVM_Exit,          "VirtualMachine.Exit" },
    { 1,    11, handleVM_CreateString,  "VirtualMachine.CreateString" },
    { 1,    12, handleVM_Capabilities,  "VirtualMachine.Capabilities" },
    { 1,    13, handleVM_ClassPaths,    "VirtualMachine.ClassPaths" },
    { 1,    14, HandleVM_DisposeObjects, "VirtualMachine.DisposeObjects" },
    //1,    15, HoldEvents
    //1,    16, ReleaseEvents
    { 1,    17, handleVM_CapabilitiesNew,
                                        "VirtualMachine.CapabilitiesNew" },
    //1,    18, RedefineClasses
    //1,    19, SetDefaultStratum
    { 1,    20, handleVM_AllClassesWithGeneric,
                                        "VirtualMachine.AllClassesWithGeneric"},
    //1,    21, InstanceCounts

    /* ReferenceType command set (2) */
    { 2,    1,  handleRT_Signature,     "ReferenceType.Signature" },
    { 2,    2,  handleRT_ClassLoader,   "ReferenceType.ClassLoader" },
    { 2,    3,  handleRT_Modifiers,     "ReferenceType.Modifiers" },
    //2,    4,  Fields
    //2,    5,  Methods
    { 2,    6,  handleRT_GetValues,     "ReferenceType.GetValues" },
    { 2,    7,  handleRT_SourceFile,    "ReferenceType.SourceFile" },
    //2,    8,  NestedTypes
    { 2,    9,  handleRT_Status,        "ReferenceType.Status" },
    { 2,    10, handleRT_Interfaces,    "ReferenceType.Interfaces" },
    //2,    11, ClassObject
    { 2,    12, handleRT_SourceDebugExtension,
                                        "ReferenceType.SourceDebugExtension" },
    { 2,    13, handleRT_SignatureWithGeneric,
                                        "ReferenceType.SignatureWithGeneric" },
    { 2,    14, handleRT_FieldsWithGeneric,
                                        "ReferenceType.FieldsWithGeneric" },
    { 2,    15, handleRT_MethodsWithGeneric,
                                        "ReferenceType.MethodsWithGeneric" },
    //2,    16, Instances
    //2,    17, ClassFileVersion
    //2,    18, ConstantPool

    /* ClassType command set (3) */
    { 3,    1,  handleCT_Superclass,    "ClassType.Superclass" },
    { 3,    2,  handleCT_SetValues,     "ClassType.SetValues" },
    { 3,    3,  handleCT_InvokeMethod,  "ClassType.InvokeMethod" },
    //3,    4,  NewInstance

    /* ArrayType command set (4) */
    //4,    1,  NewInstance

    /* InterfaceType command set (5) */

    /* Method command set (6) */
    { 6,    1,  handleM_LineTable,      "Method.LineTable" },
    //6,    2,  VariableTable
    //6,    3,  Bytecodes
    //6,    4,  IsObsolete
    { 6,    5,  handleM_VariableTableWithGeneric,
                                        "Method.VariableTableWithGeneric" },

    /* Field command set (8) */

    /* ObjectReference command set (9) */
    { 9,    1,  handleOR_ReferenceType, "ObjectReference.ReferenceType" },
    { 9,    2,  handleOR_GetValues,     "ObjectReference.GetValues" },
    { 9,    3,  handleOR_SetValues,     "ObjectReference.SetValues" },
    //9,    4,  (not defined)
    //9,    5,  MonitorInfo
    { 9,    6,  handleOR_InvokeMethod,  "ObjectReference.InvokeMethod" },
    { 9,    7,  handleOR_DisableCollection,
                                        "ObjectReference.DisableCollection" },
    { 9,    8,  handleOR_EnableCollection,
                                        "ObjectReference.EnableCollection" },
    { 9,    9,  handleOR_IsCollected,   "ObjectReference.IsCollected" },
    //9,    10, ReferringObjects

    /* StringReference command set (10) */
    { 10,   1,  handleSR_Value,         "StringReference.Value" },

    /* ThreadReference command set (11) */
    { 11,   1,  handleTR_Name,          "ThreadReference.Name" },
    { 11,   2,  handleTR_Suspend,       "ThreadReference.Suspend" },
    { 11,   3,  handleTR_Resume,        "ThreadReference.Resume" },
    { 11,   4,  handleTR_Status,        "ThreadReference.Status" },
    { 11,   5,  handleTR_ThreadGroup,   "ThreadReference.ThreadGroup" },
    { 11,   6,  handleTR_Frames,        "ThreadReference.Frames" },
    { 11,   7,  handleTR_FrameCount,    "ThreadReference.FrameCount" },
    //11,   8,  OwnedMonitors
    { 11,   9,  handleTR_CurrentContendedMonitor,
                                    "ThreadReference.CurrentContendedMonitor" },
    //11,   10, Stop
    //11,   11, Interrupt
    { 11,   12, handleTR_SuspendCount,  "ThreadReference.SuspendCount" },
    //11,   13, OwnedMonitorsStackDepthInfo
    //11,   14, ForceEarlyReturn

    /* ThreadGroupReference command set (12) */
    { 12,   1,  handleTGR_Name,         "ThreadGroupReference.Name" },
    { 12,   2,  handleTGR_Parent,       "ThreadGroupReference.Parent" },
    { 12,   3,  handleTGR_Children,     "ThreadGroupReference.Children" },

    /* ArrayReference command set (13) */
    { 13,   1,  handleAR_Length,        "ArrayReference.Length" },
    { 13,   2,  handleAR_GetValues,     "ArrayReference.GetValues" },
    { 13,   3,  handleAR_SetValues,     "ArrayReference.SetValues" },

    /* ClassLoaderReference command set (14) */
    { 14,   1,  handleCLR_VisibleClasses,
                                        "ClassLoaderReference.VisibleClasses" },

    /* EventRequest command set (15) */
    { 15,   1,  handleER_Set,           "EventRequest.Set" },
    { 15,   2,  handleER_Clear,         "EventRequest.Clear" },
    //15,   3,  ClearAllBreakpoints

    /* StackFrame command set (16) */
    { 16,   1,  handleSF_GetValues,     "StackFrame.GetValues" },
    { 16,   2,  handleSF_SetValues,     "StackFrame.SetValues" },
    { 16,   3,  handleSF_ThisObject,    "StackFrame.ThisObject" },
    //16,   4,  PopFrames

    /* ClassObjectReference command set (17) */
    { 17,   1,  handleCOR_ReflectedType,"ClassObjectReference.ReflectedType" },

    /* Event command set (64) */
    //64,  100, Composite   <-- sent from VM to debugger, never received by VM

    { 199,  1,  handleDDM_Chunk,        "DDM.Chunk" },
};

 

(二)具体的请求和响应:

Eclipse进入debug状态

(1)发送JDWP的ReferenceType.FieldsWithGeneric命令包, dalvik响应调用handleRT_FieldsWithGeneric->dvmDbgOutputAllFields将某个类的所有域信息放入缓冲,Eclipse读取以显示

(2)发送JDWP的ObjectReference.GetValues命令包,dalvik响应调用handleOR_GetValues->dvmDbgGetFieldValue将域值放入缓冲区

在自定义编辑器中集成 JDWP 以支持对反射加载的类进行调试,需要从 JVM 调试协议、反射机制处理以及调试事件监听等多个方面进行设计与实现。 ### 启动 JVM 并启用 JDWP 调试 为了能够通过 JDWP 连接目标 JVM 并控制其执行流程,首先需要确保 JVM 是在启用调试模式的情况下启动的。可以通过添加如下参数来配置: ```shell java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 -jar yourapplication.jar ``` 该参数指示 JVM 在指定端口监听来自调试客户端的连接请求,并使用 socket 作为传输方式。其中: - `transport=dt_socket` 表示使用 socket 通信。 - `server=y` 表示 JVM 将作为调试服务器等待客户端连接。 - `suspend=n` 表示 JVM 启动时不挂起程序执行[^2]。 如果希望程序在启动时等待调试器连接后再继续执行,则可以将 `suspend` 设置为 `y`。 ### 建立 JDWP 客户端连接 在编辑器中集成 JDWP 支持的关键是实现一个调试客户端,用于与目标 JVM 的 JDWP 服务建立连接并发送调试命令。这通常涉及以下几个关键步骤: 1. **建立 Socket 连接**:通过 TCP/IP 协议连接到目标 JVM 的调试端口(如 `address=8000`)。 2. **处理 JDWP 协议交互**:JDWP 使用二进制协议进行通信,需要解析和生成符合规范的数据包。包括握手、命令发送、事件接收等过程。 3. **管理调试会话**:维护调试上下文,例如线程状态、类加载信息、断点设置等。 以下是一个简单的示例代码片段,展示如何使用 Java 中的 `Socket` 类建立与 JDWP 端口的连接: ```java import java.io.*; import java.net.*; public class JDWPClient { public static void main(String[] args) throws IOException { String host = "localhost"; int port = 8000; try (Socket socket = new Socket(host, port)) { System.out.println("Connected to JDWP server at " + host + ":" + port); // 获取输入输出流 InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); // 发送握手数据 byte[] handshake = "JDWP-Handshake".getBytes(); output.write(handshake); output.flush(); // 接收响应 byte[] response = new byte[14]; input.read(response); String respStr = new String(response); if (!respStr.equals("JDWP-Handshake")) { throw new IOException("Handshake failed"); } System.out.println("Handshake successful"); // 此处可继续发送调试命令 } } } ``` 此代码仅展示了基础的握手流程,实际开发中需要进一步解析 JDWP 的命令结构并实现完整的调试功能。 ### 处理反射加载类的调试问题 反射加载的类在 JVM 中动态生成,传统的调试器往往无法直接识别这些类,因此在调试过程中可能无法设置断点或查看变量值。解决这一问题的核心在于: - **监听 ClassPrepare 事件**:通过 JDWP 监听 `ClassPrepare` 事件,当某个类被加载时,可以立即对其设置断点或获取类信息。 - **动态匹配类名或方法签名**:对于反射加载的类,通常没有固定的命名规则,因此需要根据运行时的类名、方法名或字节码特征进行匹配。 - **利用 VM Start 事件初始化调试环境**:在 JVM 启动完成后,主动查询已加载的类列表,以便提前注册断点。 以下是监听 `ClassPrepare` 事件的基本示例: ```java // 构造 EventRequest Set 命令,监听 ClassPrepare 事件 byte[] commandPacket = new byte[11]; commandPacket[0] = 0x00; // flags commandPacket[1] = 0x00; // reserved commandPacket[2] = 0x0F; // command set (EventRequest) commandPacket[3] = 0x01; // command (Set) commandPacket[4] = 0x01; // suspend policy (SUSPEND_ALL) commandPacket[5] = 0x01; // event count (1) commandPacket[6] = 0x02; // event kind (ClassPrepare) commandPacket[7] = 0x00; // modifier count (0) // ... 继续填充过滤条件 ``` 一旦某个类被加载,调试客户端将收到 `ClassPrepare` 事件通知,此时可以对该类的方法设置断点。 ### 集成用户界面与调试逻辑 在编辑器中提供可视化的调试体验是关键。需要实现以下功能模块: - **断点管理界面**:允许用户点击源代码行号旁设置或删除断点。 - **变量查看窗口**:显示当前作用域内的局部变量和对象属性。 - **调用栈跟踪**:展示当前线程的堆栈信息,支持逐层展开。 - **控制按钮**:提供“继续”、“单步进入”、“单步跳过”、“停止”等操作控件。 这些功能需要与底层的 JDWP 客户端紧密协作,实时更新调试状态并与用户交互。 ### 注意事项 - **安全性与性能**:在生产环境中启用 JDWP 调试可能会带来潜在的安全风险和性能开销,建议仅在开发阶段使用。 - **兼容性**:不同版本的 JVM 可能对 JDWP 协议的支持存在差异,需确保编辑器能够适配多种环境。 - **错误处理**:网络中断、超时、无效命令等情况需要有完善的异常捕获和恢复机制。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值