各位大佬好,我现在有一个需求:
启动一个程序 A 一直执行单测 B ,对于每次测试 C ,需要获取到 C 当次测试用例的执行路径信息: 即调用点在哪里,依次经过了哪些函数,最后在哪里终止(终止点某个函数的某一行)
程序的执行过程是一个不断压栈弹栈的过程,想要获取完整的执行路径,只有最后的栈快照是不行的。 目前到的方法是这样的:
在每个方法的第一行插入一段代码,这段代码可以完成以下功能: 调用 A 程序中的静态方法,把自己的函数名称传递给 A 。
在 A 程序中维护一个 C 的执行路径序列,这样在 C 运行结束的时候,就可以获取到 C 执行了哪些函数。具体在某一行截至这个还没考虑。
不知道自己考虑的对不对,或者大家还有没有什么更好的方案,希望可以多多交流
1 foolishcrab 2024-03-30 16:46:58 +08:00 via iPhone 写过几乎一模一样的代码,基于 agent 的字节码修改,逻辑跟你描述的差不多,是可以实现的 |
2 iseki 2024-03-30 16:58:46 +08:00 主要还是得考虑跨线程时怎么处理 |
![]() | 4 Zzhiter OP @foolishcrab 好的,谢谢大佬 |
5 Mmahaha 2024-03-30 18:23:23 +08:00 前段时间也在研究这个,方法和 1L 说的一样,用的是 javaassist |
7 Ayanokouji 2024-03-30 19:06:13 +08:00 如果只是调试,arthas 应该可以吧 |
![]() | 8 Zzhiter OP @Ayanokouji 嗯嗯,关键在于想把整个流程自动化,如果要是 arthas 有 api 就好了 |
![]() | 9 winv87 2024-03-30 20:14:02 +08:00 ![]() 几年前看过 jvm sandbox ,Arthas 相当于成品,看看这个试试呢 |
10 Ayanokouji 2024-03-30 20:20:26 +08:00 @winv87 arthas 有 api 的,但是不清楚满不满足你的需求 |
![]() | 11 Zzhiter OP @Ayanokouji 感谢!我去看一下 |
12 mpi2018 2024-03-30 21:50:11 +08:00 pinpoint 可以看一下 https://github.com/pinpoint-apm/pinpoint |
![]() | 13 wolfie 2024-03-30 22:49:30 +08:00 ![]() 感觉实现原理不是很难,需要增强全部 class ,skywalking 就在启动时指定 javaagent 并用 bytebuddy 增强的。 https://github.com/apache/skywalking |
14 dyv9 2024-04-01 06:52:27 +08:00 via Android 以前为了测试公司自研的框架事务不能回滚错在回哪里,俺用 AspectJ 读取 ejb-jar.xml / web.xml 里面的事务相关设置,用 AspectJ 把涉及到事务边界的地方来记录事件,在事务上下文切换时要检测在这事务边界内使用的 JDBC 连接等资源当前应该是绑定到哪个事务,为什么作为参数传递下去会越界,检测到 把一个在事务 A 中打开的连接传参到事务 B 中导致的事务不一致。这些类似的东西只能是操作字节码自动插入代码,用 AspectJ 静态编织很方便。 |
![]() | 15 Aresxue 2024-04-01 13:37:49 +08:00 ![]() 这不就是 arthas 的 trace 嘛,直接搞过来。pinpoint 、jvm sandbox 等也可以抄一抄。 |
16 xhd2015 2024-04-06 13:41:32 +08:00 via iPhone 这个在 go 中已经实现了,原理是代码重写。java 应该更容易,参考这个例子: https://github.com/xhd2015/xgo/blob/master/cmd/xgo/trace/testdata/stack_trace.jpg 这是一个完整的调用堆栈 |
18 xhd2015 2024-04-06 18:59:47 +08:00 via iPhone 巧了。你的关于插入代码想法是正确的 |
![]() | 19 Zzhiter OP @xhd2015 我现在在想的问题是,A 中调用了很多次的 B ,比如 B 在一个循环中的话,B 是存几次,存一次感觉有点不精确,存多次好像又不太合理 |
20 xhd2015 2024-04-06 21:48:18 +08:00 via iPhone ![]() @Zzhiter 存多次,存在数组中。其实调用栈就是一棵树,每次调用都是产生一个子结点。存储结构: StactTrace{ ClassName string Method string Request Object Response Object Children StackTrace[] } 可以参考这个定义: https://github.com/xhd2015/xgo/blob/1211c519c8005ddbd66189cf64e958aa69e5789f/runtime/trace/stack.go#L16 |
21 xhd2015 2024-04-07 15:41:05 +08:00 via iPhone go 的原理可以参考我之前写的一篇文章: https://blog.xhd2015.xyz/zh/posts/xgo-monkey-patching-in-go-using-toolexec/ |