changeset 1999:8a65bb11a6f9

Add profiler treemap PR3070 Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/020062.html Original-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-December/017148.html
author James Aziz <jaziz@redhat.com>
date Thu, 30 Jun 2016 11:36:54 -0400
parents c5a118f7a7e2
children 59385cdc7d0d
files client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponent.java client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponentTest.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/Activator.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/LocaleResources.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ProfilingResultNodeDataExtractor.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileTreeMapView.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileTreeMapViewProvider.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileService.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapView.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapViewProvider.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java vm-profiler/client-swing/src/main/resources/com/redhat/thermostat/vm/profiler/client/swing/internal/strings.properties vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ActivatorTest.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ProfilingResultNodeDataExtractorTest.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/TimeToolTipRendererTest.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileServiceTest.java
diffstat 19 files changed, 627 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponent.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponent.java	Thu Jun 30 11:36:54 2016 -0400
@@ -76,8 +76,6 @@
 import javax.swing.border.LineBorder;
 
 import com.redhat.thermostat.client.swing.ThermostatSwingCursors;
-import com.redhat.thermostat.common.Size;
-import com.redhat.thermostat.common.Size.Unit;
 
 /**
  * This class allows to represent a hierarchical data structure as a TreeMap.
@@ -145,7 +143,7 @@
     /**
      * Variable in which store last resize event call time.
      */
-    private static long lastCall = 0;
+    private long lastCall = 0;
 
     /**
      * Wait time in millisec to resize the TreeMap.
@@ -162,7 +160,7 @@
      * This object stores the last clicked rectangle in the TreeMap, in order to 
      * repaint it when another rectangle will be selected.
      */
