debugger
2016-04-17 00:00
204 查看
摘要: 文本将介绍调试器如何实现断点,单步,即时执行函数的功能。
而断点的实现就是通过INT 3来实现,实现的流程如下:
寻找到代码中被下断点的内存位置
记录原先位置的指令
使用INT 3来替换该指令,如果长度不符合,则使用NOP来填充。
这么做是因为涉及到偏移量的问题,不能简单的插入INT 3指令,而是采用替换的方式。[b]这样子,就完成了打断点[/b]的功能。而当代码执行到刚刚下断点(INT 3指令)的时候,会执行如下流程:
INT 3指令引发中断门
Linux内核切换当前任务(被调试程序)到阻塞状态
Linux唤醒调试器(GDB)[b]告知被调试程序现在处于中断状态[/b]。
此时,我们就可以通过GDB来查看被调试程序的变量,堆栈等信息。
首先我们需要了解一下Linux下TSS的概念:
任务状态段(Task State Segment)是保存一个任务重要信息的特殊段。
TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的执行。在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到TR所指定的TSS中;然后,下一任务的TSS的选择子被装入TR;最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现任务的切换。
所以在Linux中,是通过TSS来描述一个程序运行的状态,并且进行多任务轮转的。TSS结构图:
还原被替换的指令,然后通过修改EIP寄存器,将它指向被替换指令地址,因此被调试的程序可以执行刚刚被替换的指令。
JMP : 直接跳转到指定目标代码
CALL: 进行调用制定代码目标函数
在多任务模式下,通过JMP/CALL + TSS 来完成任务的切换。如:
jmp 0x1000:0x10000000 #TSS地址
这样子,就实现了任务的切换。
这个功能和下断点的原理基本一致,也是通过替换指令和插入INT 3来完成。完成运行后,再还原之前被替换的代码,并且修改TSS中的EIP寄存器数值。
所以,即时运行的调用栈就是使用原先的调用栈,而非新开一个THREAD来执行即时运行代码。
而即时运行功能也和GDB实现类似,调用栈也是使用原先的调用栈,而非新开THREAD运行及时代码:
断点处运行:
执行结果为:
所以,及时运行的调用栈环境就是断点位置的环境。
JVM调试参数:
-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=3999,suspend=n
说明:
这样子,就开启了JVM的调试功能,然后通过远程调试器连接上,既可以进行调试了。
因为通过Socket调试非常的方便,所以现在流行的语言基本上都支持远程调试的功能。
调试器工作原理(2):实现断点
调试器工作原理(3):调试信息
TSS任务状态段
Java远程调试
debugger
文本将介绍调试器如何实现断点,单步,即时执行函数的功能。GDB
在Linux C编程中,一般都使用GDB来进行调试代码。通过GDB我们可以实现断点,单步,即时执行这些调试手段。INT 3 和 断点
80x86 CPU调试的核心就是通过INT 3 (80x86 CPU)[b]中断来实现。当执行INT 3 的时候,在Linux中会引发SIGTRAP信号[/b]。从而进一步的实现具体的功能:查看变量,即时执行...。而断点的实现就是通过INT 3来实现,实现的流程如下:
寻找到代码中被下断点的内存位置
记录原先位置的指令
使用INT 3来替换该指令,如果长度不符合,则使用NOP来填充。
这么做是因为涉及到偏移量的问题,不能简单的插入INT 3指令,而是采用替换的方式。[b]这样子,就完成了打断点[/b]的功能。而当代码执行到刚刚下断点(INT 3指令)的时候,会执行如下流程:
INT 3指令引发中断门
Linux内核切换当前任务(被调试程序)到阻塞状态
Linux唤醒调试器(GDB)[b]告知被调试程序现在处于中断状态[/b]。
此时,我们就可以通过GDB来查看被调试程序的变量,堆栈等信息。
TSS
那么问题来了:刚刚被INT 3替换的指令,如何执行?因为被调试程序的用户态EIP已经指向了INT 3下一条指令了。首先我们需要了解一下Linux下TSS的概念:
任务状态段(Task State Segment)是保存一个任务重要信息的特殊段。
TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的执行。在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到TR所指定的TSS中;然后,下一任务的TSS的选择子被装入TR;最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现任务的切换。
所以在Linux中,是通过TSS来描述一个程序运行的状态,并且进行多任务轮转的。TSS结构图:
还原被替换的指令,然后通过修改EIP寄存器,将它指向被替换指令地址,因此被调试的程序可以执行刚刚被替换的指令。
JMP/CALL
80x86 CPU的EIP寄存器不能直接修改,而是需要通过专门指令来修改EIP寄存器:JMP : 直接跳转到指定目标代码
CALL: 进行调用制定代码目标函数
在多任务模式下,通过JMP/CALL + TSS 来完成任务的切换。如:
jmp 0x1000:0x10000000 #TSS地址
这样子,就实现了任务的切换。
即时运行
即时运行指的是使用** GDB CALL 命令**来完成运行函数的功能。这个功能和下断点的原理基本一致,也是通过替换指令和插入INT 3来完成。完成运行后,再还原之前被替换的代码,并且修改TSS中的EIP寄存器数值。
所以,即时运行的调用栈就是使用原先的调用栈,而非新开一个THREAD来执行即时运行代码。
JVM
Java的调试技术是非常成熟的,我们可以通过各种方式来调试Java代码,这也是VM的强大之处。即时运行
在调试的时候,我们想知道一段代码执行的结果是什么。比如说,System.out.println(var)。这时候就需要使用即时运行的功能。而即时运行功能也和GDB实现类似,调用栈也是使用原先的调用栈,而非新开THREAD运行及时代码:
public class Main { static public void main(String args[]) { Main main = new Main(); main.a(); } public void a() { System.out.println("A"); } public void stack() { try { throw new RuntimeException("A"); } catch (Exception e) { e.printStackTrace(); } } }
断点处运行:
执行结果为:
java.lang.RuntimeException: A at Main.stack(Main.java:18) at Main.a(Main.java:13) at Main.main(Main.java:9)
所以,及时运行的调用栈环境就是断点位置的环境。
远程调试
JVM也支持远程调试,这个功能对排查线上BUG是非常方便的,而这个功能是通过SOCKET来完成。JVM调试参数:
-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=3999,suspend=n
说明:
参数 | 含义 |
---|---|
-XDebug | 启用调试。 |
-Xnoagent | 禁用默认sun.tools.debug调试器。 |
-Djava.compiler=NONE | 禁止 JIT 编译器的加载。 |
-Xrunjdwp | 加载JDWP的JPDA参考执行实例。 |
transport | 用于在调试程序和 VM 使用的进程之间通讯。 |
dt_socket | 套接字传输。 |
dt_shmem | 共享内存传输,仅限于 Windows。 |
server=y/n | VM 是否需要作为调试服务器执行。 |
address=3999 | 调试服务器的端口号,客户端用来连接服务器的端口号。 |
suspend=y/n | 是否在调试客户端建立连接之后启动 VM 。 |
因为通过Socket调试非常的方便,所以现在流行的语言基本上都支持远程调试的功能。
日志
在某些时候(生产环境),我们不能通过调试器来完成调试的功能,此时我们需要借助最原始的方式:日志。总结
一个好用的DEBUGGER能极大的增加软件开发效率。参考
调试器工作原理(1):基础篇调试器工作原理(2):实现断点
调试器工作原理(3):调试信息
TSS任务状态段
Java远程调试
相关文章推荐
- Linux 自检和 SystemTap
- 神器SystemTap
- Java 6 JVM参数选项大全(中文版)
- Python 七步捉虫法
- 路由器的配置与调试
- 对于技术人员的出现了运行时间错误,是否要进行调试的解决方法
- Lua编程示例(一):select、debug、可变参数、table操作、error
- C#中的两种debug方法介绍
- 用Ruby实现一个单元测试框架的教程
- 讲解WordPress开发中一些常用的debug技巧
- JavaScript程序设计之JS调试
- 可以用来调试JavaScript错误的解决方案
- 如何调试异步加载页面里包含的js文件
- js调试工具 Javascript Debug Toolkit 2.0.0版本发布
- jQuery下的Ajax调试步骤
- 深入解析JVM对dll文件和对类的装载过程
- php debug 安装技巧
- 调试一段PHP程序时遇到的三个问题
- JavaScript高级程序设计 错误处理与调试学习笔记