Java远程调试(Remote Debugging)的那些事

Wesley13
• 阅读 656

Java远程调试(Remote Debugging)的那些事

本文主要基于一篇英文原作翻译而成,删减部分无用文字,添加了必要的注解和补充。

英文原文是一篇对远程调试讲解很通俗的博文:

http://blog.trifork.com/2014/07/14/how-to-remotely-debug-application-running-on-tomcat-from-within-intellij-idea/

前言

       这篇文章将研究如何处理和调试那些只发生在生产环境(或其他远程环境)而本地开发环境可能没办法重现的“问题”。任何碰到过这种情况的人都不得不承认,试图定位这种“问题”原因的过程,很大可能性是以一堆胡乱猜测而告终:一个非常耗时且低效的过程。

      还有一种场景, 你得到了一个可以部署的war/jar包,只有class没有java源代码,而应用部署在本地/远程后,是否可以调试? 读完本文就明白怎么做了。

配置

远程调试包括两个步骤:

1. 启动Tomcat启用远程调试

2. 用 IDE (这里用IntelliJ IDEA)要能够调试远程Tomcat应用

Tomcat启用远程调试

这里有多种方法可以做到,根据tomcat所运行的操作系统而有些微的不同。但是不管用哪种方法,这些配置的背后都做了同一件事:传递特定的启动参数给 JVM,让它启用远程调试(remote debugging)。

JVM 激活远程调试的启动参数有 JPDA_OPTS, CATALINA_OPTS 和  JAVA_OPTS。其中 JAVA_OPTS 是通常不建议使用的, 因为基于 JAVA_OPTS 的参数设定会暴露给所有的 JVM 应用, 而 CATALINA_OPTS 定义的设定值限制在Tomcat 内。

1

使用JPDA_OPTS

在 CATALINA_HOME/bin 目录下创建可执行脚本文件 setenv.sh ( Windows 创建 setenv.bat ),加入内容:

Linux setenv.sh

export JPDA_OPTS="-agentlib:jdwp=transport=dt_socket, address=1043, server=y, suspend=n"

Windows setenv.bat

set JPDA_OPTS="-agentlib:jdwp=transport=dt_socket, address=1043, server=y, suspend=n"

这些参数要做的事情就是启用远程调试和配置有效的选项:

  • 指定运行的被调试应用和调试者之间的通信协议,(ie:  transport=dt_socket)

  • 远程被调试应用开通的端口,(ie: address=1043), 可定义其他端口,比如9999

  • server=y 表示这个 JVM 即将被调试

  • suspend=n 用来告知 JVM 立即执行,不要等待未来将要附着上/连上(attached)的调试者。如果设成 y, 则应用将暂停不运行,直到有调试者连接上

suspend=y的一个比较适用的场景是,当debug一个会阻止应用成功启动的问题时, 通过suspend=y可以确保调试者连上来之后再启动应用,否则应用已经启动报错了再调试也没意义了。

当然上面的设置也可以直接放到 catalina.sh (catalina.bat )内,但是有个 setevn.* 额外的配置文件一直是最佳选择, tomcat会自动读取。

要注意的是, 有些人会碰到过用另一个配置方法来启用远程调试:

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=1043, suspend=n

-Xdebug and -Xrunjdw 与我们上面推荐的设置不同之处在于, 它是一种旧方式,适用于JVM 小于 JAVA 5.0 的版本(包括5.0), 而 agentlib:jdwp适用于 JAVA 5.0 和以后版本。

最后通过下面的命令行启动tomcat,即可完成tomcat启用远程调试啦。

$CATALINA_HOME/bin/catalina.sh jpda start

2

使用 JAVA_OPTS / CATALINA_OPTS

如果你是在 Windows 系统把 Tomcat 作为系统服务来运行的,直接打开 Apache Tomcat 的属性对话框,在Java Tab也添加启动参数:

-agentlib:jdwp=transport=dt_socket,

address=1043,server=y,suspend=n

请确保每一条配置都是新的行,参数选项之间没有空格

但如果Tomcat没有作为 Windows 系统服务, 启用方法与前面类似,在 setenv.bat 文件中写入:

set CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"

如果运行在Linux上, setenv.sh 中写入:

export CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"

按照普通的方式启动Tomcat即可;

./startup.sh

或者

./catalina.sh start

3

使用JPDA启动

最后一种启用远程调试的方式是用 JPDA 切换, 用如下的启动命令将使用默认值自动启用远程调试,

catalina jpda start

该命令默认使用的设置是

-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n

