使用JDI监听Java程序运行

2023-05-16

Java虚拟机提供了一套用于调试(JVMDI)和监视(JVMPI)的接口,Java5之后统一为JVMTI: http://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/ 。

 

其中JVMDI分为三个部分:JVMDI,JDWP和JDI . http://docs.oracle.com/javase/1.4.2/docs/guide/jpda/architecture.html

 

这篇就是简单的介绍一下怎么使用JDI去监视程序的运行的。

 

首先假设有一个简单的程序:

package test;

public class Test {

	public static void main(String[] args) {
		new Thread() {
			@Override
			public void run() {
				Test test = new Test();
				while (true) {
					try {
						sleep(5000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					test.printHello();
				}
			}
		}.start();
	}

	protected void printHello() {
		System.out.println("hello");

	}

}

 

程序中,每隔五秒种,printHello()方法就会执行一次。

 

如果你希望每次printHell()被执行的时候通知你一下,在不修改代码的情况下,你要怎么办?没办法吧?

 

看看JDI的定义:

JDI - Java Debug Interface 
Defines a high-level Java language interface which tool developers can easily use to write remote debugger applications. 

 

所以首先,我们先以远程调试的方式启动上面的Test类:

java -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8800 -cp . test.Test

 

大致就是以socket传输的方式调试Test类,调试的连接端口为8800,并且连接过程不挂起。

 

一量启动,就可以看到如下的输出:

 

Listening for transport dt_socket at address: 8800
hello
hello
hello

 

这样Server被调试端就准备好了,下面就是写监听端了。这里就要用到jdk中提供的JDI接口了。要使用此接口,我们需要在类路径里包含JDK下的tools.jar等包,可以在<JDK>/lib目录下找着。

一、取得连接器

		VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
		List<AttachingConnector> connectors = vmm.attachingConnectors();
		SocketAttachingConnector sac = null;
		for (AttachingConnector ac : connectors) {
			if (ac instanceof SocketAttachingConnector) {
				sac = (SocketAttachingConnector) ac;
				break;
			}
		}
		if (sac == null) {
			System.out.println("JDI error");
			return;
		}

 

二、连接到远程虚拟器

		Map arguments = sac.defaultArguments();
		Connector.Argument hostArg = (Connector.Argument) arguments.get(HOST);
		Connector.Argument portArg = (Connector.Argument) arguments.get(PORT);

		hostArg.setValue("127.0.0.1");
		portArg.setValue(String.valueOf(8800));

		vm = sac.attach(arguments);

 

三、取得要关注的类和方法

		List<ReferenceType> classesByName = vm.classesByName("test.Test");
		if (classesByName == null || classesByName.size() == 0) {
			System.out.println("No class found");
			return;
		}
		ReferenceType rt = classesByName.get(0);
		List<Method> methodsByName = rt.methodsByName("printHello");
		if (methodsByName == null || methodsByName.size() == 0) {
			System.out.println("No method found");
			return;
		}
		Method method = methodsByName.get(0);

 

四、注册监听

		vm.setDebugTraceMode(VirtualMachine.TRACE_EVENTS);
		vm.resume();
		EventRequestManager erm = vm.eventRequestManager();
		
		MethodEntryRequest methodEntryRequest = erm.createMethodEntryRequest();
		methodEntryRequest.addClassFilter(rt);
		methodEntryRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
		methodEntryRequest.enable();
		
		BreakpointRequest breakpointRequest = erm
				.createBreakpointRequest(method.location());
		breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
		breakpointRequest.enable();

		eventLoop();

 

这里监听每次方法入口的时候,以及在方法上注册一个断点,发一个通知。

 

四、eventLoop()的实现

	private static void eventLoop() throws Exception {
		eventQueue = vm.eventQueue();
		while (true) {
			if (vmExit == true) {
				break;
			}
			eventSet = eventQueue.remove();
			EventIterator eventIterator = eventSet.eventIterator();
			while (eventIterator.hasNext()) {
				Event event = (Event) eventIterator.next();
				execute(event);
			}
		}
	}

	private static void execute(Event event) throws Exception {
		if (event instanceof VMStartEvent) {
			System.out.println("VM started");
			eventSet.resume();
		} else if (event instanceof BreakpointEvent) {
			System.out
					.println("Reach Method printHello of test.Test");
			eventSet.resume();
		} else if (event instanceof MethodEntryEvent) {
			MethodEntryEvent mee = (MethodEntryEvent) event;
			Method method = mee.method();
			System.out.println(method.name() + " was Entered!");
			eventSet.resume();
		} else if (event instanceof VMDisconnectEvent) {
			vmExit = true;
		} else {
			eventSet.resume();
		}
	}

 

最后看输出:

[JDI: EventSet: SUSPEND_EVENT_THREAD]
[JDI: Event: MethodEntryEvent@test.Test:23 in thread Thread-0]
[JDI: Event: BreakpointEvent@test.Test:23 in thread Thread-0]
printHello was Entered!
Reach Method printHello of test.Test
[JDI: EventSet: SUSPEND_EVENT_THREAD]
printHello was Entered!
[JDI: Event: MethodEntryEvent@test.Test:23 in thread Thread-0]
[JDI: Event: BreakpointEvent@test.Test:23 in thread Thread-0]
Reach Method printHello of test.Test

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用JDI监听Java程序运行 的相关文章

随机推荐

  • launch文件加载urdf到rviz和gazebo中报错 Invalid <param> tag: Cannot load command parameter [robot_description]

    在学习赵虚左老师的ros入门教程urdf一章时 xff0c 使用 launch文件加载urdf到rviz和gazebo中的时候 xff08 命令如下 xff09 roslaunch learning gazebo demo2 car lau
  • Linux下如何启动Tomcat像Windows启动并显示控制台日志信息一样?

    Linux 下如何启动Tomcat 像Windows 启动并显示控制台日志信息一样 xff1f Windows 下启动tomcat xff0c 一般直接运行startup bat xff0c 启动后如下图所示 xff1a Linux 下直接
  • An overview of time series forecasting models

    An overview of time series forecasting models 2019 10 04 09 47 05 This blog is from https towardsdatascience com an over
  • swap与zRam能否提升游戏性能【转】

    http bbs angeeks com thread 2384563 1 1 html 先来谈谈swap xff0c swap是创建在sdcard上的一个缓存文件或者也可以创建在手机NAND上的 xff0c 其两者的速度不 说 xff0c
  • 如何在linux脚本里面套娃运行脚本

    直接在脚本里面写sudo bash xx sh 有时候可能会出现问题 xff0c 这个时候就可以使用gnome重新打开新终端运行 首先安装gnome的库 sudo apt get install gnome terminal 然后在脚本里面
  • 树莓派如何设置开机自启动脚本

    参考文章 xff1a 史鑫龙 xff1a 树莓派开机自动执行程序 通过桌面启动 by xinlong 通过该文章的操作可以实现开机启动python程序 xff0c 但并不能实现开机运行shell脚本 xff0c 结合我之前的文章发现配合gn
  • Markdown学习记录

    Markdown 1 代码块 xff1a 代码块语法 xff1a 96 96 96 python 96 96 96 shell 1 python代码 print 34 Hello World 34 2 shell脚本 linux下重启的命令
  • MAC终端代理设置

    移动开发有时需要设置代理 xff0c 不然太慢 在终端中输代码即可显示隐藏文件 defaults write com apple finder AppleShowAllFiles boolean true killall Finder 再次
  • C#:如何查看.net core版本?

    C xff1a 如何查看 net core版本 xff1f 打开控制面板 xff0c 选择 程序和功能 xff0c 找到下图选项 xff0c 即可查看 net core版本 检查是否已正确安装所有内容 xff1a 安装完成后 xff0c 打
  • Qt信号槽如何传递参数

    Qt信号槽如何传递参数 利用 Qt 进行程序开发时 xff0c 有时需要信号 槽来完成参数传递 带参数的信号 槽在使用时 xff0c 有几点需要注意的地方 xff0c 下面结合实例进行介绍 1 当信号与槽函数的参数数量相同时 xff0c 它
  • 解决 zsh: command not found 报错

    问题描述 最近在开发 Go 项目 xff0c 使用 go get u xxxx 成功下载安装包后 xff0c 在终端执行新下载包的命令 xff0c 一直报 zsh command not found 的错误 一开始以为是包没安装成功 xff
  • Ubuntu 安装scipy错误解决办法

    在ubuntu 14 04使用pip3 install scipy时报错 xff1a numpy distutils system info NotFoundError no lapack blas resources found 百度了一
  • LA5016-IIC EEPROM协议解析

    写入 LA5016 解析协议设置 xff1a 波形 读取 波形 xff1a
  • Linux下安装oracle数据库提示DISPLAY not set. Please set the DISPLAY and try again.解决方法

    问题描述 xff1a Linux下安装oracle数据库提示DISPLAY not set Please set the DISPLAY and try again 如下图所示 xff1a 解决办法 xff1a 切换到root 用户 xff
  • html 清除缓存样式

    autocomplete 61 off
  • ArcGIS Server for linux 服务无法启动解决简记

    今天在一台Linux虚拟机上安装了一个ArcGIS Server For Linux 只ArcSOC 组件 xff0c 一切正常 xff0c 但是启动服务的时候报一下的错误 xff1a root 64 rhsde scripts start
  • Java中swap()方法的实现

    为了能更多的掌握C C 43 43 xff0c 时不时的就会拿起一本什么书看看 昨天又看到了请指针和引用的部分 xff0c 又会有经典的swap 方法的实现 几乎所有人都知道了 xff0c 要实现一个正确的swap 方法需要以指针或引用为参
  • 渐变色原理

    引用 http www islandcn com post 311 html 在图象图形的编程中 经常会见到渐变色以及各种图片的叠加等效果 这篇文章就是要对这些效果的原理加以分析 并在Elastos 操作系统 Mobile Edition
  • JAX-WS 学习二:创建客户端

    上一节中介绍了怎么基于JAX WS的API创建服务端 xff0c 这一节介绍一下创建一个客户端调用WebService服务 要创建一个Client端也相当简单 xff0c 不过需要知道几个东西 xff1a 1 wsdl文件路径 需要读取服务
  • 使用JDI监听Java程序运行

    Java虚拟机提供了一套用于调试 xff08 JVMDI xff09 和监视 xff08 JVMPI xff09 的接口 xff0c Java5之后统一为JVMTI xff1a http docs oracle com javase 1 5