-    private static Comp lastClicked;
+    private Comp lastClicked;
     
     /**
      * List of objects observing this.
@@ -591,7 +589,7 @@
     /**
      * Switch the component's border style to the one given in input.
      *
-     * @param borderStyle the border style to use
+     * @param newBorderStyle the border style to use
      */
     public void setBorderStyle(int newBorderStyle) {
         Border border;
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponentTest.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/experimental/TreeMapComponentTest.java	Thu Jun 30 11:36:54 2016 -0400
@@ -36,18 +36,14 @@
 
 package com.redhat.thermostat.client.swing.components.experimental;
 
-import org.jfree.chart.renderer.category.GradientBarPainter;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
-import javax.swing.JButton;
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
 import java.awt.BorderLayout;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -58,13 +54,18 @@
 import java.util.List;
 import java.util.Random;
 
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
 
 public class TreeMapComponentTest {
 
@@ -521,6 +522,10 @@
                 modelA.addChild(new TreeMapNode("AA", 2.0));
                 modelA.addChild(new TreeMapNode("AB", 3.0));
 
+                final TreeMapNode modelC = new TreeMapNode("C", 1.0);
+                modelC.addChild(new TreeMapNode("CA", 2.0));
+                modelC.addChild(new TreeMapNode("CB", 3.0));
+
                 final TreeMapNode modelB = new TreeMapNode("B", 5.0);
                 final Random generator = new Random();
                 for (int i = 0; i < 100; i++) {
@@ -535,19 +540,29 @@
                 // FIXME this hack should not be needed
                 UIManager.put("thermostat-default-font", Font.decode(Font.MONOSPACED));
 
-                final TreeMapComponent treeMap = new TreeMapComponent();
-                treeMap.setToolTipRenderer(new WeightRenderer());
-                treeMap.setModel(modelA);
+                JPanel container = new JPanel(new BorderLayout());
+                final JTabbedPane tabbedPane = new JTabbedPane();
 
-                JPanel container = new JPanel(new BorderLayout());
+                final TreeMapComponent treeMapA = new TreeMapComponent();
+                treeMapA.setToolTipRenderer(new WeightRenderer());
+                treeMapA.setModel(modelA);
+
+                final TreeMapComponent treeMapC = new TreeMapComponent();
+                treeMapC.setToolTipRenderer(new WeightRenderer());
+                treeMapC.setModel(modelC);
 
                 JPanel buttonPanel = new JPanel();
                 JButton changeModelButton = new JButton("Change model");
                 changeModelButton.addActionListener(new ActionListener() {
                     @Override
                     public void actionPerformed(ActionEvent e) {
-                        TreeMapNode newModel = treeMap.getTreeMapRoot() == modelA ? modelB : modelA;
-                        treeMap.setModel(newModel);
+                        if (tabbedPane.getSelectedIndex() == 0) {
+                            TreeMapNode newModel = treeMapA.getTreeMapRoot() == modelA ? modelB : modelA;
+                            treeMapA.setModel(newModel);
+                        } else {
+                            TreeMapNode newModel = treeMapC.getTreeMapRoot() == modelC ? modelB : modelC;
+                            treeMapC.setModel(newModel);
+                        }
                     }
                 });
                 buttonPanel.add(changeModelButton);
@@ -556,10 +571,15 @@
                 addChildToRootButton.addActionListener(new ActionListener() {
                     @Override
                     public void actionPerformed(ActionEvent e) {
-                        TreeMapNode currentModel = treeMap.getTreeMapRoot();
-                        currentModel.addChild(new TreeMapNode("new", 5.0));
-
-                        treeMap.setModel(currentModel);
+                        if (tabbedPane.getSelectedIndex() == 0) {
+                            TreeMapNode currentModel = treeMapA.getTreeMapRoot();
+                            currentModel.addChild(new TreeMapNode("new", 5.0));
+                            treeMapA.setModel(currentModel);
+                        } else {
+                            TreeMapNode currentModel = treeMapC.getTreeMapRoot();
+                            currentModel.addChild(new TreeMapNode("new", 5.0));
+                            treeMapC.setModel(currentModel);
+                        }
                     }
                 });
                 buttonPanel.add(addChildToRootButton);
@@ -568,21 +588,34 @@
                 addRandomNodeButton.addActionListener(new ActionListener() {
                     @Override
                     public void actionPerformed(ActionEvent e) {
-                        TreeMapNode currentModel = treeMap.getTreeMapRoot();
-                        List<TreeMapNode> allNodes = new ArrayList<>();
-                        findAllNodes(allNodes, currentModel);
-                        TreeMapNode parent = allNodes.get(generator.nextInt(allNodes.size()));
-                        double weight = Math.pow(10, generator.nextInt(4));
-                        parent.addChild(new TreeMapNode("rand", weight));
+                        if (tabbedPane.getSelectedIndex() == 0) {
+                            TreeMapNode currentModel = treeMapA.getTreeMapRoot();
+                            List<TreeMapNode> allNodes = new ArrayList<>();
+                            findAllNodes(allNodes, currentModel);
+                            TreeMapNode parent = allNodes.get(generator.nextInt(allNodes.size()));
+                            double weight = Math.pow(10, generator.nextInt(4));
+                            parent.addChild(new TreeMapNode("rand", weight));
 
-                        treeMap.setModel(currentModel);
+                            treeMapA.setModel(currentModel);
+                        } else {
+                            TreeMapNode currentModel = treeMapC.getTreeMapRoot();
+                            List<TreeMapNode> allNodes = new ArrayList<>();
+                            findAllNodes(allNodes, currentModel);
+                            TreeMapNode parent = allNodes.get(generator.nextInt(allNodes.size()));
+                            double weight = Math.pow(10, generator.nextInt(4));
+                            parent.addChild(new TreeMapNode("rand", weight));
+
+                            treeMapC.setModel(currentModel);
+                        }
                     }
                 });
                 buttonPanel.add(addRandomNodeButton);
 
                 container.add(buttonPanel, BorderLayout.PAGE_START);
-                container.add(treeMap, BorderLayout.CENTER);
+                tabbedPane.addTab("TMA", treeMapA);
+                tabbedPane.addTab("TMC", treeMapC);
 
+                container.add(tabbedPane, BorderLayout.CENTER);
                 mainWindow.add(container, BorderLayout.CENTER);
 
                 mainWindow.setSize(500, 200);
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/Activator.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/Activator.java	Thu Jun 30 11:36:54 2016 -0400
@@ -61,6 +61,9 @@
 
     @Override
     public void start(final BundleContext context) throws Exception {
+        VmProfileTreeMapViewProvider vmProfileTreeMapViewProvider = new SwingVmProfileTreeMapViewProvider();
+        context.registerService(VmProfileTreeMapViewProvider.class.getName(),
+                vmProfileTreeMapViewProvider, null);
 
         Class<?>[] deps = new Class<?>[] {
                 ApplicationService.class,
@@ -69,6 +72,7 @@
                 VmInfoDAO.class,
                 ProfileDAO.class,
                 RequestQueue.class,
+                VmProfileTreeMapViewProvider.class,
         };
 
         tracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
@@ -80,8 +84,11 @@
                 VmInfoDAO vmInfoDao = (VmInfoDAO) services.get(VmInfoDAO.class.getName());
                 ProfileDAO profileDao = (ProfileDAO) services.get(ProfileDAO.class.getName());
                 RequestQueue queue = (RequestQueue) services.get(RequestQueue.class.getName());
+                VmProfileTreeMapViewProvider treeMapViewProvider = (VmProfileTreeMapViewProvider) services
+                        .get(VmProfileTreeMapViewProvider.class.getName());
 
-                InformationService<VmRef> profileService = new VmProfileService(service, notifier, agentInfoDao, vmInfoDao, profileDao, queue);
+                InformationService<VmRef> profileService = new VmProfileService(service, notifier,
+                        agentInfoDao, vmInfoDao, profileDao, queue, treeMapViewProvider);
 
                 Hashtable<String,String> properties = new Hashtable<>();
                 properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName());
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/LocaleResources.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/LocaleResources.java	Thu Jun 30 11:36:54 2016 -0400
@@ -54,6 +54,8 @@
     STOPPING_PROFILING,
 
     PROFILER_LIST_ITEM,
+    PROFILER_RESULTS_TABLE,
+    PROFILER_RESULTS_TREEMAP,
     PROFILER_RESULTS_METHOD,
     PROFILER_RESULTS_PERCENTAGE_TIME,
     PROFILER_RESULTS_TIME,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ProfilingResultNodeDataExtractor.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,64 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+import java.util.Collection;
+import java.util.regex.Pattern;
+
+import com.redhat.thermostat.client.swing.components.experimental.NodeDataExtractor;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult.MethodInfo;
+
+public class ProfilingResultNodeDataExtractor implements NodeDataExtractor<ProfilingResult, MethodInfo> {
+
+    static final String DELIMITER = ".";
+
+    @Override
+    public String[] getNodes(MethodInfo methodInfo) {
+        return methodInfo.decl.getName().split(Pattern.quote(DELIMITER));
+    }
+
+    @Override
+    public double getWeight(MethodInfo methodInfo) {
+        return methodInfo.totalTimeInMillis;
+    }
+
+    @Override
+    public Collection<MethodInfo> getAsCollection(ProfilingResult result) {
+        return result.getMethodInfo();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileTreeMapView.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,91 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.experimental.TreeMap;
+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;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
+
+public class SwingVmProfileTreeMapView extends VmProfileTreeMapView implements SwingComponent {
+    private TreeMapComponent treeMapComp;
+    private final JPanel panel;
+
+    public SwingVmProfileTreeMapView() {
+        panel = new JPanel();
+        treeMapComp = new TreeMapComponent();
+        treeMapComp.setToolTipRenderer(new TimeToolTipRenderer());
+        panel.setLayout(new BorderLayout());
+    }
+
+    @Override
+    public void display(final ProfilingResult result) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                TreeMap<ProfilingResult, ProfilingResult.MethodInfo> treeMap =
+                        new TreeMap<>(result, new ProfilingResultNodeDataExtractor());
+                treeMapComp.setModel(treeMap.getRoot());
+                panel.removeAll();
+                panel.add(treeMapComp, BorderLayout.CENTER);
+                panel.add(new TreeMapToolbar(treeMapComp), BorderLayout.NORTH);
+                panel.revalidate();
+                panel.repaint();
+            }
+        });
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return panel;
+    }
+
+    public static class TimeToolTipRenderer implements TreeMapComponent.ToolTipRenderer {
+        @Override
+        public String render(TreeMapNode node) {
+            return node.getLabel() + " - " + (long) node.getRealWeight() + "ms";
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileTreeMapViewProvider.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,45 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+public class SwingVmProfileTreeMapViewProvider implements VmProfileTreeMapViewProvider {
+
+    @Override
+    public VmProfileTreeMapView createView() {
+        return new SwingVmProfileTreeMapView();
+    }
+}
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java	Thu Jun 30 11:36:54 2016 -0400
@@ -59,6 +59,7 @@
 import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
 import javax.swing.JTable;
 import javax.swing.JToggleButton;
 import javax.swing.ListSelectionModel;
@@ -84,8 +85,9 @@
 import com.redhat.thermostat.client.ui.Palette;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.utils.MethodDescriptorConverter.MethodDeclaration;
 import com.redhat.thermostat.common.utils.StringUtils;
-import com.redhat.thermostat.common.utils.MethodDescriptorConverter.MethodDeclaration;
+import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult.MethodInfo;
@@ -106,6 +108,7 @@
     private final CopyOnWriteArrayList<ActionListener<ProfileAction>> listeners = new CopyOnWriteArrayList<>();
 
     private HeaderPanel mainContainer;
+    private JTabbedPane tabPane;
 
     private ActionToggleButton toggleButton;
 
@@ -136,6 +139,7 @@
 
     public SwingVmProfileView() {
         listModel = new DefaultListModel<>();
+        tabPane = new JTabbedPane();
 
         toggleButton = new ActionToggleButton(START_ICON, STOP_ICON, translator.localize(LocaleResources.START_PROFILING));
         toggleButton.toggleText(false);
@@ -259,8 +263,10 @@
             }
         };
 
-        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
-                profileListPane, profileTable.wrap());
+        tabPane.addTab(translator.localize(LocaleResources.PROFILER_RESULTS_TABLE).getContents(),
+                profileTable.wrap());
+
+        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, profileListPane, tabPane);
         splitPane.setDividerLocation(SPLIT_PANE_RATIO);
         splitPane.setResizeWeight(0.5);
 
@@ -416,6 +422,16 @@
     }
 
     @Override
