BTrace: DTrace for Java
BTrace: DTrace for Java… ish
DTrace first peered into Java in early 2005 thanks to an early prototype by Jarod Jenson that led eventually to the inclusion of USDT probes in the HotSpot JVM. If you want to see where, say, the java.net.SocketOutputStream.write() method is called, you can simply run this DTrace script:
hotspot$target:::method-entry
/copyinstr(arg1, arg2) == "java/net/SocketOutputStream" &&
copyinstr(arg3, arg4) == "write"/
{
jstack(50, 8000);
}
And that will work as long as you rememember to start your JVM with the -XX:+ExtendedDTraceProbes option or you use the jinfo utility to enable it after the fact. And as long as you don’t mind a crippling performance penalty (hint: you probably do).
Inspired by dtrace.conf a few weeks ago, I wanted to sketch out what the real Java provider would look like:
java$target:java.net.SocketOutputStream:write:entry
{
jstack(50,8000);
}
And check it out:
# jdtrace.pl -p $(pgrep java) -n 'java$target:java.net.SocketOutputStream::entry{ jstack(50,8000); }'
dtrace: script '/tmp/jdtrace.19092/jdtrace.d' matched 0 probes
CPU ID FUNCTION:NAME
0 64991 Java_com_sun_btrace_BTraceRuntime_dtraceProbe0:event
libbtrace.so`Java_com_sun_btrace_BTraceRuntime_dtraceProbe0+0xbb
com/sun/btrace/BTraceRuntime.dtraceProbe0(Ljava/lang/String;Ljava/lang/String;II)I
com/sun/btrace/BTraceRuntime.dtraceProbe(Ljava/lang/String;Ljava/lang/String;II)I
com/sun/btrace/BTraceUtils$D.probe(Ljava/lang/String;Ljava/lang/String;II)I
com/sun/btrace/BTraceUtils$D.probe(Ljava/lang/String;Ljava/lang/String;)I
java/net/SocketOutputStream.$btrace$jdtrace$probe1(Ljava/lang/String;Ljava/lang/String;)V
java/net/SocketOutputStream.write([BII)V
sun/nio/cs/StreamEncoder.writeBytes()V
sun/nio/cs/StreamEncoder.implFlushBuffer()V
sun/nio/cs/StreamEncoder.implFlush()V
sun/nio/cs/StreamEncoder.flush()V
java/io/OutputStreamWriter.flush()V
java/io/BufferedWriter.flush()V
java/io/PrintWriter.newLine()V
java/io/PrintWriter.println()V
java/io/PrintWriter.println(Ljava/lang/String;)V
com/delphix/appliance/server/ham/impl/HAMonitorServerThread.run()V
java/util/concurrent/ThreadPoolExecutor$Worker.runTask(Ljava/lang/Runnable;)V
java/util/concurrent/ThreadPoolExecutor$Worker.run()V
java/lang/Thread.run()V
StubRoutines (1)
libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x21d
libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_+0x27
libjvm.so`__1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_+0x149
libjvm.so`__1cMthread_entry6FpnKJavaThread_pnGThread__v_+0x113
libjvm.so`__1cKJavaThreadDrun6M_v_+0x2c6
libjvm.so`java_start+0x1f2
libc.so.1`_thrp_setup+0x9b
libc.so.1`_lwp_start
Obviously there's something fishy going on. First, we're using perl -- the shibboleth of fake-o-ware -- and there's this BTrace stuff in the output.
Faking it with BTrace
BTrace is a dynamic instrumentation tool for Java; it is both inspired by DTrace and contains some DTrace integration. The perl script above takes the DTrace syntax and generates a DTrace script and a BTrace-enabled Java source file.
Like DTrace, BTrace lets you specify the points of instrumentation in your Java program as well as the actions to take. Here's what our generated source file looks like.
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class jdtrace {
@OnMethod(clazz="java.net.SocketOutputStream", method="write", location=@Location(Kind.ENTRY))
public static void probe1(@ProbeClassName String c,
@ProbeMethodName String m) {
String name = "entry";
String p = Strings.strcat(c, Strings.strcat(":",
Strings.strcat(m, Strings.strcat(":", name))));
D.probe(p, "");
}
}
Note that we specify where to trace (this can be a regular expression), and then take the action of joining the class, method, and "entry" string into a single string that we pass to the D.probe() method that causes a BTrace USDT probe to fire.
Here's what the D script looks like:
btrace$target:::event
{
this->__jd_arg = copyinstr(arg0);
this->__jd_mod = strtok(this->__jd_arg, ":");
this->__jd_func = strtok(NULL, ":");
this->__jd_name = strtok(NULL, ":");
} btrace$target:::event
/((this->__jd_mod == "java.net.SocketOutputStream" &&
this->__jd_func == "write" &&
this->__jd_name == "entry"))/
{
jstack(50,8000);
}
It's pretty simple. We parse the string that was passed to D.probe(), and disassemble it into the DTrace notion of module, function, and name. We then use that information so that the specified actions are executed as appropriate (we could have specified different Java methods to probe, and different actions to take for each). Here's the code if you're interested.
This isn't the real Java provider, but is it close enough? Unfortunately not. The most glaring problem is that BTrace sometimes renders my Java process unresponsive. Other times it leaves instrumentation behind with no way of extracting it. The word "safe" appears as the third word on the BTrace website ("BTrace is safe"), but apparently there's still some way to go to achieve the requisite level of safety.
A Better BTrace
BTrace is an interesting tool for examining Java programs, but one obvious obstacle is that the programs are pretty cumbersome to write. With BTrace, we should be able to write a simple one-liner to see where we are when the java.net.SocketOutputStream.write() method is called, but instead we have to write a fairly cumbersome program:
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TraceWrite {
@OnMethod(clazz="java.net.SocketOutputStream", method="write", location=@Location(Kind.ENTRY))
public static void onWrite() {
jstack();
}
}
DTrace-inspired syntax would let users iterate much more quickly:
$ dbtrace -p $(pgrep -n java) -n 'java.net.SocketOutputStream:write:entry{ jstack(); }'
java.net.SocketOutputStream.write(SocketOutputStream.java)
sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
java.io.BufferedWriter.flush(BufferedWriter.java:236)
java.io.PrintWriter.newLine(PrintWriter.java:438)
java.io.PrintWriter.println(PrintWriter.java:585)
java.io.PrintWriter.println(PrintWriter.java:696)
com.delphix.appliance.server.ham.impl.HAMonitorServerThread.run(HAMonitorServerThread.java:56)
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
java.lang.Thread.run(Thread.java:662)
With BTrace, you can trace nearly arbitrary information about a program's state, but instead of doing something like this:
dbtrace -p $(pgrep -n java) -n 'java.net.SocketOutputStream:write:entry{ printFields(this.impl); }'
You have to do this:
import com.sun.btrace.annotations.*;
import com.sun.btrace.AnyType;
import static com.sun.btrace.BTraceUtils.Reflective.*;
@BTrace
public class TraceWrite {
@OnMethod(clazz="java.net.SocketOutputStream", method="write", location=@Location(Kind.ENTRY))
public static void onWrite(@Self Object self) {
Object impl = get(field(classOf(self), "impl"), self);
printFields(impl);
}
}
$ ./bin/btrace $(pgrep -n java) TraceWrite.java
{server=null, port=1080, external_address=null, useV4=false, cmdsock=null, cmdIn=null, cmdOut=null, applicationSetProxy=false, timeout=0, trafficClass=0, shut_rd=false, shut_wr=false, socketInputStream=java.net.SocketInputStream@9993a1, fdUseCount=0, fdLock=java.lang.Object@ab5443, closePending=false, CONNECTION_NOT_RESET=0, CONNECTION_RESET_PENDING=1, CONNECTION_RESET=2, resetState=0, resetLock=java.lang.Object@292936, fd1=null, anyLocalBoundAddr=null, lastfd=-1, stream=false, socket=Socket[addr=/127.0.0.1,port=38832,localport=8765], serverSocket=null, fd=java.io.FileDescriptor@50abcc, address=/127.0.0.1, port=38832, localport=8765, }
BTrace needs a language that enables rapid iteration — piggybacking on Java is holding it back — and it needs some hard safety guarantees. With those, many developers and support engineers would use BTrace as part of their daily work — we certainly would here at Delphix.
Back to DTrace. Even with a useable solution for Java only, the ability to have lightweight and focused tracing for Java (and other dynamic languages) could be highly valuable. We’ll see how far BTrace can take us.
In: DTrace · Tagged with: BTrace, DTrace, Java
BTrace: DTrace for Java的更多相关文章
- BTrace: DTrace for Java2
BTrace: DTrace for Java… ish 时间 2012-04-24 16:17:55 dtrace.org 原文 http://dtrace.org/blogs/ahl/2012 ...
- 基于BTrace监控调试Java代码
BTrace是Java的一个动态代码追踪工具,通过编写btrace脚本,它可以动态的向目标应用程序的字节码注入追踪代码,通过修改字节码的方式,达到监控调试和定位问题的目的,是解决线上问题的利器. BT ...
- btrace拓展工具-java应用性能诊断优化利器
Btrace是一个实时监控工具,可以无需修改应用代码(事实上它修改了字节码),来达到不可告人的秘密!这是性能调优和诊断的利器! 它可以获取应用程序代码的执行时间,他可以让你无需修改代码,帮你做时间的打 ...
- End-to-End Tracing of Ajax/Java Applications Using DTrace
End-to-End Tracing of Ajax/Java Applications Using DTrace By Amit Hurvitz, July 2007 Aja ...
- BTrace : Java 线上问题排查神器
BTrace 是什么 BTrace 是检查和解决线上的问题的杀器,BTrace 可以通过编写脚本的方式,获取程序执行过程中的一切信息,并且,注意了,不用重启服务,是的,不用重启服务.写好脚本,直接用命 ...
- Java软件生产监控工具Btrace的使用
Btrace BTrace是sun公司推出的一款Java 动态.安全追踪(监控)工具,可以在不用重启的情况下监控系统运行情况,方便的获取程序运行时的数据信息,如方法参数.返回值.全局变量和堆栈信息等, ...
- 认识一下java神器Btrace
转载: http://calvin1978.blogcn.com/articles/btrace1.html BTrace是神器,每一个需要每天解决线上问题,但完全不用BTrace的Java工程师,都 ...
- BTrace学习总结
一.简介: 在生产环境中经常遇到格式各样的问题,如OOM或者莫名其妙的进程死掉.一般情况下是通过修改程序,添加打印日志:然后重新发布程序来完成.然而,这不仅麻烦,而且带来很多不可控的因素.有没有一种方 ...
- btrace使用
btrace使用 目录btracee是btrace的解压目录 btrace/btrace是btrace的项目工程 root@ubuntu:/usr/local/bogon/btrace# tree b ...
随机推荐
- JS身份证真实性校验(一)
//这个可以验证15位和18位的身份证,并且包含生日和校验位的验证. //如果有兴趣,还可以加上身份证所在地的验证,就是前6位有些数字合法有些数字不合法. function isIdCardNo(nu ...
- 高级PHP应用程序漏洞审核技术
前言 PHP是一种被广泛使用的脚本语言,尤其适合于web开发.具有跨平台,容易学习,功能强大等特点,据统计全世界有超过34%的网站有php的应 用,包括Yahoo.sina.163.sohu等大型门户 ...
- uva 10652 Board Wrapping
主要是凸包的应用: #include <cstdio> #include <cmath> #include <cstring> #include <algor ...
- Mysql大表查询优化技巧总结及案例分析
http://www.169it.com/article/3219955334.html sql语句使用基本原则:1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 orde ...
- Android 使用XmlSerializer生成xml文件
在Android开发中,我们时常要用到xml文件. xml作为一种数据载体,在数据传输中发挥着重要的作用,而且它可读性比较强. 下面给出在Android开发中使用XmlSerializer类生成一个简 ...
- 【SGU 390】Tickets (数位DP)
Tickets Description Conductor is quite a boring profession, as all you have to do is just to sell ...
- 李洪强漫谈iOS开发[C语言-030]-逻辑运算符
- VC下载文件 + 显示进度条
在codeproject里找了许久,发现这样一个VC下载文件并显示进度条的源码,于是添加了些中文注释: 1.下载线程函数: UINT DownloadFile(LPVOID pParam) { CWn ...
- C++ Prime:范围for语句
C++11新标准引入了一种更简单的for语句,这种语句可以遍历容器或者其他序列的所有元素.范围for语句的语法形式是: for( declaration : expression) statement ...
- bzoj2257
不难发现其实这个捣鼓过程就是一个辗转相减所以最小燃料是瓶容量的最小公约数然后穷举约数即可 ..] of longint; n,k,x,i,m,j,t:longint; procedure sort(l ...