Geertjan is a DZone Zone Leader and has posted 457 posts at DZone. You can read more from them at their website. View Full User Profile

Porting a Hot Java Thread Detector to VisualVM

04.03.2008
| 13847 views |
  • submit to reddit
Over on java.net, Bruce Chapman recently described a tool he's created "to output the stack traces for the three busiest threads in a Java process". He closes the article by suggesting that his hot thread detector might be useful in jConsole and VisualVM. Fortunately, he provided all his source code...

Today, in an attempt to come to grips with some significant API changes in VisualVM, I played with Bruce's code and then moved it over to VisualVM. First, to understand what his tool is all about, let's quote a snippet from Bruce's description of what it does:

The program attaches to the local java process (specified by the PID on the command line), it grabs information about the processing time of all threads, twice 500ms apart, and uses that to find the three busiest threads. It then takes 10 stacktrace snapshots of those three threads at 10ms intervals, and looks for the common parts on those stack traces for each thread. If a thread is busy, normally most of the stack stays the same, and just the top part changes. The program then outputs to common parts of the stack traces. From there you can see which thread is running hot, and where it is.

So, what that means is that when I run his code, after sending in the PID of the application I'm interested in, I get the following output:

So, how to get the single class that Bruce created (get it from his blog) into VisualVM? Soon, once the APIs in question have stabilized, which should be in the coming weeks, round about the time VisualVM Beta 2 is released, I will describe everything in great detail. For now, here is a general overview:

  1. Create a module. You need to create a NetBeans module, since VisualVM is created on top of the NetBeans Platform. You can create and work with your NetBeans module in any IDE. However, NetBeans IDE has many productivity tools for creating NetBeans modules, so it is easiest to use NetBeans IDE. In the end, your module will look like this:

    If someone from Eclipse or IntelliJ or JDeveloper is interested in making the same productivity tools there (i.e., templates and so on for generating module source structures and API class stubs), they are more than welcome to do so.


  2. Set dependencies. Once you have created your NetBeans module, you need to set dependencies on the modules that provide the APIs that you need. In this case, you will need Module System API (for the installer class that installs the NetBeans module into VisualVM), the VisualVM-Core (for the base functionality and structure of VisualVM), and VisualVM-Application (for plugging your NetBeans module into the application-level functionality of VisualVM). Finally, Utilities API is also useful, for easily accessing images and converting them. The most significant change in the APIs between VisualVM Beta and VisualVM Beta 2 is that the APIs have been repackaged. Simply put, they've been reorganized, which implies the approach of the end of the first round of API design for the first official version of VisualVM.

  3. Code your DataSourceViewsProvider. Next, once you've set your dependencies, you need to code your classes. VisualVM relies heavily on the factory pattern. So, the first class you would want to create, in this case, is a class that implements "com.sun.tools.visualvm.core.ui.DataSourceViewsProvider<Application>". This is the class that, pretty much as its name implies, provides a data source view for applications. The class provides a method called "supportsViewsFor", which returns the specific applications for which you are providing a view. (Typically, an application is identified by its main class. In this method, therefore, you would specify the main class[es] of the application[s] that you are interested in.) Simply return "true", in this case, because we want to see the hot threads of all kinds of applications. The second important method is "getViews", which returns a set of views. In our case, we'll return one view. But the set could return different views under different conditions, for example. Finally, there are two methods "supportsSaveViewsFor" and "saveViews", which are there to enable the ability for the view to provide snapshot functionality. In other words, if the first of these returns "true", the user is able to make snapshots of the current state of the application so that the current state's snapshot can then be compared with a snapshot taken later.

  4. Code your DataSourceView. With that class under your belt, you need to create the view that is returned from the provider's "getViews" method. So you need a class that extends "com.sun.tools.visualvm.core.ui.DataSourceView". Here there is one method to override, "getView", which returns a "DataViewComponent". That's the main part of the new view that you're creating for Bruce's hot threads. Each view is divided into various parts: a "MasterView", a "MasterViewConfiguration", a "DetailsView", and a "DetailsViewConfiguration". Each of these is a class. You simply (really simply) create JPanels (or other Swing components, such as JScrollPanes) and pass them as arguments to these classes. You can also specify a small image to appear in the tab and there are other small details, such as whether the view is closeable, which can result in a small X appearing in the right corner of a tab.

  5. Hook your own classes into the module. The above step is where you hook your own class(es) into VisualVM. If you already have JPanels (or other Swing components), you're in luck, because you can pass these as arguments to VisualVM's own layout classes (which were outlined in the previous step). If you don't have Swing containers already, you can create them, somehow, and pass their content to these layout classes. That's what I did with Bruce's code. You need to find the places where the existing code (i.e., the classes you're porting) creates output and then do something with that output, while letting the rest of the code do exactly what it did before. In this case, I found all the "System.out" lines:

    And then I wrote those to the text property of JLabels:

    ...which I passed to the JPanels, which were sent as arguments of the VisualVM layout classes. Fairly easy, all of it. What this also implies is that your knowledge/understanding of the application you're porting can be INCREDIBLY minimal. So long as you can figure out where/how the application creates its output, you know exactly what you need.


  6. Create a module installer. Finally, you need a class that extends "org.openide.modules.ModuleInstall". That's the class that's instantiated at the time the module is installed into VisualVM. Using its "restored" method, you can call VisualVM's "DataSourceViewsManager" and then use that class to add the provider to the manager.

  7. Remember the metadata. Of course, since this is a NetBeans module, you also need some metadata that is specific to NetBeans modules, such as some specific entries in the manifest, a file named project.xml that contains your dependencies, and so on. These are created (and maintained) automatically for you when you use the productivity tools in NetBeans IDE to create your modules for VisualVM.

