2012/01/07

Byteman plays with Drools

Due to my recent suspicion that there is a bug in Drools I needed to monitor its guts in detail. To be more specific, I needed to see what is in the working memory after execution of all rules.

Debbuger was not a good option since there were hundruds of invocations of the problematic part and I needed to get overview of quite a large amount of data. If I inly could store all the data for each invocation in a file and then grep through it.

But wait! Of course I can. Byteman will help me! The identified point in source code was execution of fireAllRules() in org.drools.common.AbstractWorkingMemory. Ideally, just before return from the method. So here is the corresponding rule:

RULE Debug fireAllRules
HELPER org.drools.planner.examples.tournaments.helper.BytemanHelper
CLASS ^org.drools.common.AbstractWorkingMemory
METHOD fireAllRules
AT EXIT
IF TRUE
DO
  printWorkingMemory($0.getKnowledgeRuntime());
ENDRULE

Just a few notes to it:

  • ^ means to apply to child classes as well
  • $0 refers to this

As you can see there is a custom helper class to print the working memory content since Byteman rules are little bit limited in the supported Java syntax.

import org.drools.impl.StatefulKnowledgeSessionImpl;
import org.jboss.byteman.rule.Rule;
import org.jboss.byteman.rule.helper.Helper;

public class BytemanHelper extends Helper {
   protected BytemanHelper(Rule rule) {
      super(rule);
   }
   
   public void printWorkingMemory(Object obj) {
      StatefulKnowledgeSessionImpl session = (StatefulKnowledgeSessionImpl) obj;
      debug("vvvvvvvvvvvvvvvvvvvvvvvvvvv");
      debug(session.toString());
      for (Object o: session.getObjects()) {
         debug(o.toString());
      }
      debug("^^^^^^^^^^^^^^^^^^^^^^^^^^^");
   }
}

And now I was able to get some nice output of individual working memories! So the resulting bug is JBRULES-3337.

3 comments:

  1. Very cool!

    It's nice to see just how powerful this is - only a few lines of code in a Byteman script to find your bug.

    ReplyDelete
  2. Interesting solution, but you underestimate what can be done with the standard Java debugger (assuming a good Java IDE).

    Try this with IntelliJ IDEA 11 (any edition):
    1) Set a regular breakpoint at the last line in the "fireAllRules" method.
    2) Open the breakpoint configuration window.
    3) Change the "Suspend policy" to "None".
    4) Mark the "Log evaluated expression" option.
    5) Enter "new Helper().printWorkingMemory(getKnowledgeRuntime())" as the expression to be evaluated.
    6) Run your test/app in debug mode and see it working.

    As it turns out, the debugger is a lot more powerful and flexible than most people think!

    ReplyDelete
  3. Yes, of course I use debugger for its purpose. However, in this case there were two limitations that made me use Byteman:
    1) Helper was not part of the project and it was not easy to recompile the packages (the project is Mavenized).
    2) I did not originally know the correct location of the invocation, so I just have a Byteman rule with all the places and it was easier to manage a text file than clicking it in GUI.

    ReplyDelete

. .