├── MatCalciteTest ├── dumps │ ├── .gitignore │ └── mvn1m_jdk18.hprof ├── build.properties ├── src │ └── com │ │ └── github │ │ └── vlsi │ │ └── mat │ │ └── tests │ │ └── calcite │ │ ├── AllTests.java │ │ ├── SampleHeapDumpTests.java │ │ ├── GetByKeyTests.java │ │ ├── TableFunctionsTests.java │ │ ├── AbstractQueriesTests.java │ │ └── CollectionsFunctionsTests.java ├── META-INF │ └── MANIFEST.MF └── pom.xml ├── MatCalciteFeature ├── build.properties └── pom.xml ├── MatCalcitePlugin ├── .gitignore ├── .settings │ ├── org.eclipse.core.resources.prefs │ ├── org.eclipse.m2e.core.prefs │ ├── org.eclipse.ltk.core.refactoring.prefs │ └── org.eclipse.jdt.core.prefs ├── resources │ └── icons │ │ ├── run.png │ │ ├── explain.png │ │ └── plugin.png ├── build.properties ├── src │ └── com │ │ └── github │ │ └── vlsi │ │ └── mat │ │ └── calcite │ │ ├── schema │ │ ├── objects │ │ │ ├── SpecialFields.java │ │ │ ├── Field.java │ │ │ ├── SnapshotRexExpressions.java │ │ │ ├── InstanceByClassTableScan.java │ │ │ ├── IClassesList.java │ │ │ ├── InstanceByClassTable.java │ │ │ ├── InstanceAccessByClassIdRule.java │ │ │ ├── InstanceIdsByClassTable.java │ │ │ ├── HeapOperatorTable.java │ │ │ └── ClassRowTypeCache.java │ │ └── references │ │ │ └── OutboundReferencesTable.java │ │ ├── collections │ │ ├── IObjectsPair.java │ │ ├── CollectionsActions.java │ │ ├── CompactHashMapCollectionExtractor.java │ │ └── DexxHashMapCollectionExtractor.java │ │ ├── SnapshotHolder.java │ │ ├── editor │ │ ├── HeapEditorContributions.java │ │ ├── OpenCalciteAction.java │ │ ├── StringScanner.java │ │ ├── CommentScanner.java │ │ ├── CalcitePartitionScanner.java │ │ ├── CalciteScanner.java │ │ ├── CalciteSourceViewerConfiguration.java │ │ └── CalciteContentAssistantProcessor.java │ │ ├── rules │ │ ├── InstanceAccessByIdRule.java │ │ └── DefaultRuleConfig.java │ │ ├── functions │ │ ├── IObjectMethods.java │ │ ├── IClassMethods.java │ │ ├── ISnapshotMethods.java │ │ ├── HeapFunctionsBase.java │ │ ├── SnapshotFunctions.java │ │ ├── CollectionsFunctions.java │ │ ├── HeapFunctions.java │ │ └── TableFunctions.java │ │ ├── action │ │ ├── CommentLineAction.java │ │ ├── ExecuteQueryAction.java │ │ └── CalciteJob.java │ │ ├── rex │ │ ├── ExecutionRexBuilderContext.java │ │ └── RexBuilderContext.java │ │ ├── neo │ │ ├── GroupEnumerator.java │ │ ├── SnapshotThreadStacksTable.java │ │ └── PackageSchema.java │ │ ├── Query.java │ │ ├── HeapReference.java │ │ ├── Executor.java │ │ ├── CalciteDataSource.java │ │ └── RowSetTable.java ├── .classpath ├── .project ├── plugin.xml ├── META-INF │ └── MANIFEST.MF └── pom.xml ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .gitignore ├── renovate.json ├── MatCalciteTargetPlatform ├── pom.xml └── MatCalciteTargetPlatform.target ├── .github ├── actions │ ├── build │ │ └── action.yml │ ├── setup_java │ │ └── action.yml │ └── publish-update-site │ │ └── action.yml └── workflows │ ├── main.yml │ └── release.yml ├── MatCalciteRepository ├── category.xml ├── matcalcite.product └── pom.xml ├── HISTORY.md ├── .editorconfig ├── CHANGELOG.md ├── pom.xml └── mvnw.cmd /MatCalciteTest/dumps/.gitignore: -------------------------------------------------------------------------------- 1 | *.index 2 | *.threads 3 | -------------------------------------------------------------------------------- /MatCalciteFeature/build.properties: -------------------------------------------------------------------------------- 1 | bin.includes=feature.xml 2 | -------------------------------------------------------------------------------- /MatCalcitePlugin/.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | bin/ 3 | lib/ 4 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlsi/mat-calcite-plugin/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /MatCalcitePlugin/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /MatCalciteTest/build.properties: -------------------------------------------------------------------------------- 1 | source.. = src/ 2 | output.. = bin/ 3 | bin.includes = META-INF/,\ 4 | . 5 | -------------------------------------------------------------------------------- /MatCalciteTest/dumps/mvn1m_jdk18.hprof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlsi/mat-calcite-plugin/HEAD/MatCalciteTest/dumps/mvn1m_jdk18.hprof -------------------------------------------------------------------------------- /MatCalcitePlugin/resources/icons/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlsi/mat-calcite-plugin/HEAD/MatCalcitePlugin/resources/icons/run.png -------------------------------------------------------------------------------- /MatCalcitePlugin/resources/icons/explain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlsi/mat-calcite-plugin/HEAD/MatCalcitePlugin/resources/icons/explain.png -------------------------------------------------------------------------------- /MatCalcitePlugin/resources/icons/plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlsi/mat-calcite-plugin/HEAD/MatCalcitePlugin/resources/icons/plugin.png -------------------------------------------------------------------------------- /MatCalcitePlugin/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /MatCalcitePlugin/.settings/org.eclipse.ltk.core.refactoring.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false 3 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /MatCalcitePlugin/build.properties: -------------------------------------------------------------------------------- 1 | source.. = src/ 2 | output.. = bin/ 3 | bin.includes = META-INF/,\ 4 | .,\ 5 | plugin.xml,\ 6 | resources/ 7 | jars.compile.order = . 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | target/ 3 | coverage-report/ 4 | dependency-reduced-pom.xml 5 | /source/revision.txt 6 | .m2 7 | 8 | #IDE projects 9 | .idea/ 10 | *.iml 11 | *.ipr 12 | *.iws 13 | 14 | # MacOS cache file 15 | .DS_Store 16 | 17 | # Windows image cache files 18 | Thumbs.db 19 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/SpecialFields.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | public interface SpecialFields { 4 | String CLASS = "@class"; 5 | String SUPER = "@super"; 6 | String CLASS_LOADER = "@classLoader"; 7 | String CLASS_NAME = "@className"; 8 | } 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ], 6 | "schedule": ["every 3 weeks on Monday"], 7 | "packageRules": [ 8 | { 9 | "matchPackagePrefixes": ["org.codehaus.janino"], 10 | "groupName": "org.codehaus.janino" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /MatCalcitePlugin/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MatCalciteTest/src/com/github/vlsi/mat/tests/calcite/AllTests.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.tests.calcite; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | 6 | @RunWith(Suite.class) 7 | @Suite.SuiteClasses({ 8 | BasicQueriesTests.class, 9 | GetByKeyTests.class, 10 | TableFunctionsTests.class, 11 | CollectionsFunctionsTests.class 12 | }) 13 | public class AllTests { 14 | } 15 | -------------------------------------------------------------------------------- /MatCalciteTest/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: MatCalciteTest 4 | Bundle-SymbolicName: MatCalciteTest 5 | Bundle-Version: 1.6.4.qualifier 6 | Require-Bundle: org.junit, 7 | org.eclipse.core.runtime, 8 | org.eclipse.mat.api;bundle-version="1.8.0", 9 | org.eclipse.mat.parser;bundle-version="1.8.0", 10 | org.eclipse.mat.ui;bundle-version="1.8.0", 11 | org.eclipse.mat.hprof;bundle-version="1.8.0" 12 | Import-Package: com.github.vlsi.mat.calcite, 13 | com.google.common.base 14 | -------------------------------------------------------------------------------- /MatCalciteTargetPlatform/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | MatCalciteTargetPlatform 8 | 1.6.4-SNAPSHOT 9 | eclipse-target-definition 10 | Eclipse target definition 11 | 12 | 13 | parent 14 | com.github.vlsi.mat.calcite 15 | 1.6.4-SNAPSHOT 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.github/actions/build/action.yml: -------------------------------------------------------------------------------- 1 | name: Build plugin 2 | description: Builds the plugin 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Test 7 | shell: bash 8 | run: | 9 | ./mvnw --color=always -B verify 10 | - uses: actions/upload-artifact@v6 11 | name: Attach generated update site as zip 12 | if: runner.os == 'Linux' 13 | with: 14 | name: mat-calcite-plugin-update-site 15 | # actions/upload-artifact always creates .zip, so we specify directory here to prevent double zip archiving 16 | path: MatCalciteRepository/target/repository/* 17 | # Default retention is 90 days, and we publish the archives to the update site anyway 18 | retention-days: 4 19 | -------------------------------------------------------------------------------- /MatCalcitePlugin/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=17 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.source=17 13 | -------------------------------------------------------------------------------- /.github/actions/setup_java/action.yml: -------------------------------------------------------------------------------- 1 | name: Caches Maven dependencies 2 | description: Caches Maven dependencies 3 | inputs: 4 | java_version: 5 | required: true 6 | description: Java version for building the plugin 7 | default: '17' 8 | runs: 9 | using: "composite" 10 | steps: 11 | - name: 'Set up JDK ${{ inputs.java_version }}' 12 | uses: actions/setup-java@v5 13 | with: 14 | distribution: zulu 15 | java-version: ${{ inputs.java_version }} 16 | - name: Cache local Maven repository 17 | uses: actions/cache@v5 18 | with: 19 | path: ~/.m2/repository 20 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 21 | restore-keys: | 22 | ${{ runner.os }}-maven- 23 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/collections/IObjectsPair.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.collections; 2 | 3 | import org.eclipse.mat.snapshot.model.IObject; 4 | 5 | import java.util.Map; 6 | 7 | class IObjectsPair implements Map.Entry { 8 | private final IObject key; 9 | private final IObject value; 10 | 11 | public IObjectsPair(IObject key, IObject value) { 12 | this.key = key; 13 | this.value = value; 14 | } 15 | 16 | @Override 17 | public IObject getKey() { 18 | return key; 19 | } 20 | 21 | @Override 22 | public IObject getValue() { 23 | return value; 24 | } 25 | 26 | @Override 27 | public IObject setValue(IObject value) { 28 | throw new UnsupportedOperationException(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MatCalciteRepository/category.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Extended queries for MAT 12 | 13 | 14 | 15 | 16 | Extended queries for MAT (source code) 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /MatCalcitePlugin/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | mat-calcite-plugin 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.pde.ManifestBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.pde.SchemaBuilder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.pde.PluginNature 26 | org.eclipse.jdt.core.javanature 27 | 28 | 29 | -------------------------------------------------------------------------------- /MatCalciteRepository/matcalcite.product: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %product.aboutText 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /MatCalciteTest/src/com/github/vlsi/mat/tests/calcite/SampleHeapDumpTests.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.tests.calcite; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.snapshot.ISnapshot; 5 | import org.junit.AfterClass; 6 | import org.junit.BeforeClass; 7 | 8 | import java.io.File; 9 | 10 | public abstract class SampleHeapDumpTests extends AbstractQueriesTests { 11 | protected static ISnapshot snapshot; 12 | 13 | @BeforeClass 14 | public static void openSnapshot() throws SnapshotException { 15 | snapshot = openSnapshot(new File("dumps", "mvn1m_jdk18.hprof")); 16 | } 17 | 18 | @AfterClass 19 | public static void closeSnapshot() { 20 | closeSnapshot(snapshot); 21 | snapshot = null; 22 | } 23 | 24 | protected ISnapshot getSnapshot() { 25 | return snapshot; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/SnapshotHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite; 2 | 3 | import org.eclipse.mat.snapshot.ISnapshot; 4 | 5 | import java.lang.ref.Reference; 6 | import java.lang.ref.WeakReference; 7 | import java.util.List; 8 | import java.util.concurrent.CopyOnWriteArrayList; 9 | 10 | public class SnapshotHolder { 11 | private static final List> SNAPSHOTS = new CopyOnWriteArrayList<>(); 12 | 13 | public static ISnapshot get(int index) { 14 | return SNAPSHOTS.get(index).get(); 15 | } 16 | 17 | public static synchronized int put(ISnapshot snapshot) { 18 | for (int i = 0; i < SNAPSHOTS.size(); i++) { 19 | Reference ref = SNAPSHOTS.get(i); 20 | if (ref.get() == snapshot) { 21 | return i; 22 | } 23 | } 24 | SNAPSHOTS.add(new WeakReference<>(snapshot)); 25 | return SNAPSHOTS.size() - 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/HeapEditorContributions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import org.eclipse.jface.action.Action; 4 | import org.eclipse.jface.action.IToolBarManager; 5 | import org.eclipse.jface.action.Separator; 6 | import org.eclipse.mat.ui.editor.IMultiPaneEditorContributor; 7 | import org.eclipse.mat.ui.editor.MultiPaneEditor; 8 | 9 | public class HeapEditorContributions implements IMultiPaneEditorContributor { 10 | MultiPaneEditor editor; 11 | Action openCalcitePane; 12 | 13 | @Override 14 | public void dispose() { 15 | } 16 | 17 | @Override 18 | public void init(MultiPaneEditor editor) { 19 | this.editor = editor; 20 | openCalcitePane = new OpenCalciteAction(); 21 | } 22 | 23 | @Override 24 | public void contributeToToolbar(IToolBarManager manager) { 25 | manager.add(new Separator()); 26 | manager.add(openCalcitePane); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/rules/InstanceAccessByIdRule.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.rules; 2 | 3 | import org.apache.calcite.plan.RelOptRuleCall; 4 | import org.apache.calcite.plan.RelRule; 5 | import org.apache.calcite.rel.core.Correlate; 6 | 7 | public class InstanceAccessByIdRule extends RelRule { 8 | public static final InstanceAccessByIdRule INSTANCE = 9 | new InstanceAccessByIdRule( 10 | DefaultRuleConfig.EMPTY 11 | .withOperandSupplier( 12 | b0 -> 13 | b0.operand(Correlate.class) 14 | .anyInputs() 15 | ) 16 | ); 17 | 18 | public InstanceAccessByIdRule(RelRule.Config config) { 19 | super(config); 20 | } 21 | 22 | @Override 23 | public void onMatch(RelOptRuleCall call) { 24 | System.out.println("InstanceAccessByIdRule fired for call " + call); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MatCalciteFeature/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | MatCalciteFeature 7 | 1.6.4-SNAPSHOT 8 | eclipse-feature 9 | Calcite SQL feature 10 | 11 | 12 | com.github.vlsi.mat.calcite 13 | parent 14 | 1.6.4-SNAPSHOT 15 | 16 | 17 | 18 | 19 | com.github.vlsi.mat.calcite 20 | MatCalcitePlugin 21 | ${project.version} 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/OpenCalciteAction.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import org.eclipse.jface.action.Action; 4 | import org.eclipse.mat.ui.editor.MultiPaneEditor; 5 | import org.eclipse.ui.IEditorPart; 6 | import org.eclipse.ui.IWorkbenchPage; 7 | import org.eclipse.ui.PlatformUI; 8 | import org.eclipse.ui.plugin.AbstractUIPlugin; 9 | 10 | 11 | public class OpenCalciteAction extends Action { 12 | 13 | public OpenCalciteAction() { 14 | super("SQL", AbstractUIPlugin.imageDescriptorFromPlugin("MatCalcitePlugin", "resources/icons/plugin.png")); 15 | } 16 | 17 | @Override 18 | public void run() { 19 | IWorkbenchPage page = PlatformUI.getWorkbench() 20 | .getActiveWorkbenchWindow().getActivePage(); 21 | IEditorPart part = page == null ? null : page.getActiveEditor(); 22 | 23 | if (part instanceof MultiPaneEditor) { 24 | ((MultiPaneEditor) part).addNewPage("CALCITE", null);//$NON-NLS-1$ 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/StringScanner.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import org.eclipse.jface.text.TextAttribute; 4 | import org.eclipse.jface.text.rules.IRule; 5 | import org.eclipse.jface.text.rules.RuleBasedScanner; 6 | import org.eclipse.jface.text.rules.SingleLineRule; 7 | import org.eclipse.jface.text.rules.Token; 8 | import org.eclipse.swt.graphics.Color; 9 | import org.eclipse.swt.graphics.RGB; 10 | import org.eclipse.swt.widgets.Display; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class StringScanner extends RuleBasedScanner { 16 | public StringScanner() { 17 | List rules = new ArrayList<>(); 18 | 19 | Token stringToken = new Token(new TextAttribute(new Color( 20 | Display.getCurrent(), new RGB(42, 0, 255)))); 21 | rules.add(new SingleLineRule("'", "'", stringToken)); 22 | rules.add(new SingleLineRule("\"", "\"", stringToken)); 23 | 24 | setRules(rules.toArray(new IRule[0])); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/IObjectMethods.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | 5 | import org.eclipse.mat.SnapshotException; 6 | import org.eclipse.mat.snapshot.model.IClass; 7 | import org.eclipse.mat.snapshot.model.IObject; 8 | 9 | @SuppressWarnings("unused") 10 | public class IObjectMethods { 11 | public static Object resolveSimpleValue(IObject object, String name) { 12 | try { 13 | if (object instanceof IClass) { 14 | IClass clazz = (IClass) object; 15 | if ("name".equalsIgnoreCase(name)) { 16 | return IClassMethods.getClassName(object); 17 | } 18 | } 19 | return object.resolveValue(name); 20 | } catch (SnapshotException e) { 21 | throw new IllegalArgumentException("Unable to resolve value " + name + " for object " + object, e); 22 | } 23 | } 24 | 25 | public static HeapReference toHeapReference(Object object) { 26 | return HeapReference.valueOf((IObject) object); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MatCalciteTest/src/com/github/vlsi/mat/tests/calcite/GetByKeyTests.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.tests.calcite; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | 6 | import java.sql.SQLException; 7 | 8 | public class GetByKeyTests extends SampleHeapDumpTests { 9 | @Test 10 | public void testHashMapByKey() throws SQLException { 11 | returnsInOrder("select getByKey(m.this, 'GMT') v from java.util.HashMap m where getSize(m.this) = 208", 12 | "v", 13 | "Etc/GMT"); 14 | } 15 | 16 | @Test 17 | public void testReferenceResult() throws SQLException { 18 | returnsInOrder("select length(getByKey(m.this, 'GMT')['value']) l from java.util.HashMap m where getSize(m.this) " + 19 | "= 208", 20 | "l", 21 | "7"); 22 | } 23 | 24 | @Test 25 | @Ignore 26 | public void testConcurrentHashMap() throws SQLException { 27 | returnsInOrder("select getByKey(m.this, 'MST')['ID'] c from java.util.concurrent.ConcurrentHashMap m where " + 28 | "getByKey(m.this, 'MST') is not null", 29 | "c", 30 | "MST"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | v1.3.0-SNAPSHOT 2 | * Optimize @THIS computation 3 | * Optimize functions registration 4 | * Added icon for plugin, actions on toolbar (Alexey Makhmutov) 5 | 6 | v1.2.0 7 | * Released at bintray 8 | * `@THIS` column in each table (Alexey Makhmutov) 9 | * `length(obj)` function to get array length (Alexey Makhmutov) 10 | * `get_type(obj)` function to get class name (Alexey Makhmutov) 11 | * `get_by_key(obj, string)` function to extract value by key from HashMaps (Alexey Makhmutov) 12 | * Display superclasses fields in results (Alexey Makhmutov) 13 | * Scroll bars and remove word wrapping from Calcite SQL view 14 | * F10 hotkey for explain plan 15 | * Pretty print explain plan output 16 | * Fix query copy command after switching to other pane or page 17 | 18 | v1.1.1 19 | * Updated to Apache Calcite 0.9.1 20 | * Switch to double-quotes 21 | * Enable maven-only build 22 | 23 | v1.1 24 | * Undo/redo support 25 | * Syntax highlighting 26 | 27 | v1.0.1 28 | * Updated optiq to 0.4.18: enable case-sensitive identifiers by default, back-tick quotes 29 | 30 | v1.0.0 31 | * Proof of concept. -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/Field.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import org.eclipse.mat.snapshot.model.FieldDescriptor; 4 | 5 | public class Field { 6 | private final String name; 7 | private final int type; 8 | 9 | public Field(String name, int type) { 10 | this.name = name; 11 | this.type = type; 12 | } 13 | 14 | public Field(FieldDescriptor descriptor) { 15 | this(descriptor.getName(), descriptor.getType()); 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public int getType() { 23 | return type; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) { 29 | return true; 30 | } 31 | if (o == null || getClass() != o.getClass()) { 32 | return false; 33 | } 34 | 35 | Field field = (Field) o; 36 | 37 | return type == field.type && name.equals(field.name); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int result = name.hashCode(); 43 | result = 31 * result + type; 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/CommentScanner.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import org.eclipse.jface.text.TextAttribute; 4 | import org.eclipse.jface.text.rules.EndOfLineRule; 5 | import org.eclipse.jface.text.rules.IRule; 6 | import org.eclipse.jface.text.rules.MultiLineRule; 7 | import org.eclipse.jface.text.rules.RuleBasedScanner; 8 | import org.eclipse.jface.text.rules.Token; 9 | import org.eclipse.swt.graphics.Color; 10 | import org.eclipse.swt.graphics.RGB; 11 | import org.eclipse.swt.widgets.Display; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class CommentScanner extends RuleBasedScanner { 17 | public CommentScanner() { 18 | List rules = new ArrayList<>(); 19 | 20 | Token commentToken = new Token(new TextAttribute(new Color( 21 | Display.getCurrent(), new RGB(63, 127, 95)))); 22 | rules.add(new EndOfLineRule("--", commentToken)); 23 | rules.add(new EndOfLineRule("//", commentToken)); 24 | rules.add(new MultiLineRule("/*", "*/", commentToken)); 25 | 26 | setRules(rules.toArray(new IRule[0])); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MatCalcitePlugin/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | 12 | 16 | 17 | 18 | 20 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/IClassMethods.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.snapshot.model.IClass; 5 | import org.eclipse.mat.snapshot.model.IObject; 6 | 7 | @SuppressWarnings("unused") 8 | public abstract class IClassMethods { 9 | public static IClass getSuper(IObject clazz) { 10 | if (clazz instanceof IClass) { 11 | return ((IClass) clazz).getSuperClass(); 12 | } 13 | return null; 14 | } 15 | 16 | public static IObject getClassLoader(IObject clazz) { 17 | if (!(clazz instanceof IClass)) { 18 | return null; 19 | } 20 | int classLoaderId = ((IClass) clazz).getClassLoaderId(); 21 | try { 22 | return clazz.getSnapshot().getObject(classLoaderId); 23 | } catch (SnapshotException e) { 24 | throw new IllegalArgumentException( 25 | "Unable to retrieve classloader of class " + clazz + " in heap " + clazz.getSnapshot(), e); 26 | } 27 | } 28 | 29 | public static String getClassName(IObject clazz) { 30 | if (clazz instanceof IClass) { 31 | return ((IClass) clazz).getName(); 32 | } 33 | return null; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /MatCalciteTest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | MatCalciteTest 8 | eclipse-test-plugin 9 | 10 | 11 | com.github.vlsi.mat.calcite 12 | parent 13 | 1.6.4-SNAPSHOT 14 | 15 | 16 | 17 | src 18 | 19 | 20 | 21 | 22 | com.github.vlsi.mat.calcite 23 | MatCalcitePlugin 24 | ${project.version} 25 | 26 | 27 | junit 28 | junit 29 | 4.13.2 30 | test 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /MatCalciteTest/src/com/github/vlsi/mat/tests/calcite/TableFunctionsTests.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.tests.calcite; 2 | 3 | import org.junit.Test; 4 | 5 | import java.sql.SQLException; 6 | 7 | public class TableFunctionsTests extends SampleHeapDumpTests { 8 | @Test 9 | public void testGetMapEntries() throws SQLException { 10 | returnsInOrder("select count(*) cnt from java.util.HashMap m, lateral table(getMapEntries(m.this)) r where m.size" + 11 | " = 4", "cnt", "4"); 12 | } 13 | 14 | @Test 15 | public void selectOutboundReferences() throws SQLException { 16 | returnsInOrder("select sum(retainedSize(og.this)) retained_size, count(og.name) cnt_name from java.util.HashMap " + 17 | "hm, lateral (select * from table(getOutboundReferences(hm.this))) as og(name, this)", 18 | "retained_size|cnt_name", 19 | "113712|155"); 20 | } 21 | 22 | @Test 23 | public void selectOutboundReferencesCrossApply() throws SQLException { 24 | returnsInOrder("select sum(retainedSize(og.this)) retained_size, count(og.name) cnt_name from java.util.HashMap " + 25 | "hm cross apply table(getOutboundReferences(hm.this)) as og(name, this)", 26 | "retained_size|cnt_name", 27 | "113712|155"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/CalcitePartitionScanner.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import org.eclipse.jface.text.rules.EndOfLineRule; 4 | import org.eclipse.jface.text.rules.IPredicateRule; 5 | import org.eclipse.jface.text.rules.IToken; 6 | import org.eclipse.jface.text.rules.MultiLineRule; 7 | import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; 8 | import org.eclipse.jface.text.rules.SingleLineRule; 9 | import org.eclipse.jface.text.rules.Token; 10 | 11 | public class CalcitePartitionScanner extends RuleBasedPartitionScanner { 12 | public static final String SQL_COMMENT = "__calcite_comment"; //$NON-NLS-1$ 13 | public static final String SQL_STRING = "__calcite_string"; //$NON-NLS-1$ 14 | public static final String SQL_QUOTED_IDENTIFIER = "__calcite_quoted_identifier"; //$NON-NLS-1$ 15 | public static final String OTHER = "__calcite_other"; //$NON-NLS-1$ 16 | 17 | public CalcitePartitionScanner() { 18 | IToken comment = new Token(SQL_COMMENT); 19 | IToken string = new Token(SQL_STRING); 20 | IToken quotedIdentifier = new Token(SQL_QUOTED_IDENTIFIER); 21 | 22 | setPredicateRules(new IPredicateRule[]{ 23 | new EndOfLineRule("//", comment), 24 | new EndOfLineRule("--", comment), 25 | new MultiLineRule("/*", "*/", comment), 26 | new SingleLineRule("\"", "\"", quotedIdentifier), 27 | new MultiLineRule("'", "'", string), 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/action/CommentLineAction.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.action; 2 | 3 | import org.eclipse.jface.action.Action; 4 | import org.eclipse.swt.custom.ST; 5 | import org.eclipse.swt.custom.StyledText; 6 | 7 | public class CommentLineAction extends Action { 8 | private final StyledText text; 9 | 10 | public CommentLineAction(StyledText text) { 11 | this.text = text; 12 | } 13 | 14 | @Override 15 | public void run() { 16 | int caretOffset = text.getCaretOffset(); 17 | int lineIndex = text.getLineAtOffset(caretOffset); 18 | int lineStart = getLineStart(lineIndex); 19 | String lineText = text.getLine(lineIndex); 20 | 21 | // Move caret to next line 22 | if (lineIndex + 1 < text.getLineCount()) { 23 | text.invokeAction(ST.LINE_DOWN); 24 | } 25 | 26 | if (lineText.startsWith("--")) { 27 | // Uncomment 28 | text.replaceTextRange(lineStart, 2, ""); 29 | } else { 30 | // Comment 31 | text.replaceTextRange(lineStart, 0, "--"); 32 | } 33 | } 34 | 35 | private int getLineStart(int lineIndex) { 36 | String textContent = text.getText(); 37 | String delimiter = text.getLineDelimiter(); 38 | int lineStart = 0; 39 | 40 | while (lineIndex > 0) { 41 | lineStart = textContent.indexOf(delimiter, lineStart); 42 | if (lineStart > 0) { 43 | lineStart += delimiter.length(); 44 | } 45 | lineIndex--; 46 | } 47 | return lineStart; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/SnapshotRexExpressions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.github.vlsi.mat.calcite.rex.RexBuilderContext; 4 | 5 | import org.apache.calcite.rex.RexNode; 6 | 7 | public interface SnapshotRexExpressions { 8 | static RexNode computeThis(RexBuilderContext context) { 9 | return context.getBuilder() 10 | .makeCall(HeapOperatorTable.TO_HEAP_REFERENCE, context.getIObject()); 11 | } 12 | 13 | static RexNode resolveField(RexBuilderContext context, String fieldName) { 14 | return context.getBuilder() 15 | .makeCall(HeapOperatorTable.RESOLVE_VALUE, 16 | context.getIObject(), 17 | context.getBuilder().makeLiteral(fieldName)); 18 | } 19 | 20 | static RexNode getClassOf(RexBuilderContext context, RexNode iobject) { 21 | return context.getBuilder().makeCall(HeapOperatorTable.GET_CLASS_OF, context.getSnapshot(), iobject); 22 | } 23 | 24 | static RexNode getSuper(RexBuilderContext context, RexNode iclass) { 25 | return context.getBuilder().makeCall(HeapOperatorTable.GET_SUPER, iclass); 26 | } 27 | 28 | static RexNode getClassLoader(RexBuilderContext context, RexNode iclass) { 29 | return context.getBuilder().makeCall(HeapOperatorTable.GET_CLASS_LOADER, iclass); 30 | } 31 | 32 | static RexNode getClassName(RexBuilderContext context, RexNode iclass) { 33 | return context.getBuilder().makeCall(HeapOperatorTable.GET_CLASS_NAME, iclass); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | concurrency: 12 | # On master/release, we don't want any jobs cancelled so the sha is used to name the group 13 | # On PR branches, we cancel the job if new commits are pushed 14 | # More info: https://stackoverflow.com/a/68422069/253468 15 | group: ${{ (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release' ) && format('ci-main-{0}', github.sha) || format('ci-main-{0}', github.ref) }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | build-test: 20 | strategy: 21 | matrix: 22 | java: [ 17 ] 23 | os: [ ubuntu-latest, windows-latest ] 24 | name: 'Test, ${{ matrix.os }}, java ${{ matrix.java }}' 25 | runs-on: ${{ matrix.os }} 26 | steps: 27 | - name: Checkout sources 28 | uses: actions/checkout@v6 29 | - name: Setup Java 30 | uses: ./.github/actions/setup_java 31 | with: 32 | java_version: ${{ matrix.java }} 33 | - name: Build 34 | uses: ./.github/actions/build 35 | with: 36 | java_version: ${{ matrix.java }} 37 | - name: Publish Eclipse update site 38 | if: runner.os == 'Linux' && github.repository == 'vlsi/mat-calcite-plugin' && github.ref == 'refs/heads/main' 39 | uses: ./.github/actions/publish-update-site 40 | with: 41 | update_site_token: 42 | ${{ secrets.UPDATE_SITE_TOKEN }} 43 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/ISnapshotMethods.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.snapshot.ISnapshot; 5 | import org.eclipse.mat.snapshot.model.IClass; 6 | import org.eclipse.mat.snapshot.model.IObject; 7 | 8 | @SuppressWarnings("unused") 9 | public class ISnapshotMethods { 10 | public static long getShallowSize(ISnapshot snapshot, int id) { 11 | try { 12 | return snapshot.getHeapSize(id); 13 | } catch (SnapshotException e) { 14 | throw new IllegalArgumentException("Unable to get shallow size of object " + id + " in heap " + snapshot, e); 15 | } 16 | } 17 | 18 | public static long getRetainedSize(ISnapshot snapshot, int id) { 19 | try { 20 | return snapshot.getRetainedHeapSize(id); 21 | } catch (SnapshotException e) { 22 | throw new IllegalArgumentException("Unable to get retained size of object " + id + " in heap " + snapshot, e); 23 | } 24 | } 25 | 26 | public static IObject getIObject(ISnapshot snapshot, int id) { 27 | try { 28 | return snapshot.getObject(id); 29 | } catch (SnapshotException e) { 30 | throw new IllegalArgumentException("Unable to get object " + id + " in heap " + snapshot, e); 31 | } 32 | } 33 | 34 | public static IClass getClassOf(ISnapshot snapshot, int id) { 35 | try { 36 | return snapshot.getClassOf(id); 37 | } catch (SnapshotException e) { 38 | throw new IllegalArgumentException("Unable to get class of " + id + " in heap " + snapshot, e); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/collections/CollectionsActions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.collections; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils; 5 | import org.eclipse.mat.inspections.collectionextract.ExtractedMap; 6 | import org.eclipse.mat.inspections.collectionextract.IMapExtractor; 7 | import org.eclipse.mat.snapshot.model.IClass; 8 | import org.eclipse.mat.snapshot.model.IObject; 9 | 10 | public class CollectionsActions { 11 | 12 | private static class MapExtractorInfo { 13 | final String className; 14 | final IMapExtractor extractor; 15 | 16 | MapExtractorInfo(String className, IMapExtractor extractor) { 17 | this.className = className; 18 | this.extractor = extractor; 19 | } 20 | } 21 | 22 | private static final MapExtractorInfo[] knownExtractors = new MapExtractorInfo[] 23 | { 24 | new MapExtractorInfo("com.github.andrewoma.dexx.collection.HashMap", new DexxHashMapCollectionExtractor()), 25 | new MapExtractorInfo("vlsi.utils.CompactHashMap", new CompactHashMapCollectionExtractor()) 26 | }; 27 | 28 | public static ExtractedMap extractMap(IObject object) throws SnapshotException { 29 | IClass clazz = object.getClazz(); 30 | for (MapExtractorInfo info : knownExtractors) { 31 | if (clazz.doesExtend(info.className)) { 32 | return CollectionExtractionUtils.extractMap(object, info.className, info.extractor); 33 | } 34 | } 35 | return CollectionExtractionUtils.extractMap(object); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/actions/publish-update-site/action.yml: -------------------------------------------------------------------------------- 1 | name: Publish update site 2 | description: Publishes the updated update-site 3 | inputs: 4 | update_site_token: 5 | required: true 6 | description: GitHub token for publishing 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Checkout update site 11 | uses: actions/checkout@v6 12 | with: 13 | repository: vlsi/mat-calcite-plugin-update-site 14 | path: MatCalciteRepository/target/mat-calcite-plugin-update-site 15 | token: ${{ inputs.update_site_token }} 16 | - name: Publish 17 | shell: bash 18 | run: | 19 | PLUGIN_VERSION=$(./mvnw help:evaluate -q -DforceStdout -D"expression=project.version") 20 | cd MatCalciteRepository/target 21 | # git clone https://github.com/vlsi/mat-calcite-plugin-update-site.git 22 | git config --global user.name "${GITHUB_ACTOR}" 23 | git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" 24 | if [ -z ${PLUGIN_VERSION##*-SNAPSHOT} ]; then 25 | REPO=ea 26 | else 27 | REPO=stable 28 | fi 29 | DIR=mat-calcite-plugin-update-site/$REPO 30 | rm -rf $DIR 31 | # Copy "repository" recursively as "ea" or "stable" folder 32 | cp -R repository $DIR 33 | cd mat-calcite-plugin-update-site 34 | # Restore the deleted readme, so the browser can display it 35 | git checkout $REPO/README.md 36 | git add -A 37 | git commit -m "Update site for ${PLUGIN_VERSION}, ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}" 38 | git push 39 | 40 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/HeapFunctionsBase.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | 5 | import org.eclipse.mat.SnapshotException; 6 | import org.eclipse.mat.snapshot.ISnapshot; 7 | import org.eclipse.mat.snapshot.model.IObject; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | public class HeapFunctionsBase { 12 | protected static Object resolveReference(Object value) { 13 | return value instanceof IObject ? HeapReference.valueOf((IObject) value) : value; 14 | } 15 | 16 | protected static HeapReference resolveReference(ISnapshot snapshot, long address) { 17 | if (address == 0) { 18 | // Eclipse MAT always returns "SystemClassLoader" for address=0, so we return null instead 19 | return null; 20 | } 21 | try { 22 | return HeapReference.valueOf(snapshot.getObject(snapshot.mapAddressToId(address))); 23 | } catch (SnapshotException e) { 24 | return null; 25 | } 26 | } 27 | 28 | protected static HeapReference ensureHeapReference(Object r) { 29 | return r instanceof HeapReference ? (HeapReference) r : null; 30 | } 31 | 32 | protected static String toString(IObject o) { 33 | String classSpecific = o.getClassSpecificName(); 34 | if (classSpecific != null) { 35 | return classSpecific; 36 | } 37 | return o.getDisplayName(); 38 | } 39 | 40 | protected static Method findMethod(Class cls, String name) { 41 | for (Method m : cls.getMethods()) { 42 | if (m.getName().equals(name)) { 43 | return m; 44 | } 45 | } 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/InstanceByClassTableScan.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.plan.RelOptCluster; 5 | import org.apache.calcite.plan.RelOptPlanner; 6 | import org.apache.calcite.plan.RelOptTable; 7 | import org.apache.calcite.plan.RelTraitSet; 8 | import org.apache.calcite.rel.RelNode; 9 | import org.apache.calcite.rel.core.TableScan; 10 | import org.apache.calcite.rel.rules.CoreRules; 11 | 12 | import java.util.List; 13 | 14 | public class InstanceByClassTableScan extends TableScan { 15 | private final InstanceByClassTable instanceByClassTable; 16 | 17 | public InstanceByClassTableScan(RelOptCluster cluster, RelOptTable relOptTable, 18 | InstanceByClassTable instanceByClassTable) { 19 | super(cluster, cluster.traitSet(), ImmutableList.of(), relOptTable); 20 | this.instanceByClassTable = instanceByClassTable; 21 | } 22 | 23 | @Override 24 | public void register(RelOptPlanner planner) { 25 | planner.addRule(InstanceAccessByClassIdRule.INSTANCE); 26 | planner.addRule(CoreRules.PROJECT_JOIN_TRANSPOSE); 27 | // Does not yet work. 28 | // These rules should convert join (a."@ID" = :var) to "snapshot.getObject(:var)" 29 | // planner.addRule(NestedLoopsJoinRule.INSTANCE); 30 | // planner.addRule(InstanceAccessByIdRule.INSTANCE); 31 | } 32 | 33 | @Override 34 | public RelNode copy(RelTraitSet traitSet, List inputs) { 35 | assert inputs.isEmpty(); 36 | return new InstanceByClassTableScan(getCluster(), table, instanceByClassTable); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | 9 | [*.md] 10 | trim_trailing_whitespace = false 11 | 12 | [{*.sh, gradlew}] 13 | end_of_line = lf 14 | 15 | [{*.bat, *.cmd}] 16 | end_of_line = crlf 17 | 18 | [{*.kts, *.kt}] 19 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 20 | ij_kotlin_name_count_to_use_star_import = 99 21 | ij_kotlin_name_count_to_use_star_import_for_members = 99 22 | ij_java_use_single_class_imports = true 23 | max_line_length = 120 24 | ij_any_wrap_long_lines = true 25 | 26 | [*.java] 27 | # Doc: https://youtrack.jetbrains.com/issue/IDEA-170643#focus=streamItem-27-3708697.0-0 28 | # $ means "static" 29 | ij_java_imports_layout = com.github.vlsi.**, |, *, |, java.**, |, javax.**, |, $*, |, $java.**, |, $javax.** 30 | indent_size = 2 31 | tab_width = 2 32 | max_line_length = 120 33 | ij_any_spaces_around_additive_operators = true 34 | ij_any_spaces_around_assignment_operators = true 35 | ij_any_spaces_around_bitwise_operators = true 36 | ij_any_spaces_around_equality_operators = true 37 | ij_any_spaces_around_lambda_arrow = true 38 | ij_any_spaces_around_logical_operators = true 39 | ij_any_spaces_around_multiplicative_operators = true 40 | ij_any_spaces_around_relational_operators = true 41 | ij_any_spaces_around_shift_operators = true 42 | ij_continuation_indent_size = 4 43 | ij_java_if_brace_force = always 44 | ij_java_indent_case_from_switch = false 45 | ij_java_space_after_colon = true 46 | ij_java_space_before_colon = true 47 | ij_java_ternary_operation_signs_on_next_line = true 48 | ij_java_use_single_class_imports = true 49 | ij_java_wrap_long_lines = true 50 | ij_java_align_multiline_parameters = false 51 | -------------------------------------------------------------------------------- /MatCalciteRepository/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | MatCalciteRepository 7 | 1.6.4-SNAPSHOT 8 | eclipse-repository 9 | Calcite SQL update site 10 | 11 | 12 | com.github.vlsi.mat.calcite 13 | parent 14 | 1.6.4-SNAPSHOT 15 | 16 | 17 | 18 | 19 | com.github.vlsi.mat.calcite 20 | MatCalciteFeature 21 | ${project.version} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.eclipse.tycho 30 | tycho-p2-repository-plugin 31 | ${tycho-version} 32 | 33 | ${project.artifactId}-${qualifiedVersion} 34 | true 35 | true 36 | true 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/IClassesList.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.snapshot.ISnapshot; 5 | import org.eclipse.mat.snapshot.model.IClass; 6 | 7 | import java.util.Collection; 8 | 9 | public class IClassesList { 10 | public final ISnapshot snapshot; 11 | public final String className; 12 | public final boolean includeSubClasses; 13 | private long totalObjects = -1; 14 | 15 | public IClassesList(ISnapshot snapshot, String className, boolean includeSubClasses) { 16 | this.snapshot = snapshot; 17 | this.className = className; 18 | this.includeSubClasses = includeSubClasses; 19 | } 20 | 21 | public Collection getClasses() { 22 | return getClasses(false); 23 | } 24 | 25 | public Collection getRootClasses() { 26 | return getClasses(false); 27 | } 28 | 29 | public double getTotalObjects() { 30 | if (totalObjects >= 0) { 31 | return totalObjects; 32 | } 33 | long rows = 0; 34 | for (IClass iClass : getClasses()) { 35 | rows += iClass.getNumberOfObjects(); 36 | } 37 | return totalObjects = rows; 38 | } 39 | 40 | private Collection getClasses(boolean justFirstItem) { 41 | Collection classesByName; 42 | try { 43 | classesByName = snapshot.getClassesByName(className, 44 | includeSubClasses && !justFirstItem); 45 | } catch (SnapshotException e) { 46 | throw new IllegalStateException("Unable to get class " + className); 47 | } 48 | return classesByName; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "IClassesList{" + 54 | "className='" + className + '\'' + 55 | ", includeSubClasses=" + includeSubClasses + 56 | '}'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /MatCalcitePlugin/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: MatCalcitePlugin 4 | Bundle-SymbolicName: MatCalcitePlugin;singleton:=true 5 | Bundle-Version: 1.6.4.qualifier 6 | Bundle-Vendor: Vladimir Sitnikov 7 | Bundle-RequiredExecutionEnvironment: JavaSE-17 8 | Require-Bundle: org.eclipse.mat.api;bundle-version="1.13.0", 9 | org.eclipse.mat.ui;bundle-version="1.13.0", 10 | org.eclipse.ui;bundle-version="3.6.0", 11 | org.eclipse.core.runtime;bundle-version="3.7.0", 12 | org.eclipse.jface.text;bundle-version="3.7.2", 13 | org.eclipse.core.resources;bundle-version="3.7.101" 14 | Export-Package: com.github.vlsi.mat.calcite, 15 | com.github.vlsi.mat.calcite.functions 16 | Import-Package: com.fasterxml.jackson.core.util, 17 | com.google.common.base, 18 | com.google.common.cache, 19 | com.google.common.collect, 20 | com.google.common.escape, 21 | com.google.common.primitives, 22 | com.google.protobuf, 23 | com.jayway.jsonpath.spi.mapper, 24 | org.apache.calcite, 25 | org.apache.calcite.adapter.enumerable, 26 | org.apache.calcite.adapter.java, 27 | org.apache.calcite.avatica, 28 | org.apache.calcite.jdbc, 29 | org.apache.calcite.linq4j, 30 | org.apache.calcite.linq4j.tree, 31 | org.apache.calcite.plan, 32 | org.apache.calcite.rel, 33 | org.apache.calcite.rel.core, 34 | org.apache.calcite.rel.rules, 35 | org.apache.calcite.rel.type, 36 | org.apache.calcite.rex, 37 | org.apache.calcite.runtime, 38 | org.apache.calcite.schema, 39 | org.apache.calcite.schema.impl, 40 | org.apache.calcite.sql, 41 | org.apache.calcite.sql.advise, 42 | org.apache.calcite.sql.parser, 43 | org.apache.calcite.sql.type, 44 | org.apache.calcite.sql.validate, 45 | org.apache.calcite.tools, 46 | org.apache.calcite.util, 47 | org.apache.commons.codec, 48 | org.apache.commons.math3.fraction, 49 | org.apache.commons.text.similarity, 50 | org.codehaus.commons.compiler, 51 | org.codehaus.janino, 52 | org.locationtech.jts.geom 53 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/action/ExecuteQueryAction.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.action; 2 | 3 | import com.github.vlsi.mat.calcite.editor.CalcitePane; 4 | 5 | import org.eclipse.jface.action.Action; 6 | import org.eclipse.mat.ui.util.PaneState; 7 | import org.eclipse.swt.custom.StyledText; 8 | import org.eclipse.ui.plugin.AbstractUIPlugin; 9 | 10 | import java.util.regex.Pattern; 11 | 12 | public class ExecuteQueryAction extends Action { 13 | public static final Pattern STARTS_WITH_EXPLAIN_PLAN = Pattern.compile("^\\s*explain\\s+plan\\s+for", 14 | Pattern.CASE_INSENSITIVE); 15 | 16 | private final boolean doExplain; 17 | private final CalcitePane pane; 18 | private final PaneState state; 19 | 20 | public ExecuteQueryAction(CalcitePane pane, PaneState state, boolean doExplain) { 21 | super(null); 22 | this.pane = pane; 23 | this.state = state; 24 | this.doExplain = doExplain; 25 | setText(doExplain ? "Explain" : "Run"); 26 | setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin("MatCalcitePlugin", doExplain ? "resources/icons" + 27 | "/explain.png" : "resources/icons/run.png")); 28 | } 29 | 30 | private String getSelectedQuery() { 31 | StyledText queryString = pane.getQueryString(); 32 | String query = queryString.getSelectionText(); 33 | 34 | if ("".equals(query)) //$NON-NLS-1$ 35 | { 36 | query = queryString.getText(); 37 | } 38 | 39 | // Temporary workaround for https://issues.apache.org/jira/browse/CALCITE-459 40 | query = query + queryString.getLineDelimiter(); 41 | 42 | return query; 43 | } 44 | 45 | @Override 46 | public void run() { 47 | String query = getSelectedQuery(); 48 | //TODO: actually check with this regexp doesn't work if there are comments in the beginning of the query 49 | if (doExplain && !STARTS_WITH_EXPLAIN_PLAN.matcher(query).find()) { 50 | query = "explain plan for " + query; 51 | } 52 | new CalciteJob(query, pane, state).schedule(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/rules/DefaultRuleConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.rules; 2 | 3 | import org.apache.calcite.plan.RelOptRule; 4 | import org.apache.calcite.plan.RelRule; 5 | import org.apache.calcite.rel.core.RelFactories; 6 | import org.apache.calcite.tools.RelBuilderFactory; 7 | 8 | public class DefaultRuleConfig implements RelRule.Config { 9 | public static final RelRule.Config EMPTY = 10 | new DefaultRuleConfig(RelFactories.LOGICAL_BUILDER, null, null); 11 | 12 | protected final RelBuilderFactory relBuilderFactory; 13 | protected final String description; 14 | protected final RelRule.OperandTransform operandTransform; 15 | 16 | protected DefaultRuleConfig(RelBuilderFactory relBuilderFactory, String description, RelRule.OperandTransform operandTransform) { 17 | this.relBuilderFactory = relBuilderFactory; 18 | this.description = description; 19 | this.operandTransform = operandTransform; 20 | } 21 | 22 | @Override 23 | public RelRule.Config withRelBuilderFactory(RelBuilderFactory relBuilderFactory) { 24 | return new DefaultRuleConfig(relBuilderFactory, description, operandTransform); 25 | } 26 | 27 | @Override 28 | public String description() { 29 | return description; 30 | } 31 | 32 | @Override 33 | public RelBuilderFactory relBuilderFactory() { 34 | return relBuilderFactory; 35 | } 36 | 37 | @Override 38 | public RelRule.Config withDescription(@org.checkerframework.checker.nullness.qual.Nullable String description) { 39 | return new DefaultRuleConfig(relBuilderFactory, description, operandTransform); 40 | } 41 | 42 | @Override 43 | public RelRule.OperandTransform operandSupplier() { 44 | return operandTransform; 45 | } 46 | 47 | @Override 48 | public RelRule.Config withOperandSupplier(RelRule.OperandTransform operandTransform) { 49 | return new DefaultRuleConfig(relBuilderFactory, description, operandTransform); 50 | } 51 | 52 | @Override 53 | public RelOptRule toRule() { 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release_version: 7 | description: 'Release version' 8 | required: true 9 | next_version: 10 | description: 'Next version to set after release (without -SNAPSHOT)' 11 | required: true 12 | 13 | jobs: 14 | release-job: 15 | permissions: 16 | # For creating releases 17 | contents: write 18 | env: 19 | RELEASE_VERSION: ${{ inputs.release_version }} 20 | NEXT_VERSION: ${{ inputs.next_version }} 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout sources 24 | uses: actions/checkout@v6 25 | - name: Setup Java 26 | uses: ./.github/actions/setup_java 27 | - name: Update version to stable 28 | shell: bash 29 | run: | 30 | git config --global user.name "${GITHUB_ACTOR}" 31 | git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" 32 | ./mvnw tycho-versions:set-version -DnewVersion=$RELEASE_VERSION 33 | git add -u 34 | git commit -m "Prepare for release v$RELEASE_VERSION" 35 | git tag v$RELEASE_VERSION 36 | - name: Build 37 | uses: ./.github/actions/build 38 | - name: Push release tag 39 | shell: bash 40 | run: | 41 | git push origin main v$RELEASE_VERSION 42 | - name: Publish release 43 | uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 44 | with: 45 | tag_name: v${{ inputs.release_version }} 46 | generate_release_notes: true 47 | draft: true 48 | files: | 49 | MatCalciteRepository/target/MatCalciteRepository*.zip 50 | - name: Publish Eclipse update site 51 | if: github.repository == 'vlsi/mat-calcite-plugin' 52 | uses: ./.github/actions/publish-update-site 53 | with: 54 | update_site_token: 55 | ${{ secrets.UPDATE_SITE_TOKEN }} 56 | - name: Prepare for next development iteration 57 | shell: bash 58 | run: | 59 | ./mvnw tycho-versions:set-version -DnewVersion=$NEXT_VERSION-SNAPSHOT 60 | git add -u 61 | git commit -m "Update version to $NEXT_VERSION-SNAPSHOT" 62 | git push origin main v$RELEASE_VERSION 63 | 64 | 65 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/InstanceByClassTable.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.github.vlsi.mat.calcite.rex.RexBuilderContext; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | import org.apache.calcite.plan.RelOptTable; 7 | import org.apache.calcite.rel.RelNode; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rel.type.RelDataTypeFactory; 10 | import org.apache.calcite.rex.RexNode; 11 | import org.apache.calcite.schema.Statistic; 12 | import org.apache.calcite.schema.Statistics; 13 | import org.apache.calcite.schema.TranslatableTable; 14 | import org.apache.calcite.schema.impl.AbstractTable; 15 | import org.apache.calcite.util.ImmutableBitSet; 16 | import org.apache.calcite.util.Pair; 17 | import org.eclipse.mat.snapshot.ISnapshot; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.ExecutionException; 21 | import java.util.function.Function; 22 | 23 | public class InstanceByClassTable extends AbstractTable implements TranslatableTable { 24 | public final ISnapshot snapshot; 25 | public final IClassesList classesList; 26 | private List> resolvers; 27 | 28 | public InstanceByClassTable(IClassesList classesList) { 29 | this.classesList = classesList; 30 | this.snapshot = classesList.snapshot; 31 | } 32 | 33 | public List> getResolvers() { 34 | return resolvers; 35 | } 36 | 37 | @Override 38 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 39 | Pair>> typeAndResolvers; 40 | try { 41 | typeAndResolvers = ClassRowTypeCache.CACHE.get(typeFactory).get( 42 | classesList); 43 | } catch (ExecutionException e) { 44 | throw new IllegalStateException( 45 | "Unable to identify row type for class " + classesList); 46 | } 47 | this.resolvers = typeAndResolvers.right; 48 | 49 | return typeAndResolvers.left; 50 | } 51 | 52 | @Override 53 | public Statistic getStatistic() { 54 | List uniqueKeys = ImmutableList.of(ImmutableBitSet.of(0)); 55 | return Statistics.of(classesList.getTotalObjects(), uniqueKeys); 56 | } 57 | 58 | @Override 59 | public RelNode toRel(RelOptTable.ToRelContext context, RelOptTable table) { 60 | return new InstanceByClassTableScan(context.getCluster(), table, this); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/rex/ExecutionRexBuilderContext.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.rex; 2 | 3 | import com.github.vlsi.mat.calcite.SnapshotHolder; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | import org.apache.calcite.plan.RelOptCluster; 7 | import org.apache.calcite.rel.type.RelDataTypeFactory; 8 | import org.apache.calcite.rex.RexBuilder; 9 | import org.apache.calcite.rex.RexNode; 10 | import org.apache.calcite.schema.impl.ScalarFunctionImpl; 11 | import org.apache.calcite.sql.SqlFunction; 12 | import org.apache.calcite.sql.SqlIdentifier; 13 | import org.apache.calcite.sql.SqlKind; 14 | import org.apache.calcite.sql.parser.SqlParserPos; 15 | import org.apache.calcite.sql.type.OperandTypes; 16 | import org.apache.calcite.sql.type.ReturnTypes; 17 | import org.apache.calcite.sql.type.SqlTypeFamily; 18 | import org.apache.calcite.sql.type.SqlTypeName; 19 | import org.apache.calcite.sql.validate.SqlUserDefinedFunction; 20 | import org.eclipse.mat.snapshot.ISnapshot; 21 | 22 | public class ExecutionRexBuilderContext extends RexBuilderContext { 23 | private final int snapshotId; 24 | private final RexNode objectId; 25 | 26 | private RexNode snapshot; 27 | 28 | private static final SqlFunction GET_SNAPSHOT = 29 | new SqlUserDefinedFunction( 30 | new SqlIdentifier("GET_SNAPSHOT", SqlParserPos.ZERO), 31 | SqlKind.OTHER_FUNCTION, 32 | ReturnTypes.explicit(tf -> tf.createTypeWithNullability(tf.createJavaType(ISnapshot.class), 33 | false)), 34 | null, 35 | OperandTypes.operandMetadata( 36 | ImmutableList.of(SqlTypeFamily.NUMERIC), 37 | tf -> ImmutableList.of( 38 | tf.createJavaType(int.class)), 39 | i -> "snapshotId", 40 | i -> false), 41 | ScalarFunctionImpl.create(SnapshotHolder.class, "get")); 42 | 43 | public ExecutionRexBuilderContext(RelOptCluster cluster, int snapshotId, RexNode objectId) { 44 | super(cluster); 45 | this.snapshotId = snapshotId; 46 | this.objectId = objectId; 47 | } 48 | 49 | @Override 50 | public RexNode getSnapshot() { 51 | if (snapshot == null) { 52 | RelDataTypeFactory typeFactory = getCluster().getTypeFactory(); 53 | RexBuilder b = getBuilder(); 54 | snapshot = b.makeCall(GET_SNAPSHOT, b.makeLiteral(snapshotId, typeFactory.createSqlType(SqlTypeName.INTEGER), false)); 55 | } 56 | return snapshot; 57 | } 58 | 59 | @Override 60 | public RexNode getIObjectId() { 61 | return objectId; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/InstanceAccessByClassIdRule.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.github.vlsi.mat.calcite.SnapshotHolder; 4 | import com.github.vlsi.mat.calcite.rex.ExecutionRexBuilderContext; 5 | import com.github.vlsi.mat.calcite.rex.RexBuilderContext; 6 | import com.github.vlsi.mat.calcite.rules.DefaultRuleConfig; 7 | 8 | import org.apache.calcite.plan.*; 9 | import org.apache.calcite.rex.RexNode; 10 | import org.apache.calcite.tools.RelBuilder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.function.Function; 15 | 16 | public class InstanceAccessByClassIdRule extends RelRule { 17 | public static final InstanceAccessByClassIdRule INSTANCE = 18 | new InstanceAccessByClassIdRule( 19 | DefaultRuleConfig.EMPTY 20 | .withOperandSupplier( 21 | b0 -> 22 | b0.operand(InstanceByClassTableScan.class) 23 | .anyInputs() 24 | ) 25 | ); 26 | 27 | public InstanceAccessByClassIdRule(RelRule.Config config) { 28 | super(config); 29 | } 30 | 31 | @Override 32 | public void onMatch(RelOptRuleCall call) { 33 | InstanceByClassTableScan scan = call.rel(0); 34 | RelOptTable table = scan.getTable(); 35 | RelOptSchema schema = table.getRelOptSchema(); 36 | List indexName = new ArrayList<>(table.getQualifiedName()); 37 | indexName.set(indexName.size() - 1, "$ids$:" + indexName.get(indexName.size() - 1)); 38 | RelBuilder relBuilder = call.builder(); 39 | relBuilder.push( 40 | relBuilder.getScanFactory().createScan( 41 | ViewExpanders.simpleContext(relBuilder.getCluster()), 42 | schema.getTableForMember(indexName))); 43 | 44 | InstanceByClassTable instanceByClassTable = table.unwrap(InstanceByClassTable.class); 45 | int snapshotId = SnapshotHolder.put(instanceByClassTable.snapshot); 46 | 47 | RexBuilderContext rexContext = new ExecutionRexBuilderContext( 48 | scan.getCluster(), snapshotId, relBuilder.field(0)); 49 | 50 | List> resolvers = instanceByClassTable.getResolvers(); 51 | List exprs = new ArrayList<>(resolvers.size()); 52 | for (Function resolver : resolvers) { 53 | exprs.add(resolver.apply(rexContext)); 54 | } 55 | call.transformTo( 56 | relBuilder.projectNamed(exprs, table.getRowType().getFieldNames(), false) 57 | .build()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/action/CalciteJob.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.action; 2 | 3 | import com.github.vlsi.mat.calcite.editor.CalcitePane; 4 | 5 | import org.eclipse.core.runtime.IProgressMonitor; 6 | import org.eclipse.core.runtime.IStatus; 7 | import org.eclipse.core.runtime.Status; 8 | import org.eclipse.mat.query.registry.ArgumentSet; 9 | import org.eclipse.mat.query.registry.QueryDescriptor; 10 | import org.eclipse.mat.query.registry.QueryRegistry; 11 | import org.eclipse.mat.query.registry.QueryResult; 12 | import org.eclipse.mat.query.results.TextResult; 13 | import org.eclipse.mat.ui.editor.AbstractPaneJob; 14 | import org.eclipse.mat.ui.util.PaneState; 15 | import org.eclipse.mat.ui.util.ProgressMonitorWrapper; 16 | 17 | import java.io.PrintWriter; 18 | import java.io.StringWriter; 19 | 20 | public class CalciteJob extends AbstractPaneJob { 21 | 22 | private final String sql; 23 | private final CalcitePane calcitePane; 24 | private final PaneState state; 25 | 26 | public CalciteJob(String sql, CalcitePane pane, PaneState state) { 27 | super(sql, pane); 28 | this.sql = sql; 29 | this.state = state; 30 | calcitePane = pane; 31 | this.setUser(true); 32 | } 33 | 34 | @Override 35 | protected IStatus doRun(IProgressMonitor monitor) { 36 | final QueryDescriptor descriptor = QueryRegistry.instance().getQuery("calcite");//$NON-NLS-1$ 37 | final ArgumentSet argumentSet; 38 | try { 39 | argumentSet = descriptor.createNewArgumentSet(getPane().getEditor() 40 | .getQueryContext()); 41 | argumentSet.setArgumentValue("sql", sql);//$NON-NLS-1$ 42 | final QueryResult result = argumentSet 43 | .execute(new ProgressMonitorWrapper(monitor)); 44 | calcitePane.getQueryString().getDisplay().asyncExec(() -> calcitePane.initQueryResult(result, state)); 45 | } catch (final Throwable e) { 46 | calcitePane.getQueryString().getDisplay().asyncExec(() -> { 47 | String keyError = calcitePane.highlightError(e); 48 | 49 | StringWriter sw = new StringWriter(); 50 | if (keyError != null) { 51 | sw.append(keyError).append("\n"); 52 | } 53 | if (sql != null) { 54 | sw.append(sql).append('\n'); 55 | } 56 | e.printStackTrace(new PrintWriter(sw)); 57 | String exceptionText = sw.toString(); 58 | 59 | TextResult tr = new TextResult(exceptionText, false); 60 | QueryResult result = new QueryResult(descriptor, "calcite", tr); 61 | calcitePane.initQueryResult(result, state); 62 | }); 63 | e.printStackTrace(); 64 | } 65 | 66 | return Status.OK_STATUS; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/rex/RexBuilderContext.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.rex; 2 | 3 | import com.github.vlsi.mat.calcite.functions.ISnapshotMethods; 4 | import com.github.vlsi.mat.calcite.schema.objects.HeapOperatorTable; 5 | 6 | import com.google.common.collect.ImmutableList; 7 | import org.apache.calcite.plan.RelOptCluster; 8 | import org.apache.calcite.rex.RexBuilder; 9 | import org.apache.calcite.rex.RexNode; 10 | import org.apache.calcite.schema.impl.ScalarFunctionImpl; 11 | import org.apache.calcite.sql.SqlFunction; 12 | import org.apache.calcite.sql.SqlIdentifier; 13 | import org.apache.calcite.sql.SqlKind; 14 | import org.apache.calcite.sql.parser.SqlParserPos; 15 | import org.apache.calcite.sql.type.OperandTypes; 16 | import org.apache.calcite.sql.type.ReturnTypes; 17 | import org.apache.calcite.sql.type.SqlTypeFamily; 18 | import org.apache.calcite.sql.validate.SqlUserDefinedFunction; 19 | import org.eclipse.mat.snapshot.ISnapshot; 20 | import org.eclipse.mat.snapshot.model.IObject; 21 | 22 | public abstract class RexBuilderContext { 23 | private final RelOptCluster cluster; 24 | private RexNode object; 25 | 26 | private static final SqlFunction GET_IOBJECT = 27 | new SqlUserDefinedFunction( 28 | new SqlIdentifier("GET_IOBJECT", SqlParserPos.ZERO), 29 | SqlKind.OTHER_FUNCTION, 30 | ReturnTypes.explicit(tf -> 31 | tf.createTypeWithNullability(tf.createJavaType(IObject.class), false)), 32 | null, 33 | OperandTypes.operandMetadata( 34 | ImmutableList.of(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC), 35 | tf -> ImmutableList.of( 36 | tf.createTypeWithNullability(tf.createJavaType(ISnapshot.class), false), 37 | tf.createJavaType(int.class)), 38 | i -> i == 0 ? "snapshotId" : "objectId", 39 | i -> false), 40 | ScalarFunctionImpl.create(ISnapshotMethods.class, "getIObject")); 41 | 42 | public RexBuilderContext(RelOptCluster cluster) { 43 | this.cluster = cluster; 44 | } 45 | 46 | public RexBuilder getBuilder() { 47 | return cluster.getRexBuilder(); 48 | } 49 | 50 | public RelOptCluster getCluster() { 51 | return cluster; 52 | } 53 | 54 | public abstract RexNode getSnapshot(); 55 | 56 | public abstract RexNode getIObjectId(); 57 | 58 | public RexNode toHeapReference(RexNode node) { 59 | return getBuilder().makeCall(HeapOperatorTable.TO_HEAP_REFERENCE, node); 60 | } 61 | 62 | public RexNode getIObject() { 63 | if (object == null) { 64 | object = getBuilder().makeCall(GET_IOBJECT, getSnapshot(), getIObjectId()); 65 | } 66 | return object; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/references/OutboundReferencesTable.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.references; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | import org.apache.calcite.adapter.java.AbstractQueryableTable; 7 | import org.apache.calcite.linq4j.Enumerator; 8 | import org.apache.calcite.linq4j.Linq4j; 9 | import org.apache.calcite.linq4j.QueryProvider; 10 | import org.apache.calcite.linq4j.Queryable; 11 | import org.apache.calcite.rel.type.RelDataType; 12 | import org.apache.calcite.rel.type.RelDataTypeFactory; 13 | import org.apache.calcite.schema.SchemaPlus; 14 | import org.apache.calcite.schema.Statistic; 15 | import org.apache.calcite.schema.Statistics; 16 | import org.apache.calcite.schema.impl.AbstractTableQueryable; 17 | import org.apache.calcite.sql.type.SqlTypeName; 18 | import org.apache.calcite.util.ImmutableBitSet; 19 | import org.apache.calcite.util.Util; 20 | import org.eclipse.mat.SnapshotException; 21 | import org.eclipse.mat.snapshot.model.NamedReference; 22 | 23 | import java.util.List; 24 | 25 | public class OutboundReferencesTable extends AbstractQueryableTable { 26 | private final static List NON_UNIQUE_KEYS_STATISTICS = ImmutableList.of(ImmutableBitSet.of()); 27 | 28 | private final List references; 29 | 30 | public OutboundReferencesTable(List references) { 31 | super(Object[].class); 32 | this.references = references; 33 | } 34 | 35 | @Override 36 | public Queryable asQueryable(QueryProvider queryProvider, SchemaPlus schemaPlus, String tableName) { 37 | return new AbstractTableQueryable(queryProvider, schemaPlus, this, tableName) { 38 | @Override 39 | public Enumerator enumerator() { 40 | List it = Util.transform(references, namedReference -> { 41 | HeapReference ref = null; 42 | try { 43 | ref = HeapReference.valueOf(namedReference.getObject()); 44 | } catch (SnapshotException e) { 45 | e.printStackTrace(); 46 | } 47 | return new Object[]{namedReference.getName(), ref}; 48 | }); 49 | 50 | return Linq4j.iterableEnumerator(it); 51 | } 52 | }; 53 | } 54 | 55 | @Override 56 | public Statistic getStatistic() { 57 | return Statistics.of(references.size(), NON_UNIQUE_KEYS_STATISTICS); 58 | } 59 | 60 | @Override 61 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 62 | return typeFactory.builder() 63 | .add("name", typeFactory.createJavaType(String.class)) 64 | .add("this", typeFactory.createSqlType(SqlTypeName.ANY)) 65 | .build(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/neo/GroupEnumerator.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.neo; 2 | 3 | import org.apache.calcite.linq4j.Enumerator; 4 | 5 | import java.util.NoSuchElementException; 6 | 7 | public abstract class GroupEnumerator implements Enumerator { 8 | private final GroupType[] groups; 9 | private RowsType rows; 10 | private int rowsCount; 11 | 12 | private int currentRow = -1; 13 | private int currentGroup = -1; 14 | 15 | private ResultType currentResult; 16 | 17 | public GroupEnumerator(GroupType[] groups) { 18 | this.groups = groups; 19 | } 20 | 21 | @Override 22 | public ResultType current() { 23 | if (currentResult == null) { 24 | throw new NoSuchElementException(); 25 | } else { 26 | return currentResult; 27 | } 28 | } 29 | 30 | @Override 31 | public boolean moveNext() { 32 | do { 33 | if (advanceRow()) { 34 | return true; 35 | } 36 | } while (advanceGroup()); 37 | return false; 38 | } 39 | 40 | @Override 41 | public void reset() { 42 | currentRow = -1; 43 | currentGroup = -1; 44 | currentResult = null; 45 | } 46 | 47 | @Override 48 | public void close() { 49 | reset(); 50 | } 51 | 52 | private boolean advanceRow() { 53 | if (currentGroup == -1) { 54 | return false; 55 | } else if (currentRow < rowsCount - 1) { 56 | currentRow++; 57 | resolveRow(); 58 | return true; 59 | } else { 60 | return false; 61 | } 62 | } 63 | 64 | private boolean advanceGroup() { 65 | if (currentGroup < groups.length - 1) { 66 | currentGroup++; 67 | currentRow = -1; 68 | resolveGroup(); 69 | return true; 70 | } else { 71 | currentResult = null; 72 | return false; 73 | } 74 | } 75 | 76 | private void resolveGroup() { 77 | try { 78 | rows = resolveGroup(groups[currentGroup]); 79 | rowsCount = rowsCount(rows); 80 | } catch (RuntimeException e) { 81 | throw e; 82 | } catch (Exception e) { 83 | throw new RuntimeException(e); 84 | } 85 | } 86 | 87 | private void resolveRow() { 88 | try { 89 | currentResult = resolveRow(groups[currentGroup], rows, currentRow); 90 | } catch (RuntimeException e) { 91 | throw e; 92 | } catch (Exception e) { 93 | throw new RuntimeException(e); 94 | } 95 | } 96 | 97 | protected abstract int rowsCount(RowsType rows); 98 | 99 | protected abstract RowsType resolveGroup(GroupType group) throws Exception; 100 | 101 | protected abstract ResultType resolveRow(GroupType group, RowsType rows, int currentRow) throws Exception; 102 | } 103 | -------------------------------------------------------------------------------- /MatCalciteTargetPlatform/MatCalciteTargetPlatform.target: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | net.minidev 15 | accessors-smart 16 | 2.5.0 17 | 18 | 19 | net.minidev 20 | json-smart 21 | 2.5.0 22 | 23 | 24 | org.apache.calcite 25 | calcite-core 26 | 1.36.0 27 | 28 | 29 | com.google.guava 30 | guava 31 | 32.1.3-jre 32 | 33 | 34 | com.google.code.findbugs 35 | jsr305 36 | 3.0.2 37 | 38 | 39 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/InstanceIdsByClassTable.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.google.common.collect.FluentIterable; 4 | import com.google.common.collect.ImmutableList; 5 | import com.google.common.primitives.Ints; 6 | import org.apache.calcite.adapter.java.AbstractQueryableTable; 7 | import org.apache.calcite.linq4j.Enumerator; 8 | import org.apache.calcite.linq4j.Linq4j; 9 | import org.apache.calcite.linq4j.QueryProvider; 10 | import org.apache.calcite.linq4j.Queryable; 11 | import org.apache.calcite.rel.type.RelDataType; 12 | import org.apache.calcite.rel.type.RelDataTypeFactory; 13 | import org.apache.calcite.schema.Schema; 14 | import org.apache.calcite.schema.SchemaPlus; 15 | import org.apache.calcite.schema.Statistic; 16 | import org.apache.calcite.schema.Statistics; 17 | import org.apache.calcite.schema.impl.AbstractTableQueryable; 18 | import org.apache.calcite.util.ImmutableBitSet; 19 | import org.eclipse.mat.SnapshotException; 20 | import org.eclipse.mat.snapshot.ISnapshot; 21 | 22 | import java.util.Collections; 23 | import java.util.List; 24 | 25 | public class InstanceIdsByClassTable extends AbstractQueryableTable { 26 | private final ISnapshot snapshot; 27 | private final IClassesList classesList; 28 | 29 | public InstanceIdsByClassTable(IClassesList classesList) { 30 | super(Object[].class); 31 | this.classesList = classesList; 32 | this.snapshot = classesList.snapshot; 33 | } 34 | 35 | @Override 36 | public Schema.TableType getJdbcTableType() { 37 | return Schema.TableType.SYSTEM_TABLE; 38 | } 39 | 40 | @Override 41 | public Queryable asQueryable(QueryProvider queryProvider, SchemaPlus schemaPlus, String tableName) { 42 | return new AbstractTableQueryable(queryProvider, schemaPlus, this, tableName) { 43 | @Override 44 | public Enumerator enumerator() { 45 | FluentIterable it = FluentIterable 46 | .from(classesList.getClasses()) 47 | .transformAndConcat( 48 | input -> { 49 | try { 50 | return Ints.asList(input.getObjectIds()); 51 | } catch (SnapshotException e) { 52 | e.printStackTrace(); 53 | return Collections.emptyList(); 54 | } 55 | }); 56 | 57 | return Linq4j.iterableEnumerator(it); 58 | } 59 | }; 60 | } 61 | 62 | @Override 63 | public Statistic getStatistic() { 64 | List uniqueKeys = ImmutableList.of(ImmutableBitSet.of(0)); 65 | return Statistics.of(classesList.getTotalObjects(), uniqueKeys); 66 | } 67 | 68 | @Override 69 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 70 | return typeFactory.createStructType( 71 | Collections.singletonList(typeFactory.createJavaType(int.class)), 72 | Collections.singletonList("@ID") 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/CalciteScanner.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import com.github.vlsi.mat.calcite.CalciteDataSource; 4 | 5 | import com.google.common.base.Splitter; 6 | import org.apache.calcite.jdbc.CalciteConnection; 7 | import org.apache.calcite.sql.parser.SqlAbstractParserImpl; 8 | import org.eclipse.jface.text.TextAttribute; 9 | import org.eclipse.jface.text.rules.IRule; 10 | import org.eclipse.jface.text.rules.IWhitespaceDetector; 11 | import org.eclipse.jface.text.rules.IWordDetector; 12 | import org.eclipse.jface.text.rules.RuleBasedScanner; 13 | import org.eclipse.jface.text.rules.Token; 14 | import org.eclipse.jface.text.rules.WordRule; 15 | import org.eclipse.swt.graphics.Color; 16 | import org.eclipse.swt.graphics.RGB; 17 | import org.eclipse.swt.widgets.Display; 18 | 19 | import java.sql.Connection; 20 | import java.sql.SQLException; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | public class CalciteScanner extends RuleBasedScanner { 25 | static class CalciteWhitespaceDetector implements IWhitespaceDetector { 26 | @Override 27 | public boolean isWhitespace(char c) { 28 | return Character.isWhitespace(c); 29 | } 30 | } 31 | 32 | public static class CalciteWordDetector implements IWordDetector { 33 | @Override 34 | public boolean isWordStart(char c) { 35 | return Character.isJavaIdentifierStart(c); 36 | } 37 | 38 | @Override 39 | public boolean isWordPart(char c) { 40 | return Character.isJavaIdentifierPart(c) || c == '.'; 41 | } 42 | } 43 | 44 | public CalciteScanner() { 45 | List rules = new ArrayList<>(); 46 | 47 | Token other = new Token(CalcitePartitionScanner.OTHER); 48 | Token keywordToken = new Token(new TextAttribute(new Color( 49 | Display.getCurrent(), new RGB(127, 0, 85)))); 50 | Token functionToken = new Token(new TextAttribute(new Color( 51 | Display.getCurrent(), new RGB(26, 0, 192)))); 52 | 53 | // "other" class is importantThis is required so Eclipse does not try to highlight in the middle of the words 54 | WordRule keywordRule = new WordRule(new CalciteWordDetector(), 55 | Token.UNDEFINED, true); 56 | WordRule functionRule = new WordRule(new CalciteWordDetector(), 57 | other, true); 58 | 59 | // Get keyword list from Calcite 60 | Connection con = null; 61 | try { 62 | con = CalciteDataSource.getConnection(CalciteContentAssistantProcessor.getSnapshot()); 63 | String keywords = con.getMetaData().getSQLKeywords(); 64 | for (String keyword : Splitter.on(',').split(keywords)) { 65 | keywordRule.addWord(keyword, keywordToken); 66 | } 67 | CalciteConnection ccon = con.unwrap(CalciteConnection.class); 68 | for (String fun : ccon.getRootSchema().getSubSchema(ccon.getSchema()).getFunctionNames()) { 69 | functionRule.addWord(fun, functionToken); 70 | } 71 | } catch (SQLException e) { 72 | 73 | } finally { 74 | CalciteDataSource.close(null, null, con); 75 | } 76 | 77 | for (String keyword : SqlAbstractParserImpl.getSql92ReservedWords()) { 78 | keywordRule.addWord(keyword, keywordToken); 79 | } 80 | 81 | rules.add(keywordRule); 82 | rules.add(functionRule); 83 | 84 | setRules(rules.toArray(new IRule[0])); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [1.6.2] - 2023-11-17 4 | ### Fixed 5 | - Fixed java.lang.IllegalArgumentException: Illegal group reference when explain plan has table names with dollars 6 | 7 | ## [1.6.1] - 2023-11-15 8 | ### Fixed 9 | - Fixed java.lang.NoClassDefFoundError: org/apache/calcite/runtime/CalciteContextException when SQL fails in MAT UI (regression since 1.6.0) 10 | 11 | ## [1.6.0] - 2023-11-15 12 | ### Changed 13 | - Eclipse Memory Analyzer 1.14.0 or higher is required 14 | - Java 17 is required 15 | - Update Apache Calcite to 1.36.0 (see https://calcite.apache.org/news/2023/11/10/release-1.36.0/) 16 | - Update Guava to 32.1.3-jre 17 | - Update net.minidev:accessors-smart to 2.5.0 18 | - Update net.minidev:json-smart to 2.5.0 19 | 20 | ## [1.5.0] - 2020-10-12 21 | ### Added 22 | - Extra property for all objects: `@class` (references to `java.lang.Class`) 23 | - Extra property for all objects: `@className` (returns the name of the class) 24 | - Extra property for Class objects: `@classLoader` (references to `java.lang.ClassLoader`) 25 | - Extra property for Class objects: `@super` (references to `java.lang.Class`) 26 | 27 | ### Changed 28 | - Eclipse Memory Analyzer 1.8.0 or higher is required 29 | - Java 1.8 or higher is required 30 | - Update Apache Calcite to 1.26.0 (see https://calcite.apache.org/news/2020/10/06/release-1.26.0/) 31 | - Update Guava to 29.0-jre 32 | 33 | ### Fixed 34 | - `name` for `IClass` return class name rather than the value of `Class.name` field (OpenJDK uses the field as a cache, so it might be null) 35 | - Declare reference columns as nullable, so the engine does not optimize `count(name)` into `count(*)` 36 | 37 | ## [1.4.0] - 2018-09-08 38 | ### Added 39 | - New functions: `asMap`, `getMapEntries`, `asMultiSet`, `getStringContent` 40 | - Support entries extraction from Dexx HashMap 41 | - Support entries extraction from vlsi.CompactHashMap 42 | - Commandline mode 43 | 44 | ### Changed 45 | - `ThreadStackFrames` is moved to `native` schema 46 | 47 | ### Fixed 48 | - Autocomplete is improved in lots of cases 49 | 50 | ## [1.3.0] - 2017-09-14 51 | ### Added 52 | - "class" tables can be accessed without quotes (e.g. `java.lang.HashMap`). Technically speaking, Java package maps to SQL schema. (Alexey Makhmutov) 53 | - Ability to access properties via `[...]` operation. For instance `x.this['field']` 54 | - New functions: `getId`, `getAddress`, `getType`, `getId`, `toString`, `shallowSize`, `retainedSize`, `length`, `getSize`, `getByKey`, `getField` (Alexey Makhmutov) 55 | - Table functions: `getValues`, `getRetainedSet`, `getOutboundReferences`, `getInboundReferences` (Alexey Makhmutov) 56 | - `CROSS APPLY`, `OUTER APPLY` syntax to call table functions 57 | - Error highlight in the SQL editor 58 | - Highlight of known heap functions 59 | 60 | ### Changed 61 | - `@THIS` column is renamed to `this` 62 | - `get_id` is renamed to `getId` 63 | - Eclipse Memory Analyzer 1.5.0 or higher is required 64 | 65 | ### Removed 66 | - `@RETAINED`, `@SHALLOW` columns from heap tables. Use `shallowSize(ref)` and `retainedSize(ref)` functions 67 | - `@ID` column from heap tables. Use `getId(ref)` function 68 | 69 | ## [1.2.0] - 2014-12-06 70 | ### Added 71 | - Functions: `length`, `get_type`, `get_by_key`, `get_id` 72 | - F10 hotkey for explain plan 73 | 74 | ### Fixed 75 | - Ctrl+Enter 76 | 77 | ### Changed 78 | - `@ID` column is replaced to `@THIS` 79 | 80 | ### Removed 81 | - `@PK` column, use `get_id` instead 82 | 83 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/Query.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite; 2 | 3 | import org.eclipse.mat.query.IQuery; 4 | import org.eclipse.mat.query.IResult; 5 | import org.eclipse.mat.query.annotations.Argument; 6 | import org.eclipse.mat.query.annotations.Category; 7 | import org.eclipse.mat.query.annotations.CommandName; 8 | import org.eclipse.mat.query.annotations.Name; 9 | import org.eclipse.mat.query.results.TextResult; 10 | import org.eclipse.mat.snapshot.ISnapshot; 11 | import org.eclipse.mat.util.IProgressListener; 12 | 13 | import java.sql.Connection; 14 | import java.sql.ResultSet; 15 | import java.sql.ResultSetMetaData; 16 | import java.sql.Statement; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | import javax.sql.rowset.CachedRowSet; 21 | import javax.sql.rowset.RowSetFactory; 22 | import javax.sql.rowset.RowSetProvider; 23 | 24 | @Name("SQL via Calcite") 25 | @Category("SQL") 26 | @CommandName("calcite") 27 | public class Query implements IQuery { 28 | public static final Pattern EXPLAIN_PLAN = Pattern.compile("explain\\s+plan\\s+for", Pattern.CASE_INSENSITIVE); 29 | public static final Pattern TRIM_ENUMERABLE = Pattern.compile("Enumerable(\\w+)Rel"); 30 | // EnumerableTableScan(table=[[HEAP, java, net, $ids$:URL]]) 31 | public static final Pattern RENAME_INDEX = Pattern.compile("EnumerableTableScan\\(table=\\[\\[HEAP, ((?:[^\\$][^," + 32 | "\\]]*+, )*+)\\$ids\\$:([^]]+)\\]\\]\\)"); 33 | public static final Pattern RENAME_JOIN = Pattern.compile("EnumerableJoin"); 34 | public static final Pattern RENAME_CALC = Pattern.compile("EnumerableCalc"); 35 | 36 | @Argument 37 | public ISnapshot snapshot; 38 | 39 | @Argument(flag = Argument.UNFLAGGED) 40 | public String sql; 41 | 42 | @Override 43 | public IResult execute(IProgressListener listener) throws Exception { 44 | Connection con = null; 45 | Statement st = null; 46 | ResultSet rs = null; 47 | 48 | try { 49 | con = CalciteDataSource.getConnection(snapshot); 50 | 51 | st = con.createStatement(); 52 | rs = st.executeQuery(sql); 53 | 54 | RowSetFactory rowSetFactory = RowSetProvider.newFactory(); 55 | CachedRowSet rowSet = rowSetFactory.createCachedRowSet(); 56 | rowSet.populate(rs); 57 | 58 | ResultSetMetaData md = rowSet.getMetaData(); 59 | if (md.getColumnCount() == 1 && "PLAN".equals(md.getColumnName(1)) 60 | && rowSet.size() == 1 61 | && EXPLAIN_PLAN.matcher(sql).find()) { 62 | rowSet.absolute(1); 63 | String plan = rowSet.getString(1); 64 | System.out.println("plan = " + plan); 65 | { 66 | Matcher ind = RENAME_INDEX.matcher(plan); 67 | StringBuffer sb = new StringBuffer(); 68 | while (ind.find()) { 69 | String className = ind.group(1).replace(", ", ".") + ind.group(2); 70 | ind.appendReplacement(sb, ""); 71 | sb.append("GetObjectIdsByClass (class=").append(className).append(")"); 72 | } 73 | ind.appendTail(sb); 74 | plan = sb.toString(); 75 | } 76 | plan = RENAME_JOIN.matcher(plan).replaceAll("HashJoin "); 77 | plan = RENAME_CALC.matcher(plan).replaceAll("View "); 78 | plan = TRIM_ENUMERABLE.matcher(plan).replaceAll("$1 "); 79 | return new TextResult(plan, false); 80 | } 81 | 82 | return new RowSetTable(rowSet); 83 | } finally { 84 | CalciteDataSource.close(rs, st, con); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/CalciteSourceViewerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import org.eclipse.jface.text.IDocument; 4 | import org.eclipse.jface.text.contentassist.ContentAssistant; 5 | import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 6 | import org.eclipse.jface.text.contentassist.IContentAssistant; 7 | import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; 8 | import org.eclipse.jface.text.presentation.IPresentationReconciler; 9 | import org.eclipse.jface.text.presentation.PresentationReconciler; 10 | import org.eclipse.jface.text.rules.DefaultDamagerRepairer; 11 | import org.eclipse.jface.text.source.ISourceViewer; 12 | import org.eclipse.jface.text.source.SourceViewerConfiguration; 13 | 14 | public class CalciteSourceViewerConfiguration extends SourceViewerConfiguration { 15 | @Override 16 | public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { 17 | return new String[]{IDocument.DEFAULT_CONTENT_TYPE, 18 | CalcitePartitionScanner.SQL_COMMENT, 19 | CalcitePartitionScanner.SQL_QUOTED_IDENTIFIER, 20 | CalcitePartitionScanner.SQL_STRING}; 21 | } 22 | 23 | @Override 24 | public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) { 25 | /* When using default implementation, the following exception appears: 26 | java.lang.IllegalStateException 27 | at org.eclipse.jface.text.TextViewer.setHyperlinkPresenter(TextViewer.java:5712) 28 | at org.eclipse.jface.text.source.SourceViewer.configure(SourceViewer.java:491) 29 | at CalcitePane.createPartControl(CalcitePane.java:84) 30 | at org.eclipse.mat.ui.editor.MultiPaneEditor.addPage(MultiPaneEditor.java:585) 31 | at org.eclipse.mat.ui.editor.MultiPaneEditor.addPage(MultiPaneEditor.java:574) 32 | at org.eclipse.mat.ui.editor.MultiPaneEditor.doAddNewPage(MultiPaneEditor.java:552) 33 | at org.eclipse.mat.ui.editor.MultiPaneEditor.addNewPage(MultiPaneEditor.java:535) 34 | at org.eclipse.mat.ui.editor.MultiPaneEditor.addNewPage(MultiPaneEditor.java:489) 35 | at OpenCalciteAction.run(OpenCalciteAction.java:22) 36 | */ 37 | return null; 38 | } 39 | 40 | @Override 41 | public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { 42 | IContentAssistProcessor proc = new CalciteContentAssistantProcessor(); 43 | ContentAssistant assistant = new ContentAssistant(); 44 | assistant.enableAutoActivation(false); 45 | assistant.setAutoActivationDelay(500); 46 | assistant.setContentAssistProcessor(proc, IDocument.DEFAULT_CONTENT_TYPE); 47 | assistant.setContentAssistProcessor(proc, CalcitePartitionScanner.SQL_QUOTED_IDENTIFIER); 48 | return assistant; 49 | } 50 | 51 | @Override 52 | public IPresentationReconciler getPresentationReconciler( 53 | ISourceViewer sourceViewer) { 54 | PresentationReconciler reconciler = new PresentationReconciler(); 55 | DefaultDamagerRepairer dr; 56 | 57 | dr = new DefaultDamagerRepairer(new CalciteScanner()); 58 | reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); 59 | reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); 60 | 61 | dr = new DefaultDamagerRepairer(new StringScanner()); 62 | reconciler.setDamager(dr, CalcitePartitionScanner.SQL_QUOTED_IDENTIFIER); 63 | reconciler.setRepairer(dr, CalcitePartitionScanner.SQL_QUOTED_IDENTIFIER); 64 | reconciler.setDamager(dr, CalcitePartitionScanner.SQL_STRING); 65 | reconciler.setRepairer(dr, CalcitePartitionScanner.SQL_STRING); 66 | 67 | dr = new DefaultDamagerRepairer(new CommentScanner()); 68 | reconciler.setDamager(dr, CalcitePartitionScanner.SQL_COMMENT); 69 | reconciler.setRepairer(dr, CalcitePartitionScanner.SQL_COMMENT); 70 | return reconciler; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/HeapReference.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite; 2 | 3 | import com.github.vlsi.mat.calcite.functions.HeapFunctions; 4 | 5 | import org.eclipse.mat.snapshot.model.IArray; 6 | import org.eclipse.mat.snapshot.model.IObject; 7 | 8 | import java.util.Collection; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | public class HeapReference implements Comparable, Map { 13 | private final IObject o; 14 | 15 | public HeapReference(IObject o) { 16 | this.o = o; 17 | } 18 | 19 | public static HeapReference valueOf(IObject o) { 20 | return o == null ? null : new HeapReference(o); 21 | } 22 | 23 | public IObject getIObject() { 24 | return o; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o1) { 29 | if (this == o1) { 30 | return true; 31 | } 32 | if (o1 == null || getClass() != o1.getClass()) { 33 | return false; 34 | } 35 | 36 | HeapReference that = (HeapReference) o1; 37 | 38 | return o.equals(that.o); 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | return o.hashCode(); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | String classSpecific = o.getClassSpecificName(); 49 | if (classSpecific != null) { 50 | return classSpecific; 51 | } 52 | return o.getDisplayName(); 53 | } 54 | 55 | @Override 56 | public int compareTo(HeapReference o) { 57 | int cmp = this.toString().compareTo(o.toString()); 58 | if (cmp != 0) { 59 | return cmp; 60 | } 61 | return getIObject().getObjectId() - o.getIObject().getObjectId(); 62 | } 63 | 64 | @Override 65 | public Object get(Object key) { 66 | // This Map.get is called by Calcite when this['fieldA'] SQL syntax is used 67 | if (key == null) { 68 | return null; 69 | } 70 | if (key instanceof Number && getIObject() instanceof IArray) { 71 | return HeapFunctions.getField(this, "[" + key + "]"); 72 | } 73 | String fieldName = String.valueOf(key); 74 | if (fieldName.charAt(0) == '@') { 75 | if ("@shallow".equalsIgnoreCase(fieldName)) { 76 | return HeapFunctions.shallowSize(this); 77 | } 78 | if ("@retained".equalsIgnoreCase(fieldName)) { 79 | return HeapFunctions.retainedSize(this); 80 | } 81 | } 82 | return HeapFunctions.getField(this, fieldName); 83 | } 84 | 85 | @Override 86 | public int size() { 87 | throw new UnsupportedOperationException(); 88 | } 89 | 90 | @Override 91 | public boolean isEmpty() { 92 | throw new UnsupportedOperationException(); 93 | } 94 | 95 | @Override 96 | public boolean containsKey(Object key) { 97 | throw new UnsupportedOperationException(); 98 | } 99 | 100 | @Override 101 | public boolean containsValue(Object value) { 102 | throw new UnsupportedOperationException(); 103 | } 104 | 105 | @Override 106 | public Object put(Object key, Object value) { 107 | throw new UnsupportedOperationException(); 108 | } 109 | 110 | @Override 111 | public Object remove(Object key) { 112 | throw new UnsupportedOperationException(); 113 | } 114 | 115 | @Override 116 | public void putAll(Map m) { 117 | throw new UnsupportedOperationException(); 118 | } 119 | 120 | @Override 121 | public void clear() { 122 | throw new UnsupportedOperationException(); 123 | } 124 | 125 | @Override 126 | public Set keySet() { 127 | throw new UnsupportedOperationException(); 128 | } 129 | 130 | @Override 131 | public Collection values() { 132 | throw new UnsupportedOperationException(); 133 | } 134 | 135 | @Override 136 | public Set entrySet() { 137 | throw new UnsupportedOperationException(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/Executor.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.common.escape.Escaper; 5 | import com.google.common.escape.Escapers; 6 | import org.eclipse.equinox.app.IApplication; 7 | import org.eclipse.equinox.app.IApplicationContext; 8 | import org.eclipse.mat.SnapshotException; 9 | import org.eclipse.mat.snapshot.ISnapshot; 10 | import org.eclipse.mat.snapshot.SnapshotFactory; 11 | import org.eclipse.mat.util.VoidProgressListener; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.BufferedWriter; 15 | import java.io.File; 16 | import java.io.FileReader; 17 | import java.io.FileWriter; 18 | import java.sql.Connection; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.ResultSetMetaData; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class Executor implements IApplication { 26 | 27 | @Override 28 | public Object start(IApplicationContext context) { 29 | String[] args = (String[]) context.getArguments().get("application.args"); 30 | char delimiter = ','; 31 | 32 | if (args.length < 3) { 33 | System.out.println("java com.github.vlsi.mat.calcite.Executor "); 34 | return IApplication.EXIT_OK; 35 | } 36 | 37 | File heapFile = new File(args[0]); 38 | if (!heapFile.isFile()) { 39 | System.out.println("Heap dump file " + args[0] + " does not exists!"); 40 | return IApplication.EXIT_OK; 41 | } 42 | 43 | File queryFile = new File(args[1]); 44 | if (!queryFile.isFile()) { 45 | System.out.println("Query file " + args[1] + " does not exists!"); 46 | return IApplication.EXIT_OK; 47 | } 48 | File resultsFile = new File(args[2]); 49 | 50 | StringBuilder sbQuery = new StringBuilder(); 51 | try (BufferedReader r = new BufferedReader(new FileReader(queryFile))) { 52 | for (String line = r.readLine(); line != null; line = r.readLine()) { 53 | sbQuery.append(line).append("\n"); 54 | } 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | return IApplication.EXIT_OK; 58 | } 59 | 60 | 61 | try (Connection con = CalciteDataSource.getConnection(openSnapshot(heapFile)); 62 | BufferedWriter w = new BufferedWriter(new FileWriter(resultsFile))) { 63 | PreparedStatement ps = con.prepareStatement(sbQuery.toString()); 64 | ResultSet rs = ps.executeQuery(); 65 | ResultSetMetaData md = rs.getMetaData(); 66 | Joiner joiner = Joiner.on(delimiter); 67 | Escaper escaper = Escapers.builder().addEscape('"', "\"\"").build(); 68 | 69 | List row = new ArrayList<>(); 70 | final int columnCount = md.getColumnCount(); 71 | for (int i = 1; i <= columnCount; i++) { 72 | row.add(escape(md.getColumnName(i), delimiter, escaper)); 73 | } 74 | w.write(joiner.join(row)); 75 | w.newLine(); 76 | while (rs.next()) { 77 | row.clear(); 78 | for (int i = 1; i <= columnCount; i++) { 79 | row.add(escape(String.valueOf(rs.getObject(i)), delimiter, escaper)); 80 | } 81 | w.write(joiner.join(row)); 82 | w.newLine(); 83 | } 84 | } catch (Exception e) { 85 | e.printStackTrace(); 86 | } 87 | 88 | return IApplication.EXIT_OK; 89 | } 90 | 91 | @Override 92 | public void stop() { 93 | 94 | } 95 | 96 | private static ISnapshot openSnapshot(File heapDump) throws SnapshotException { 97 | System.out.println("exists = " + heapDump.exists() + ", file = " + heapDump.getAbsolutePath()); 98 | return SnapshotFactory.openSnapshot(heapDump, new VoidProgressListener()); 99 | } 100 | 101 | private static String escape(String val, char delimiter, Escaper escaper) { 102 | return val.indexOf(delimiter) == -1 ? val : "\"" + escaper.escape(val) + "\""; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/CalciteDataSource.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite; 2 | 3 | import com.github.vlsi.mat.calcite.neo.PackageSchema; 4 | 5 | import com.google.common.cache.CacheBuilder; 6 | import com.google.common.cache.CacheLoader; 7 | import com.google.common.cache.LoadingCache; 8 | import org.apache.calcite.jdbc.CalciteConnection; 9 | import org.apache.calcite.schema.Schema; 10 | import org.apache.calcite.schema.SchemaPlus; 11 | import org.codehaus.commons.compiler.CompilerFactoryFactory; 12 | import org.codehaus.janino.CompilerFactory; 13 | import org.eclipse.mat.snapshot.ISnapshot; 14 | 15 | import java.sql.Connection; 16 | import java.sql.DriverManager; 17 | import java.sql.ResultSet; 18 | import java.sql.SQLException; 19 | import java.sql.Statement; 20 | import java.util.Properties; 21 | import java.util.concurrent.ExecutionException; 22 | 23 | public class CalciteDataSource { 24 | 25 | private static final LoadingCache SCHEMA_CACHE = CacheBuilder 26 | .newBuilder() 27 | .weakKeys().build(new CacheLoader() { 28 | @Override 29 | public Schema load(ISnapshot key) throws Exception { 30 | // return new HeapSchema(key); 31 | return PackageSchema.resolveSchema(key); 32 | } 33 | }); 34 | 35 | private static boolean initCompilerDone; 36 | 37 | public static Connection getConnection(ISnapshot snapshot) 38 | throws SQLException { 39 | initJanino(); 40 | 41 | try { 42 | Class.forName("org.apache.calcite.jdbc.Driver"); 43 | } catch (ClassNotFoundException e) { 44 | throw new SQLException( 45 | "Unable to load Calcite JDBC driver", e); 46 | } 47 | Properties info = new Properties(); 48 | info.put("lex", "JAVA"); 49 | info.put("quoting", "DOUBLE_QUOTE"); 50 | info.put("conformance", "LENIENT"); // enable cross apply, etc 51 | Connection connection = DriverManager.getConnection( 52 | "jdbc:calcite:", info); 53 | CalciteConnection con = connection 54 | .unwrap(CalciteConnection.class); 55 | 56 | if (snapshot == null) { 57 | return connection; 58 | } 59 | 60 | if ("HEAP".equals(con.getSchema())) { 61 | return connection; 62 | } 63 | 64 | SchemaPlus root = con.getRootSchema(); 65 | Schema heapSchema; 66 | try { 67 | heapSchema = SCHEMA_CACHE.get(snapshot); 68 | } catch (ExecutionException e) { 69 | throw new SQLException("Unable to create heap schema", e); 70 | } 71 | root.add("HEAP", heapSchema); 72 | con.setSchema("HEAP"); 73 | 74 | return connection; 75 | } 76 | 77 | private static void initJanino() throws SQLException { 78 | // For unknown reason, threadContextClassLoader.getResource("org.codehaus.commons.compiler.properties") 79 | // returns null when accessed via BundleClassLoader 80 | // We make a shortcut 81 | // Some OSGi WA might probably exist 82 | if (initCompilerDone) { 83 | return; 84 | } 85 | initCompilerDone = true; 86 | try { 87 | if (CompilerFactoryFactory.getDefaultCompilerFactory(CompilerFactory.class.getClassLoader()) == null) { 88 | throw new SQLException("Janino compiler is not initialized: CompilerFactoryFactory.getDefaultCompilerFactory" + 89 | "() == null"); 90 | } 91 | } catch (Exception e) { 92 | throw new SQLException("Unable to load Janino compiler", e); 93 | } 94 | } 95 | 96 | public static void close(ResultSet rs, Statement st, Connection con) { 97 | if (rs != null) { 98 | try { 99 | rs.close(); 100 | } catch (SQLException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | if (st != null) { 105 | try { 106 | st.close(); 107 | } catch (SQLException e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | if (con != null) { 112 | try { 113 | con.close(); 114 | } catch (SQLException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /MatCalciteTest/src/com/github/vlsi/mat/tests/calcite/AbstractQueriesTests.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.tests.calcite; 2 | 3 | import com.github.vlsi.mat.calcite.CalciteDataSource; 4 | 5 | import com.google.common.base.Joiner; 6 | import org.eclipse.mat.SnapshotException; 7 | import org.eclipse.mat.snapshot.ISnapshot; 8 | import org.eclipse.mat.snapshot.SnapshotFactory; 9 | import org.eclipse.mat.util.VoidProgressListener; 10 | import org.junit.Assert; 11 | 12 | import java.io.File; 13 | import java.sql.Connection; 14 | import java.sql.PreparedStatement; 15 | import java.sql.ResultSet; 16 | import java.sql.ResultSetMetaData; 17 | import java.sql.SQLException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | public abstract class AbstractQueriesTests { 22 | 23 | protected abstract ISnapshot getSnapshot(); 24 | 25 | protected static ISnapshot openSnapshot(File heapDump) throws SnapshotException { 26 | System.out.println("exists = " + heapDump.exists() + ", file = " + heapDump.getAbsolutePath()); 27 | return SnapshotFactory.openSnapshot(heapDump, new VoidProgressListener()); 28 | } 29 | 30 | public static void closeSnapshot(ISnapshot snapshot) { 31 | SnapshotFactory.dispose(snapshot); 32 | } 33 | 34 | protected void returnsInOrder(String sql, String... expected) throws SQLException { 35 | String[] actuals = new String[0]; 36 | try { 37 | actuals = executeToCSV(sql).toArray(actuals); 38 | } catch (SQLException e) { 39 | e.printStackTrace(); // tycho-surefire-plugin forces trimStackTrace=true 40 | } 41 | System.out.println("Arrays.toString(expected) = " + String.join("\n", expected)); 42 | System.out.println("Arrays.toString(actuals) = " + String.join("\n", actuals)); 43 | Assert.assertArrayEquals(sql, nnl(expected), nnl(actuals)); 44 | } 45 | 46 | protected List executeToCSV(String sql) throws SQLException { 47 | List res = new ArrayList<>(); 48 | System.out.println("sql = " + sql); 49 | try (Connection con = CalciteDataSource.getConnection(getSnapshot())) { 50 | PreparedStatement ps = con.prepareStatement(sql); 51 | ResultSet rs = ps.executeQuery(); 52 | ResultSetMetaData md = rs.getMetaData(); 53 | Joiner joiner = Joiner.on('|'); 54 | List row = new ArrayList<>(); 55 | final int columnCount = md.getColumnCount(); 56 | for (int i = 1; i <= columnCount; i++) { 57 | row.add(md.getColumnName(i)); 58 | } 59 | res.add(joiner.join(row)); 60 | while (rs.next()) { 61 | row.clear(); 62 | for (int i = 1; i <= columnCount; i++) { 63 | row.add(String.valueOf(rs.getObject(i))); 64 | } 65 | res.add(joiner.join(row)); 66 | } 67 | } catch (SQLException e) { 68 | e.printStackTrace(); // tycho-surefire-plugin forces trimStackTrace=true 69 | throw e; 70 | } 71 | return res; 72 | } 73 | 74 | protected void execute(String sql, int limit) throws SQLException { 75 | System.out.println("sql = " + sql); 76 | 77 | try (Connection con = CalciteDataSource.getConnection(getSnapshot()); 78 | PreparedStatement ps = con.prepareStatement(sql); 79 | ResultSet rs = ps.executeQuery(); 80 | ) { 81 | ResultSetMetaData md = rs.getMetaData(); 82 | for (int j = 0; rs.next() && j < limit; j++) { 83 | for (int i = 1; i <= md.getColumnCount(); i++) { 84 | System.out.println(md.getColumnName(i) + ": " + rs.getObject(i)); 85 | } 86 | System.out.println(); 87 | } 88 | } catch (SQLException e) { 89 | e.printStackTrace(); // tycho-surefire-plugin forces trimStackTrace=true 90 | throw e; 91 | } 92 | } 93 | 94 | protected static String nnl(String text) { 95 | return text.replace("\r\n", "\n").replace("\r", "\n"); 96 | } 97 | 98 | protected static Object[] nnl(Object[] array) { 99 | for (int i = 0; i < array.length; i++) { 100 | if (array[i] instanceof String) { 101 | array[i] = nnl((String) array[i]); 102 | } 103 | } 104 | return array; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/SnapshotFunctions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | import com.github.vlsi.mat.calcite.SnapshotHolder; 5 | 6 | import com.google.common.collect.ImmutableMultimap; 7 | import com.google.common.collect.Multimap; 8 | import org.apache.calcite.adapter.enumerable.CallImplementor; 9 | import org.apache.calcite.adapter.enumerable.NotNullImplementor; 10 | import org.apache.calcite.adapter.enumerable.NullPolicy; 11 | import org.apache.calcite.adapter.enumerable.RexImpTable; 12 | import org.apache.calcite.adapter.enumerable.RexToLixTranslator; 13 | import org.apache.calcite.linq4j.tree.Expression; 14 | import org.apache.calcite.linq4j.tree.Expressions; 15 | import org.apache.calcite.rel.type.RelDataType; 16 | import org.apache.calcite.rel.type.RelDataTypeFactory; 17 | import org.apache.calcite.rex.RexCall; 18 | import org.apache.calcite.schema.Function; 19 | import org.apache.calcite.schema.FunctionParameter; 20 | import org.apache.calcite.schema.ImplementableFunction; 21 | import org.apache.calcite.schema.ScalarFunction; 22 | import org.apache.calcite.schema.impl.ReflectiveFunctionBase; 23 | import org.eclipse.mat.snapshot.ISnapshot; 24 | 25 | import java.lang.reflect.Constructor; 26 | import java.lang.reflect.Method; 27 | import java.util.List; 28 | 29 | public class SnapshotFunctions { 30 | private final ISnapshot snapshot; 31 | 32 | public SnapshotFunctions(int snapshotId) { 33 | snapshot = SnapshotHolder.get(snapshotId); 34 | } 35 | 36 | @SuppressWarnings("unused") 37 | public HeapReference getReference(String address) { 38 | return HeapFunctionsBase.resolveReference(snapshot, Long.decode(address)); 39 | } 40 | 41 | public static Multimap createAll(ISnapshot snapshot) { 42 | ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); 43 | builder.put("getReference", getFunction(snapshot, "getReference", String.class)); 44 | return builder.build(); 45 | } 46 | 47 | private static Function getFunction(ISnapshot snapshot, String name, Class ... argumentClasses) { 48 | return new SnapshotFunction(snapshot, getMethod(SnapshotFunctions.class, name, argumentClasses)); 49 | } 50 | 51 | private static Method getMethod(Class cls, String name, Class ... argumentClasses) { 52 | try { 53 | return cls.getMethod(name, argumentClasses); 54 | } catch (NoSuchMethodException e) { 55 | throw new RuntimeException(e); 56 | } 57 | } 58 | 59 | private static Constructor getConstructor(Class cls, Class ... argumentClasses) { 60 | try { 61 | return cls.getConstructor(argumentClasses); 62 | } catch (NoSuchMethodException e) { 63 | throw new RuntimeException(e); 64 | } 65 | } 66 | 67 | private static class SnapshotFunction implements ScalarFunction, ImplementableFunction, NotNullImplementor { 68 | private final ISnapshot snapshot; 69 | private final Method functionMethod; 70 | 71 | public SnapshotFunction(ISnapshot snapshot, Method functionMethod) { 72 | this.snapshot = snapshot; 73 | this.functionMethod = functionMethod; 74 | } 75 | 76 | @Override 77 | public CallImplementor getImplementor() { 78 | return RexImpTable.createImplementor(this, NullPolicy.NONE, false); 79 | } 80 | 81 | @Override 82 | public Expression implement(RexToLixTranslator rexToLixTranslator, RexCall rexCall, List operands) { 83 | int snapshotId = SnapshotHolder.put(snapshot); 84 | 85 | return Expressions.call( 86 | Expressions.new_(getConstructor(SnapshotFunctions.class, Integer.TYPE), Expressions.constant(snapshotId, 87 | Integer.TYPE)), 88 | functionMethod, operands); 89 | } 90 | 91 | @Override 92 | public RelDataType getReturnType(RelDataTypeFactory typeFactory) { 93 | return typeFactory.createJavaType(functionMethod.getReturnType()); 94 | } 95 | 96 | @Override 97 | public List getParameters() { 98 | return ReflectiveFunctionBase.builder().addMethodParameters(functionMethod).build(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/RowSetTable.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite; 2 | 3 | import org.eclipse.mat.query.Column; 4 | import org.eclipse.mat.query.ContextProvider; 5 | import org.eclipse.mat.query.IContextObject; 6 | import org.eclipse.mat.query.IResultTable; 7 | import org.eclipse.mat.query.ResultMetaData; 8 | 9 | import java.sql.ResultSetMetaData; 10 | import java.sql.SQLException; 11 | import java.sql.Types; 12 | 13 | import javax.sql.rowset.CachedRowSet; 14 | 15 | public class RowSetTable implements IResultTable { 16 | 17 | private final ResultMetaData metaData; 18 | private final CachedRowSet rowSet; 19 | Column[] columns; 20 | int idColumnPosition = -1; 21 | 22 | public RowSetTable(CachedRowSet rowSet) throws SQLException { 23 | this.rowSet = rowSet; 24 | ResultSetMetaData md = rowSet.getMetaData(); 25 | 26 | Column[] columns = new Column[md.getColumnCount()]; 27 | 28 | ResultMetaData.Builder mdBuilder = new ResultMetaData.Builder(); 29 | for (int i = 0; i < columns.length; i++) { 30 | String className = md.getColumnClassName(i + 1); 31 | Class clazz; 32 | try { 33 | clazz = Class.forName(className); 34 | } catch (ClassNotFoundException e) { 35 | clazz = String.class; 36 | } 37 | String columnName = md.getColumnName(i + 1); 38 | columns[i] = new Column(columnName, clazz); 39 | if (md.getColumnType(i + 1) == Types.JAVA_OBJECT) { 40 | // Most likely a HeapReference 41 | final int columnPosition = i; 42 | String tableName = md.getTableName(i + 1); 43 | final String label; 44 | if (tableName == null || tableName.isEmpty()) { 45 | label = columnName; 46 | } else { 47 | label = tableName + "." + columnName; 48 | } 49 | mdBuilder.addContext(new ContextProvider(label) { 50 | @Override 51 | public IContextObject getContext(Object row) { 52 | return RowSetTable.getContext(row, columnPosition); 53 | } 54 | }); 55 | if (idColumnPosition == -1) { 56 | // Use first object column as context provider (e.g. in case "this" column is missing) 57 | idColumnPosition = i; 58 | } 59 | } 60 | if (idColumnPosition == -1 && "this".equals(columns[i].getLabel())) { 61 | idColumnPosition = i; 62 | } 63 | } 64 | this.metaData = mdBuilder.build(); 65 | this.columns = columns; 66 | } 67 | 68 | @Override 69 | public ResultMetaData getResultMetaData() { 70 | return metaData; 71 | } 72 | 73 | @Override 74 | public Object getColumnValue(Object row, int columnIndex) { 75 | if (row == null) { 76 | return "null"; 77 | } 78 | return ((Object[]) row)[columnIndex]; 79 | } 80 | 81 | @Override 82 | public Column[] getColumns() { 83 | return columns; 84 | } 85 | 86 | @Override 87 | public IContextObject getContext(final Object row) { 88 | if (idColumnPosition == -1) { 89 | return null; 90 | } 91 | return getContext(row, idColumnPosition); 92 | } 93 | 94 | private static IContextObject getContext(final Object row, final int columnPosition) { 95 | if (!(row instanceof Object[])) { 96 | return null; 97 | } 98 | final Object[] data = (Object[]) row; 99 | if (columnPosition >= data.length) { 100 | return null; 101 | } 102 | final Object ref = data[columnPosition]; 103 | if (!(ref instanceof HeapReference)) { 104 | return null; 105 | } 106 | return () -> ((HeapReference) ref).getIObject().getObjectId(); 107 | } 108 | 109 | @Override 110 | public Object getRow(int rowId) { 111 | try { 112 | rowSet.absolute(rowId + 1); 113 | } catch (SQLException e1) { 114 | e1.printStackTrace(); 115 | return null; 116 | } 117 | Object[] row = new Object[columns.length]; 118 | for (int i = 0; i < row.length; i++) { 119 | try { 120 | row[i] = rowSet.getObject(i + 1); 121 | } catch (SQLException e) { 122 | e.printStackTrace(); 123 | } 124 | } 125 | return row; 126 | } 127 | 128 | @Override 129 | public int getRowCount() { 130 | System.out.println("size: " + rowSet.size()); 131 | return rowSet.size(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /MatCalciteTest/src/com/github/vlsi/mat/tests/calcite/CollectionsFunctionsTests.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.tests.calcite; 2 | 3 | import org.junit.Test; 4 | 5 | import java.sql.SQLException; 6 | 7 | public class CollectionsFunctionsTests extends SampleHeapDumpTests { 8 | // asMap 9 | 10 | @Test 11 | public void testAsMap() throws SQLException { 12 | returnsInOrder("select getField(asMap(m.this)['org.codehaus.plexus.classworlds'],'pkgName') pkgName from java" + 13 | ".util.HashMap m where m.size = 4", 14 | "pkgName", "org.codehaus.plexus.classworlds"); 15 | } 16 | 17 | // asMultiSet 18 | 19 | @Test 20 | public void testAsMultiset() throws SQLException { 21 | returnsInOrder("select n from (\n" + 22 | "select \n" + 23 | " toString(getField(r.this, 'name')) n\n" + 24 | "from \n" + 25 | " java.util.logging.LogManager lm,\n" + 26 | " unnest(asMultiSet(lm.rootLogger['kids'])) r(this)\n" + 27 | ") order by n", 28 | "n", 29 | "com.google.inject.internal.util.Stopwatch", 30 | "global"); 31 | } 32 | 33 | // asArray 34 | 35 | @Test 36 | public void testAsArray() throws SQLException { 37 | returnsInOrder("select \n" + 38 | " g.index,\n" + 39 | " toString(g.threadName) threadName\n" + 40 | "from \n" + 41 | " java.lang.ThreadGroup tg,\n" + 42 | " unnest(asArray(tg.threads)) with ordinality g(threadName, index)\n" + 43 | "where \n" + 44 | " toString(tg.name)='system'", 45 | "index|threadName", 46 | "1|Reference Handler", 47 | "2|Finalizer", 48 | "3|Signal Dispatcher", 49 | "4|null"); 50 | } 51 | 52 | // asByteArray 53 | 54 | @Test 55 | public void testAsByteArray() throws SQLException { 56 | returnsInOrder("select asByteArray(ps.\"out\"['buf'])[1] b from java.io.PrintStream ps", 57 | "b", "0", "0"); 58 | } 59 | 60 | // asShortArray 61 | 62 | @Test 63 | public void testAsShortArray() throws SQLException { 64 | // Test dump has no short[] objects, so we just check that function could be invoked 65 | returnsInOrder("select cardinality(asShortArray(o.this)) m from java.lang.Object o limit 1", 66 | "m", "0"); 67 | } 68 | 69 | // asIntArray 70 | 71 | @Test 72 | public void testAsIntArray() throws SQLException { 73 | returnsInOrder("select sum(cs.v) s from java.util.GregorianCalendar c, unnest(asIntArray(c.stamp)) cs(v)", 74 | "s", "68"); 75 | } 76 | 77 | // asLongArray 78 | 79 | @Test 80 | public void testAsLongArray() throws SQLException { 81 | returnsInOrder("select asLongArray(m.this)[14] c from \"long[]\" m where cardinality(asLongArray(m.this)) = 16", 82 | "c", "1388527200000"); 83 | } 84 | 85 | // asBooleanArray 86 | 87 | @Test 88 | public void testAsBooleanArray() throws SQLException { 89 | returnsInOrder("select min(case when v.this then 1 else 0 end) minv, max(case when v.this then 1 else 0 end) maxv" + 90 | " from \"boolean[]\" b, unnest(asBooleanArray(b.this)) v(this)", 91 | "minv|maxv","0|1"); 92 | } 93 | 94 | // asCharArray 95 | 96 | @Test 97 | public void testAsCharArray() throws SQLException { 98 | returnsInOrder("select listagg(toString(v.this),'') ns from java.lang.String s, unnest(asCharArray(s.\"value\")) " + 99 | "v(this) where toString(s.this) = 'com.google.common.collect.Maps'", 100 | "ns","com.google.common.collect.Maps"); 101 | } 102 | 103 | // asFloatArray 104 | 105 | @Test 106 | public void testAsFloatArray() throws SQLException { 107 | // Test dump has no float[] objects, so we just check that function could be invoked 108 | returnsInOrder("select cardinality(asFloatArray(o.this)) m from java.lang.Object o limit 1", 109 | "m", "0"); 110 | } 111 | 112 | // asDoubleArray 113 | 114 | @Test 115 | public void testAsDoubleArray() throws SQLException { 116 | // Test dump has no double[] objects, so we just check that function could be invoked 117 | returnsInOrder("select cardinality(asDoubleArray(o.this)) m from java.lang.Object o limit 1", 118 | "m", "0"); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/neo/SnapshotThreadStacksTable.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.neo; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | import org.apache.calcite.DataContext; 7 | import org.apache.calcite.linq4j.AbstractEnumerable; 8 | import org.apache.calcite.linq4j.Enumerable; 9 | import org.apache.calcite.linq4j.Enumerator; 10 | import org.apache.calcite.rel.type.RelDataType; 11 | import org.apache.calcite.rel.type.RelDataTypeFactory; 12 | import org.apache.calcite.schema.ScannableTable; 13 | import org.apache.calcite.schema.Statistic; 14 | import org.apache.calcite.schema.Statistics; 15 | import org.apache.calcite.schema.impl.AbstractTable; 16 | import org.apache.calcite.sql.type.SqlTypeName; 17 | import org.apache.calcite.util.ImmutableBitSet; 18 | import org.eclipse.mat.SnapshotException; 19 | import org.eclipse.mat.snapshot.ISnapshot; 20 | import org.eclipse.mat.snapshot.model.IClass; 21 | import org.eclipse.mat.snapshot.model.IStackFrame; 22 | import org.eclipse.mat.snapshot.model.IThreadStack; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class SnapshotThreadStacksTable extends AbstractTable implements ScannableTable { 28 | private final ISnapshot snapshot; 29 | 30 | public SnapshotThreadStacksTable(ISnapshot snapshot) { 31 | this.snapshot = snapshot; 32 | } 33 | 34 | @Override 35 | public Statistic getStatistic() { 36 | int counter = 0; 37 | for (IThreadStack threadStack : getThreadStacks()) { 38 | counter += threadStack.getStackFrames().length; 39 | } 40 | return Statistics.of(counter, ImmutableList.of(ImmutableBitSet.of(0, 1))); 41 | } 42 | 43 | @Override 44 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 45 | RelDataTypeFactory.Builder builder = typeFactory.builder(); 46 | RelDataType anyType = typeFactory.createSqlType(SqlTypeName.ANY); 47 | builder.add("thread", anyType); // non-null 48 | builder.add("depth", typeFactory.createJavaType(int.class)); 49 | builder.add("text", typeFactory.createTypeWithNullability( 50 | typeFactory.createJavaType(String.class), true)); 51 | builder.add("objects", typeFactory.createTypeWithNullability( 52 | typeFactory.createMultisetType(anyType, -1), true)); 53 | return builder.build(); 54 | } 55 | 56 | @Override 57 | public Enumerable scan(DataContext dataContext) { 58 | return new AbstractEnumerable() { 59 | @Override 60 | public Enumerator enumerator() { 61 | return new StackFramesEnumerator(snapshot, getThreadStacks()); 62 | } 63 | }; 64 | } 65 | 66 | private IThreadStack[] getThreadStacks() { 67 | try { 68 | List threadStacks = new ArrayList<>(); 69 | for (IClass threadClass : snapshot.getClassesByName("java.lang.Thread", true)) { 70 | if (threadClass.getNumberOfObjects() > 0) { 71 | for (int threadObject : threadClass.getObjectIds()) { 72 | IThreadStack threadStack = snapshot.getThreadStack(threadObject); 73 | if (threadStack != null) { 74 | threadStacks.add(threadStack); 75 | } 76 | } 77 | } 78 | } 79 | return threadStacks.toArray(new IThreadStack[0]); 80 | } catch (SnapshotException e) { 81 | throw new RuntimeException(e); 82 | } 83 | } 84 | 85 | private static class StackFramesEnumerator extends GroupEnumerator { 86 | private final ISnapshot snapshot; 87 | 88 | public StackFramesEnumerator(ISnapshot snapshot, IThreadStack[] groups) { 89 | super(groups); 90 | this.snapshot = snapshot; 91 | } 92 | 93 | @Override 94 | protected IStackFrame[] resolveGroup(IThreadStack group) throws Exception { 95 | return group.getStackFrames(); 96 | } 97 | 98 | @Override 99 | protected int rowsCount(IStackFrame[] rows) { 100 | return rows.length; 101 | } 102 | 103 | @Override 104 | protected Object[] resolveRow(IThreadStack group, IStackFrame[] rows, int currentRow) throws Exception { 105 | Object[] result = new Object[4]; 106 | IStackFrame frame = rows[currentRow]; 107 | result[0] = HeapReference.valueOf(snapshot.getObject(group.getThreadId())); 108 | result[1] = currentRow; 109 | result[2] = frame.getText(); 110 | List objects = new ArrayList<>(); 111 | for (int objectId : frame.getLocalObjectsIds()) { 112 | objects.add(HeapReference.valueOf(snapshot.getObject(objectId))); 113 | } 114 | result[3] = objects; 115 | return result; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/collections/CompactHashMapCollectionExtractor.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.collections; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.inspections.collectionextract.IMapExtractor; 5 | import org.eclipse.mat.snapshot.ISnapshot; 6 | import org.eclipse.mat.snapshot.model.IObject; 7 | import org.eclipse.mat.snapshot.model.IObjectArray; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashSet; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Map.Entry; 15 | 16 | public class CompactHashMapCollectionExtractor implements IMapExtractor { 17 | @Override 18 | public boolean hasSize() { 19 | return true; 20 | } 21 | 22 | @Override 23 | public Integer getSize(IObject iObject) throws SnapshotException { 24 | // TODO more efficient 25 | int size = 0; 26 | for (Iterator> it = extractMapEntries(iObject); it.hasNext(); it.next()) { 27 | size++; 28 | } 29 | return size; 30 | } 31 | 32 | @Override 33 | public boolean hasExtractableContents() { 34 | return true; 35 | } 36 | 37 | @Override 38 | public Iterator> extractMapEntries(IObject iObject) throws SnapshotException { 39 | List> result = new ArrayList<>(); 40 | 41 | ISnapshot snapshot = iObject.getSnapshot(); 42 | IObject v1 = (IObject) iObject.resolveValue("v1"); 43 | IObject v2 = (IObject) iObject.resolveValue("v2"); 44 | IObject v3 = (IObject) iObject.resolveValue("v3"); 45 | HashSet explicitNames = new HashSet<>(); // Keys explicitly set in key2slot map 46 | 47 | IObject mapKlass = (IObject) iObject.resolveValue("klass"); 48 | for (Entry entry : CollectionsActions.extractMap((IObject) mapKlass.resolveValue("key2slot"))) { 49 | IObject key = entry.getKey(); 50 | IObject value; 51 | 52 | int slot = (Integer) entry.getValue().resolveValue("value"); 53 | switch (slot) { 54 | case -1: 55 | value = v1; 56 | break; 57 | case -2: 58 | value = v2; 59 | break; 60 | case -3: 61 | value = v3; 62 | break; 63 | default: 64 | value = getObject(snapshot, ((IObjectArray) v1).getReferenceArray()[slot]); 65 | } 66 | 67 | result.add(new IObjectsPair(key, value)); 68 | explicitNames.add(toString(key)); 69 | } 70 | 71 | // This is not entirely correct, as we are comparing String representation of the keys instead of real keys, 72 | // but it's best what we can do here 73 | if (getClassName(mapKlass).equals("vlsi.utils.CompactHashMapClassWithDefaults")) { 74 | for (Entry entry : CollectionsActions.extractMap((IObject) mapKlass.resolveValue( 75 | "defaultValues"))) { 76 | IObject key = entry.getKey(); 77 | IObject value = entry.getValue(); 78 | 79 | if (!explicitNames.contains(toString(key))) { 80 | result.add(new IObjectsPair(key, value)); 81 | } 82 | } 83 | } 84 | 85 | return result.iterator(); 86 | } 87 | 88 | // Internal 89 | 90 | private String getClassName(IObject obj) { 91 | return obj.getClazz().getName(); 92 | } 93 | 94 | private IObject getObject(ISnapshot snapshot, long address) throws SnapshotException { 95 | return address == 0 ? null : snapshot.getObject(snapshot.mapAddressToId(address)); 96 | } 97 | 98 | private String toString(IObject object) { 99 | String name = object.getClassSpecificName(); 100 | return name != null ? name : object.getDisplayName(); 101 | } 102 | 103 | // Not implemented 104 | 105 | @Override 106 | public boolean hasCollisionRatio() { 107 | return false; 108 | } 109 | 110 | @Override 111 | public Double getCollisionRatio(IObject iObject) throws SnapshotException { 112 | return null; 113 | } 114 | 115 | @Override 116 | public boolean hasCapacity() { 117 | return false; 118 | } 119 | 120 | @Override 121 | public Integer getCapacity(IObject iObject) throws SnapshotException { 122 | return null; 123 | } 124 | 125 | @Override 126 | public boolean hasFillRatio() { 127 | return false; 128 | } 129 | 130 | @Override 131 | public Double getFillRatio(IObject iObject) throws SnapshotException { 132 | return null; 133 | } 134 | 135 | @Override 136 | public int[] extractEntryIds(IObject iObject) throws SnapshotException { 137 | return new int[0]; 138 | } 139 | 140 | @Override 141 | public boolean hasExtractableArray() { 142 | return false; 143 | } 144 | 145 | @Override 146 | public IObjectArray extractEntries(IObject iObject) throws SnapshotException { 147 | return null; 148 | } 149 | 150 | @Override 151 | public Integer getNumberOfNotNullElements(IObject iObject) throws SnapshotException { 152 | return null; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/HeapOperatorTable.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | import com.github.vlsi.mat.calcite.functions.IClassMethods; 5 | import com.github.vlsi.mat.calcite.functions.IObjectMethods; 6 | import com.github.vlsi.mat.calcite.functions.ISnapshotMethods; 7 | 8 | import com.google.common.collect.ImmutableList; 9 | import org.apache.calcite.schema.impl.ScalarFunctionImpl; 10 | import org.apache.calcite.sql.SqlFunction; 11 | import org.apache.calcite.sql.SqlIdentifier; 12 | import org.apache.calcite.sql.SqlKind; 13 | import org.apache.calcite.sql.parser.SqlParserPos; 14 | import org.apache.calcite.sql.type.OperandTypes; 15 | import org.apache.calcite.sql.type.ReturnTypes; 16 | import org.apache.calcite.sql.type.SqlTypeFamily; 17 | import org.apache.calcite.sql.validate.SqlUserDefinedFunction; 18 | import org.eclipse.mat.snapshot.ISnapshot; 19 | import org.eclipse.mat.snapshot.model.IClass; 20 | import org.eclipse.mat.snapshot.model.IObject; 21 | 22 | public interface HeapOperatorTable { 23 | // IObject 24 | SqlFunction TO_HEAP_REFERENCE = new SqlUserDefinedFunction( 25 | new SqlIdentifier("TO_HEAP_REFERENCE", SqlParserPos.ZERO), 26 | SqlKind.OTHER_FUNCTION, 27 | ReturnTypes.explicit(tf -> 28 | tf.createTypeWithNullability(tf.createJavaType(HeapReference.class), true)), 29 | null, 30 | OperandTypes.operandMetadata( 31 | ImmutableList.of(SqlTypeFamily.ANY), 32 | tf -> ImmutableList.of( 33 | tf.createTypeWithNullability(tf.createJavaType(Object.class), true)), 34 | i -> "iobject", 35 | i -> false), 36 | ScalarFunctionImpl.create(IObjectMethods.class, "toHeapReference")); 37 | 38 | SqlFunction RESOLVE_VALUE = new SqlUserDefinedFunction( 39 | new SqlIdentifier("RESOLVE_VALUE", SqlParserPos.ZERO), 40 | SqlKind.OTHER_FUNCTION, 41 | ReturnTypes.explicit(typeFactory -> typeFactory.createJavaType(Object.class)), 42 | null, 43 | OperandTypes.operandMetadata( 44 | ImmutableList.of(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER), 45 | tf -> ImmutableList.of( 46 | tf.createTypeWithNullability(tf.createJavaType(IObject.class), false), 47 | tf.createTypeWithNullability(tf.createJavaType(String.class), false)), 48 | i -> i == 0 ? "iobject" : "fieldName", 49 | i -> false), 50 | ScalarFunctionImpl.create(IObjectMethods.class, "resolveSimpleValue")); 51 | 52 | SqlFunction GET_CLASS_OF = new SqlUserDefinedFunction( 53 | new SqlIdentifier("GET_CLASS_OF", SqlParserPos.ZERO), 54 | SqlKind.OTHER_FUNCTION, 55 | ReturnTypes.explicit(tf -> 56 | tf.createTypeWithNullability(tf.createJavaType(IClass.class), true)), 57 | null, 58 | OperandTypes.operandMetadata( 59 | ImmutableList.of(SqlTypeFamily.ANY), 60 | tf -> ImmutableList.of( 61 | tf.createTypeWithNullability(tf.createJavaType(ISnapshot.class), false), 62 | tf.createTypeWithNullability(tf.createJavaType(int.class), false)), 63 | i -> i == 0 ? "snapshot" : "id", 64 | i -> false), 65 | ScalarFunctionImpl.create(ISnapshotMethods.class, "getClassOf")); 66 | 67 | // IClass 68 | SqlFunction GET_SUPER = new SqlUserDefinedFunction( 69 | new SqlIdentifier("GET_SUPER", SqlParserPos.ZERO), 70 | SqlKind.OTHER_FUNCTION, 71 | ReturnTypes.explicit(tf -> 72 | tf.createTypeWithNullability(tf.createJavaType(IClass.class), true)), 73 | null, 74 | OperandTypes.operandMetadata( 75 | ImmutableList.of(SqlTypeFamily.ANY), 76 | tf -> ImmutableList.of( 77 | tf.createTypeWithNullability(tf.createJavaType(IObject.class), true)), 78 | i -> "iclass", 79 | i -> false), 80 | ScalarFunctionImpl.create(IClassMethods.class, "getSuper")); 81 | 82 | SqlFunction GET_CLASS_LOADER = new SqlUserDefinedFunction( 83 | new SqlIdentifier("GET_CLASS_LOADER", SqlParserPos.ZERO), 84 | SqlKind.OTHER_FUNCTION, 85 | ReturnTypes.explicit(tf -> 86 | tf.createTypeWithNullability(tf.createJavaType(IObject.class), true)), 87 | null, 88 | OperandTypes.operandMetadata( 89 | ImmutableList.of(SqlTypeFamily.ANY), 90 | tf -> ImmutableList.of( 91 | tf.createTypeWithNullability(tf.createJavaType(IObject.class), false)), 92 | i -> "iclass", 93 | i -> false), 94 | ScalarFunctionImpl.create(IClassMethods.class, "getClassLoader")); 95 | 96 | SqlFunction GET_CLASS_NAME = new SqlUserDefinedFunction( 97 | new SqlIdentifier("GET_CLASS_NAME", SqlParserPos.ZERO), 98 | SqlKind.OTHER_FUNCTION, 99 | ReturnTypes.explicit(tf -> 100 | tf.createTypeWithNullability(tf.createJavaType(String.class), true)), 101 | null, 102 | OperandTypes.operandMetadata( 103 | ImmutableList.of(SqlTypeFamily.ANY), 104 | tf -> ImmutableList.of( 105 | tf.createTypeWithNullability(tf.createJavaType(IObject.class), true)), 106 | i -> "iclass", 107 | i -> false), 108 | ScalarFunctionImpl.create(IClassMethods.class, "getClassName")); 109 | } 110 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/collections/DexxHashMapCollectionExtractor.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.collections; 2 | 3 | import org.eclipse.mat.SnapshotException; 4 | import org.eclipse.mat.inspections.collectionextract.IMapExtractor; 5 | import org.eclipse.mat.snapshot.ISnapshot; 6 | import org.eclipse.mat.snapshot.model.IObject; 7 | import org.eclipse.mat.snapshot.model.IObjectArray; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class DexxHashMapCollectionExtractor implements IMapExtractor { 15 | 16 | @Override 17 | public boolean hasSize() { 18 | return true; 19 | } 20 | 21 | @Override 22 | public Integer getSize(IObject iObject) throws SnapshotException { 23 | IObject chmObj = getCHM(iObject); 24 | 25 | if (chmObj != null) { 26 | String typeName = getClassName(chmObj); 27 | if ("com.github.andrewoma.dexx.collection.internal.hashmap.HashMap1".equals(typeName)) { 28 | return 1; 29 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.CompactHashMap".equals(typeName)) { 30 | return 0; 31 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.HashMapCollision1".equals(typeName)) { 32 | int count = 0; 33 | IObject item = (IObject) chmObj.resolveValue("kvs"); 34 | while (getClassName(item).equals("com.github.andrewoma.dexx.collection.internal.hashmap.ListMap$Node")) { 35 | count++; 36 | item = (IObject) chmObj.resolveValue("this$0"); 37 | } 38 | return count; 39 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.HashTrieMap".equals(typeName)) { 40 | return (Integer) chmObj.resolveValue("size"); 41 | } 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public boolean hasExtractableContents() { 48 | return true; 49 | } 50 | 51 | @Override 52 | public Iterator> extractMapEntries(IObject iObject) throws SnapshotException { 53 | List> result = new ArrayList<>(); 54 | IObject chmObj = getCHM(iObject); 55 | if (chmObj != null) { 56 | extractPairs(chmObj, result); 57 | } 58 | return result.iterator(); 59 | } 60 | 61 | // Internal 62 | 63 | private String getClassName(IObject obj) { 64 | return obj.getClazz().getName(); 65 | } 66 | 67 | private IObject getCHM(IObject iObject) throws SnapshotException { 68 | Object obj = iObject.resolveValue("compactHashMap"); 69 | return obj instanceof IObject ? (IObject) obj : null; 70 | } 71 | 72 | private void extractPairs(IObject chmObj, List> result) throws SnapshotException { 73 | String typeName = getClassName(chmObj); 74 | if ("com.github.andrewoma.dexx.collection.Pair".equals(typeName)) { 75 | // Single entry 76 | IObject key = (IObject) chmObj.resolveValue("component1"); 77 | IObject value = (IObject) chmObj.resolveValue("component2"); 78 | result.add(new IObjectsPair(key, value)); 79 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.HashMap1".equals(typeName)) { 80 | // Single entry 81 | extractPairs((IObject) chmObj.resolveValue("value"), result); 82 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.HashMapCollision1".equals(typeName)) { 83 | // Multiple entries with hash collision - chain of nested Node classes in 'kvs' field 84 | extractPairs((IObject) chmObj.resolveValue("kvs"), result); 85 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.ListMap$Node".equals(typeName)) { 86 | // Get current value 87 | extractPairs((IObject) chmObj.resolveValue("value"), result); 88 | // Try to get next object in the list 89 | extractPairs((IObject) chmObj.resolveValue("this$0"), result); 90 | } else if ("com.github.andrewoma.dexx.collection.internal.hashmap.HashTrieMap".equals(typeName)) { 91 | // Multiple entries 92 | Object obj = chmObj.resolveValue("elems"); 93 | if (obj instanceof IObjectArray) { 94 | ISnapshot snapshot = chmObj.getSnapshot(); 95 | for (long elem : ((IObjectArray) obj).getReferenceArray()) { 96 | if (elem != 0) { 97 | extractPairs(snapshot.getObject(snapshot.mapAddressToId(elem)), result); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | // Not implemented 105 | 106 | @Override 107 | public boolean hasCollisionRatio() { 108 | return false; 109 | } 110 | 111 | @Override 112 | public Double getCollisionRatio(IObject iObject) throws SnapshotException { 113 | return null; 114 | } 115 | 116 | @Override 117 | public boolean hasCapacity() { 118 | return false; 119 | } 120 | 121 | @Override 122 | public Integer getCapacity(IObject iObject) throws SnapshotException { 123 | return null; 124 | } 125 | 126 | @Override 127 | public boolean hasFillRatio() { 128 | return false; 129 | } 130 | 131 | @Override 132 | public Double getFillRatio(IObject iObject) throws SnapshotException { 133 | return null; 134 | } 135 | 136 | @Override 137 | public int[] extractEntryIds(IObject iObject) throws SnapshotException { 138 | // TODO: return values? 139 | return new int[0]; 140 | } 141 | 142 | @Override 143 | public boolean hasExtractableArray() { 144 | return false; 145 | } 146 | 147 | @Override 148 | public IObjectArray extractEntries(IObject iObject) throws SnapshotException { 149 | return null; 150 | } 151 | 152 | @Override 153 | public Integer getNumberOfNotNullElements(IObject iObject) throws SnapshotException { 154 | return null; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/editor/CalciteContentAssistantProcessor.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.editor; 2 | 3 | import com.github.vlsi.mat.calcite.CalciteDataSource; 4 | 5 | import org.apache.calcite.DataContext; 6 | import org.apache.calcite.jdbc.CalciteConnection; 7 | import org.apache.calcite.jdbc.CalciteMetaImpl; 8 | import org.apache.calcite.sql.advise.SqlAdvisor; 9 | import org.apache.calcite.sql.validate.SqlMoniker; 10 | import org.apache.calcite.sql.validate.SqlMonikerType; 11 | import org.apache.calcite.util.Util; 12 | import org.eclipse.jface.text.ITextViewer; 13 | import org.eclipse.jface.text.contentassist.CompletionProposal; 14 | import org.eclipse.jface.text.contentassist.ContextInformationValidator; 15 | import org.eclipse.jface.text.contentassist.ICompletionProposal; 16 | import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 17 | import org.eclipse.jface.text.contentassist.IContextInformation; 18 | import org.eclipse.jface.text.contentassist.IContextInformationValidator; 19 | import org.eclipse.mat.query.IQueryContext; 20 | import org.eclipse.mat.snapshot.ISnapshot; 21 | import org.eclipse.mat.ui.editor.MultiPaneEditor; 22 | import org.eclipse.ui.IEditorPart; 23 | import org.eclipse.ui.IWorkbenchPage; 24 | import org.eclipse.ui.PlatformUI; 25 | 26 | import java.sql.Connection; 27 | import java.sql.PreparedStatement; 28 | import java.sql.ResultSet; 29 | import java.sql.SQLException; 30 | import java.util.ArrayList; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | 34 | public class CalciteContentAssistantProcessor implements IContentAssistProcessor { 35 | public static ISnapshot getSnapshot() { 36 | IWorkbenchPage page = PlatformUI.getWorkbench() 37 | .getActiveWorkbenchWindow().getActivePage(); 38 | IEditorPart part = page == null ? null : page.getActiveEditor(); 39 | 40 | if (!(part instanceof MultiPaneEditor)) { 41 | return null; 42 | } 43 | IQueryContext queryContext = ((MultiPaneEditor) part).getQueryContext(); 44 | ISnapshot snapshot = (ISnapshot) queryContext.get(ISnapshot.class, null); 45 | return snapshot; 46 | } 47 | 48 | @Override 49 | public ICompletionProposal[] computeCompletionProposals(ITextViewer iTextViewer, int offset) { 50 | ISnapshot snapshot = getSnapshot(); 51 | 52 | String sql = iTextViewer.getDocument().get(); 53 | 54 | try (Connection con = CalciteDataSource.getConnection(snapshot)) { 55 | CalciteConnection ccon = con.unwrap(CalciteConnection.class); 56 | if (false) { 57 | // This is an official API 58 | System.out.println("advisor: "); 59 | try (PreparedStatement ps = con.prepareStatement("select id, names, type from table(getHints(?, ?)) as t")) { 60 | ps.setString(1, sql); 61 | ps.setInt(2, offset); 62 | int cnt = 0; 63 | try (ResultSet rs = ps.executeQuery()) { 64 | System.out.println(); 65 | while (rs.next()) { 66 | System.out.println("rs = " + rs.getString(1) + ", " + rs.getString(2) + ", " + rs.getString(3)); 67 | cnt++; 68 | } 69 | } 70 | System.out.println(cnt + " items suggested"); 71 | } 72 | } 73 | 74 | // This is unofficial API, however it enables more methods of SqlAdvisor 75 | final DataContext dataContext = CalciteMetaImpl.createDataContext(ccon); 76 | SqlAdvisor advisor = DataContext.Variable.SQL_ADVISOR.get(dataContext); 77 | 78 | String[] replaced = new String[1]; 79 | List completionHints = advisor.getCompletionHints(sql, offset, replaced); 80 | String replacement = replaced[0]; 81 | 82 | List hints = new ArrayList<>(completionHints.size()); 83 | for (SqlMoniker hint : completionHints) { 84 | if (Util.last(hint.toIdentifier().names).startsWith("$")) { 85 | continue; 86 | } 87 | hints.add(hint); 88 | } 89 | 90 | hints.sort(new Comparator() { 91 | private int order(SqlMonikerType type) { 92 | switch (type) { 93 | case CATALOG: 94 | return 0; 95 | case SCHEMA: 96 | return 1; 97 | case TABLE: 98 | return 2; 99 | case VIEW: 100 | return 3; 101 | case COLUMN: 102 | return 4; 103 | case FUNCTION: 104 | return 5; 105 | case KEYWORD: 106 | return 6; 107 | case REPOSITORY: 108 | return 7; 109 | default: 110 | return 10; 111 | } 112 | } 113 | 114 | @Override 115 | public int compare(SqlMoniker o1, SqlMoniker o2) { 116 | int a = order(o1.getType()); 117 | int b = order(o2.getType()); 118 | if (a != b) { 119 | return a < b ? -1 : 1; 120 | } 121 | return o1.id().compareTo(o2.id()); 122 | } 123 | }); 124 | 125 | ICompletionProposal[] res = new ICompletionProposal[hints.size()]; 126 | for (int i = 0; i < hints.size(); i++) { 127 | SqlMoniker hint = hints.get(i); 128 | String hintStr = advisor.getReplacement(hint, replacement); 129 | res[i] = new CompletionProposal(hintStr, offset - replacement.length(), 130 | replacement.length(), hintStr.length(), 131 | null, hintStr + ", " + hint.getType().name(), null, ""); 132 | } 133 | return res; 134 | } catch (SQLException e) { 135 | return null; 136 | } 137 | } 138 | 139 | @Override 140 | public IContextInformation[] computeContextInformation(ITextViewer iTextViewer, int i) { 141 | return new IContextInformation[0]; 142 | } 143 | 144 | @Override 145 | public char[] getCompletionProposalAutoActivationCharacters() { 146 | return new char[]{'.', '"'}; 147 | } 148 | 149 | @Override 150 | public char[] getContextInformationAutoActivationCharacters() { 151 | return new char[]{' ', '(', '.'}; 152 | } 153 | 154 | @Override 155 | public String getErrorMessage() { 156 | return null; 157 | } 158 | 159 | @Override 160 | public IContextInformationValidator getContextInformationValidator() { 161 | return new ContextInformationValidator(this); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/neo/PackageSchema.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.neo; 2 | 3 | import com.github.vlsi.mat.calcite.functions.CollectionsFunctions; 4 | import com.github.vlsi.mat.calcite.functions.HeapFunctions; 5 | import com.github.vlsi.mat.calcite.functions.SnapshotFunctions; 6 | import com.github.vlsi.mat.calcite.functions.TableFunctions; 7 | import com.github.vlsi.mat.calcite.schema.objects.IClassesList; 8 | import com.github.vlsi.mat.calcite.schema.objects.InstanceByClassTable; 9 | import com.github.vlsi.mat.calcite.schema.objects.InstanceIdsByClassTable; 10 | 11 | import com.google.common.collect.ImmutableMap; 12 | import com.google.common.collect.ImmutableMultimap; 13 | import com.google.common.collect.Multimap; 14 | import org.apache.calcite.schema.Function; 15 | import org.apache.calcite.schema.Schema; 16 | import org.apache.calcite.schema.Table; 17 | import org.apache.calcite.schema.impl.AbstractSchema; 18 | import org.apache.calcite.schema.impl.ScalarFunctionImpl; 19 | import org.apache.calcite.sql.advise.SqlAdvisorGetHintsFunction; 20 | import org.eclipse.mat.SnapshotException; 21 | import org.eclipse.mat.snapshot.ISnapshot; 22 | import org.eclipse.mat.snapshot.model.IClass; 23 | 24 | import java.util.Collection; 25 | import java.util.Collections; 26 | import java.util.HashMap; 27 | import java.util.HashSet; 28 | import java.util.Map; 29 | 30 | public class PackageSchema extends AbstractSchema { 31 | private final Multimap functions; 32 | private final Map subPackages = new HashMap<>(); 33 | private final Map classes = new HashMap<>(); 34 | 35 | private PackageSchema() { 36 | this(ImmutableMultimap.of()); 37 | } 38 | 39 | private PackageSchema(Multimap functions) { 40 | this.functions = functions; 41 | } 42 | 43 | private PackageSchema getPackage(String subSchemaName) { 44 | 45 | PackageSchema subSchema = subPackages.get(subSchemaName); 46 | if (subSchema == null) { 47 | subSchema = new PackageSchema(); 48 | subPackages.put(subSchemaName, subSchema); 49 | } 50 | return subSchema; 51 | } 52 | 53 | private void addClass(String name, Table table) { 54 | if (!classes.containsKey(name)) { 55 | classes.put(name, table); 56 | } 57 | } 58 | 59 | private void addClass(String className, IClassesList classesList) { 60 | addClass(className, new InstanceByClassTable(classesList)); 61 | addClass("$ids$:" + className, new InstanceIdsByClassTable(classesList)); 62 | } 63 | 64 | @Override 65 | protected Map getSubSchemaMap() { 66 | return ImmutableMap.copyOf(subPackages); 67 | } 68 | 69 | @Override 70 | protected Map getTableMap() { 71 | return Collections.unmodifiableMap(classes); 72 | } 73 | 74 | @Override 75 | protected Multimap getFunctionMultimap() { 76 | return functions; 77 | } 78 | 79 | @Override 80 | public boolean isMutable() { 81 | return false; 82 | } 83 | 84 | private static String getClassName(final String fullClassName) { 85 | int lastDotIndex = fullClassName.lastIndexOf('.'); 86 | return lastDotIndex == -1 ? fullClassName : fullClassName.substring(lastDotIndex + 1); 87 | } 88 | 89 | private static PackageSchema getPackage(final PackageSchema rootPackage, final String fullClassName) { 90 | String[] nameParts = fullClassName.split("\\."); 91 | PackageSchema targetSchema = rootPackage; 92 | for (int i = 0; i < nameParts.length - 1; i++) { 93 | targetSchema = targetSchema.getPackage(nameParts[i]); 94 | } 95 | return targetSchema; 96 | } 97 | 98 | public static PackageSchema resolveSchema(ISnapshot snapshot) { 99 | 100 | try { 101 | // Create functions for schema 102 | ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); 103 | builder.putAll(ScalarFunctionImpl.functions(HeapFunctions.class)); 104 | builder.putAll(CollectionsFunctions.createAll()); 105 | builder.putAll(TableFunctions.createAll()); 106 | builder.putAll(SnapshotFunctions.createAll(snapshot)); 107 | builder.put("getHints", new SqlAdvisorGetHintsFunction()); 108 | ImmutableMultimap functions = builder.build(); 109 | 110 | // Create default schema 111 | PackageSchema defaultSchema = new PackageSchema(functions); 112 | 113 | // Collect all classes names 114 | Collection classes = snapshot.getClasses(); 115 | HashSet classesNames = new HashSet<>(); 116 | for (IClass iClass : classes) { 117 | classesNames.add(iClass.getName()); 118 | } 119 | 120 | PackageSchema instanceOfPackage = defaultSchema.getPackage("instanceof"); 121 | 122 | // Add all classes to schema 123 | for (String fullClassName : classesNames) { 124 | IClassesList classOnly = new IClassesList(snapshot, fullClassName, false); 125 | 126 | // Make class available via "package.name.ClassName" (full class name in a root schema) 127 | defaultSchema.addClass(fullClassName, classOnly); 128 | 129 | String simpleClassName = getClassName(fullClassName); 130 | 131 | // Make class available via package.name.ClassName (schema.schema.Class) 132 | PackageSchema packageSchema = getPackage(defaultSchema, fullClassName); 133 | packageSchema.addClass(simpleClassName, classOnly); 134 | 135 | // Add instanceof 136 | IClassesList withSubClasses = new IClassesList(snapshot, fullClassName, true); 137 | 138 | // Make class available via "instanceof.package.name.ClassName" 139 | defaultSchema.addClass("instanceof." + fullClassName, withSubClasses); 140 | 141 | // Make class available via instanceof.package.name.ClassName 142 | PackageSchema instanceOfSchema = getPackage(instanceOfPackage, fullClassName); 143 | instanceOfSchema.addClass(simpleClassName, withSubClasses); 144 | 145 | } 146 | 147 | // Add thread stacks table 148 | defaultSchema.getPackage("native").addClass("ThreadStackFrames", new SnapshotThreadStacksTable(snapshot)); 149 | 150 | return defaultSchema; 151 | } catch (SnapshotException e) { 152 | throw new RuntimeException("Cannot resolve package schemes", e); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /MatCalcitePlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | MatCalcitePlugin 7 | 1.6.4-SNAPSHOT 8 | eclipse-plugin 9 | Calcite SQL plugin 10 | 11 | 12 | com.github.vlsi.mat.calcite 13 | parent 14 | 1.6.4-SNAPSHOT 15 | 16 | 17 | 18 | 19 | net.minidev 20 | accessors-smart 21 | 2.6.0 22 | 23 | 24 | net.minidev 25 | json-smart 26 | 2.6.0 27 | 28 | 29 | org.apache.calcite 30 | calcite-core 31 | 1.41.0 32 | 33 | 34 | org.apache.commons 35 | commons-dbcp2 36 | 2.14.0 37 | 38 | 39 | com.google.guava 40 | guava 41 | 33.5.0-jre 42 | 43 | 44 | com.google.code.findbugs 45 | jsr305 46 | 3.0.2 47 | 48 | 49 | org.apache.geronimo.specs 50 | geronimo-jta_1.1_spec 51 | 1.1.1 52 | 53 | 54 | org.codehaus.janino 55 | commons-compiler 56 | 3.1.12 57 | 58 | 59 | org.codehaus.janino 60 | janino 61 | 3.1.12 62 | 63 | 64 | 65 | 66 | src 67 | 68 | 69 | 70 | resources 71 | resources 72 | 73 | 74 | 75 | 76 | 77 | org.apache.felix 78 | maven-bundle-plugin 79 | 6.0.0 80 | 81 | 82 | bundle-manifest 83 | process-classes 84 | 85 | manifest 86 | 87 | 88 | 89 | 90 | 91 | maven-dependency-plugin 92 | 93 | 94 | copy-dependencies 95 | 96 | copy-dependencies 97 | 98 | initialize 99 | 100 | com.github.vlsi.mat.calcite 101 | true 102 | 103 | 104 | 105 | classpath 106 | 107 | build-classpath 108 | 109 | 110 | / 111 | target/dependency 112 | ${project.build.directory}/classPath.txt 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.eclipse.tycho 120 | tycho-compiler-plugin 121 | ${tycho-version} 122 | 123 | true 124 | 125 | 126 | 127 | org.eclipse.tycho.extras 128 | tycho-eclipserun-plugin 129 | ${tycho-version} 130 | 131 | -product org.eclipse.mat.ui.rcp.MemoryAnalyzer 132 | 133 | 134 | Eclipse 135 | p2 136 | ${eclipse-repository} 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.vlsi.mat.calcite 7 | parent 8 | 1.6.4-SNAPSHOT 9 | pom 10 | MAT Calcite plugin parent 11 | 12 | 13 | 4.0.13 14 | https://download.eclipse.org/releases/2022-12 15 | UTF-8 16 | 17 17 | 17 18 | 19 | 20 | 21 | MatCalciteTargetPlatform 22 | MatCalcitePlugin 23 | MatCalciteFeature 24 | MatCalciteTest 25 | MatCalciteRepository 26 | 27 | 28 | 29 | 30 | Eclipse 31 | p2 32 | ${eclipse-repository} 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.eclipse.tycho 40 | tycho-maven-plugin 41 | ${tycho-version} 42 | true 43 | 44 | 45 | org.eclipse.tycho 46 | tycho-source-plugin 47 | ${tycho-version} 48 | 49 | 50 | plugin-source 51 | 52 | plugin-source 53 | 54 | 55 | 56 | feature-source 57 | 58 | feature-source 59 | 60 | 61 | 62 | 63 | 64 | org.eclipse.tycho 65 | tycho-p2-plugin 66 | ${tycho-version} 67 | 68 | 69 | attach-p2-metadata 70 | package 71 | 72 | p2-metadata 73 | 74 | 75 | 76 | 77 | 78 | org.eclipse.tycho 79 | tycho-packaging-plugin 80 | ${tycho-version} 81 | 82 | 83 | validate-pom 84 | verify 85 | 86 | verify-osgi-pom 87 | 88 | 89 | 90 | 91 | 92 | true 93 | 94 | true 95 | 96 | 97 | 98 | org.eclipse.tycho 99 | tycho-surefire-plugin 100 | ${tycho-version} 101 | 102 | -ea -Xmx768m -Dcalcite.debug=${calcite.debug} 103 | 104 | 105 | 106 | **/AllTests.class 107 | 108 | 109 | 110 | 111 | org.eclipse.tycho 112 | tycho-versions-plugin 113 | ${tycho-version} 114 | 115 | 116 | org.eclipse.tycho 117 | target-platform-configuration 118 | ${tycho-version} 119 | 120 | wrapAsBundle 121 | JavaSE-17 122 | 123 | 124 | com.github.vlsi.mat.calcite 125 | MatCalciteTargetPlatform 126 | ${project.version} 127 | 128 | 129 | 130 | 131 | win32 132 | win32 133 | x86_64 134 | 135 | 136 | linux 137 | gtk 138 | x86_64 139 | 140 | 141 | macosx 142 | cocoa 143 | x86_64 144 | 145 | 146 | macosx 147 | cocoa 148 | aarch64 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/CollectionsFunctions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | import com.github.vlsi.mat.calcite.collections.CollectionsActions; 5 | 6 | import com.google.common.collect.ImmutableMultimap; 7 | import com.google.common.collect.Multimap; 8 | import org.apache.calcite.adapter.enumerable.CallImplementor; 9 | import org.apache.calcite.adapter.enumerable.NullPolicy; 10 | import org.apache.calcite.adapter.enumerable.ReflectiveCallNotNullImplementor; 11 | import org.apache.calcite.adapter.enumerable.RexImpTable; 12 | import org.apache.calcite.rel.type.RelDataType; 13 | import org.apache.calcite.rel.type.RelDataTypeFactory; 14 | import org.apache.calcite.schema.ImplementableFunction; 15 | import org.apache.calcite.schema.ScalarFunction; 16 | import org.apache.calcite.schema.impl.ReflectiveFunctionBase; 17 | import org.eclipse.mat.SnapshotException; 18 | import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils; 19 | import org.eclipse.mat.inspections.collectionextract.ExtractedCollection; 20 | import org.eclipse.mat.inspections.collectionextract.ExtractedMap; 21 | import org.eclipse.mat.snapshot.ISnapshot; 22 | import org.eclipse.mat.snapshot.model.IArray; 23 | import org.eclipse.mat.snapshot.model.IObject; 24 | import org.eclipse.mat.snapshot.model.IObjectArray; 25 | import org.eclipse.mat.snapshot.model.IPrimitiveArray; 26 | 27 | import java.lang.reflect.Method; 28 | import java.util.ArrayList; 29 | import java.util.Collections; 30 | import java.util.HashMap; 31 | import java.util.List; 32 | import java.util.Map; 33 | 34 | public class CollectionsFunctions extends HeapFunctionsBase { 35 | 36 | public abstract static class BaseImplementableFunction extends ReflectiveFunctionBase implements ScalarFunction, 37 | ImplementableFunction { 38 | final CallImplementor implementor; 39 | 40 | BaseImplementableFunction(Method method) { 41 | super(method); 42 | implementor = RexImpTable.createImplementor(new ReflectiveCallNotNullImplementor(method), NullPolicy.NONE, false); 43 | } 44 | 45 | @Override 46 | public CallImplementor getImplementor() { 47 | return implementor; 48 | } 49 | } 50 | 51 | public static class MapFunction extends BaseImplementableFunction { 52 | MapFunction(Method method) { 53 | super(method); 54 | } 55 | 56 | @Override 57 | public RelDataType getReturnType(RelDataTypeFactory relDataTypeFactory) { 58 | return relDataTypeFactory.createMapType(relDataTypeFactory.createJavaType(String.class), 59 | relDataTypeFactory.createJavaType(HeapReference.class)); 60 | } 61 | } 62 | 63 | public static class MultiSetFunction extends BaseImplementableFunction { 64 | MultiSetFunction(Method method) { 65 | super(method); 66 | } 67 | 68 | @Override 69 | public RelDataType getReturnType(RelDataTypeFactory relDataTypeFactory) { 70 | return relDataTypeFactory.createMultisetType(relDataTypeFactory.createJavaType(HeapReference.class), -1); 71 | } 72 | } 73 | 74 | public static class ArrayFunction extends BaseImplementableFunction { 75 | private final Class elementType; 76 | 77 | ArrayFunction(Method method, Class elementType) { 78 | super(method); 79 | this.elementType = elementType; 80 | } 81 | 82 | @Override 83 | public RelDataType getReturnType(RelDataTypeFactory relDataTypeFactory) { 84 | return relDataTypeFactory.createArrayType(relDataTypeFactory.createJavaType(elementType), -1); 85 | } 86 | } 87 | 88 | public static Multimap createAll() { 89 | ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); 90 | builder.put("asMap", new MapFunction(findMethod(CollectionsFunctions.class, "asMap"))); 91 | builder.put("asMultiSet", new MultiSetFunction(findMethod(CollectionsFunctions.class, "asMultiSet"))); 92 | builder.put("asArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), HeapReference.class)); 93 | builder.put("asByteArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Byte.class)); 94 | builder.put("asShortArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Short.class)); 95 | builder.put("asIntArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Integer.class)); 96 | builder.put("asLongArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Long.class)); 97 | builder.put("asBooleanArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Boolean.class)); 98 | builder.put("asCharArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Character.class)); 99 | builder.put("asFloatArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Float.class)); 100 | builder.put("asDoubleArray", new ArrayFunction(findMethod(CollectionsFunctions.class, "asArray"), Double.class)); 101 | return builder.build(); 102 | } 103 | 104 | @SuppressWarnings("unused") 105 | public static Map asMap(Object r) { 106 | HeapReference ref = ensureHeapReference(r); 107 | if (ref == null) { 108 | return null; 109 | } 110 | 111 | try { 112 | ExtractedMap extractedMap = CollectionsActions.extractMap(ref.getIObject()); 113 | if (extractedMap == null) { 114 | return Collections.emptyMap(); 115 | } else { 116 | Map result = new HashMap<>(); 117 | for (Map.Entry entry : extractedMap) { 118 | result.put(toString(entry.getKey()), 119 | resolveReference(entry.getValue()) 120 | ); 121 | } 122 | return result; 123 | } 124 | } catch (SnapshotException e) { 125 | throw new RuntimeException("Unable to extract map from " + r, e); 126 | } 127 | } 128 | 129 | @SuppressWarnings("unused") 130 | public static List asMultiSet(Object r) { 131 | HeapReference ref = ensureHeapReference(r); 132 | if (ref == null) { 133 | return null; 134 | } 135 | 136 | try { 137 | ExtractedCollection extractedCollection = CollectionExtractionUtils.extractList(ref.getIObject()); 138 | if (extractedCollection == null) { 139 | return Collections.emptyList(); 140 | } else { 141 | List result = new ArrayList<>(); 142 | for (IObject entry : extractedCollection) { 143 | result.add((HeapReference) resolveReference(entry)); 144 | } 145 | return result; 146 | } 147 | } catch (SnapshotException e) { 148 | throw new RuntimeException("Unable to extract collection from " + r, e); 149 | } 150 | } 151 | 152 | @SuppressWarnings("unused") 153 | public static List asArray(Object r) { 154 | HeapReference ref = ensureHeapReference(r); 155 | if (ref == null) { 156 | return null; 157 | } 158 | 159 | IObject iObject = ref.getIObject(); 160 | if (!(iObject instanceof IArray)) { 161 | return null; 162 | } 163 | 164 | if (iObject instanceof IPrimitiveArray) { 165 | IPrimitiveArray arrayObject = (IPrimitiveArray) iObject; 166 | int length = arrayObject.getLength(); 167 | List result = new ArrayList<>(length); 168 | for (int i = 0; i < length; i++) { 169 | result.add(arrayObject.getValueAt(i)); 170 | } 171 | return result; 172 | } else { 173 | IObjectArray arrayObject = (IObjectArray) iObject; 174 | ISnapshot snapshot = arrayObject.getSnapshot(); 175 | int length = arrayObject.getLength(); 176 | List result = new ArrayList<>(length); 177 | for (long objectAddress : arrayObject.getReferenceArray()) { 178 | result.add(resolveReference(snapshot, objectAddress)); 179 | } 180 | return result; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.4 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | 82 | $MAVEN_M2_PATH = "$HOME/.m2" 83 | if ($env:MAVEN_USER_HOME) { 84 | $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" 85 | } 86 | 87 | if (-not (Test-Path -Path $MAVEN_M2_PATH)) { 88 | New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null 89 | } 90 | 91 | $MAVEN_WRAPPER_DISTS = $null 92 | if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { 93 | $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" 94 | } else { 95 | $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" 96 | } 97 | 98 | $MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" 99 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 100 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 101 | 102 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 103 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 104 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 105 | exit $? 106 | } 107 | 108 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 109 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 110 | } 111 | 112 | # prepare tmp dir 113 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 114 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 115 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 116 | trap { 117 | if ($TMP_DOWNLOAD_DIR.Exists) { 118 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 119 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 120 | } 121 | } 122 | 123 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 124 | 125 | # Download and Install Apache Maven 126 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 127 | Write-Verbose "Downloading from: $distributionUrl" 128 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 129 | 130 | $webclient = New-Object System.Net.WebClient 131 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 132 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 133 | } 134 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 135 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 136 | 137 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 138 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 139 | if ($distributionSha256Sum) { 140 | if ($USE_MVND) { 141 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 142 | } 143 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 144 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 145 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 146 | } 147 | } 148 | 149 | # unzip and move 150 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 151 | 152 | # Find the actual extracted directory name (handles snapshots where filename != directory name) 153 | $actualDistributionDir = "" 154 | 155 | # First try the expected directory name (for regular distributions) 156 | $expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" 157 | $expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" 158 | if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { 159 | $actualDistributionDir = $distributionUrlNameMain 160 | } 161 | 162 | # If not found, search for any directory with the Maven executable (for snapshots) 163 | if (!$actualDistributionDir) { 164 | Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { 165 | $testPath = Join-Path $_.FullName "bin/$MVN_CMD" 166 | if (Test-Path -Path $testPath -PathType Leaf) { 167 | $actualDistributionDir = $_.Name 168 | } 169 | } 170 | } 171 | 172 | if (!$actualDistributionDir) { 173 | Write-Error "Could not find Maven distribution directory in extracted archive" 174 | } 175 | 176 | Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" 177 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null 178 | try { 179 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 180 | } catch { 181 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 182 | Write-Error "fail to move MAVEN_HOME" 183 | } 184 | } finally { 185 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 186 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 187 | } 188 | 189 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 190 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/HeapFunctions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | import com.github.vlsi.mat.calcite.collections.CollectionsActions; 5 | import com.github.vlsi.mat.calcite.schema.objects.SpecialFields; 6 | 7 | import org.eclipse.mat.SnapshotException; 8 | import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils; 9 | import org.eclipse.mat.inspections.collectionextract.ICollectionExtractor; 10 | import org.eclipse.mat.snapshot.ISnapshot; 11 | import org.eclipse.mat.snapshot.model.*; 12 | 13 | import java.util.Map; 14 | 15 | public class HeapFunctions extends HeapFunctionsBase { 16 | 17 | @SuppressWarnings("unused") 18 | public static int getId(Object r) { 19 | HeapReference ref = ensureHeapReference(r); 20 | return ref == null ? -1 : ref.getIObject().getObjectId(); 21 | } 22 | 23 | @SuppressWarnings("unused") 24 | public static Object getClass(Object r) { 25 | HeapReference ref = ensureHeapReference(r); 26 | return ref == null ? null : HeapReference.valueOf(ref.getIObject().getClazz()); 27 | } 28 | 29 | @SuppressWarnings("unused") 30 | public static String getType(Object r) { 31 | HeapReference ref = ensureHeapReference(r); 32 | return ref == null ? "" : ref.getIObject().getClazz().getName(); 33 | } 34 | 35 | @SuppressWarnings("unused") 36 | public static String toString(Object r) { 37 | if (r == null) { 38 | return null; 39 | } 40 | return r.toString(); 41 | } 42 | 43 | @SuppressWarnings("unused") 44 | public static String getStringContent(Object r, int limit) { 45 | try { 46 | HeapReference ref = ensureHeapReference(r); 47 | return ref == null ? "" : PrettyPrinter.objectAsString(ref.getIObject(), limit); 48 | } catch (SnapshotException e) { 49 | throw new RuntimeException("Unable to represent as string", e); 50 | } 51 | } 52 | 53 | @SuppressWarnings("unused") 54 | public static String introspect(Object r) { 55 | if (r instanceof HeapReference) { 56 | return "HeapReference: " + toString(r); 57 | } else if (r instanceof Object[]) { 58 | return "Array, length = " + ((Object[]) r).length; 59 | } else { 60 | return "Primitive type: " + toString(r); 61 | } 62 | } 63 | 64 | @SuppressWarnings("unused") 65 | public static Object getByKey(Object r, String key) { 66 | HeapReference ref = ensureHeapReference(r); 67 | if (ref == null) { 68 | return null; 69 | } 70 | 71 | try { 72 | for (Map.Entry entry : CollectionsActions.extractMap(ref.getIObject())) { 73 | if (key.equals(toString(entry.getKey()))) { 74 | return resolveReference(entry.getValue()); 75 | } 76 | } 77 | return null; 78 | } catch (SnapshotException e) { 79 | throw new RuntimeException("Unable to lookup key " + key + " in " + r, e); 80 | } 81 | } 82 | 83 | @SuppressWarnings("unused") 84 | public static int getSize(Object r) { 85 | HeapReference ref = ensureHeapReference(r); 86 | if (ref == null) { 87 | return -1; 88 | } 89 | 90 | try { 91 | ICollectionExtractor collectionExtractor = CollectionExtractionUtils.findCollectionExtractor(ref.getIObject()); 92 | if (collectionExtractor != null && collectionExtractor.hasSize()) { 93 | return collectionExtractor.getSize(ref.getIObject()); 94 | } else { 95 | return -1; 96 | } 97 | } catch (SnapshotException e) { 98 | throw new RuntimeException("Unable to obtain collection size for " + r, e); 99 | } 100 | } 101 | 102 | @SuppressWarnings("unused") 103 | public static int length(Object r) { 104 | HeapReference ref = ensureHeapReference(r); 105 | 106 | if (ref == null) { 107 | return -1; 108 | } 109 | 110 | IObject obj = ref.getIObject(); 111 | 112 | return obj instanceof IArray ? ((IArray) obj).getLength() : -1; 113 | } 114 | 115 | @SuppressWarnings("unused") 116 | public static long shallowSize(Object r) { 117 | HeapReference ref = ensureHeapReference(r); 118 | if (ref == null) { 119 | return -1; 120 | } 121 | try { 122 | return ref.getIObject().getSnapshot().getHeapSize(ref.getIObject().getObjectId()); 123 | } catch (SnapshotException e) { 124 | throw new RuntimeException("Cannot calculate shallow size for " + r, e); 125 | } 126 | } 127 | 128 | @SuppressWarnings("unused") 129 | public static long retainedSize(Object r) { 130 | HeapReference ref = ensureHeapReference(r); 131 | if (ref == null) { 132 | return -1; 133 | } 134 | 135 | try { 136 | return ref.getIObject().getSnapshot().getRetainedHeapSize(ref.getIObject().getObjectId()); 137 | } catch (SnapshotException e) { 138 | throw new RuntimeException("Cannot calculate retained size for " + r, e); 139 | } 140 | } 141 | 142 | @SuppressWarnings("unused") 143 | public static Object getField(Object r, String fieldName) { 144 | HeapReference ref = ensureHeapReference(r); 145 | if (ref == null) { 146 | return null; 147 | } 148 | IObject iObject = ref.getIObject(); 149 | if (fieldName.charAt(0) == '@') { 150 | if ("@shallow".equalsIgnoreCase(fieldName)) { 151 | return HeapFunctions.shallowSize(ref); 152 | } 153 | if ("@retained".equalsIgnoreCase(fieldName)) { 154 | return HeapFunctions.retainedSize(ref); 155 | } 156 | if (SpecialFields.CLASS_NAME.equalsIgnoreCase(fieldName)) { 157 | return IClassMethods.getClassName(iObject.getClazz()); 158 | } 159 | if (iObject instanceof IClass) { 160 | if (SpecialFields.CLASS_LOADER.equalsIgnoreCase(fieldName)) { 161 | return resolveReference(IClassMethods.getClassLoader(iObject)); 162 | } else if (SpecialFields.SUPER.equalsIgnoreCase(fieldName)) { 163 | return resolveReference(IClassMethods.getSuper(iObject)); 164 | } 165 | } else if (SpecialFields.CLASS.equalsIgnoreCase(fieldName)) { 166 | return resolveReference(iObject.getClazz()); 167 | } 168 | } else if (iObject instanceof IArray 169 | && fieldName.length() > 2 170 | && fieldName.charAt(0) == '[' 171 | && fieldName.charAt(fieldName.length() - 1) == ']') { 172 | try { 173 | // Field name is '[]' and target object is array. 174 | // Initially such calls were routed to IObject.resolveValue, which accepts field names as '[]' for arrays. 175 | // However, this have two problems: 176 | // 1. This doesn't work with primitive arrays (they always return null) 177 | // 2. This doesn't correctly work with values - MAT code doesn't handle 0 address properly in this case 178 | // So, now we handle this case directly in our code 179 | int index = Integer.parseInt(fieldName.substring(1, fieldName.length() - 1)); 180 | int length = ((IArray) iObject).getLength(); 181 | if (index >= 0 && index < length) { 182 | if (iObject instanceof IPrimitiveArray) { 183 | return ((IPrimitiveArray) iObject).getValueAt(index); 184 | } else if (iObject instanceof IObjectArray) { 185 | return resolveReference(iObject.getSnapshot(), ((IObjectArray) iObject).getReferenceArray()[index]); 186 | } 187 | } else { 188 | return null; 189 | } 190 | } catch (NumberFormatException e) { 191 | // fall down 192 | } 193 | } 194 | return resolveReference(IObjectMethods.resolveSimpleValue(iObject, fieldName)); 195 | } 196 | 197 | @SuppressWarnings("unused") 198 | public static Object getStaticField(Object r, String name) { 199 | HeapReference ref = ensureHeapReference(r); 200 | if (ref == null) { 201 | return null; 202 | } 203 | 204 | IObject iObject = ref.getIObject(); 205 | if (iObject instanceof IClass) { 206 | IClass iClass = (IClass) iObject; 207 | for (Field field : iClass.getStaticFields()) { 208 | if (field.getName().equals(name)) { 209 | Object value = field.getValue(); 210 | if (value instanceof IObject) { 211 | return new HeapReference((IObject) value); 212 | } else { 213 | return value; 214 | } 215 | } 216 | } 217 | } 218 | return null; 219 | } 220 | 221 | @SuppressWarnings("unused") 222 | public static long getAddress(Object r) { 223 | HeapReference ref = ensureHeapReference(r); 224 | if (ref == null) { 225 | return -1; 226 | } 227 | 228 | return ref.getIObject().getObjectAddress(); 229 | } 230 | 231 | @SuppressWarnings("unused") 232 | public static long toLong(String value) { 233 | return Long.decode(value); 234 | } 235 | 236 | @SuppressWarnings("unused") 237 | public static Object getDominator(Object r) { 238 | HeapReference ref = ensureHeapReference(r); 239 | if (ref == null) { 240 | return null; 241 | } 242 | 243 | try { 244 | ISnapshot snapshot = ref.getIObject().getSnapshot(); 245 | return HeapReference.valueOf(snapshot.getObject(snapshot.getImmediateDominatorId(ref.getIObject().getObjectId()))); 246 | } catch (SnapshotException e) { 247 | throw new RuntimeException("Cannot obtain immediate dominator object for " + r, e); 248 | } 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/functions/TableFunctions.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.functions; 2 | 3 | import com.github.vlsi.mat.calcite.HeapReference; 4 | import com.github.vlsi.mat.calcite.collections.CollectionsActions; 5 | import com.github.vlsi.mat.calcite.schema.references.OutboundReferencesTable; 6 | 7 | import com.google.common.collect.ImmutableList; 8 | import com.google.common.collect.ImmutableMultimap; 9 | import com.google.common.collect.Multimap; 10 | import org.apache.calcite.adapter.java.AbstractQueryableTable; 11 | import org.apache.calcite.linq4j.BaseQueryable; 12 | import org.apache.calcite.linq4j.Enumerator; 13 | import org.apache.calcite.linq4j.Linq4j; 14 | import org.apache.calcite.linq4j.QueryProvider; 15 | import org.apache.calcite.linq4j.Queryable; 16 | import org.apache.calcite.rel.type.RelDataType; 17 | import org.apache.calcite.rel.type.RelDataTypeFactory; 18 | import org.apache.calcite.schema.QueryableTable; 19 | import org.apache.calcite.schema.SchemaPlus; 20 | import org.apache.calcite.schema.Statistic; 21 | import org.apache.calcite.schema.Statistics; 22 | import org.apache.calcite.schema.TableFunction; 23 | import org.apache.calcite.schema.impl.TableFunctionImpl; 24 | import org.apache.calcite.sql.type.SqlTypeName; 25 | import org.apache.calcite.util.ImmutableBitSet; 26 | import org.eclipse.mat.SnapshotException; 27 | import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils; 28 | import org.eclipse.mat.inspections.collectionextract.ExtractedMap; 29 | import org.eclipse.mat.inspections.collectionextract.ICollectionExtractor; 30 | import org.eclipse.mat.snapshot.ISnapshot; 31 | import org.eclipse.mat.snapshot.model.IObject; 32 | import org.eclipse.mat.snapshot.model.NamedReference; 33 | import org.eclipse.mat.util.VoidProgressListener; 34 | 35 | import java.lang.reflect.Type; 36 | import java.util.ArrayList; 37 | import java.util.Collection; 38 | import java.util.Collections; 39 | import java.util.List; 40 | import java.util.Map; 41 | 42 | public class TableFunctions { 43 | 44 | public static Multimap createAll() { 45 | ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); 46 | builder.put("getValues", TableFunctionImpl.create(TableFunctions.class, "getValues")); 47 | builder.put("getMapEntries", TableFunctionImpl.create(TableFunctions.class, "getMapEntries")); 48 | builder.put("getRetainedSet", TableFunctionImpl.create(TableFunctions.class, "getRetainedSet")); 49 | builder.put("getOutboundReferences", TableFunctionImpl.create(TableFunctions.class, "getOutboundReferences")); 50 | builder.put("getInboundReferences", TableFunctionImpl.create(TableFunctions.class, "getInboundReferences")); 51 | return builder.build(); 52 | } 53 | 54 | @SuppressWarnings("unused") 55 | public static QueryableTable getValues(Object r) { 56 | List references; 57 | if (!(r instanceof HeapReference)) { 58 | references = Collections.emptyList(); 59 | } else { 60 | HeapReference ref = (HeapReference) r; 61 | try { 62 | ICollectionExtractor collectionExtractor = CollectionExtractionUtils.findCollectionExtractor(ref.getIObject()); 63 | if (collectionExtractor == null) { 64 | references = Collections.emptyList(); 65 | } else { 66 | references = collectReferences(ref.getIObject().getSnapshot(), 67 | collectionExtractor.extractEntryIds(ref.getIObject())); 68 | } 69 | } catch (SnapshotException e) { 70 | throw new RuntimeException("Cannot extract values from " + ref, e); 71 | } 72 | } 73 | return new HeapReferenceTable(references, false); 74 | } 75 | 76 | @SuppressWarnings("unused") 77 | public static QueryableTable getMapEntries(Object r) { 78 | List references; 79 | if (!(r instanceof HeapReference)) { 80 | references = Collections.emptyList(); 81 | } else { 82 | HeapReference ref = (HeapReference) r; 83 | try { 84 | ExtractedMap extractedMap = CollectionsActions.extractMap(ref.getIObject()); 85 | if (extractedMap == null) { 86 | references = Collections.emptyList(); 87 | } else { 88 | references = new ArrayList<>(); 89 | for (Map.Entry entry : extractedMap) { 90 | references.add(new HeapReference[]{ 91 | HeapReference.valueOf(entry.getKey()), 92 | HeapReference.valueOf(entry.getValue()) 93 | }); 94 | } 95 | } 96 | } catch (SnapshotException e) { 97 | throw new RuntimeException("Cannot extract values from " + ref, e); 98 | } 99 | } 100 | return new HeapReferencesTable(new String[]{"key", "value"}, references); 101 | } 102 | 103 | @SuppressWarnings("unused") 104 | public static QueryableTable getRetainedSet(Object r) { 105 | List references; 106 | if (!(r instanceof HeapReference)) { 107 | references = Collections.emptyList(); 108 | } else { 109 | HeapReference ref = (HeapReference) r; 110 | ISnapshot snapshot = ref.getIObject().getSnapshot(); 111 | try { 112 | references = collectReferences 113 | ( 114 | snapshot, 115 | snapshot.getRetainedSet(new int[]{ref.getIObject().getObjectId()}, new VoidProgressListener()) 116 | ); 117 | } catch (SnapshotException e) { 118 | throw new RuntimeException("Cannot extract retained set from " + r, e); 119 | } 120 | } 121 | return new HeapReferenceTable(references, true); 122 | } 123 | 124 | @SuppressWarnings("unused") 125 | public static QueryableTable getOutboundReferences(Object r) { 126 | List references; 127 | if (!(r instanceof HeapReference)) { 128 | references = Collections.emptyList(); 129 | } else { 130 | HeapReference ref = (HeapReference) r; 131 | references = ref.getIObject().getOutboundReferences(); 132 | } 133 | return new OutboundReferencesTable(references); 134 | } 135 | 136 | @SuppressWarnings("unused") 137 | public static QueryableTable getInboundReferences(Object r) { 138 | List references; 139 | if (!(r instanceof HeapReference)) { 140 | references = Collections.emptyList(); 141 | } else { 142 | HeapReference ref = (HeapReference) r; 143 | ISnapshot snapshot = ref.getIObject().getSnapshot(); 144 | try { 145 | references = collectReferences 146 | ( 147 | snapshot, 148 | snapshot.getInboundRefererIds(ref.getIObject().getObjectId()) 149 | ); 150 | } catch (SnapshotException e) { 151 | throw new RuntimeException("Cannot extract inbound references from " + r, e); 152 | } 153 | } 154 | return new HeapReferenceTable(references, true); 155 | } 156 | 157 | private static List collectReferences(ISnapshot snapshot, int[] objectIds) throws SnapshotException { 158 | if (objectIds != null && objectIds.length > 0) { 159 | List references = new ArrayList<>(); 160 | for (int objectId : objectIds) { 161 | references.add(HeapReference.valueOf(snapshot.getObject(objectId))); 162 | } 163 | return references; 164 | } else { 165 | return Collections.emptyList(); 166 | } 167 | } 168 | 169 | private static class HeapReferenceTable extends ValuesListTable { 170 | HeapReferenceTable(Collection references, boolean unique) { 171 | super(HeapReference.class, new String[]{"this"}, references, unique); 172 | } 173 | } 174 | 175 | private static class HeapReferencesTable extends ValuesListTable { 176 | HeapReferencesTable(String[] columnNames, Collection references) { 177 | super(HeapReference[].class, columnNames, references, false); 178 | } 179 | } 180 | 181 | private static class ValuesListTable extends AbstractQueryableTable { 182 | private final static List UNIQUE_KEYS_STATISTICS = ImmutableList.of(ImmutableBitSet.of(0)); 183 | private final static List NON_UNIQUE_KEYS_STATISTICS = ImmutableList.of(ImmutableBitSet.of()); 184 | 185 | private final String[] columnNames; 186 | private final Collection values; 187 | private final boolean unique; 188 | 189 | ValuesListTable(Type targetRowType, String[] columnNames, Collection values, boolean unique) { 190 | super(targetRowType); 191 | this.columnNames = columnNames; 192 | this.values = values; 193 | this.unique = unique; 194 | } 195 | 196 | @SuppressWarnings("unchecked") 197 | @Override 198 | public Queryable asQueryable(QueryProvider queryProvider, SchemaPlus schemaPlus, String s) { 199 | BaseQueryable queryable = new BaseQueryable(null, getElementType(), null) { 200 | @Override 201 | public Enumerator enumerator() { 202 | return Linq4j.enumerator(values); 203 | } 204 | }; 205 | return (Queryable) queryable; 206 | } 207 | 208 | @Override 209 | public RelDataType getRowType(RelDataTypeFactory relDataTypeFactory) { 210 | RelDataTypeFactory.Builder builder = relDataTypeFactory.builder(); 211 | RelDataType anyNull = relDataTypeFactory.createTypeWithNullability( 212 | relDataTypeFactory.createSqlType(SqlTypeName.ANY), true); 213 | for (String columnName : columnNames) { 214 | builder.add(columnName, anyNull); 215 | } 216 | return builder.build(); 217 | } 218 | 219 | @Override 220 | public Statistic getStatistic() { 221 | return Statistics.of(values.size(), unique ? UNIQUE_KEYS_STATISTICS : NON_UNIQUE_KEYS_STATISTICS); 222 | } 223 | } 224 | } 225 | 226 | -------------------------------------------------------------------------------- /MatCalcitePlugin/src/com/github/vlsi/mat/calcite/schema/objects/ClassRowTypeCache.java: -------------------------------------------------------------------------------- 1 | package com.github.vlsi.mat.calcite.schema.objects; 2 | 3 | import com.github.vlsi.mat.calcite.rex.RexBuilderContext; 4 | 5 | import com.google.common.cache.CacheBuilder; 6 | import com.google.common.cache.CacheLoader; 7 | import com.google.common.cache.LoadingCache; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rel.type.RelDataTypeFactory; 10 | import org.apache.calcite.rex.RexNode; 11 | import org.apache.calcite.sql.type.SqlTypeName; 12 | import org.apache.calcite.util.Pair; 13 | import org.eclipse.mat.snapshot.model.FieldDescriptor; 14 | import org.eclipse.mat.snapshot.model.IClass; 15 | import org.eclipse.mat.snapshot.model.IObject; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.Iterator; 20 | import java.util.LinkedHashMap; 21 | import java.util.List; 22 | import java.util.ListIterator; 23 | import java.util.Map; 24 | import java.util.function.Function; 25 | 26 | import static com.github.vlsi.mat.calcite.schema.objects.SnapshotRexExpressions.getClassLoader; 27 | import static com.github.vlsi.mat.calcite.schema.objects.SnapshotRexExpressions.getClassName; 28 | import static com.github.vlsi.mat.calcite.schema.objects.SnapshotRexExpressions.getClassOf; 29 | import static com.github.vlsi.mat.calcite.schema.objects.SnapshotRexExpressions.getSuper; 30 | import static com.github.vlsi.mat.calcite.schema.objects.SnapshotRexExpressions.resolveField; 31 | 32 | public class ClassRowTypeCache { 33 | 34 | public static LoadingCache>>>> CACHE = CacheBuilder 36 | .newBuilder() 37 | .weakKeys() 38 | .build(new CacheLoader>>>>() { 40 | @Override 41 | public LoadingCache>>> load( 42 | final RelDataTypeFactory typeFactory) throws Exception { 43 | return CacheBuilder.newBuilder().weakKeys() 44 | .build(new ClassRowTypeResolver(typeFactory)); 45 | } 46 | }); 47 | 48 | private interface ExtraTypes extends IObject.Type { 49 | int ANY = -1; 50 | int CHARACTER = -2; 51 | } 52 | 53 | private static final class ClassRowTypeResolver 54 | extends 55 | CacheLoader>>> { 56 | private final RelDataTypeFactory typeFactory; 57 | 58 | private ClassRowTypeResolver(RelDataTypeFactory typeFactory) { 59 | this.typeFactory = typeFactory; 60 | } 61 | 62 | private LinkedHashMap getAllInstanceFields(IClass clazz) { 63 | // In case base class and subclass have a field with the same name, we just use the one from a subclass 64 | // TODO: use org.eclipse.mat.snapshot.model.IInstance.getFields() in those cases 65 | LinkedHashMap seenFields = new LinkedHashMap<>(); 66 | for (IClass i = clazz; i != null; i = i.getSuperClass()) { 67 | List fds = i.getFieldDescriptors(); 68 | // Iterate fields in a reversed order in order. 69 | // We iterate class hierarchy in reverse as well (subclass -> superclass), 70 | for (ListIterator it = fds.listIterator(fds.size()); it.hasPrevious(); ) { 71 | FieldDescriptor fd = it.previous(); 72 | if (!seenFields.containsKey(fd.getName())) { 73 | seenFields.put(fd.getName(), new Field(fd)); 74 | } 75 | } 76 | if (IClass.JAVA_LANG_CLASS.equals(i.getName())) { 77 | seenFields.put(SpecialFields.CLASS_LOADER, new Field(SpecialFields.CLASS_LOADER, IObject.Type.OBJECT)); 78 | seenFields.put(SpecialFields.SUPER, new Field(SpecialFields.SUPER, IObject.Type.OBJECT)); 79 | seenFields.put(SpecialFields.CLASS_NAME, new Field(SpecialFields.CLASS_NAME, ExtraTypes.CHARACTER)); 80 | // Hide the default "Class.name" field as it might be null 81 | seenFields.remove("name"); 82 | } 83 | } 84 | seenFields.put(SpecialFields.CLASS, new Field(SpecialFields.CLASS, IObject.Type.OBJECT)); 85 | return seenFields; 86 | } 87 | 88 | @Override 89 | public Pair>> load( 90 | IClassesList classesList) throws Exception { 91 | List> resolvers = new ArrayList<>(); 92 | List names = new ArrayList<>(); 93 | List types = new ArrayList<>(); 94 | 95 | names.add("this"); 96 | RelDataType any = typeFactory.createSqlType(SqlTypeName.ANY); 97 | RelDataType anyNull = typeFactory.createTypeWithNullability(any, true); 98 | types.add(any); 99 | resolvers.add(SnapshotRexExpressions::computeThis); 100 | 101 | // In case multiple classes have a field with different datatype, we just make field type "ANY" 102 | 103 | LinkedHashMap fields = null; 104 | for (IClass aClass : classesList.getRootClasses()) { 105 | LinkedHashMap allInstanceFields = getAllInstanceFields(aClass); 106 | if (fields == null) { 107 | fields = allInstanceFields; 108 | continue; 109 | } 110 | for (Iterator> it = fields.entrySet().iterator(); it.hasNext(); ) { 111 | Map.Entry entry = it.next(); 112 | String fieldName = entry.getKey(); 113 | Field field = allInstanceFields.get(fieldName); 114 | if (field == null) { 115 | // Keep just common fields 116 | it.remove(); 117 | continue; 118 | } 119 | if (entry.getValue().getType() == field.getType()) { 120 | continue; 121 | } 122 | // If data type differs, use "ANY" type 123 | entry.setValue(new Field(fieldName, ExtraTypes.ANY)); 124 | } 125 | } 126 | 127 | List fieldsInOrder = fields == null ? Collections.emptyList() : new ArrayList<>(fields.values()); 128 | Collections.reverse(fieldsInOrder); 129 | 130 | for (Field field : fieldsInOrder) { 131 | int type = field.getType(); 132 | RelDataType dataType; 133 | switch (type) { 134 | case IObject.Type.BOOLEAN: 135 | dataType = typeFactory.createJavaType(boolean.class); 136 | break; 137 | case IObject.Type.BYTE: 138 | dataType = typeFactory.createJavaType(byte.class); 139 | break; 140 | case IObject.Type.CHAR: 141 | dataType = typeFactory.createJavaType(char.class); 142 | break; 143 | case IObject.Type.DOUBLE: 144 | dataType = typeFactory.createJavaType(double.class); 145 | break; 146 | case IObject.Type.FLOAT: 147 | dataType = typeFactory.createJavaType(float.class); 148 | break; 149 | case IObject.Type.SHORT: 150 | dataType = typeFactory.createJavaType(short.class); 151 | break; 152 | case IObject.Type.INT: 153 | dataType = typeFactory.createJavaType(int.class); 154 | break; 155 | case IObject.Type.LONG: 156 | dataType = typeFactory.createJavaType(long.class); 157 | break; 158 | case IObject.Type.OBJECT: 159 | // fall-through 160 | case ExtraTypes.ANY: 161 | dataType = anyNull; 162 | break; 163 | case ExtraTypes.CHARACTER: 164 | dataType = typeFactory.createTypeWithNullability( 165 | typeFactory.createSqlType(SqlTypeName.VARCHAR), true); 166 | break; 167 | default: 168 | dataType = typeFactory.createJavaType(String.class); 169 | break; 170 | } 171 | types.add(dataType); 172 | String fieldName = field.getName(); 173 | Function columnCalc; 174 | switch (fieldName) { 175 | case SpecialFields.CLASS: 176 | // This is Object#getClass 177 | // For java.lang.Class it returns "java.lang.Class" 178 | columnCalc = (RexBuilderContext context) -> 179 | getClassOf(context, context.getIObjectId()); 180 | break; 181 | case SpecialFields.SUPER: 182 | // This property is available only for classes 183 | // It is assumed that getIObject would return IClass 184 | columnCalc = (RexBuilderContext context) -> 185 | getSuper(context, context.getIObject()); 186 | break; 187 | case SpecialFields.CLASS_LOADER: 188 | // This property is available only for classes 189 | // It is assumed that getIObject would return IClass 190 | columnCalc = (RexBuilderContext context) -> 191 | getClassLoader(context, context.getIObject()); 192 | break; 193 | case SpecialFields.CLASS_NAME: 194 | // This property is available only for classes 195 | // It is assumed that getIObject would return IClass 196 | columnCalc = (RexBuilderContext context) -> 197 | getClassName(context, context.getIObject()); 198 | fieldName = "name"; 199 | break; 200 | default: 201 | String resolvedField = fieldName; 202 | columnCalc = (RexBuilderContext context) -> 203 | resolveField(context, resolvedField); 204 | } 205 | if (type == IObject.Type.OBJECT) { 206 | // Wrap object fields with HeapReference 207 | Function prev = columnCalc; 208 | columnCalc = (RexBuilderContext context) -> 209 | context.toHeapReference(prev.apply(context)); 210 | } else if (dataType != anyNull) { 211 | Function prev = columnCalc; 212 | columnCalc = (RexBuilderContext context) -> 213 | context.getBuilder().makeCast(dataType, prev.apply(context)); 214 | } 215 | names.add(fieldName); 216 | resolvers.add(columnCalc); 217 | } 218 | 219 | return Pair.of( 220 | typeFactory.createStructType(types, names), 221 | resolvers); 222 | } 223 | } 224 | } 225 | --------------------------------------------------------------------------------