Having taken all the above steps, I installed the module into VisualVM, with this result:

So... now I know the three hottest threads in IntelliJ! (Although, in this example, they're not very hot at all.) Someone at IntelliJ should find that info pretty useful, I reckon. Once VisualVM Beta 2 is released, I will provide complete instructions for how to create the above view for applications in VisualVM.

AttachmentSize
bchapman-1.png139.66 KB
bchapman-3.png61.48 KB
bchapman-2.png20.96 KB
bchapman-4.png11.55 KB
bchapman7.png19.61 KB
bchapman6.png22.81 KB
Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Jess Holle replied on Fri, 2008/04/04 - 9:26am

So why wouldn't you just do this as a JConsole Plug-In?

It is within the realm of what's easily possible given the jconsole Plug-In API and the JDK itself.  There's no apparent need to use the (admittedly richer and higher level) VisualVM APIs -- and doing so has the downside of preventing use in standalone jconsole.

There's plenty of cases where the VisualVM API is a compelling solution, but I don't see that here.

Geertjan Wielenga replied on Fri, 2008/04/04 - 10:08am

I wouldn't do this in a JConsole plugin because I'm not using JConsole. I'm using VisualVM.

Jess Holle replied on Fri, 2008/04/04 - 10:22am

But VisualVM supports jconsole plug-ins, right?

If not, it should, but I'm 99% certain it does.

If so, why not use the lowest-common denominator API with no loss in functionality, more places you can use it, and fewer dependencies -- at build time, run time, and learning time?

I can understand that this might have been a nice way to learn the VisualVM API, but it does not seem like the problem at hand benefits from the approach.

Geertjan Wielenga replied on Fri, 2008/04/04 - 10:33am

Oh, yes, certainly. VisualVM supports JConsole plugins. Definitely. You can start up VisualVM with an argument to the command line, pointing to the dir that contains the plugins, similar to how it works with JConsole, which then loads the JConsole plugins found in the specified dir. So, your suggestion would definitely work. However, what if I want to expand the functionality offered by my plugin? Say I want to add a menu item to VisualVM, specifically for the "Hot Threads" panel? In that case, I can't use the JConsole APIs. So it makes sense to, with a view to future development of my plugin, chose the VisualVM APIs rather than those offered by JConsole.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.