+    public void addTabToTabbedPane(final LocalizedString title, final Component component) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                tabPane.addTab(title.getContents(), component);
+            }
+        });
+    }
+
+    @Override
     public Component getUiComponent() {
         return mainContainer;
     }
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java	Thu Jun 30 11:36:54 2016 -0400
@@ -44,6 +44,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 import com.redhat.thermostat.client.command.RequestQueue;
@@ -53,6 +54,7 @@
 import com.redhat.thermostat.client.core.views.BasicView;
 import com.redhat.thermostat.client.core.views.BasicView.Action;
 import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ApplicationService;
@@ -97,6 +99,7 @@
     private final VmRef vm;
 
     private VmProfileView view;
+    private VmProfileTreeMapView treeMapView;
 
     private Timer updater;
 
@@ -111,15 +114,15 @@
 
     public VmProfileController(ApplicationService service, ProgressNotifier notifier,
             AgentInfoDAO agentInfoDao, VmInfoDAO vmInfoDao, ProfileDAO dao,
-            RequestQueue queue,
-            VmRef vm) {
-        this(service, notifier, agentInfoDao, vmInfoDao, dao, queue, new SystemClock(), new SwingVmProfileView(), vm);
+            RequestQueue queue, VmProfileTreeMapViewProvider treeMapViewProvider, VmRef vm) {
+        this(service, notifier, agentInfoDao, vmInfoDao, dao, queue, new SystemClock(),
+                new SwingVmProfileView(), vm, treeMapViewProvider);
     }
 
     VmProfileController(final ApplicationService service, ProgressNotifier notifier,
             AgentInfoDAO agentInfoDao, VmInfoDAO vmInfoDao, ProfileDAO dao,
-            RequestQueue queue, Clock clock,
-            final VmProfileView view, VmRef vm) {
+            RequestQueue queue, Clock clock, final VmProfileView view, VmRef vm,
+            VmProfileTreeMapViewProvider treeMapViewProvider) {
         this.service = service;
         this.notifier = notifier;
         this.agentInfoDao = agentInfoDao;
@@ -134,6 +137,10 @@
             service.getApplicationCache().addAttribute(STATE_MAP_KEY, STATE_BUNDLE_MAP);
         }
 
