IDE 版本
- 2023.1(Ultimate Edition)
前言
- 不知道大家对这样一张图片是不是很熟悉,当我们的程序需要排查一个很隐蔽的问题时,各处打印日志进行排查错误,排查完之后又将所有的排查日志进行一个一个删除。不知道你们有没有干过,我以前是经常这么干哈哈哈。
- 在分享之前,大家可以先思考一下以下几个问题,看看大家在日常工作中是怎么处理的,我会在文章末尾给出一些解决思路供大家参考:
1、排查一个隐蔽错误时到处打印日志?如果是第三方源码怎么办呢?下载源码调试?
2、源码调试时,一个类有多个实现,我们想知道调用了哪个实现?一步步断点进去?
3、源码调试时,调用链路很长,一不小心调用到哪里去了怎么办?我以前曾经被绕晕过
4、源码调试时想跳过某段逻辑执行自己的逻辑怎么办呢?
5、调试程序中某个变量在哪里被改变或者被访问?
6、如何进行多线程断点调试?
IDE Debug
- IDE Debug 是一个非常强大的功能,可以帮助我们非常便捷的进行程序调试,支持 字段断点、行断点、方法断点、异常断点等 多种调试方式,下面我们来看看如何正确的使用这些调试方式。
主界面介绍
- 我们先看看 Debug 的主界面:
- 横向功能区
Show Execution Point (Alt + F10):如果你的光标在其它行或其它页面,点击按钮可跳转到当前代码执行所在行。
Step Over (F8):步过,一行一行地往下走,如果这一行上有方法不会进入方法。
Step Into (F7):步入,如果当前行有方法,可以进入方法内部。
Step Out (Shift + F8):步出,从步入的方法内退出到方法调用处,此时方法已执行完毕。
Drop Frame (默认无):回退断点,后面章节详细说明。
Run to Cursor (Alt + F9):运行到光标处,你可以将光标定位到你需要查看的那一行,然后使用这个功能,代码会运行至光标行,而不需要打断点。
Evaluate Expression (Alt + F8):计算表达式,用于执行自己想执行的代码,这个是一个比较重要的功能。
Trace Current Stream Chain:用于 Stream 调试。
- 纵向功能区
Rerun 'xxxx':重新 Debug 程序,会关闭服务后重新启动程序进行 Debug。
Modify Run Configuration:更改程序运行配置。
Resume Program (F9):恢复程序继续运行,若有断点则会运行到下一个断点,若无断点,则直接运行到程序结束。
Pause Program:暂停程序,以便检查代码的运行情况、查看变量的值。
Stop 'xxx' (Ctrl + F2):关闭程序。
View Breakpoints (Ctrl + Shift + F8):查看所有断点。
Mute Breakpoints:失效断点,选择这个后,所有断点变为灰色,断点失效,按F9则可以直接运行完程序。再次点击,断点变为红色,有效。如果只想使某一个断点失效,可以在断点上右键取消Enabled。
Get Thrad Dump:获取线程转储或线程快照,用于获取线程的堆栈信息。
字段断点(field breakpoints)
- 当指定的字段被读取或修改时会进入断点,暂停程序,常用于观察变量在何处被修改和访问。
对方法内部的局部变量无法使用字段断点,只能使用行断点,可以使用 Condition 条件判断实现这个字段监测功能。
使用方式
- 在需要断点的字段前左键点击,会出现一个眼睛的标志。
配置
- 右键眼睛标志,选择 More 进行配置。
- 上面是一些 字段断点、行断点、方法断点 的通用配置,字段断点的特性化配置在于 Field access、Field Modification ,即当字段被访问或被修改时进入断点,在观察变量什么时候被使用和什么时候被修改时十分有用。
Enabled
- 是否启用断点。
Suspend
- 当断点的 Suppend 属性被勾选,触发该断点时,程序挂起;当该属性未选中时,程序触发该断点时,程序不会挂起。常用于和Log联合使用,在不暂停程序的情况输出打印日志。
当断点的 All 属性被勾选,触发该断点时,会挂起所有线程;
当断点的 Thead 属性被勾选,触发该断点时,只会挂起触发该断点的那个线程,不影响其他线程;
Log
- Log 是一个很有用的属性,可以帮助我们在不修改源码的情况下,打印排查日志。
- 常见的两个配置项:
Breakpoint hit message:触发断点是打印日志信息
Stack trace:输出触发断点时的堆栈信息
- 示例:
// Breakpoint hit message
Breakpoint reached at org.example.TestA.main(TestA.java:17)
// Stack trace
Breakpoint reached
at org.example.TestA.main(TestA.java:17)
// Evaluate and log 计算表达式和日志
执行methodA 前 monitorNum = 1
行断点(line breakpoints)
- 在指定代码行设置断点,程序在到达设置断点的代码行时暂停程序。这种类型的断点可以设置在任何可执行的代码行上。这是我们最常使用的断点方式。
使用方式
- 在需要断点的行前左键点击,会出现一个红色的小圆点。
配置方式
- 右键小圆点标志,选择 More 进行配置,主要是一些通用化配置,无特殊配置。
方法断点(method breakpoints)
- 在进入某个方法或者退出某个方法时暂停程序进行断点。
- 这个功能在阅读源码时非常有用,比如一个接口有很多的实现类,我们并不知道实际调用了哪个方法,我们可以使用方法断点直接进入实际使用的实现类方法中。
使用方式
- 在定义的方法名或接口方法前左键点击,会出现一个菱形。
配置方式
- 右键菱形标志,选择 More 进行配置。
Emulated:会将方法断点优化成方法中第一条和最后一条语句的行断点,这样会优化调试的性能,因此在IDE中会默认选中。
Method Entry:方法进入第一行时进行断点。
Method exit:方法退出时最后一条语句进行断点。
异常断点(exception breakpoints)
- 异常断点和上述几种断点不同,不会与源代码映射,而是程序抛出异常时就会激活对应的异常断点,属于程序级别断点。
- 在我们程序调试的时候推荐开启这个功能,帮助我们在异常发生时观察变量的状态,以及如何发生的,而不需要重新运行程序 Debug 进行断点。
异常断点分为两类:
1、Any Exception,任意Throwable异常被捕获或未被捕获就会触发断点
2、指定类型的异常及其该异常子类被捕获或未被捕获会触发断点
使用方式
- 点击 View Breakpoints,添加异常断点。
配置方式
Caught exception:当指定的异常被捕获时,触发断点程序挂起;
Uncaught exception:当指定的异常未被捕获时,触发断点程序挂起;
IDE Debug 调试技巧
1、排查一个隐蔽错误时到处打印日志?如果是第三方源码怎么办呢?下载源码调试?
- 我们可以使用 Log 打印辅助日志而不用改动源码。
2、源码调试时,一个类有多个实现,我们想知道调用了哪个实现?一步步断点进去?
- 我们可以使用 方法断点(method breakpoints)在进入方法和退出方法时断点,帮助我们快速找到实际调用的类。
3、源码调试时,调用链路很长,一不小心调用到哪里去了怎么办?我以前曾经被绕晕过
-
这里介绍 debug 的另一个功能 BookMarks(书签),可以帮助我们整理排序我们整个流程,并进行快速跳转。
-
在需要标记位置右键添加书签并制定快捷键。
- 按快捷键 Ctrl + 3、Ctrl + 4 快速跳转待对应标签处,并支持拖动标签按照我们想要的顺序显示。
4、源码调试时想跳过某段逻辑执行自己的逻辑怎么办呢?
- 这里有几种思路:
- 1、如果需要增加逻辑进行简单的调试可以使用上面的提到的一个重要工具 Evaluate Expression(计算表达式)这个工具我们执行我们的代码。
- 2、Evaluate Expression(计算表达式)支持一些简单的改动,如果需要大量改动源代码(比如删除某段逻辑),且如果是第三方代码库时(不下载源代码的情况下),我们可以考虑另外两种思路:1、现在第三方框架有很多拓展点,在拓展点引入我们的类。2、是否支持 SPI 使用 SPI 的方式注入。
5、调试程序中某个变量在哪里被改变或者被访问?
- 类属性我们可以使用 字段断点(field breakpoints)的 Field access、Field Modification 功能。
- 方法内的局部变量我们可以使用,Condition 条件表达式,在一些循环处理逻辑中十分有效,可以帮助我们快速跳断点到我们想要的变量状态时刻。
6、如何进行多线程断点调试?
- 使用 Suspend 功能根据我们实际需求进行断点调试。