Mercurial > hg > release > thermostat-1.6
changeset 1988:44ded819f2bf
HistogramConverter refactor
PR3059
Reviewed-by: jkang
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/019844.html
Original-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-November/016870.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/AbstractTreeAssembler.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.util.List; +import java.util.regex.Pattern; + +public abstract class AbstractTreeAssembler<T> implements TreeAssembler<T> { + + @Override + public abstract void buildTree(T data, TreeMapNode root); + + public static TreeMapNode processRecord(String name, String token, TreeMapNode lastProcessed) { + while (!name.equals("")) { + + String nodeId = name.split(Pattern.quote(token))[0]; + + TreeMapNode child = searchNode(lastProcessed.getChildren(), nodeId); + if (child == null) { + child = new TreeMapNode(nodeId, 0); + lastProcessed.addChild(child); + } + + lastProcessed = child; + + name = name.substring(nodeId.length()); + if (name.startsWith(".")) { + name = name.substring(1); + } + } + return lastProcessed; + } + + public static TreeMapNode searchNode(List<TreeMapNode> nodes, String nodeId) { + for (TreeMapNode node : nodes) { + if (node.getLabel().equals(nodeId)) { + return node; + } + } + return null; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeAssembler.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +public interface TreeAssembler<T> { + + void buildTree(T data, TreeMapNode root); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeConverter.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,103 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import java.util.List; + +public class TreeConverter { + + /** + * This method builds a tree map model of the {@link T} data provided, using the + * specified {@link TreeAssembler} assembler. + * + * @return the root of the resulting tree + */ + public static <T> TreeMapNode convertToTreeMap(T data, TreeAssembler<T> assembler) { + TreeMapNode root = new TreeMapNode("", 0); + + assembler.buildTree(data, root); + // calculates weights for inner nodes + fillWeights(root); + // collapse nodes with only one child + packTree(root); + return root; + } + + /** + * This method calculates the real weights using a bottom-up traversal. The weight of a + * parent node is the sum of its children's weights. + * + * Package-private for testing purposes. + * + * @param node the root of the subtree + * @return the real weight of the root + */ + static double fillWeights(TreeMapNode node) { + if (node.getChildren().size() == 0) { + return node.getRealWeight(); + } + + double sum = 0; + for (TreeMapNode child : node.getChildren()) { + sum += fillWeights(child); + } + node.setRealWeight(sum); + return node.getRealWeight(); + } + + /** + * This method allows the collapse of a series of nodes, each with at most one child, into a + * single node placed at the root of the series. + * + * Package-private for testing purposes. + * + * @param node the root of the subtree + */ + static void packTree(TreeMapNode node) { + List<TreeMapNode> children = node.getChildren(); + if (children.size() == 1) { + TreeMapNode child = children.get(0); + node.setLabel(node.getLabel() + "." + child.getLabel()); + node.setChildren(child.getChildren()); + packTree(node); + } else { + for (TreeMapNode child : children) { + packTree(child); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/AbstractTreeAssemblerTest.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class AbstractTreeAssemblerTest { + + final private String SOME_ROOT_LABEL = "root"; + final private double SOME_ROOT_WEIGHT = 25.0; + final private String SPLIT_TOKEN = "."; + + private TreeMapNode root; + + @Before + public void setup() { + root = new TreeMapNode(SOME_ROOT_LABEL, SOME_ROOT_WEIGHT); + } + + @Test + public void testProcessRecord() { + String class1 = "com.Class1"; + AbstractTreeAssembler.processRecord(class1, SPLIT_TOKEN, root); + + List<TreeMapNode> children = root.getChildren(); + assertEquals(1, children.size()); + assertEquals("com", children.get(0).getLabel()); + + children = children.get(0).getChildren(); + assertEquals(1, children.size()); + assertEquals("Class1", children.get(0).getLabel()); + assertEquals(0, children.get(0).getChildren().size()); + + String class2 = "com.Class2"; + AbstractTreeAssembler.processRecord(class2, SPLIT_TOKEN, root); + + children = root.getChildren(); + assertEquals(1, children.size()); + assertEquals("com", children.get(0).getLabel()); + + children = children.get(0).getChildren(); + assertEquals(2, children.size()); + assertNotNull(AbstractTreeAssembler.searchNode(children, "Class1")); + assertNotNull(AbstractTreeAssembler.searchNode(children, "Class2")); + assertEquals(0, children.get(0).getChildren().size()); + assertEquals(0, children.get(1).getChildren().size()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeConverterTest.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,228 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components.experimental; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.Pair; + +public class TreeConverterTest { + + final private String SPLIT_TOKEN = "."; + final private String SOME_ROOT_LABEL = "root"; + final private double SOME_ROOT_WEIGHT = 25.0; + final private double DELTA = 0.01; + final private double SOME_WEIGHT = 3.14; + + private ArrayList<Pair<String, Double>> data; + private TreeMapNode root; + + @Before + public void setUp() { + /* + * This is the classes structure used for the test and built using + * the data. Nodes are compacted at the point of branching + * so for example the node relative to "example2.package1.Class1" is + * still just one node "example2.package1.Class1" but the node relative + * to "com.example1.package1.Class1" branches at "com" so becomes the + * common parent node "com" with children "example1" and "example2", + * and so forth: + * + * ________com_______ java + * / \ | + * __example1__ example2 lang + * / \ | | + * package1 package2 package1 Object + * / \ | | + * Class1 Class2 Class3 Class4 + * + * + * + * + * Expected tree after conversion: + * + * ________________________root_______________ + * / \ + * ________com_______ java.lang.Object + * / \ + * __example1__ example2.package1.Class4 + * / \ + * package1 package2.Class3 + * / \ + * Class1 Class2 + * + */ + final String[] classes = { + "com.example1.package1.Class1", + "com.example1.package1.Class2", + "com.example1.package2.Class3", + "com.example2.package1.Class4", + "example2.package1.Class1", + "java.lang.Object", + "repeat1.repeat1.RepeatClass", + "repeat2.repeat2A.RepeatClass", + "repeat2.repeat2B.RepeatClass", + }; + + data = new ArrayList<Pair<String, Double>>(); + + for (int i = 0; i < classes.length; i++) { + data.add(new Pair<String, Double>(classes[i], new Double(i))); + } + + root = new TreeMapNode(SOME_ROOT_LABEL, SOME_ROOT_WEIGHT); + } + + @Test + public void testConvertToTreeMap() { + AbstractTreeAssembler<ArrayList<Pair<String, Double>>> assembler = + new AbstractTreeAssembler<ArrayList<Pair<String, Double>>>() { + + @Override + public void buildTree(ArrayList<Pair<String, Double>> data, TreeMapNode root) { + for (Pair<String, Double> element: data) { + TreeMapNode lastProcessed = processRecord(element.getFirst(), SPLIT_TOKEN, root); + lastProcessed.setRealWeight(element.getSecond()); + } + } + }; + TreeMapNode tree = TreeConverter.convertToTreeMap(data, assembler); + + List<TreeMapNode> nodes = tree.getChildren(); + assertEquals(5, nodes.size()); + + TreeMapNode node = AbstractTreeAssembler.searchNode(nodes, "com"); + assertNotNull(node); + + List<TreeMapNode> nodesFromCom = node.getChildren(); + + // example1 and example2 + assertEquals(2, nodesFromCom.size()); + node = AbstractTreeAssembler.searchNode(nodesFromCom, "example1"); + assertNotNull(node); + + // package2.Class3 and package1 + assertEquals(2, node.getChildren().size()); + + node = AbstractTreeAssembler.searchNode(node.getChildren(), "package2.Class3"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + node = AbstractTreeAssembler.searchNode(nodes, "java.lang.Object"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + node = AbstractTreeAssembler.searchNode(nodes, "example2.package1.Class1"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + // now on to the "repeat1.repeat1.RepeatClass" bunch + node = AbstractTreeAssembler.searchNode(nodes, "repeat1.repeat1.RepeatClass"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + node = AbstractTreeAssembler.searchNode(nodes, "repeat2"); + assertNotNull(node); + assertEquals(2, node.getChildren().size()); + + List<TreeMapNode> nodesFromRepeat2 = node.getChildren(); + node = AbstractTreeAssembler.searchNode(nodesFromRepeat2, "repeat2A.RepeatClass"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + node = AbstractTreeAssembler.searchNode(nodesFromRepeat2, "repeat2B.RepeatClass"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + } + + @Test + public void testFillWeights() { + assertEquals(SOME_ROOT_WEIGHT, TreeConverter.fillWeights(root), DELTA); + TreeMapNode parent = new TreeMapNode("childA", SOME_WEIGHT); + root.addChild(parent); + + final double weightAA = 5.0; + final double weightAB = 10.0; + parent.addChild(new TreeMapNode("childAA", weightAA)); + parent.addChild(new TreeMapNode("childAB", weightAB)); + + assertEquals(SOME_ROOT_WEIGHT, root.getRealWeight(), DELTA); + TreeConverter.fillWeights(root); + assertEquals(weightAA + weightAB, root.getRealWeight(), DELTA); + + List<TreeMapNode> children = parent.getChildren(); + + TreeMapNode child = AbstractTreeAssembler.searchNode(children, "childAA"); + assertNotNull(child); + assertEquals(weightAA, child.getRealWeight(), DELTA); + + child = AbstractTreeAssembler.searchNode(children, "childAB"); + assertNotNull(child); + assertEquals(weightAB, child.getRealWeight(), DELTA); + } + + @Test + public void testPackTree() { + TreeMapNode child = new TreeMapNode("childA", SOME_WEIGHT); + root.addChild(child); + child.addChild(new TreeMapNode("childAA", SOME_WEIGHT)); + child.addChild(new TreeMapNode("childAB", SOME_WEIGHT)); + child = new TreeMapNode("childB", SOME_WEIGHT); + root.addChild(child); + TreeMapNode grandchild = new TreeMapNode("childBA", SOME_WEIGHT); + child.addChild(grandchild); + grandchild.addChild(new TreeMapNode("childBAA", SOME_WEIGHT)); + + TreeConverter.packTree(root); + + assertEquals(0, child.getChildren().size()); + assertEquals("childB.childBA.childBAA", child.getLabel()); + + child = AbstractTreeAssembler.searchNode(root.getChildren(), "childA"); + assertNotNull(child); + assertNotNull(AbstractTreeAssembler.searchNode(child.getChildren(), "childAA")); + assertNotNull(AbstractTreeAssembler.searchNode(child.getChildren(), "childAB")); + } +}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HistogramConverter.java Wed Jun 29 12:23:10 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,182 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.util.ArrayList; -import java.util.List; - -import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; -import com.redhat.thermostat.common.utils.DescriptorConverter; -import com.redhat.thermostat.vm.heap.analysis.common.HistogramRecord; -import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; - -/** - * This class provides statics function to create a {@link TreeMapNode} tree - * from an ObjectHistrogram. - */ -public class HistogramConverter { - - /** - * The splitter regular expression to split records' className - */ - private static final String SPLIT_REG_EXP = "\\."; //escaped dot - - /** - * Key used to put into nodes the <i>number of instances</i> information - * stored in histogram records. - */ - private static final String NUMBER_OF = "Number Of Instances"; - - /** - * Call this method to create the full TreeMapNode object corresponding to - * the {@link ObjectHistogram} histogram given in input. - * @param histrogram the histogram to represent as TreeMapNode - * @return the resulting tree - */ - public static TreeMapNode convertToTreeMap(ObjectHistogram histrogram) { - TreeMapNode root = new TreeMapNode("", 0); - - List<HistogramRecord> records = new ArrayList<>(); - records.addAll(histrogram.getHistogram()); - - // build the tree from the histogram object - processRecords(records, root); - // calculates weights for inner nodes - fillWeights(root); - // collapse nodes with only one child - packTree(root); - return root; - } - - /** - * This method is responsible for building correctly the histogram - * corresponding tree. For each histogram record, a tree branch is created - * but only leaves node have a weight value. - * Furthermore, additional information are added to the nodes' map. - * - * @param records {@list} of HistogramRecord used to build the tree. - * @param root the tree's root. - */ - private static void processRecords(List<HistogramRecord> records, TreeMapNode root) { - - for (int i = 0; i < records.size(); i++) { - - TreeMapNode lastProcessed = root; - String className = records.get(i).getClassname(); - - // if className is a primitive type it is converted with its full name - className = DescriptorConverter.toJavaType(className); - - while (!className.equals("")) { - - String nodeId = className.split(SPLIT_REG_EXP)[0]; - - TreeMapNode child = searchNode(lastProcessed, nodeId); - if (child == null) { - child = new TreeMapNode(nodeId, 0); - lastProcessed.addChild(child); - } - - lastProcessed = child; - - className = className.substring(nodeId.length()); - if (className.startsWith(".")) { - className = className.substring(1); - } - // removes semicolon from leaves - if (className.endsWith(";")) { - className.replace(";", ""); - } - } - - // at this point lastProcessed references to a leaf - lastProcessed.setRealWeight(records.get(i).getTotalSize()); - lastProcessed.addInfo(NUMBER_OF, - Long.toString(records.get(i).getNumberOf())); - } - } - - private static TreeMapNode searchNode(TreeMapNode root, String nodeId) { - List<TreeMapNode> nodes = root.getChildren(); - for (TreeMapNode node : nodes) { - if (node.getLabel().equals(nodeId)) { - return node; - } - } - return null; - } - - /** - * This method calcs the real weights using a bottom-up traversal. From leaves, - * weights are passed to parent nodem which will have as weight the sum of - * the children's weights. - * - * @param node the subtree's root from which start to calc weights. - * @return the node's real weight. - */ - private static double fillWeights(TreeMapNode node) { - if (node.getChildren().size() == 0) { - return node.getRealWeight(); - } - - double sum = 0; - for (TreeMapNode child : node.getChildren()) { - sum += fillWeights(child); - } - node.setRealWeight(sum); - return node.getRealWeight(); - } - - /** - * This method allows to collapse nodes which have only one child. - * E.g. nodes labeled <i>com</i> and <i>example</i> are collapsed in the - * parent node, which will have as label <i>com.example</i>. - * @param node the subree's root from which start packing. - */ - private static void packTree(TreeMapNode node) { - if (node.getChildren().size() == 1) { - TreeMapNode child = node.getChildren().get(0); - node.setLabel(node.getLabel() + "." + child.getLabel()); - node.setChildren(child.getChildren()); - packTree(node); - } else { - for (TreeMapNode child : node.getChildren()) { - packTree(child); - } - } - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ObjectHistogramTreeAssembler.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; + +import com.redhat.thermostat.client.swing.components.experimental.AbstractTreeAssembler; +import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; +import com.redhat.thermostat.common.utils.DescriptorConverter; +import com.redhat.thermostat.vm.heap.analysis.common.HistogramRecord; +import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; + +public class ObjectHistogramTreeAssembler extends AbstractTreeAssembler<ObjectHistogram> { + + /** + * Key used to put into nodes the <i>number of instances</i> information + * stored in histogram records. + */ + private static final String NUMBER_OF = "Number Of Instances"; + + @Override + public void buildTree(ObjectHistogram histogram, TreeMapNode root) { + for (HistogramRecord record : histogram.getHistogram()) { + String className = record.getClassname(); + + // if className is a primitive type it is converted with its full name + className = DescriptorConverter.toJavaType(className); + TreeMapNode lastProcessed = processRecord(className, ".", root); + + // at this point lastProcessed references to a leaf + lastProcessed.setRealWeight(record.getTotalSize()); + lastProcessed.addInfo(NUMBER_OF, Long.toString(record.getNumberOf())); + } + } +}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapView.java Wed Jun 29 12:23:10 2016 -0400 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/SwingHeapTreeMapView.java Wed Jun 29 12:23:11 2016 -0400 @@ -43,6 +43,7 @@ import javax.swing.SwingUtilities; import com.redhat.thermostat.client.swing.SwingComponent; +import com.redhat.thermostat.client.swing.components.experimental.TreeConverter; import com.redhat.thermostat.client.swing.components.experimental.TreeMapComponent; import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; import com.redhat.thermostat.client.swing.components.experimental.TreeMapToolbar; @@ -68,7 +69,8 @@ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - TreeMapNode model = HistogramConverter.convertToTreeMap(histogram); + TreeMapNode model = TreeConverter.convertToTreeMap( + histogram, new ObjectHistogramTreeAssembler()); treeMap.setModel(model); panel.removeAll(); panel.add(treeMap, BorderLayout.CENTER);
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HistogramConverterTest.java Wed Jun 29 12:23:10 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertNotNull; - -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; -import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; -import com.sun.tools.hat.internal.model.JavaClass; -import com.sun.tools.hat.internal.model.JavaHeapObject; - -public class HistogramConverterTest { - - ObjectHistogram histrogram; - - @Before - public void setUp() throws Exception { - /* - * This is the classes structure used for the test and built using - * histogram. Nodes are compacted at the point of branching - * so for example the node relative to "example2.package1.Class1" is - * still just one node "example2.package1.Class1" but the node relative - * to "com.example1.package1.Class1" branches at "com" so becomes the - * common parent node "com" with children "example1" and "example2", - * and so fort: - * - * ________com_______ java - * / \ | - * __example1__ example2 lang - * / \ | | - * package1 package2 package1 Object - * / \ | | - * Class1 Class2 Class3 Class4 - * - * - * - * - * Expected tree after conversion: - * - * ________________________root_______________ - * / \ - * ________com_______ java.lang.Object - * / \ - * __example1__ example2.package1.Class4 - * / \ - * package1 package2.Class3 - * / \ - * Class1 Class2 - * - */ - final String[] classes = { - "com.example1.package1.Class1", - "com.example1.package1.Class2", - "com.example1.package2.Class3", - "com.example2.package1.Class4", - "example2.package1.Class1", - "java.lang.Object", - "repeat1.repeat1.RepeatClass", - "repeat2.repeat2A.RepeatClass", - "repeat2.repeat2B.RepeatClass", - }; - - histrogram = new ObjectHistogram(); - - for (int i = 0; i < classes.length; i++) { - final String className = classes[i]; - - histrogram.addThing(new JavaHeapObject() { - public int getSize() { - return 0; - } - - public long getId() { - return 0; - } - - public JavaClass getClazz() { - return new JavaClass(className, 0, 0, 0, 0, null, null, 0); - } - }); - } - } - - private static TreeMapNode searchNode(List<TreeMapNode> nodes, String nodeId) { - for (TreeMapNode node : nodes) { - if (node.getLabel().equals(nodeId)) { - return node; - } - } - return null; - } - - @Test - public void testconvertToTreeMap() { - TreeMapNode tree = HistogramConverter.convertToTreeMap(histrogram); - - List<TreeMapNode> nodes = tree.getChildren(); - assertEquals(5, nodes.size()); - - TreeMapNode node = searchNode(nodes, "com"); - assertNotNull(node); - - List<TreeMapNode> nodesFromCom = node.getChildren(); - - // example1 and example2 - assertEquals(2, nodesFromCom.size()); - node = searchNode(nodesFromCom, "example1"); - assertNotNull(node); - - // package2.Class3 and package1 - assertEquals(2, node.getChildren().size()); - - node = searchNode(node.getChildren(), "package2.Class3"); - assertNotNull(node); - assertTrue(node.getChildren().isEmpty()); - - node = searchNode(nodes, "java.lang.Object"); - assertNotNull(node); - assertTrue(node.getChildren().isEmpty()); - - node = searchNode(nodes, "example2.package1.Class1"); - assertNotNull(node); - assertTrue(node.getChildren().isEmpty()); - - // now on to the "repeat1.repeat1.RepeatClass" bunch - node = searchNode(nodes, "repeat1.repeat1.RepeatClass"); - assertNotNull(node); - assertTrue(node.getChildren().isEmpty()); - - node = searchNode(nodes, "repeat2"); - assertNotNull(node); - assertEquals(2, node.getChildren().size()); - - List<TreeMapNode> nodesFromRepeat2 = node.getChildren(); - node = searchNode(nodesFromRepeat2, "repeat2A.RepeatClass"); - assertNotNull(node); - assertTrue(node.getChildren().isEmpty()); - - node = searchNode(nodesFromRepeat2, "repeat2B.RepeatClass"); - assertNotNull(node); - assertTrue(node.getChildren().isEmpty()); - } - -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/ObjectHistogramTreeAssemblerTest.java Wed Jun 29 12:23:11 2016 -0400 @@ -0,0 +1,153 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.client.swing.components.experimental.AbstractTreeAssembler; +import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode; +import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram; +import com.sun.tools.hat.internal.model.JavaClass; +import com.sun.tools.hat.internal.model.JavaHeapObject; + +public class ObjectHistogramTreeAssemblerTest { + + private final String ROOT_LABEL = "root"; + private final double ROOT_WEIGHT = 10.0; + + private ObjectHistogram histogram; + private TreeMapNode root; + + @Before + public void setup() { + + final String[] classes = { + "com.example1.Class1", + "com.example1.Class2", + "com.example2", + "java.lang.Object", + }; + + histogram = new ObjectHistogram(); + for (int i = 0; i < classes.length; i++) { + final String className = classes[i]; + + histogram.addThing(new JavaHeapObject() { + public int getSize() { + return 0; + } + + public long getId() { + return 0; + } + + public JavaClass getClazz() { + return new JavaClass(className, 0, 0, 0, 0, null, null, 0); + } + }); + } + + root = new TreeMapNode(ROOT_LABEL, ROOT_WEIGHT); + } + + @Test + public void testBuildTree() { + /* + * This is a visualization of the expected resulting tree. + * + * _____________root____________ + * / \ + * ________com_______ java + * / \ | + * __example1__ example2 lang + * / \ | + * Class1 Class2 Object + * + */ + + ObjectHistogramTreeAssembler assembler = new ObjectHistogramTreeAssembler(); + assembler.buildTree(histogram, root); + + List<TreeMapNode> children = root.getChildren(); + assertEquals(2, children.size()); + + TreeMapNode node = AbstractTreeAssembler.searchNode(children, "java"); + assertNotNull(node); + children = node.getChildren(); + assertEquals(1, children.size()); + node = AbstractTreeAssembler.searchNode(children, "lang"); + assertNotNull(node); + children = node.getChildren(); + assertEquals(1, children.size()); + node = AbstractTreeAssembler.searchNode(children, "Object"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + children = root.getChildren(); + node = AbstractTreeAssembler.searchNode(children, "com"); + assertNotNull(node); + children = node.getChildren(); + assertEquals(2, children.size()); + + node = AbstractTreeAssembler.searchNode(children, "example2"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + node = AbstractTreeAssembler.searchNode(root.getChildren(), "com"); + assertNotNull(node); + node = AbstractTreeAssembler.searchNode(node.getChildren(), "example1"); + assertNotNull(node); + children = node.getChildren(); + assertEquals(2, children.size()); + + node = AbstractTreeAssembler.searchNode(children, "Class1"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + + node = AbstractTreeAssembler.searchNode(children, "Class2"); + assertNotNull(node); + assertTrue(node.getChildren().isEmpty()); + } + +}