Search code examples
jrubyprofiler

How to add to / amend / consolidate JRuby Profiler data?


Say I have inside my JRuby program the following loop:

loop do
  x=foo()
  break if x
  bar()
end

and I want to collect profiling information just for the invocations of bar. How to do this? I got so far:

pd = []
loop do
  x=foo()
  break if x
  pd << JRuby::Profiler.profile { bar() }
end

This leaves me with an array pd of profile data objects, one for each invocation of bar. Is there a way to create a "summary" data object, by combining all the pd elements? Or even better, have a single object, where profile would just add to the existing profiling information?

I googled for a documentation of the JRuby::Profiler API, but couldn't find anything except a few simple examples, none of them covering my case.

UPDATE : Here is another attempt I tried, which does not work either.

Since the profile method initially clears the profile data inside the Profiler, I tried to separate the profiling steps from the data initializing steps, like this:

JRuby::Profiler.clear
loop do
  x=foo()
  break if x
  JRuby::Profiler.send(:current_thread_context).start_profiling
  bar()
  JRuby::Profiler.send(:current_thread_context).stop_profiling
end
profile_data = JRuby::Profiler.send(:profile_data)

This seems to work at first, but after investigation, I found that profile_data then contains the profiling information from the last (most recent) execution of bar, not of all executions collected together.


Solution

  • I figured out a solution, though I have the feeling that I'm using a ton of undocumented features to get it working. I also must add that I am using (1.7.27), so later JRuby versions might or might not need a different approach.

    The problem with profiling is that start_profiling (corresponding to the Java method startProfiling in the class Java::OrgJrubyRuntime::ThreadContext) not only turns on the profiling flag, but also allocates a fresh ProfileData object. What we want to do, is to reuse the old object. stop_profiling OTOH only toggles the profiling switch and is uncritical.

    Unfortunately, ThreadContext does not provide a method to manipulate the isProfiling toggle, so as a first step, we have to add one:

    class Java::OrgJrubyRuntime::ThreadContext
      field_writer :isProfiling 
    end
    

    With this, we can set/reset the internal isProfiling switch. Now my loop becomes:

    context = JRuby::Profiler.send(:current_thread_context)
    JRuby::Profiler.clear
    profile_data_is_allocated = nil
    loop do
      x=foo()
      break if x
      # The first time, we allocate the profile data
      profile_data_is_allocated  ||= context.start_profiling
      context.isProfiling = true
      bar()
      context.isProfiling = false
    end
    profile_data = JRuby::Profiler.send(:profile_data)
    

    In this solution, I tried to keep as close as possible to the capabilities of the JRuby::Profiler class, but we see, that the only public method still used is the clear method. Basically, I have reimplemented profiling in terms of the ThreadContext class; so if someone comes up with a better way to solve it, I will highly appreciate it.