2007年12月1日星期六

Five ways for tracing Java execution

Original address: http://zvikico.typepad.com/problog/2007/11/five-ways-for-t.html

I'm often faced with a situation where I need to dig into code that I didn't write. Most of it is poorly documented and, in some cases, I only have some of the sources, which means I need to decompile the rest. It's a tedious exploration. There are mainly two reasons for that: maintaining a code or using it. The later refers to 3rd party frameworks and class libraries I need to build upon. For example I may implement an interface of an event handler and I may be uncertain regarding which event fires when and what are the exact arguments I'm getting.

I usually find that it is faster to trace the code at runtime. Especially when it comes to non-trivial links between classes, like an interface with multiple implementations which may be picked at runtime. The smörgåsbord of design time tools is just insufficient.

In this post, I will summarize the common methods (I know of) for tracing the runtime execution. The tips and examples are assuming you're using Eclipse, but most of them can be achieved with other IDEs as well. I will provide some more detailed information regarding some of the techniques mentioned below in future posts.


The Basic: Breakpoints and Step-by-step Execution

Start with the simplest way: set up breakpoints and start tracing your execution.

Pros Cons
  • Very simple to use and setup. Practically, all you need is a running debugger.
  • Provides access to ALL the information, including method attributes, local variables, etc.
  • Does not modify the code.
  • Selective: you can decide where to break.
  • Stepping through code just takes too long. Moreover, you need constant attention. For each step you should decide whether you're stepping in, over or out. This makes this method impractical.
  • When debugging event handlers one must setup breakpoints on all methods, since you're not sure which method will be called.
  • Impossible to set breakpoints inside method when you don't have the source code.

It is best when: You need a quick and simple solution, you have all the code and you know where you want to stop. You need elaborate information in a given point (arguments, local variables, etc.).

My tip: Even if you don't have all the sources, it's possible to break when entering methods or classes by setting-up method breakpoints or class loading breakpoints.

  • Class loading breakpoints are simple to set up: from Eclipse Run menu, select "Add Class Load Breakpoint...".
  • In order to set method breakpoints, you will need to decompile the class file first. Use the JadClipse plug-in for that. After you decompile, you may select the method and choose "Toggle Method Breakpoint" from the "Run" menu (or by right clicking the left bar in the file next to the method header). Note that you may set regular breakpoints in decompiled sources as well, but these will not work.

The Primal: Debug messages

We continue by setting up debug messages. The simplest way is to use System.out.println statements to print out messages to the console. The next step would be to use a logging mechanism: the basic JDK logging, Apache Commons Logging or Log4j.

