diff --git a/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/GetCommandsTask.java b/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/GetCommandsTask.java index 3597dd7fec1..0407486995e 100644 --- a/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/GetCommandsTask.java +++ b/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/GetCommandsTask.java @@ -61,6 +61,7 @@ import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.ExtendedMethodFlagInfoCommand; import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.FindAllModulesCommand; import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.FindAllReadsCommand; +import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.FindInstances; import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.FindMethodFromPcCommand; import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.FindModuleByNameCommand; import com.ibm.j9ddr.vm29.tools.ddrinteractive.commands.FindModulesCommand; @@ -207,6 +208,7 @@ public void run(IVMData vmData, Object[] userData) toPassBack.add(new FlatObjectCommand()); } toPassBack.add(new VirtualThreadsCommand()); + toPassBack.add(new FindInstances()); loadPlugins(toPassBack, loader); diff --git a/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/commands/FindInstances.java b/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/commands/FindInstances.java new file mode 100644 index 00000000000..d1e5656c362 --- /dev/null +++ b/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/commands/FindInstances.java @@ -0,0 +1,159 @@ +/* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + */ +package com.ibm.j9ddr.vm29.tools.ddrinteractive.commands; + +import java.io.PrintStream; + +import com.ibm.j9ddr.CorruptDataException; +import com.ibm.j9ddr.tools.ddrinteractive.Command; +import com.ibm.j9ddr.tools.ddrinteractive.CommandUtils; +import com.ibm.j9ddr.tools.ddrinteractive.Context; +import com.ibm.j9ddr.tools.ddrinteractive.DDRInteractiveCommandException; +import com.ibm.j9ddr.tools.ddrinteractive.Table; +import com.ibm.j9ddr.vm29.pointer.generated.J9ClassPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ObjectPointer; +import com.ibm.j9ddr.vm29.pointer.helper.J9ClassHelper; +import com.ibm.j9ddr.vm29.pointer.helper.J9ObjectHelper; + +import com.ibm.j9ddr.vm29.j9.DataType; +import com.ibm.j9ddr.vm29.j9.walkers.ClassSegmentIterator; +import com.ibm.j9ddr.vm29.pointer.generated.J9ClassPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9JavaVMPointer; +import com.ibm.j9ddr.vm29.pointer.helper.J9RASHelper; + +import com.ibm.j9ddr.vm29.j9.gc.GCHeapRegionDescriptor; +import com.ibm.j9ddr.vm29.j9.gc.GCHeapRegionIterator; +import com.ibm.j9ddr.vm29.j9.gc.GCObjectHeapIterator; + +public class FindInstances extends Command +{ + private String className; + private J9ClassPointer classPointer; + private Table data; + private long corruptCount; + private long objectsFound; + + public FindInstances() { + addCommand("findinstances", "", "find all instances of the specific class and its subclasses"); + } + + private static void printUsage (PrintStream out) { + out.println("findinstances - find all instances of the specific class and its subclasses"); + } + + private boolean parseArgs(PrintStream out, String[] args) throws DDRInteractiveCommandException { + if (args.length != 1) { + out.println("Exactly one argument expected"); + return false; + } + String firstArg = args[0]; + if (firstArg.equals("help")) { + printUsage(out); + return false; + } + className = firstArg; + return true; + } + + @Override + public void run(String command, String[] args, Context context, PrintStream out) throws DDRInteractiveCommandException { + if (args != null) { + if (!parseArgs(out, args)) { + return; + } + } + + try { + J9JavaVMPointer vm = J9RASHelper.getVM(DataType.getJ9RASPointer()); + ClassSegmentIterator iterator = new ClassSegmentIterator(vm.classMemorySegments()); + + out.format( + "Searching for class named '%s' in VM=%s%n", + className, Long.toHexString(vm.getAddress())); + + /* Reset classPointer for each invocation of the command. */ + classPointer = null; + + while (iterator.hasNext()) { + J9ClassPointer clsPointer = (J9ClassPointer)iterator.next(); + String javaName = J9ClassHelper.getJavaName(clsPointer); + if (className.equals(javaName)) { + String hexString = clsPointer.getHexAddress(); + classPointer = clsPointer; + out.format("Found !j9class %s named %s%n", hexString, javaName); + break; + } + } + + if (classPointer == null) { + out.format("No class named %s found%n", className); + return; + } + } catch (CorruptDataException e) { + throw new DDRInteractiveCommandException(e); + } + + /* Allocate a new instance of Table for each invocation of the command. */ + data = new Table("Instances of " + className); + data.row("Address", "Class Name"); + + /* Reset counts for each invocation of the command. */ + corruptCount = 0; + objectsFound = 0; + + scanHeap(); + + out.format("Objects found: %d%n", objectsFound); + out.format("Corruptions encountered: %d%n", corruptCount); + + data.render(out); + } + + private void scanHeap() { + try { + GCHeapRegionIterator regions = GCHeapRegionIterator.from(); + while (regions.hasNext()) { + GCHeapRegionDescriptor region = regions.next(); + scanObjects(region); + } + } catch (CorruptDataException e) { + e.printStackTrace(); + } + + } + + private void scanObjects(GCHeapRegionDescriptor region) throws CorruptDataException { + GCObjectHeapIterator heapIterator = GCObjectHeapIterator.fromHeapRegionDescriptor(region, true, true); + while (heapIterator.hasNext()) { + J9ObjectPointer object = heapIterator.next(); + try { + J9ClassPointer objClass = J9ObjectHelper.clazz(object); + if (objClass.notNull() && J9ClassHelper.isSameOrSuperClassOf(classPointer, objClass)) { + data.row(object.getHexAddress(), J9ClassHelper.getJavaName(objClass)); + objectsFound += 1; + } + } catch (CorruptDataException e) { + corruptCount += 1; + } + } + } +}