+        this.treeMapView = Objects.requireNonNull(treeMapViewProvider.createView());
+        view.addTabToTabbedPane(translator.localize(LocaleResources.PROFILER_RESULTS_TREEMAP),
+                ((SwingComponent) this.treeMapView).getUiComponent());
+
         // TODO dispose the timer when done
         updater = service.getTimerFactory().createTimer();
         updater.setSchedulingType(SchedulingType.FIXED_DELAY);
@@ -340,7 +347,9 @@
         String profileId = selectedProfile.name;
         InputStream in = profileDao.loadProfileDataById(vm, profileId);
         ProfilingResult result = new ProfilingResultParser().parse(in);
+
         view.setProfilingDetailData(result);
+        treeMapView.display(result);
     }
 
     @Override
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileService.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileService.java	Thu Jun 30 11:36:54 2016 -0400
@@ -56,16 +56,18 @@
     private VmInfoDAO vmInfoDao;
     private ProfileDAO dao;
     private RequestQueue queue;
+    private VmProfileTreeMapViewProvider treeMapViewProvider;
 
     public VmProfileService(ApplicationService service, ProgressNotifier notifier,
             AgentInfoDAO agentInfoDao, VmInfoDAO vmInfoDao, ProfileDAO dao,
-            RequestQueue queue) {
+            RequestQueue queue, VmProfileTreeMapViewProvider treeMapViewProvider) {
         this.service = service;
         this.notifier = notifier;
         this.agentInfoDao = agentInfoDao;
         this.vmInfoDao = vmInfoDao;
         this.dao = dao;
         this.queue = queue;
+        this.treeMapViewProvider = treeMapViewProvider;
     }
 
     @Override
