1/*
2 * Copyright (c) 2004, 2013, 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
26package sun.tools.jconsole;
27
28import java.awt.*;
29import java.io.*;
30import java.lang.management.*;
31import java.lang.reflect.*;
32import java.text.*;
33import java.util.*;
34import java.util.concurrent.*;
35
36import javax.swing.*;
37
38
39import static sun.tools.jconsole.Formatter.*;
40import static sun.tools.jconsole.Utilities.*;
41
42@SuppressWarnings("serial")
43class 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                cpuUsage = Math.max(0F, cpuUsage);
364
365                getPlotter().addValues(result.timeStamp,
366                                Math.round(cpuUsage * Math.pow(10.0, CPU_DECIMALS)));
367                getInfoLabel().setText(Resources.format(Messages.CPU_USAGE_FORMAT,
368                                               String.format("%."+CPU_DECIMALS+"f", cpuUsage)));
369            }
370            this.prevUpTime = result.upTime;
371            this.prevProcessCpuTime = result.processCpuTime;
372        }
373    }
374}
375