Search code examples
java-11jmcjfrjava-mission-control

How to chase a JFR event over multiple threads


I'm struggling to model asynchronous servlet request processing with custom JFR events. The challenge I'm facing is that in asynchronous processing a request may be #dispatch()ed several times. This means the whole request processing chain may be executed multiple times, some time apart in different threads. How do I model this with custom JFR events?

What would help me is either the concept of a "parent" event (possibly in a different thread) or the suspension and resumption of an event.

Edit

To illustrate the issue a bit. An async request may take 100 seconds wall clock time to process. However the actual processing may happen in only 4 seconds user time in a Servlet#service() method:

  • second 0-1 in thread A, Servlet#service() method returns, AsyncContext started
  • second 10-11 in thread B, Servlet#service() method returns, AsyncContext started
  • second 80-81 in thread A, Servlet#service() method returns, AsyncContext started
  • second 99-100 in thread C, Servlet#service() method returns

I'm only interested in generating events for these four durations in these three threads and then correlating them with a single request.


Solution

  • You can add a thread field to the event

    public class MyEvent extends Event [
      @Label("Start Thread")
      @TransitionFrom
      private final Thread startThread;
      MyEvent(Thread thread) {
        this.startThread = thread;
      }
    ]
    

    When you commit the event the end thread will be stored.

    If you want to track an event over several threads, you would need to create an event for every thread and have an id so you can understand the flow.

    class MyEvent extends Event {
    
      @Label("Transition id");
      long id;
    }
    

    If you like you can create a relational id to describe the relation and JMC should be able to hint (in context menus etc.) there is a relation among events.

    @Label("Transition Id")
    @Relational
    @Target({ ElementType.FIELD })
    @Retention(RetentionPolicy.RUNTIME)
    @interface TransitionId {
    }
    

    If you don't want to repeat yourself, you can write the above functionality in a method in a base class, which you can call for every new thread the event visits.

     abstract AbstractTransition extends Event {
    
       @TransitionId
       @Label("Transition Id")
       private long id;
       public void setTransitionId(long id) {
         this.id = id;
       }
     }
    

    There is no other way to do this.

    It's not possible for the JVM to know what thread an event object is in, or what threads that should be recorded. The user needs to provide at least one method call for every thread that should be touched (together with some context).