1 /*
   2  * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.jconsole;
  27 
  28 import java.awt.*;
  29 import java.io.*;
  30 import java.lang.management.*;
  31 import java.lang.reflect.*;
  32 import java.net.URL;
  33 import java.text.*;
  34 import java.util.*;
  35 import java.util.concurrent.*;
  36 
  37 import javax.swing.*;
  38 import javax.swing.event.*;
  39 import javax.swing.text.*;
  40 
  41 import static sun.tools.jconsole.Formatter.*;
  42 import static sun.tools.jconsole.Resources.*;
  43 import static sun.tools.jconsole.Utilities.*;
  44 
  45 @SuppressWarnings("serial")
  46 class SummaryTab extends Tab {
  47     private static final String cpuUsageKey = "cpu";
  48     private static final String cpuUsageName = getText("CPU Usage");
  49     private static final String cpuUsageFormat = "CPUUsageFormat";
  50 
  51     private static final String newDivider =   "<tr><td colspan=4><font size =-1><hr>";
  52     private static final String newTable =     "<tr><td colspan=4 align=left><table cellpadding=1>";
  53     private static final String newLeftTable = "<tr><td colspan=2 align=left><table cellpadding=1>";
  54     private static final String newRightTable =  "<td colspan=2 align=left><table cellpadding=1>";
  55     private static final String endTable = "</table>";
  56 
  57     private static final int CPU_DECIMALS = 1;
  58 
  59     private CPUOverviewPanel overviewPanel;
  60     private DateFormat headerDateTimeFormat;
  61     private String pathSeparator = null;
  62     HTMLPane info;
  63 
  64     private static class Result {
  65         long upTime = -1L;
  66         long processCpuTime = -1L;
  67         long timeStamp;
  68         int nCPUs;
  69         String summary;
  70     }
  71 
  72     public static String getTabName() {
  73         return Resources.getText("SummaryTab.tabName");
  74     }
  75 
  76     public SummaryTab(VMPanel vmPanel) {
  77         super(vmPanel, getTabName());
  78 
  79         setLayout(new BorderLayout());
  80 
  81         info = new HTMLPane();
  82         setAccessibleName(info, getTabName());
  83         add(new JScrollPane(info));
  84 
  85         headerDateTimeFormat =
  86             getDateTimeFormat("SummaryTab.headerDateTimeFormat");
  87     }
  88 
  89     public SwingWorker<?, ?> newSwingWorker() {
  90         return new SwingWorker<Result, Object>() {
  91             public Result doInBackground() {
  92                 return formatSummary();
  93             }
  94 
  95 
  96             protected void done() {
  97                 try {
  98                     Result result = get();
  99                     if (result != null) {
 100                         info.setText(result.summary);
 101                         if (overviewPanel != null &&
 102                             result.upTime > 0L &&
 103                             result.processCpuTime >= 0L) {
 104 
 105                             overviewPanel.updateCPUInfo(result);
 106                         }
 107                     }
 108                 } catch (InterruptedException ex) {
 109                 } catch (ExecutionException ex) {
 110                     if (JConsole.isDebug()) {
 111                         ex.printStackTrace();
 112                     }
 113                 }
 114             }
 115         };
 116     }
 117 
 118     StringBuilder buf;
 119 
 120     synchronized Result formatSummary() {
 121         Result result = new Result();
 122         ProxyClient proxyClient = vmPanel.getProxyClient();
 123         if (proxyClient.isDead()) {
 124             return null;
 125         }
 126 
 127         buf = new StringBuilder();
 128         append("<table cellpadding=1>");
 129 
 130         try {
 131             RuntimeMXBean         rmBean     = proxyClient.getRuntimeMXBean();
 132             CompilationMXBean     cmpMBean   = proxyClient.getCompilationMXBean();
 133             ThreadMXBean          tmBean     = proxyClient.getThreadMXBean();
 134             MemoryMXBean          memoryBean = proxyClient.getMemoryMXBean();
 135             ClassLoadingMXBean    clMBean    = proxyClient.getClassLoadingMXBean();
 136             OperatingSystemMXBean osMBean    = proxyClient.getOperatingSystemMXBean();
 137             com.sun.management.OperatingSystemMXBean sunOSMBean  =
 138                proxyClient.getSunOperatingSystemMXBean();
 139 
 140             append("<tr><td colspan=4>");
 141             append("<center><b>" + getText("SummaryTab.tabName") + "</b></center>");
 142             String dateTime =
 143                 headerDateTimeFormat.format(System.currentTimeMillis());
 144             append("<center>" + dateTime + "</center>");
 145 
 146             append(newDivider);
 147 
 148             {  // VM info
 149                 append(newLeftTable);
 150                 append("Connection name", vmPanel.getDisplayName());
 151                 append("Virtual Machine",
 152                        getText("SummaryTab.vmVersion",
 153                                rmBean.getVmName(), rmBean.getVmVersion()));
 154                 append("Vendor", rmBean.getVmVendor());
 155                 append("Name", rmBean.getName());
 156                 append(endTable);
 157 
 158                 append(newRightTable);
 159                 result.upTime = rmBean.getUptime();
 160                 append("Uptime", formatTime(result.upTime));
 161                 if (sunOSMBean != null) {
 162                     result.processCpuTime = sunOSMBean.getProcessCpuTime();
 163                     append("Process CPU time", formatNanoTime(result.processCpuTime));
 164                 }
 165 
 166                 if (cmpMBean != null) {
 167                     append("JIT compiler", cmpMBean.getName());
 168                     append("Total compile time",
 169                            cmpMBean.isCompilationTimeMonitoringSupported()
 170                                     ? formatTime(cmpMBean.getTotalCompilationTime())
 171                                     : getText("Unavailable"));
 172                 } else {
 173                     append("JIT compiler", getText("Unavailable"));
 174                 }
 175                 append(endTable);
 176             }
 177 
 178             append(newDivider);
 179 
 180             {  // Threads and Classes
 181                 append(newLeftTable);
 182                 int tlCount = tmBean.getThreadCount();
 183                 int tdCount = tmBean.getDaemonThreadCount();
 184                 int tpCount = tmBean.getPeakThreadCount();
 185                 long ttCount = tmBean.getTotalStartedThreadCount();
 186                 String[] strings1 = formatLongs(tlCount, tpCount,
 187                                                 tdCount, ttCount);
 188                 append("Live Threads",          strings1[0]);
 189                 append("Peak",                  strings1[1]);
 190                 append("Daemon threads",        strings1[2]);
 191                 append("Total threads started", strings1[3]);
 192                 append(endTable);
 193 
 194                 append(newRightTable);
 195                 long clCount = clMBean.getLoadedClassCount();
 196                 long cuCount = clMBean.getUnloadedClassCount();
 197                 long ctCount = clMBean.getTotalLoadedClassCount();
 198                 String[] strings2 = formatLongs(clCount, cuCount, ctCount);
 199                 append("Current classes loaded", strings2[0]);
 200                 append("Total classes loaded",   strings2[2]);
 201                 append("Total classes unloaded", strings2[1]);
 202                 append(null, "");
 203                 append(endTable);
 204             }
 205 
 206             append(newDivider);
 207 
 208             {  // Memory
 209                 MemoryUsage u = memoryBean.getHeapMemoryUsage();
 210 
 211                 append(newLeftTable);
 212                 String[] strings1 = formatKByteStrings(u.getUsed(), u.getMax());
 213                 append("Current heap size", strings1[0]);
 214                 append("Maximum heap size", strings1[1]);
 215                 append(endTable);
 216 
 217                 append(newRightTable);
 218                 String[] strings2 = formatKByteStrings(u.getCommitted());
 219                 append("Committed memory",  strings2[0]);
 220                 append("SummaryTab.pendingFinalization.label",
 221                        getText("SummaryTab.pendingFinalization.value",
 222                                memoryBean.getObjectPendingFinalizationCount()));
 223                 append(endTable);
 224 
 225                 append(newTable);
 226                 Collection<GarbageCollectorMXBean> garbageCollectors =
 227                                             proxyClient.getGarbageCollectorMXBeans();
 228                 for (GarbageCollectorMXBean garbageCollectorMBean : garbageCollectors) {
 229                     String gcName = garbageCollectorMBean.getName();
 230                     long gcCount = garbageCollectorMBean.getCollectionCount();
 231                     long gcTime = garbageCollectorMBean.getCollectionTime();
 232 
 233                     append("Garbage collector",
 234                            getText("GcInfo", gcName, gcCount,
 235                                    (gcTime >= 0) ? formatTime(gcTime)
 236                                                  : getText("Unavailable")),
 237                            4);
 238                 }
 239                 append(endTable);
 240             }
 241 
 242             append(newDivider);
 243 
 244             {  // Operating System info
 245                 append(newLeftTable);
 246                 String osName = osMBean.getName();
 247                 String osVersion = osMBean.getVersion();
 248                 String osArch = osMBean.getArch();
 249                 result.nCPUs = osMBean.getAvailableProcessors();
 250                 append("Operating System", osName + " " + osVersion);
 251                 append("Architecture", osArch);
 252                 append("Number of processors", result.nCPUs+"");
 253 
 254                 if (pathSeparator == null) {
 255                     // Must use separator of remote OS, not File.pathSeparator
 256                     // from this local VM. In the future, consider using
 257                     // RuntimeMXBean to get the remote system property.
 258                     pathSeparator = osName.startsWith("Windows ") ? ";" : ":";
 259                 }
 260 
 261                 if (sunOSMBean != null) {
 262                     String[] kbStrings1 =
 263                         formatKByteStrings(sunOSMBean.getCommittedVirtualMemorySize());
 264 
 265                     String[] kbStrings2 =
 266                         formatKByteStrings(sunOSMBean.getTotalPhysicalMemorySize(),
 267                                            sunOSMBean.getFreePhysicalMemorySize(),
 268                                            sunOSMBean.getTotalSwapSpaceSize(),
 269                                            sunOSMBean.getFreeSwapSpaceSize());
 270 
 271                     append("Committed virtual memory", kbStrings1[0]);
 272                     append(endTable);
 273 
 274                     append(newRightTable);
 275                     append("Total physical memory", kbStrings2[0]);
 276                     append("Free physical memory",  kbStrings2[1]);
 277                     append("Total swap space",      kbStrings2[2]);
 278                     append("Free swap space",       kbStrings2[3]);
 279                 }
 280 
 281                 append(endTable);
 282             }
 283 
 284             append(newDivider);
 285 
 286             {  // VM arguments and paths
 287                 append(newTable);
 288                 String args = "";
 289                 java.util.List<String> inputArguments = rmBean.getInputArguments();
 290                 for (String arg : inputArguments) {
 291                     args += arg + " ";
 292                 }
 293                 append("VM arguments", args, 4);
 294                 append("Class path",   rmBean.getClassPath(), 4);
 295                 append("Library path", rmBean.getLibraryPath(), 4);
 296                 append("Boot class path",
 297                        rmBean.isBootClassPathSupported()
 298                                     ? rmBean.getBootClassPath()
 299                                     : getText("Unavailable"),
 300                        4);
 301                 append(endTable);
 302             }
 303         } catch (IOException e) {
 304             if (JConsole.isDebug()) {
 305                 e.printStackTrace();
 306             }
 307             proxyClient.markAsDead();
 308             return null;
 309         } catch (UndeclaredThrowableException e) {
 310             if (JConsole.isDebug()) {
 311                 e.printStackTrace();
 312             }
 313             proxyClient.markAsDead();
 314             return null;
 315         }
 316 
 317         append("</table>");
 318 
 319         result.timeStamp = System.currentTimeMillis();
 320         result.summary = buf.toString();
 321 
 322         return result;
 323     }
 324 
 325     private synchronized void append(String str) {
 326         buf.append(str);
 327     }
 328 
 329     void append(String label, String value) {
 330         append(newRow((label != null) ? getText(label) : label, value));
 331     }
 332 
 333     private void append(String label, String value, int columnPerRow) {
 334         if (columnPerRow == 4 && pathSeparator != null) {
 335             value = value.replace(pathSeparator,
 336                                   "<b></b>" + pathSeparator);
 337         }
 338         append(newRow(getText(label), value, columnPerRow));
 339     }
 340 
 341     void append(String label1, String value1,
 342                 String label2, String value2) {
 343         append(newRow(getText(label1), value1,
 344                       getText(label2), value2));
 345     }
 346 
 347     OverviewPanel[] getOverviewPanels() {
 348         if (overviewPanel == null) {
 349             overviewPanel = new CPUOverviewPanel();
 350         }
 351         return new OverviewPanel[] { overviewPanel };
 352     }
 353 
 354     private static class CPUOverviewPanel extends OverviewPanel {
 355         private long prevUpTime, prevProcessCpuTime;
 356 
 357         CPUOverviewPanel() {
 358             super(getText("CPU Usage"), cpuUsageKey, cpuUsageName, Plotter.Unit.PERCENT);
 359             getPlotter().setDecimals(CPU_DECIMALS);
 360         }
 361 
 362         public void updateCPUInfo(Result result) {
 363             if (prevUpTime > 0L && result.upTime > prevUpTime) {
 364                 // elapsedCpu is in ns and elapsedTime is in ms.
 365                 long elapsedCpu = result.processCpuTime - prevProcessCpuTime;
 366                 long elapsedTime = result.upTime - prevUpTime;
 367                 // cpuUsage could go higher than 100% because elapsedTime
 368                 // and elapsedCpu are not fetched simultaneously. Limit to
 369                 // 99% to avoid Plotter showing a scale from 0% to 200%.
 370                 float cpuUsage =
 371                     Math.min(99F,
 372                              elapsedCpu / (elapsedTime * 10000F * result.nCPUs));
 373 
 374                 getPlotter().addValues(result.timeStamp,
 375                                 Math.round(cpuUsage * Math.pow(10.0, CPU_DECIMALS)));
 376                 getInfoLabel().setText(getText(cpuUsageFormat,
 377                                                String.format("%."+CPU_DECIMALS+"f", cpuUsage)));
 378             }
 379             this.prevUpTime = result.upTime;
 380             this.prevProcessCpuTime = result.processCpuTime;
 381         }
 382     }
 383 
 384 
 385 
 386 }