@@ -81,7 +83,8 @@
 
     @Override
     public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
-        return new VmProfileController(service, notifier, agentInfoDao, vmInfoDao, dao, queue, ref);
+        return new VmProfileController(service, notifier, agentInfoDao, vmInfoDao, dao, queue,
+                treeMapViewProvider, ref);
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapView.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,46 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
+
+public abstract class VmProfileTreeMapView extends BasicView implements UIComponent{
+
+    public abstract void display(ProfilingResult result);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapViewProvider.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,45 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+import com.redhat.thermostat.client.core.views.ViewProvider;
+
+public interface VmProfileTreeMapViewProvider extends ViewProvider {
+
+    @Override
+    public VmProfileTreeMapView createView();
+}
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java	Thu Jun 30 11:36:54 2016 -0400
@@ -36,13 +36,15 @@
 
 package com.redhat.thermostat.vm.profiler.client.swing.internal;
 
+import java.awt.Component;
 import java.util.List;
 import java.util.Objects;
 
+import com.redhat.thermostat.client.core.ToggleActionState;
 import com.redhat.thermostat.client.core.views.BasicView;
 import com.redhat.thermostat.client.core.views.UIComponent;
-import com.redhat.thermostat.client.core.ToggleActionState;
 import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
 
 public abstract class VmProfileView extends BasicView implements UIComponent {
@@ -135,4 +137,6 @@
 
     public abstract void setProfilingDetailData(ProfilingResult results);
 
+    public abstract void addTabToTabbedPane(final LocalizedString title, final Component component);
+
 }
--- a/vm-profiler/client-swing/src/main/resources/com/redhat/thermostat/vm/profiler/client/swing/internal/strings.properties	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/main/resources/com/redhat/thermostat/vm/profiler/client/swing/internal/strings.properties	Thu Jun 30 11:36:54 2016 -0400
@@ -14,6 +14,8 @@
 STOPPING_PROFILING = Stopping profiling\u2026
 
 PROFILER_LIST_ITEM = Session @ {1} ({0})
+PROFILER_RESULTS_TABLE = Table
+PROFILER_RESULTS_TREEMAP = TreeMap
 PROFILER_RESULTS_METHOD = Method
 PROFILER_RESULTS_PERCENTAGE_TIME = Percentage
 PROFILER_RESULTS_TIME = Total Time ({0})
--- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ActivatorTest.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ActivatorTest.java	Thu Jun 30 11:36:54 2016 -0400
@@ -68,8 +68,8 @@
         VmInfoDAO vmInfoDao = mock(VmInfoDAO.class);
         bundleContext.registerService(VmInfoDAO.class, vmInfoDao, null);
 
-        ProfileDAO profielDao = mock(ProfileDAO.class);
-        bundleContext.registerService(ProfileDAO.class, profielDao, null);
+        ProfileDAO profileDao = mock(ProfileDAO.class);
+        bundleContext.registerService(ProfileDAO.class, profileDao, null);
 
         RequestQueue requestQueue = mock(RequestQueue.class);
         bundleContext.registerService(RequestQueue.class, requestQueue, null);
@@ -79,6 +79,7 @@
         activator.start(bundleContext);
 
         assertTrue(bundleContext.isServiceRegistered(InformationService.class.getName(), VmProfileService.class));
+        assertTrue(bundleContext.isServiceRegistered(VmProfileTreeMapViewProvider.class.getName(), SwingVmProfileTreeMapViewProvider.class));
 
         activator.stop(bundleContext);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/ProfilingResultNodeDataExtractorTest.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,98 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.utils.MethodDescriptorConverter.MethodDeclaration;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult.MethodInfo;
+
+public class ProfilingResultNodeDataExtractorTest {
+
+    private static final String[] NODES = {"foo", "bar", "jar"};
+    private static final long TOTAL_TIME = 1024;
+    private static final int SOME_INT = 50;
+    private static final double DELTA = 0.01;
+
+    private ProfilingResultNodeDataExtractor extractor;
+    private MethodInfo methodInfo;
+    private ProfilingResult result;
+
+    @Before
+    public void setUp() {
+        extractor = new ProfilingResultNodeDataExtractor();
+
+        String methodName = NODES[0] + extractor.DELIMITER + NODES[1] + extractor.DELIMITER + NODES[2];
+        MethodDeclaration decl = new MethodDeclaration(methodName, Arrays.asList("int"), "int");
+        methodInfo = new MethodInfo(decl, TOTAL_TIME, SOME_INT);
+
+        List<MethodInfo> methodInfos = new ArrayList<>();
+        methodInfos.add(methodInfo);
+        methodInfos.add(new MethodInfo(new MethodDeclaration("car", Arrays.asList("char"), "char"),
+                SOME_INT, SOME_INT));
+        result = new ProfilingResult(methodInfos);
+    }
+
+    @Test
+    public void testGetNodes() {
+        String[] result = extractor.getNodes(methodInfo);
+        assertTrue(Arrays.equals(NODES, result));
+    }
+
+    @Test
+    public void testGetWeight() {
+        double weight = extractor.getWeight(methodInfo);
+        assertEquals(TOTAL_TIME, weight, DELTA);
+    }
+
+    @Test
+    public void testGetAsCollection() {
+        Collection<MethodInfo> collection = extractor.getAsCollection(result);
+        assertEquals(2, collection.size());
+        assertTrue(collection.contains(methodInfo));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/TimeToolTipRendererTest.java	Thu Jun 30 11:36:54 2016 -0400
@@ -0,0 +1,76 @@
+/*
+ * 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.profiler.client.swing.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.client.swing.components.experimental.TreeMap;
+import com.redhat.thermostat.client.swing.components.experimental.TreeMapNode;
+import com.redhat.thermostat.vm.profiler.client.swing.internal.SwingVmProfileTreeMapView.TimeToolTipRenderer;
+
+public class TimeToolTipRendererTest {
+
+    private static final String ROOT_LABEL = "root";
+    private static final String CHILD_LABEL = "child";
+    private static final double ROOT_WEIGHT = 1024.0;
+    private static final double CHILD_WEIGHT = 3.14;
+
+    private TimeToolTipRenderer renderer;
+    private TreeMapNode root;
+
+    @Before
+    public void setUp() {
+        root = new TreeMapNode(ROOT_LABEL, ROOT_WEIGHT);
+        root.addChild(new TreeMapNode(CHILD_LABEL, CHILD_WEIGHT));
+        renderer = new TimeToolTipRenderer();
+    }
+
+    @Test
+    public void testTimeToolTipRenderer() {
+        String result = renderer.render(root);
+        assertEquals("root - 1024ms", result);
+
+        TreeMapNode child = TreeMap.searchNode(root, CHILD_LABEL);
+        assertNotNull(child);
+        result = renderer.render(child);
+        assertEquals("child - 3ms", result);
+    }
+}
--- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java	Thu Jun 30 11:36:54 2016 -0400
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.vm.profiler.client.swing.internal;
 
+import junit.framework.Assert;
+
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
@@ -52,6 +54,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.awt.Component;
 import java.io.ByteArrayInputStream;
 import java.net.InetSocketAddress;
 import java.nio.charset.StandardCharsets;
@@ -62,8 +65,6 @@
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import com.redhat.thermostat.common.ApplicationCache;
-import org.hamcrest.core.IsNull;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -71,8 +72,10 @@
 import com.redhat.thermostat.client.command.RequestQueue;
 import com.redhat.thermostat.client.core.progress.ProgressNotifier;
 import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationCache;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.common.Timer;
@@ -117,6 +120,7 @@
     private Clock clock;
     private VmProfileView view;
     private VmRef vm;
+    private VmProfileTreeMapViewProvider treeMapViewProvider;
 
     private VmProfileController controller;
     private HostRef agent;
@@ -155,12 +159,35 @@
         when(vm.getHostRef()).thenReturn(agent);
         when(vm.getVmId()).thenReturn(VM_ID);
 
+        treeMapViewProvider = mock(VmProfileTreeMapViewProvider.class);
+        VmProfileTreeMapView vmProfileTreeMapView = mock(SwingVmProfileTreeMapView.class);
+        when(treeMapViewProvider.createView()).thenReturn(vmProfileTreeMapView);
+        when(((SwingComponent) vmProfileTreeMapView).getUiComponent()).thenReturn(mock(Component.class));
+
         AgentInformation agentInfo = new AgentInformation();
         agentInfo.setAlive(true);
         agentInfo.setConfigListenAddress(AGENT_HOST + ":" + AGENT_PORT);
         when(agentInfoDao.getAgentInformation(agent)).thenReturn(agentInfo);
     }
 
+    @Test (expected = NullPointerException.class)
+    public void testMainConstructorFailsWithInvalidViewProvider() {
+        VmProfileTreeMapViewProvider viewProvider = mock(VmProfileTreeMapViewProvider.class);
+        when(viewProvider.createView()).thenReturn(null);
+        new VmProfileController(appService, notifier, agentInfoDao, vmInfoDao, profileDao, queue,
+                clock, view, vm, viewProvider);
+    }
+
+    @Test
+    public void testMainConstructorAcceptsValidParameters() {
+        try {
+            new VmProfileController(appService, notifier, agentInfoDao, vmInfoDao, profileDao,
+                    queue, clock, view, vm, treeMapViewProvider);
+        } catch (NullPointerException e) {
+            Assert.fail("No exception expected");
+        }
+    }
+
     @Test
     public void timerRunsWhenVisible() throws Exception {
         when(appCache.getAttribute(any(String.class))).thenReturn(new HashMap<>());
@@ -482,7 +509,8 @@
     }
 
     private VmProfileController createController() {
-        return new VmProfileController(appService, notifier, agentInfoDao, vmInfoDao, profileDao, queue, clock, view, vm);
+        return new VmProfileController(appService, notifier, agentInfoDao, vmInfoDao, profileDao,
+                queue, clock, view, vm, treeMapViewProvider);
     }
 
     private void assertRequestEquals(Request actual, Request expected) {
--- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileServiceTest.java	Thu Jun 30 11:35:49 2016 -0400
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileServiceTest.java	Thu Jun 30 11:36:54 2016 -0400
@@ -50,7 +50,7 @@
     public void worksWithDeadAndAliveVms() throws Exception {
         VmRef vm = mock(VmRef.class);
 
-        VmProfileService service = new VmProfileService(null, null, null, null, null, null);
+        VmProfileService service = new VmProfileService(null, null, null, null, null, null, null);
         Filter<VmRef> filter = service.getFilter();
 
         assertTrue(filter.matches(vm));