- Determining if/when/how the method is called, i.e. understanding how specific operator actions affect the program flow
- Dumping out arguments or intermediate values, to see if I recognize the data or have properly identified the purpose of the obfuscated method
- Extracting secrets buried in the program, without having to reverse engineer the storage format
- Printing out strings or byte arrays after the de-obfuscator code has run (FWIW I have also used ASM for this when I had a bunch of similarly-obfuscated .class files to process)
This tends to be a manual, iterative process - not necessarily something well-suited to applying the same "formula" to every method. So I'll find an interesting method (often identified using strings or magic numbers), instrument it, and rerun the program. That result will determine what gets instrumented next. Being able to run "make ; java -jar new.jar", and only reassemble the files that have changed, is a huge time saver.
I'd actually prefer to use a debugger, but the best solution I found was Dr. Garbage[1] and it doesn't allow inspection of objects on the operand stack, or breakpoints on specific opcodes. Also, attaching a debugger to a more complex multithreaded system, such as a J2EE application server, may be tricky.