Porting a Hot Java Thread Detector to VisualVM
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:
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.