这样调试代码,bug分分钟消灭光

这样调试代码,bug分分钟消灭光

原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。

简介

我们常使用IDE的调试功能解决程序问题,但很多同学用的是自上而下调试法,即找到一个代码入口,打上断点然后单步调试。
但一些特殊的调试场景,比如调试框架代码,在不太熟悉框架代码的情况,会因为不知道从哪个入口开始调试而感到迷茫,这时可以试试本文介绍的方法。

例子

比如,我们写了一个接口,在获取参数的时候乱码了如下:
这样调试代码,bug分分钟消灭光
如何快速定位这个问题呢?

众所周知,乱码基本都是因为请求方与服务方字符集配置不一致产生的,比如请求方使用UTF-8,服务方使用GBK。

另外,既然上述接口中参数定义的是String类型,那么spring或tomcat框架中一定有个地方将网络请求中的字节流数据转换成这个String对象。

那么就可以这样,我们在String类的所有构造方法中加入条件断点,加断点后在断点处点鼠标右键即可添加条件,比如new String().contains("abcdefg"),然后请求这个接口时使用一个特殊的参数值abcdefg,这样当框架中new出包含abcdefg的String对象时,我们的条件断点就会命中,如下:
这样调试代码,bug分分钟消灭光

在String中添加条件断点后,我们重启应用,发如下请求来触发断点:

curl http://192.168.0.103:8080/test?data=abcdefg  

命中了条件断点,如下:
这样调试代码,bug分分钟消灭光

我们往调用栈上面看,发现是handleQueryParameters中创建字符串时使用的GBK来new出String,而GBK来自queryStringCharset属性,如下:
这样调试代码,bug分分钟消灭光

那是哪里配置成了GBK呢?我们再在设置queryStringCharset属性的地方,即setQueryStringCharset方法加入断点,重启应用并重新发送请求(重启是为避免缓存导致断点触发不了),如下:
这样调试代码,bug分分钟消灭光

我们再往调用栈上面看,发现setQueryStringCharset的参数来自connector.getURICharset(),如下:
这样调试代码,bug分分钟消灭光

同理,我们在赋值connector.setURICharset的地方打上断点,重启应用,发现在重启的过程中就命中了断点,如下:
这样调试代码,bug分分钟消灭光

我们再往调用栈上面看,发现connector.setURICharset的参数来自this.getUriEncoding()方法,而this是TomcatServletWebServerFactory类型
这样调试代码,bug分分钟消灭光

同理,我们在TomcatServletWebServerFactory.setUriEncoding方法加上断点,重启应用,如下:
这样调试代码,bug分分钟消灭光

我们再往调用栈上面看,在to(Consumer<T> consumer)方法中发现uriEncoding来自this.supplier.get(),而this.supplier.get()取的应该就是ServerProperties$Tomcat对象中的urlEncoding属性
这样调试代码,bug分分钟消灭光

同理,我们在它的setter方法ServerProperties$Tomcat.setUriEncoding里面加上断点,重启应用,如下:
这样调试代码,bug分分钟消灭光

我们再往调用栈上面看,发现uriEncoding是从JavaBeanBinder.bind方法设置进来,从调用方法命名上不难看出,这个方法应该就是将配置文件中的值set到配置类属性中去的,当前被配置的类是ServerProperties$Tomcat,而正在配置的属性是uri-encoding,如下:
这样调试代码,bug分分钟消灭光

我们在此处瞄下各变量与参数的组成,很快就在BeanPropertyBinder参数中找到,uriEncoding来自application-web.yml文件的第4行第19列,如下:
这样调试代码,bug分分钟消灭光

果真,在application-web.yml中第4行,配置了uri-encoding: GBK,如下:
这样调试代码,bug分分钟消灭光

在将其改成UTF-8之后,发现乱码就不存在了,如下:
这样调试代码,bug分分钟消灭光

ok,通过这个例子,相信你已经体会到自底向上调试方法的诀窍了。

总结

上面这个例子其实可以有更快处理方法,比如在当前工程全文搜索GBK,又或者google一下springboot乱码之类的关键词,也能很快解决问题,但思路是很重要的,比如类似下面的场景,也能运用这里的方法:

  1. 系统运行时,不知道什么地方的代码老是执行了一条删除的sql,我们可以和上面一样,在String里面打上条件断点,条件是包含删除sql的部分片段。
  2. 系统启动时,不知道什么地方的代码监听了12345端口,我们可以在ServerSocket里面加上条件断点,条件是port==12345
  3. 系统运行时,不知道什么地方的代码老是写/tmp/app.log文件,我们可以在FileOutputStream.write方法加上条件断点,条件是File=="/tmp/app.log"

往期内容

真正理解可重复读事务隔离级别
Linux文本命令技巧(下)
Linux文本命令技巧(上)
原来awk真是神器啊
常用网络命令总结


长按关注【打码日记】


原文始发于微信公众号(扣钉日记):这样调试代码,bug分分钟消灭光

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/170733.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!