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.text.*;
  33 import java.util.*;
  34 import java.util.concurrent.*;
  35 
  36 import javax.swing.*;
  37 
  38 
  39 import static sun.tools.jconsole.Formatter.*;
  40 import static sun.tools.jconsole.Utilities.*;
  41 
  42 @SuppressWarnings("serial")
  43 class SummaryTab extends Tab {
  44     private static final String cpuUsageKey = "cpu";
  45 
  46     private static final String newDivider =   "<tr><td colspan=4><font size =-1><hr>";
  47     private static final String newTable =     "<tr><td colspan=4 align=left><table cellpadding=1>";
  48     private static final String newLeftTable = "<tr><td colspan=2 align=left><table cellpadding=1>";
  49     private static final String newRightTable =  "<td colspan=2 align=left><table cellpadding=1>";
  50     private static final String endTable = "</table>";
  51 
  52     private static final int CPU_DECIMALS = 1;
  53 
  54     private CPUOverviewPanel overviewPanel;
  55     private DateFormat headerDateTimeFormat;
  56     private String pathSeparator = null;
  57     HTMLPane info;
  58 
  59     private static class Result {
  60         long upTime = -1L;
  61         long processCpuTime = -1L;
  62         long timeStamp;
  63         int nCPUs;
  64         String summary;
  65     }
  66 
  67     public static String getTabName() {
  68         return Messages.SUMMARY_TAB_TAB_NAME;
  69     }
  70 
  71     public SummaryTab(VMPanel vmPanel) {
  72         super(vmPanel, getTabName());
  73 
  74         setLayout(new BorderLayout());
  75 
  76         info = new HTMLPane();
  77         setAccessibleName(info, getTabName());
  78         add(new JScrollPane(info));
  79 
  80         headerDateTimeFormat =
  81             Formatter.getDateTimeFormat(Messages.SUMMARY_TAB_HEADER_DATE_TIME_FORMAT);
  82     }
  83 
  84     public SwingWorker<?, ?> newSwingWorker() {
  85         return new SwingWorker<Result, Object>() {
  86             public Result doInBackground() {
  87                 return formatSummary();
  88             }
  89 
  90 
  91             protected void done() {
  92                 try {
  93                     Result result = get();
  94                     if (result != null) {
  95                         info.setText(result.summary);
  96                         if (overviewPanel != null &&
  97                             result.upTime > 0L &&
  98                             result.processCpuTime >= 0L) {
  99 
 100                             overviewPanel.updateCPUInfo(result);
 101                         }
 102                     }
 103                 } catch (InterruptedException ex) {
 104                 } catch (ExecutionException ex) {
 105                     if (JConsole.isDebug()) {
 106                         ex.printStackTrace();
 107                     }
 108                 }
 109             }
 110         };
 111     }
 112 
 113     StringBuilder buf;
 114 
 115     synchronized Result formatSummary() {
 116         Result result = new Result();
 117         ProxyClient proxyClient = vmPanel.getProxyClient();
 118         if (proxyClient.isDead()) {
 119             return null;
 120         }
 121 
 122         buf = new StringBuilder();
 123         append("<table cellpadding=1>");
 124 
 125         try {
 126             RuntimeMXBean         rmBean     = proxyClient.getRuntimeMXBean();
 127             CompilationMXBean     cmpMBean   = proxyClient.getCompilationMXBean();
 128             ThreadMXBean          tmBean     = proxyClient.getThreadMXBean();
 129             MemoryMXBean          memoryBean = proxyClient.getMemoryMXBean();
 130             ClassLoadingMXBean    clMBean    = proxyClient.getClassLoadingMXBean();
 131             OperatingSystemMXBean osMBean    = proxyClient.getOperatingSystemMXBean();
 132             com.sun.management.OperatingSystemMXBean sunOSMBean  =
 133                proxyClient.getSunOperatingSystemMXBean();
 134 
 135             append("<tr><td colspan=4>");
 136             append("<center><b>" + Messages.SUMMARY_TAB_TAB_NAME + "</b></center>");
 137             String dateTime =
 138                 headerDateTimeFormat.format(System.currentTimeMillis());
 139             append("<center>" + dateTime + "</center>");
 140 
 141             append(newDivider);
 142 
 143             {  // VM info
 144                 append(newLeftTable);
 145                 append(Messages.CONNECTION_NAME, vmPanel.getDisplayName());
 146                 append(Messages.VIRTUAL_MACHINE,
 147                        Resources.format(Messages.SUMMARY_TAB_VM_VERSION,
 148                                         rmBean.getVmName(), rmBean.getVmVersion()));
 149                 append(Messages.VENDOR, rmBean.getVmVendor());
 150                 append(Messages.NAME, rmBean.getName());
 151                 append(endTable);
 152 
 153                 append(newRightTable);
 154                 result.upTime = rmBean.getUptime();
 155                 append(Messages.UPTIME, formatTime(result.upTime));
 156                 if (sunOSMBean != null) {
 157                     result.processCpuTime = sunOSMBean.getProcessCpuTime();
 158                     append(Messages.PROCESS_CPU_TIME, formatNanoTime(result.processCpuTime));
 159                 }
 160 
 161                 if (cmpMBean != null) {
 162                     append(Messages.JIT_COMPILER, cmpMBean.getName());
 163                     append(Messages.TOTAL_COMPILE_TIME,
 164                            cmpMBean.isCompilationTimeMonitoringSupported()
 165                                     ? formatTime(cmpMBean.getTotalCompilationTime())
 166                                     : Messages.UNAVAILABLE);
 167                 } else {
 168                     append(Messages.JIT_COMPILER, Messages.UNAVAILABLE);
 169                 }
 170                 append(endTable);
 171             }
 172 
 173             append(newDivider);
 174 
 175             {  // Threads and Classes
 176                 append(newLeftTable);
 177                 int tlCount = tmBean.getThreadCount();
 178                 int tdCount = tmBean.getDaemonThreadCount();
 179                 int tpCount = tmBean.getPeakThreadCount();
 180                 long ttCount = tmBean.getTotalStartedThreadCount();
 181                 String[] strings1 = formatLongs(tlCount, tpCount,
 182                                                 tdCount, ttCount);
 183                 append(Messages.LIVE_THREADS, strings1[0]);
 184                 append(Messages.PEAK, strings1[1]);
 185                 append(Messages.DAEMON_THREADS, strings1[2]);
 186                 append(Messages.TOTAL_THREADS_STARTED, strings1[3]);
 187                 append(endTable);
 188 
 189                 append(newRightTable);
 190                 long clCount = clMBean.getLoadedClassCount();
 191                 long cuCount = clMBean.getUnloadedClassCount();
 192                 long ctCount = clMBean.getTotalLoadedClassCount();
 193                 String[] strings2 = formatLongs(clCount, cuCount, ctCount);
 194                 append(Messages.CURRENT_CLASSES_LOADED, strings2[0]);
 195                 append(Messages.TOTAL_CLASSES_LOADED, strings2[2]);
 196                 append(Messages.TOTAL_CLASSES_UNLOADED, strings2[1]);
 197                 append(null, "");
 198                 append(endTable);
 199             }
 200 
 201             append(newDivider);
 202 
 203             {  // Memory
 204                 MemoryUsage u = memoryBean.getHeapMemoryUsage();
 205 
 206                 append(newLeftTable);
 207                 String[] strings1 = formatKByteStrings(u.getUsed(), u.getMax());
 208                 append(Messages.CURRENT_HEAP_SIZE, strings1[0]);
 209                 append(Messages.MAXIMUM_HEAP_SIZE, strings1[1]);
 210                 append(endTable);
 211 
 212                 append(newRightTable);
 213                 String[] strings2 = formatKByteStrings(u.getCommitted());
 214                 append(Messages.COMMITTED_MEMORY,  strings2[0]);
 215                 append(Messages.SUMMARY_TAB_PENDING_FINALIZATION_LABEL,
 216                        Resources.format(Messages.SUMMARY_TAB_PENDING_FINALIZATION_VALUE,
 217                                         memoryBean.getObjectPendingFinalizationCount()));
 218                 append(endTable);
 219 
 220                 append(newTable);
 221                 Collection<GarbageCollectorMXBean> garbageCollectors =
 222                                             proxyClient.getGarbageCollectorMXBeans();
 223                 for (GarbageCollectorMXBean garbageCollectorMBean : garbageCollectors) {
 224                     String gcName = garbageCollectorMBean.getName();
 225                     long gcCount = garbageCollectorMBean.getCollectionCount();
 226                     long gcTime = garbageCollectorMBean.getCollectionTime();
 227 
 228                     append(Messages.GARBAGE_COLLECTOR,
 229                            Resources.format(Messages.GC_INFO, gcName, gcCount,
 230                                             (gcTime >= 0) ? formatTime(gcTime)
 231                                                  : Messages.UNAVAILABLE),
 232                            4);
 233                 }
 234                 append(endTable);
 235             }
 236 
 237             append(newDivider);
 238 
 239             {  // Operating System info
 240                 append(newLeftTable);
 241                 String osName = osMBean.getName();
 242                 String osVersion = osMBean.getVersion();
 243                 String osArch = osMBean.getArch();
 244                 result.nCPUs = osMBean.getAvailableProcessors();
 245                 append(Messages.OPERATING_SYSTEM, osName + " " + osVersion);
 246                 append(Messages.ARCHITECTURE, osArch);
 247                 append(Messages.NUMBER_OF_PROCESSORS, result.nCPUs+"");
 248 
 249                 if (pathSeparator == null) {
 250                     // Must use separator of remote OS, not File.pathSeparator
 251                     // from this local VM. In the future, consider using
 252                     // RuntimeMXBean to get the remote system property.
 253                     pathSeparator = osName.startsWith("Windows ") ? ";" : ":";
 254                 }
 255 
 256                 if (sunOSMBean != null) {
 257                     String[] kbStrings1 =
 258                         formatKByteStrings(sunOSMBean.getCommittedVirtualMemorySize());
 259 
 260                     String[] kbStrings2 =
 261                         formatKByteStrings(sunOSMBean.getTotalPhysicalMemorySize(),
 262                                            sunOSMBean.getFreePhysicalMemorySize(),
 263                                            sunOSMBean.getTotalSwapSpaceSize(),
 264                                            sunOSMBean.getFreeSwapSpaceSize());
 265 
 266                     append(Messages.COMMITTED_VIRTUAL_MEMORY, kbStrings1[0]);
 267                     append(endTable);
 268 
 269                     append(newRightTable);
 270                     append(Messages.TOTAL_PHYSICAL_MEMORY, kbStrings2[0]);
 271                     append(Messages.FREE_PHYSICAL_MEMORY,  kbStrings2[1]);
 272                     append(Messages.TOTAL_SWAP_SPACE,      kbStrings2[2]);
 273                     append(Messages.FREE_SWAP_SPACE,       kbStrings2[3]);
 274                 }
 275 
 276                 append(endTable);
 277             }
 278 
 279             append(newDivider);
 280 
 281             {  // VM arguments and paths
 282                 append(newTable);
 283                 String args = "";
 284                 java.util.List<String> inputArguments = rmBean.getInputArguments();
 285                 for (String arg : inputArguments) {
 286                     args += arg + " ";
 287                 }
 288                 append(Messages.VM_ARGUMENTS, args, 4);
 289                 append(Messages.CLASS_PATH,   rmBean.getClassPath(), 4);
 290                 append(Messages.LIBRARY_PATH, rmBean.getLibraryPath(), 4);
 291                 append(Messages.BOOT_CLASS_PATH,
 292                        rmBean.isBootClassPathSupported()
 293                                     ? rmBean.getBootClassPath()
 294                                     : Messages.UNAVAILABLE,
 295                        4);
 296                 append(endTable);
 297             }
 298         } catch (IOException e) {
 299             if (JConsole.isDebug()) {
 300                 e.printStackTrace();
 301             }
 302             proxyClient.markAsDead();
 303             return null;
 304         } catch (UndeclaredThrowableException e) {
 305             if (JConsole.isDebug()) {
 306                 e.printStackTrace();
 307             }
 308             proxyClient.markAsDead();
 309             return null;
 310         }
 311 
 312         append("</table>");
 313 
 314         result.timeStamp = System.currentTimeMillis();
 315         result.summary = buf.toString();
 316 
 317         return result;
 318     }
 319 
 320     private synchronized void append(String str) {
 321         buf.append(str);
 322     }
 323 
 324     void append(String label, String value) {
 325         append(newRow(label, value));
 326     }
 327 
 328     private void append(String label, String value, int columnPerRow) {
 329         if (columnPerRow == 4 && pathSeparator != null) {
 330             value = value.replace(pathSeparator,
 331                                   "<b></b>" + pathSeparator);
 332         }
 333         append(newRow(label, value, columnPerRow));
 334     }
 335 
 336     OverviewPanel[] getOverviewPanels() {
 337         if (overviewPanel == null) {
 338             overviewPanel = new CPUOverviewPanel();
 339         }
 340         return new OverviewPanel[] { overviewPanel };
 341     }
 342 
 343     private static class CPUOverviewPanel extends OverviewPanel {
 344         private long prevUpTime, prevProcessCpuTime;
 345 
 346         CPUOverviewPanel() {
 347             super(Messages.CPU_USAGE, cpuUsageKey, Messages.CPU_USAGE, Plotter.Unit.PERCENT);
 348             getPlotter().setDecimals(CPU_DECIMALS);
 349         }
 350 
 351         public void updateCPUInfo(Result result) {
 352             if (prevUpTime > 0L && result.upTime > prevUpTime) {
 353                 // elapsedCpu is in ns and elapsedTime is in ms.
 354                 long elapsedCpu = result.processCpuTime - prevProcessCpuTime;
 355                 long elapsedTime = result.upTime - prevUpTime;
 356                 // cpuUsage could go higher than 100% because elapsedTime
 357                 // and elapsedCpu are not fetched simultaneously. Limit to
 358                 // 99% to avoid Plotter showing a scale from 0% to 200%.
 359                 float cpuUsage =
 360                     Math.min(99F,
 361                              elapsedCpu / (elapsedTime * 10000F * result.nCPUs));
 362 
 363                 getPlotter().addValues(result.timeStamp,
 364                                 Math.round(cpuUsage * Math.pow(10.0, CPU_DECIMALS)));
 365                 getInfoLabel().setText(Resources.format(Messages.CPU_USAGE_FORMAT,
 366                                                String.format("%."+CPU_DECIMALS+"f", cpuUsage)));
 367             }
 368             this.prevUpTime = result.upTime;
 369             this.prevProcessCpuTime = result.processCpuTime;
 370         }
 371     }
 372 }