This section lists tools that are able to perform profiling using code-instrumentation. 5.3.1 VisualVM
VisualVM8is a popular profiling tool for Java applications that includes the functionality to create
an accurate overview of CPU usage per function and memory usage amongst others. The tool makes use of instrumentation for various parts of the functionality that the tool provides. The actual instrumentation is implemented in a library calledJFluid[2], which was developed as part of the NetBeans profiler by Sun Labs9.
An example of how VisualVM makes use of instrumentation would be their CPU statistics dis- play. The visual side of the display is handled in the "CPULivePanel" class in the profiler package. This panel makes use of a "LiveCPUViewUpdater" in order to refresh the results. This updater, in turn, has a JFluid "ProfilerClient", which provides a set of Java profiling functions. Upon update, the method "getCPUProfilingResultsSnapshot()" is called, which literally provides a snapshot of the current profiling results. This function simply reads back data from the instrumentation that was registered before this point.
At start-up, JFluid creates a CPUCallGraphBuilder in order to mark all important points where profiling should be inserted. The "ClassManager" is able to filter these points based on which class they reside in. These classes are then put into a batch that need to be instrumented.
The actual instrumentation is called from the "ProfilerClient". Some basic actions that are re- quired at the start are to load the root class and to load follow-up methods. A "RecursiveMethod- Instrumentor" is then used to collect which data should be instrumented. An "InstrumentMethod- GroupData" class is then called to write the actual instrumentation.
Some key concepts from this that can be found are:
• Parse the code to an internal Java format (performed by JFluid) • Find the root (main) classes (provided by VisualVM)
• Find the root (main) method(s) (implemented in RootMethods.java)
• Find all methods (Implemented by inheritors of AbstractDataFrameProcessor.java) • Find key-points to be instrumented (CPUCallGraphBuilder.java)
• Instrument the code (performed by JFluid)
It should also be mentioned that the way instrumentation was implemented was very custom- tailored for the scenarios of the multi-threaded JFluid profiler, which very much reduces the read- ability of how the various actions are performed. While the classes of RootMethod and Abstract- DataFrameProcessor are still of manageable size (around 100-200 lines of code), the CPUCall- GraphBuilder alone is about 1500 lines of code. The RecursiveMethodInstrumentor that is used to collect the necessary to-be-instrumented data is about 700 lines of code. Combining this with the client-server architecture and abundance of synchronized methods, this tool would be very time-consuming to analyse in more detail.
8https://visualvm.github.io/
5.3.2 Montric
Montric10, previously also known as EurekaJ, is an open-source Java profiler that is quite far in
development. Similar to other profilers, it contains functionality to monitor CPU- and heap usage, amongst others. Montric is a profiler that makes use of a combination of an Ember client, REST as intermediary and a server written in Java. Due to lack of news in recent years, the project can be considered to be on hiatus, albeit it still has most functionality that one would expect of a profiler. The Montric server makes use of a combination of native Java instrumentation functionality and Javassistin order to obtain the desired results
Instrumentation is performed in the "org.eurekaJ.agent" package, starting with the "Eureka- JTransformer". The "transform" method is the initial called function that starts off the process of redefining a class. Data is read in as a bytestream and transformed into a Javassist "CtClass" object. Additional data is read out during the transformation process in order to generate "ClassIn- strumentationInfo" which contains track of the package name, class name and if the class extends or implements other classes. Montric tries to obtain the uppermost level class to be instrumented in order to check if the implemented interface or abstract classes can be instrumented. Montric then proceeds by going through all declared methods (CtMethod) from the CtClass object, and runs instrumentMethod on each of these methods.
The way that a method is instrumented depends on the type of method. As of the time of writing this report constructors, getters and setters are not yet supported. It can be assumed that these methods would provide an overflow of data or are not properly parsed yet on the client-side of the application. A usual case of instrumentation continues to the "addTiming()" function. This method quite plainly appends timing information to the method that can be used to measure the execution performance.
What stands out from the rest is that the transformer makes use of a loadClassInClassLoader function that adds classes to the loader if this was not done yet. This seems to be an important prerequisite in order to support JEE environments, as is noted in the comments.
Some key concepts from this that can be found are: • Parse the code to an internal Java format (Javassist) • Find all methods (visitor pattern)
• Find the highest-level abstraction (ClassInstrumentationInfo) • Find the lowest-level abstraction (ClassInstrumentationInfo) • Identify the method type (Javassist)
• Instrument each method on the correct abstraction level (EurekaJTransformer)