Pros Cons
  • Very selective.
  • Speedy execution (in case you didn't put too many).
  • Allows printing the exact information you're interested in.
  • Requires code modification. This makes this impractical when you don't own the sources.
  • Takes time to set up. In case you're not sure what you're looking for, you'll need to introduce debug messages to many methods.
  • Makes the code dirty and introduces run-time overhead if you don't take the debug messages out afterwards.

It is best when: You own the code and you have a good idea of what you're looking for. Very good solution for event handlers. It's high performance makes it practical for understanding which event is fired when throughout the execution of complex flows.

My tip: It's easy to set up one generic method that prints out the method currently executing. The best way I'm familiar with is to get the stack trace. The last entry on the call stack will be the debug message method. The entry before that will be the method invoking it. How to obtain a stack trace:

  • Since JDK 5.0: Use Thread.currentThread().getStackTrace().
  • Before (JDK 1.4): create a new exception instance and use getStackTrace to obtain your call stack from the new instance (no need to "throw" it).

The Hot Shot: Dynamic Proxy

An improvement over simple debug messages. Dynamic Proxy is a special Java feature which enables the developer to introduce a proxy class, sitting in front of a given class and intercepting all the calls through a given interface. Once you intercept the calls, you may print out appropriate debug messages with all the information you require.

Pros Cons
  • No need to infest the code with debug messages. The debug messages can be done in one central location.
  • Simple to remove once you're done, thus, has no affect on performance.
  • Only works through interfaces. Useless for non-public methods and public methods which are not defined in an interface.
  • Requires code modification: you need to own the code.
  • Non selective messages printed.

It is best when: It's a great solution for event handlers. You can set up a dummy event handler with a generic proxy in seconds and see the sequence of events. This is simplest and quickest method when it comes to understanding event handlers.

My tip: You can find a code for a generic dynamic proxy on Sun's site. It's OK, but it has one small minus: it only implements the immediate interfaces implemented by the class of the instance being wrapped by the proxy. It does not implement any interface of its' ancestors. This can be easily fixed by traversing the list of ancestors and picking all the interfaces along the way. In one of the future posts I'll give a better version.


The Brute Force: Run-time Profiler

Profilers are powerful tools that trace all the calls in the system through special JVM hooks. However, it's like using a 10 pound hammer on a half inch nail.

Pros Cons
  • No code modification.
  • Can trace all the execution, even without source code.
  • Most tools provide the ability to filter data which is less interesting (like JDK calls, getters and setters, etc).
  • Most good tools are expensive.
  • Most tools take time to setup and master.
  • Profiling will greatly degrade the system performance. It's impractical to have a long execution with the profiler collecting information.
  • Most tools are geared towards performance measurement and neglect showing an execution path according to the actual time line.
  • Most tools provide the calls but not the arguments.

It is best when: You want a complete picture for a very specific operation (i.e. very short execution flow).

My tip: The good tools cost around $500 per seat. Most free tools are very basic. IMHO, The best free open-source tool for the job is Eclipse Testing and Performance Tools Platform (TPTP). I've had very good experience with the tool and I highly recommend it. It can provide a sequence diagram which is one of the best ways for understanding execution. TPTP used to be my favorite before I switched to the Mac and found it's incompatible (maybe I'll try to fix it if I'll have some spare time).


The New Age: Aspects

Aspect Oriented Programming (AOP) is a non-trivial idea. Without going into the concept of Aspects, I'm just looking at the bottom line: it's a quick and easy way of intercepting the execution of your code. You can selectively set hooks around methods, constructors, field access, etc., without modifying the original code. In these hooks you can print debug messages.

Pros Cons
  • Very quick and easy to setup.
  • Selective: you can trace just the classes or method you require.
  • Provides all the information, including arguments.
  • No modification to the original code. Very easy to remove once you're done.
  • Good performance. May be used in long executions.
  • Requires some basic understanding of AOP. Especially for setting up more complex filters.
  • Requires rebuilding your code. This can be a problem when you need to trace a code found in a packaged JAR.

It is best when: You want to trace the execution of a code you can rebuild.

My tip: Use it. AOP is still far from the mainstream. It is uncertain whether it will ever get there. Without debating its' qualities and features, most developers can be up and running with AOP based logging in about an hour. I'll show the exact steps in one of the future posts. For Eclipse users, I recommend the Aspect Java Development Toots (AJDT).


Conclusion

What method should you choose? I'll divide this into two different cases: tracing the complete execution of a short sequence and tracing events to/from a given class or tier.

When tracing the complete execution:

  1. A profiler is the simplest way. IMHO, every developer should be able to operate a profiler and have one readily available in the arsenal.
  2. The next choice would be AOP. It's the second choice only because setting up a profiler is simpler when it comes packaged JARs. However, AOP gives more control. For example, I can see the method arguments and print them.
  3. Use debug messages.

When tracing events:

  1. Use AOP. Selectively capture just the interesting calls.
  2. Use a Dynamic Proxy. It's like using debug messages, but a more elegant solution. AOP may be more complex first, but in the long run I would go for AOP.
  3. Use debug messages.
  4. Use breakpoints.

As you can see, I'm not a big fan of breakpoints in this context. Breakpoints are great when debugging your code, but not for tracing execution. There are far more efficient methods of doing that.

2007年11月5日星期一

互联互通

每天打开电脑,我都要打开IM,一个qq,一个popo,不算太多.现在的IM铺天盖地的发展,热门的还有msn,yahoo messenger,uc等等,各有各的好,我喜欢每一种都试一下,比较一下不同的IM有什么区别,即使一个好友也没有,也会装上看看.时间长了,装的 IM越来越多,机器受不了了,只好精简.我工作忙,没时间聊天,用IM都是为了干正事,所以那些没有好友的IM都作为尝鲜后的垃圾被删掉了,最后就只剩下 这两个.随着很多好友不再用qq转向msn,似乎我的IM应该再多一个,幸好popo有msn的插件,可以让我的机器少装一个,不会太影响我的洁癖. 于是我很希望IM之间能够互联互通,哪怕是有一个类似gaim的软件做一个大全也好,实在不想看着任务栏一大堆IM把我的系统资源无情地占用.最近国内的 主流IM终于有人迈出了第一步:yahoo通和msn互联互通了,欣喜之余,立刻试一下.打开yahoo的下载页面,看到如下信息:您的MSN好友必须在 使用Windows live messenger正式版,并且具备互通资格;如果对方没有使用Windows live messenger正式版,或没有互通资格,则无法互通。也就是说如果我用雅虎通去和我的msn好友互通的话,我要一个一个通知他们去申请互通资格,那还 不如我直接用msn不要去麻烦大家的好. 互通还需要资格?头一回听说.互通不就是文字图片可以传来传去吗?不就是在一个IM上可以看到另一个IM的人吗?干什么要设置这么复杂的规则呢?还要申请 资格,没听说移动的短信发给联通还要申请互通资格的,既然是互通,就别设置的这么麻烦,单点登录满大街都是了,你还故意给大家设置障碍,这不是成心吗?反 正我看到这个消息立刻就打消了下载的念头,原来怎么着还是怎么着吧,不费这个劲. 好在现在互通的资格还是免费的,不知道以后会不会出现这样的情况,仅需xxQQ币就可以申请与xx互联互通,享受沟通的乐趣. 沟通在没有障碍的情况下才是有乐趣的.

2007年6月8日星期五

宕机

销魂大街从昨天开始就不能访问了,已开始我还以为是教育网那位管理员大仙又随手屏蔽了blogspot,后来发现几个好友的blog也同样无法访问,我知道blogspot有被隔离在中国人民的视野之外了。好在blogspot的首页还是可以打开,也就是说我还可以庆幸即使没人能看到我的blog,但是我还可以继续写我的blog,不知道这算不算悲哀过后一点点的慰藉。我用过好几个blog,最后辗转到blogspot,尽管它的声名实在是狼籍的可以,但是我还是决定把这里作为新家。想象一下刚刚乔迁的新居就被人加上了一把大锁,实在让我无言以对。就在我编辑这段文字的时候,页面底下一行粉底提示告诉我,无法连接到blogger.com,存档和发表可能无效。

2007年6月7日星期四

洋人的奴才

我去奥地利使馆办签证咨询。由于是第一次去,道路不熟,一路上是问着执勤的武警战士过来的,每个人都对我非常客气,让我尽管在呼啸的冷气团中奔波了一个半小时,心里还是暖洋洋的。到了签证处门口,把门的保安有序地组织着人群,对每个来办理签证的人也是笑脸相迎,如果有什么问题的话也尽力回答。我就在这样“友好”的气氛中进了签证处大厅。
回答我咨询的问题的是一位中年,或者是更年期妇女,当我说明来意后她给了我一张纸,上面写着办理商务签证所需要的材料。因为我是出国参加学术会议,又是第一次办理签证,自然有好多问题不明白。于是我想更年期问了几个问题,立刻她就变得既不耐烦,让我仔细看说明,说道:“上面不是都写着吗?哪有你这么问的?都象你这样还怎么办公啊?”于是我不敢问了,后面还有很多人排队,如果我和她做无谓的争吵肯定会耽误后面的人的时间。仔细看了说明后,带着满腹狐疑走了出来。
外面有几个办理保险的人很热情地接住我,问我详细情况,并解答我提出的各种疑问,尽管我知道他们是希望我能在他们这里办理业务,但是我觉得要比个更年期打交道愉快的多了。事实上我根本没有流露出一点要在这里办保险的意思,他们应该也能感觉到,只不过把我当成一个潜在客户来培养。但是却并不妨碍他们耐心地回答我的问题,最后我的问题大部分是由他们解答的,虽然我不知道权威性如何。我没有给他们带来任何好处,但是他们却使我没有白来一趟,感谢他们。
在回来的路上我想起老罗的一段话:“我们生活中总是有这样的小人,拿着鸡毛当令箭。”难道给洋人当了奴才,自己就变成洋狗可以肆意咬人了吗?在现场我没有跟更年期辩解,毕竟现场还有外国人,不能丢中国人的脸,另外我也不想让亲切的保安小伙子为难,要不然我早就当场开骂了。如果更年期没见过像我这么问的,今天爷就让她见识一回。
回家后发文纪念,以后再见到这样的洋奴才一定不说“您好”和“谢谢”。坚决鄙视!!!