CLIMAT is a command line HPROF analyzer built on Eclipse MAT & supporting:
- Leak Suspects Report
- Reference Navigation (Incoming & Outgoing by Object or Class)
- Dominator Tree
- Class Histogram
- OQL Querying
- Batched navigation of Arrays, Collections, Strings
- Much more
This project consists of an Eclipse MAT plugins built using Eclipse Tycho, which is a manifest-first way to build Eclipse plug-ins/OSGi bundles.
CliMatPlugin is the core module of the project, containing all the logic to support CLI navigation of HPROFs.
CliMatPlugin provides one Eclipse Extension Point, KnownObjectResolver, which can be extended to provide custom analyses of relevant objects.
After building CLIMAT, execute from the command line using the following command:
mat/MemoryAnalyzer -application CliMatPlugin.execute <HPROF_FILE>
The results file is generated in the location of the HPROF.
The default report provides Thread statistics, executes the Leak Suspects Report, and reports analyses of knownObjectResolver extensions.
When advanced traversal is needed, the -c CLI flag should be used.
-c -cli: Opens the CLI instead of the static results file. The CLI is meant as a complete replacement and expansion of Eclipse MAT.
-i -id: Comma-separated list of hexadecimal Object IDs to analyze. This flag will bypass the default report, and only provide details on the objects requested.
To compile the plugin locally, the 'org.eclipse.mat.api*.jar' JAR that comes in the plugins directory of Eclipse MAT must added as a dependency.
This project uses a nested POM structure to support the requirements of OSGi. Each module serves a distinct purpose in the build process.
The use of each plugin is explained with a comment.
- Download & unzip the latest Eclipse MAT for your OS directly into /mat.
mvn package
- CliMatDependencies builds the local dependency JAR/OSGi bundle.
- CliMatPlugin builds the core Eclipse Plugin to a JAR/OSGi bundle.
- CliMatFeature is an Eclipse Feature bundle containing CliMatPlugin (and any custom extension bundles you wish to add).
- CliMatRepository installs CliMatFeature into a local zipped P2 Repository.
- exec-maven-plugin in CliMatRepository/pom.xml uninstalls and reinstalls CliMatFeature to the OS-appropriate Eclipse MAT installation in local mat/ directory.
These actions are performed automatically by CliMatRepository/pom.xml, so you don't need to run them manually. These steps are here for reference only.
- Install the plugin into MAT via the repository
- If the plugin is already installed in MAT, it must be uninstalled before installing a new version:
- (Windows) MemoryAnalyzerc.exe -application org.eclipse.equinox.p2.director -uninstallIU CliMatPlugin
- (Unix) MemoryAnalyzer -application org.eclipse.equinox.p2.director -uninstallIU CliMatPlugin
- Install
- (Windows)
MemoryAnalyzerc.exe -application org.eclipse.equinox.p2.director -repository "jar:file:<PATH_TO_PLUGIN>!/" -installIU CliMatPlugin
- (Unix)
MemoryAnalyzer -application org.eclipse.equinox.p2.director -repository "jar:file:<PATH_TO_PLUGIN>%21/" -installIU CliMatPlugin
- '%21' is used to encode '!' while avoiding the complications of the '!' Unix expansion.
- This may also be required on Windows depending on the CLI used.
- (Windows)
- If the plugin is already installed in MAT, it must be uninstalled before installing a new version:
Eclipse MAT does not automatically clean up old plugin installations :(
However, I have worked around this so that mvn clean
is still effective and sufficient.
- The Maven Clean Plugin in the parent pom cleans up logs that can be easily specified.
- clean.sh in the project root directory, triggered by exec-maven-plugin, performs additional cleanup that is too complicated for the Clean Plugin.
- Add the new dependency in the section of CliMatDependencies/pom.xml
- Follow the complete Build Steps.
- CliMatDependencies/pom.xml uses maven-bundle-plugin to package all dependencies into a JAR in CliMatDependencies/target
- CliMatPlugin/pom.xml uses maven-dependency-plugin to copy the dependencies JAR into CliMatPlugin/target/dependency and add it to the classpath.
By default, this project downloads and builds from an official Eclipse MAT release as defined by the concatenation of CI variables MAT_DOWNLOAD_URL
& MAT_LINUX_ZIP
.
In rare cases, you may want to build Eclipse MAT locally instead and upload the zip for CLIMAT to leverage. This should only be pursued if you cannot wait for the next Eclipse MAT release, e.g. for a CVE SLA in Eclipse Core.
To build a custom Eclipse MAT zip:
- First, check the Eclipse MAT product page to ensure that the desired fix isn't already resolved in a newer version of MAT.
- If patching a CVE, Check Google and the MAT bug forum to see if it has been reported and/or discussed yet. If not, report it (example).
- If applying a fixed-but-unreleased CVE within Eclipse, identify the latest Eclipse I-Build which includes the fix:
- Identify the in-progress (next upcoming e.g. Latest Release +1) Eclipse Platform version here.
- Identify the specific I-Build you want to use from the Update site for the above Eclipse Platform version (example link of all I-Builds for Eclipse 4.29: https://download.eclipse.org/eclipse/updates/4.29-I-builds/).
- To confirm that the I-Build has patched the vulnerable library, navigate to the plugins directory, which corresponds to the same directory in Eclipse MAT (e.g. https://download.eclipse.org/eclipse/updates/4.32-I-builds/I20240304-0140/plugins/)
- Build MAT on the target OS:
- If building a Linux ZIP, perform these steps on Linux only. Building the Linux ZIP on a Windows machine will give the target ZIP improper Unix write permission, which will break CI.
git clone git@github.com:eclipse-mat/mat.git
- If building from the MAT-default Eclipse build (e.g. fixing a bug in MAT rather than Eclipse Core):
cd parent
mvn clean install -P build-release-rcp
- If building from a custom Eclipse build using the previous step:
- Create a new product definition off of the latest release:
cp org.eclipse.mat.product/mat-<date>.p2.inf org.eclipse.mat.product/mat-<date>i.p2.inf
- Create a new target platform off of the latest release:
cp org.eclipse.mat.targetdef/mat-<date>.target org.eclipse.mat.targetdef/mat-<date>i.target
- Edit
mat-<date>i.target
to update the target platform Eclipse repository to match the I-Build repo, e.g.:<repository location="https://download.eclipse.org/eclipse/updates/4.32-I-builds/"/>
cd parent
- Build Mat using your new custom target:
mvn clean install -P build-release-rcp -Dmat-target=mat-<date>i
- Create a new product definition off of the latest release:
- The built zip will write to
<mat_src>/org.eclipse.mat.product/target/products/org.eclipse.mat.ui.rcp.MemoryAnalyzer-linux.gtk.x86_64.zip
CLIMAT can be extended to provide custom analyses of objects unique to your organization.
This is especially valuable if your heap issues are frequently caused by the same classes, with relevant root cause information in predictable heap objects.
ExampleExtensionPlugin
is an example module which includes all the required code to make an extension.
- Replace the module with your desired name and uncomment the references:
- Implement your extension by following the below steps to add new KnownObjects and/or resolve objects' names, using the example KnownObject and Name Resolver as guides.
- Update plugin.xml to include your extensions
- Build CLIMAT Your plugin should now build and deploy via CliMatFeature! The CLIMAT KnownObjectExtensionResolver will find extensions at runtime and automatically use them for analyses.
Introduction to Eclipse MAT API
Repo URL
Browser Link
There are 3 plugin packages you are likely to interact with:
- org.eclipse.mat.api holds the primary API code.
- org.eclipse.mat.report defines interfaces for many of the data types returned by the API.
- org.eclipse.mat.parser is private, but it holds the implementations of all the relevant objects.
Here are the must-know objects:
- ISnapshot is the top-level representation the Heap dump.
- Many key functions are contained here, so this object is passed through the majority of CLIMAT.
- IObject represents an object.
- Most non-primitives are represented as IObjects.
- resolveValue() is used to traverse object paths.
- ClassHistogramRecord represents the instances of a given class.
- IProgressListener is used to track the progress of MAT API operations, and is required throughout the API.
- Executor is the entry point to CLIMAT.
- ResultsBuilder is the entry point to the static report, & defines how the sections are printed.
- ThreadFinder loads Threads and Thread dumps from the .threads file.
- KnownObject defines specific analysis and printing behavior for a specific common object.
- ConsoleController is the entry point to the CLI, controlling the CLI flow.
- ConsoleCommand represents a CLI option available to the user. Executing a ConsoleCommand returns a ConsoleState.
- ConsoleState represents the current CLI state and stores the list of available options (ConsoleCommands).
- Extend NoHistoryState instead if you don't want your state stored on the history Stack.
- Extend NullState instead to add SuggestedCommands without modifying the state Stack.
- CommandExecutor is the entry point for CLI command execution. A single instance is maintained and passed through the CLI flow.
- ListManager defines the CLI behavior (choosing, printing, searching, sorting) for a list of a given type.
- Create a new class extending KnownObject.
- Create a new Resolver extending KnownObjectResolver.
- The init() method should create an instance of your KnownObject.
- The resolve() method determines whether an IObject is an instance of your KnownObject.
- Add your resolver impl to plugin.xml. Instances of your KnownObject will now automatically be analyzed with your custom code.
Use Eclipse MAT's Name Resolver Extension when you want to define how an object will be displayed.
Specifically, this controls what is returned by IObject.getClassSpecificName().
- Create an implementation of org.eclipse.mat.snapshot.extension.IClassSpecificNameResolver.
- Use the @Subject annotation to define the target class, and @Override the resolve() method to define your naming logic.
- Define your class as an extension point in plugin.xml, as shown in the API docs linked above.
Every KnownObject holds a keyResults map, which represents the most relevant details that should be aggregated between objects.
- Use KeyResultsBuilder to aggregate keyResults maps between any objects you wish.
- See ResultsBuilder for examples of this.
- Initialize a new SuggestedCommand.
- Add the SuggestedCommand to the desired state via ConsoleState.addSuggestedCommand() or ConsoleState.addSuggestedCommands().
- For an example, see CommandExecutor.getSuggestedOptionsFromLeakSuspects.
- Implement the command logic in CommandExecutor.
- Create a class extending ConsoleCommand.
- toString() defines the text of the option.
- execute() should execute the desired method in CommandExecutor.
- Instantiate your ConsoleCommand and add it to the options list in the ConsoleStates where you want your command available.
- Configure remote debugging in mat/MemoryAnalyzer.ini.
For example:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
- Configure your IDE to attach to the remote JVM on the configured port.
Real ISnapshot objects cannot be instantiated unless the parent Class has the appropriate context, e.g. the Class is defined as an Eclipse extension point in plugin.xml.
Therefore, mocking ISnapshot is recommended for testing.
HPROF navigation features are consistent in naming with Eclipse MAT.
However, this project deviates from Eclipse MAT and should not be expected to always align with it.
Eclipse MAT API is made for extending the GUI. Therefore, this project works around much of the API to achieve its goal.
For example, most extensions defined on the API home page are ignored.