如果你想要修改默认设置中的选项怎么办?可以通过修改 Tomcat 需要的这些环境变量来实现:

JPDA_TRANSPORT: 指定 jpda 传输协议
JPDA_ADDRESS: 指定远程调试端口
JPDA_SUSPEND: 指定 jvm 启动暂缓

export JDPA_ADDRESS=”8080“

然后再运行 catalina jpda start , 那么远程调试的端口将变成8080

配置Intellj Idea

确定远程 Tomcat 启动的应用已经开启了远程调试, 下一件事情就是配置Intellij Idea了。这里仍然有两种方式:Remote Tomcat 或者 Remote。

1

使用 Remote Tomcat 配置

首先保证 IDEA 里面已经打开了需要远程调试的工程源码,

然后点击 Run ➝ Edit Configurations ➝ + 按钮 ➝ Tomcat Server ➝ Remote

Java远程调试(Remote Debugging)的那些事

输入必要的远程 IP 地址和端口(Tomcat http端口);

Java远程调试(Remote Debugging)的那些事

然后转到 Startup / Connnection Tab 页,选择 ”Debug", 输入远程调试端口,我们例子是 1043。

Java远程调试(Remote Debugging)的那些事

保存后,开始 debug 启动远程调试,如果运行成功会显示如下的界面,然后在源码加断点开始调试。

Java远程调试(Remote Debugging)的那些事

2

使用 Remote 配置(推荐)

第一个方法有个缺陷,你打开的工程源码必须是编译通过的工程,否则会启动会报错;

而介绍的这第二种方法可以在你的工程目录乱七八糟,不是一个完整的可以部署的工程,甚至是一个解压缩的 war/ jar 的情况下都可以调试。

同上步骤,只是选择“Remote",然后输入Name, 修改Host, Port (1043) 即可, 保存后开始Debug。

Java远程调试(Remote Debugging)的那些事

Java远程调试(Remote Debugging)的那些事

设置比 Remote Tomcat 更简单,这里介绍一个实际案例。

我手里有一个可部署的war包,没有源码,在远程已经部署完毕。这时我想调试那个远程应用,怎么做呢?

  1. 解压缩war包到一个文件夹,然后用Intellij Idea打开这个文件夹,如图的结构,编译的Class都在 WEB-INF/classes 目录下

  2. 找到我要debug的那个class, 这里示例Handler.class, 通过Idea反编译出来的类代码,拷贝到一个新的文件Handler.java

  3. 虽然如图可以看到各种的编译错误,但是完全不影响你启动,代码中加断点和调试哦。

Java远程调试(Remote Debugging)的那些事

远程JVM调试怎么工作的

一切源于被称作 Agents 的东西。

运行着各种编译过的 .class 文件的JVM, 有一种特性,可以允许外部的库(Java或C++写的libraries)在运行时注入到 JVM 中。这些外部的库就称作 Agents, 他们有能力修改运行中 .class 文件的内容。

这些 Agents 拥有的这些 JVM 的功能权限, 是在 JVM 内运行的 Java Code 所无法获取的, 他们能用来做一些有趣的事情,比如修改运行中的源码, 性能分析等。 像 JRebel 工具就是用了这些功能达到魔术般的效果。

传递一个 Agent Lib 给 JVM, 通过添加 agentlib:libname[=options] 格式的启动参数即可办到。像上面的远程调试我们用的就是 -agentlib:jdwp=... 来引入 jdwp 这个 Agent 的。

jdwp 是一个 JVM 特定的 JDWP(Java Debug Wire Protocol) 可选实现,用来定义调试者与运行JVM之间的通讯,它的是通过 JVM 本地库的 jdwp.so 或者 jdwp.dll 支持实现的。

它到底是怎么工作的呢?

简单来说, jdwp agent 会建立运行应用的 JVM 和调试者(本地或者远程)之间的桥梁。既然他是一个Agent Library, 它就有能力拦截运行的代码。

在 JVM 架构里, debugging 功能在 JVM 本身的内部是找不到的,它是一种抽象到外部工具的方式(也称作调试者 debugger)。这些调试工具或者运行在 JVM 的本地 或者在远程。这是一种解耦,模块化的架构。

更多关于远程部署相关,以及 JDWP 的深入说明,大家有兴趣可以自己研究一下。

END

贱贱的忧桑

微信:geniusiandev

Java远程调试(Remote Debugging)的那些事

这里的东西一点都不便宜!

长按二维码关注

本文分享自微信公众号 - 曲水流觞TechRill(TechRill)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这