1 | /* | |
2 | * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE | |
3 | * HEADER. | |
4 | * | |
5 | * This code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 only, as | |
6 | * published by the Free Software Foundation. Oracle designates this particular file as subject to the "Classpath" exception as provided by | |
7 | * Oracle in the LICENSE file that accompanied this code. | |
8 | * | |
9 | * This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for more details (a copy is included in | |
11 | * the LICENSE file that accompanied this code). | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License version 2 along with this work; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
15 | * | |
16 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA or visit www.oracle.com if you need additional information or | |
17 | * have any questions. | |
18 | */ | |
19 | ||
20 | package com.reallifedeveloper.tools; | |
21 | ||
22 | import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME; | |
23 | import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy; | |
24 | ||
25 | import java.io.IOException; | |
26 | import java.lang.management.LockInfo; | |
27 | import java.lang.management.MonitorInfo; | |
28 | import java.lang.management.ThreadInfo; | |
29 | import java.lang.management.ThreadMXBean; | |
30 | import java.util.ArrayList; | |
31 | import java.util.HashMap; | |
32 | import java.util.List; | |
33 | import java.util.Map; | |
34 | ||
35 | import javax.management.MBeanServerConnection; | |
36 | import javax.management.remote.JMXConnector; | |
37 | import javax.management.remote.JMXConnectorFactory; | |
38 | import javax.management.remote.JMXServiceURL; | |
39 | ||
40 | import org.slf4j.Logger; | |
41 | import org.slf4j.LoggerFactory; | |
42 | ||
43 | /** | |
44 | * This FullThreadDump class demonstrates the capability to get a full thread dump and also detect deadlock remotely. | |
45 | * <p> | |
46 | * Based on code by Sun Microsystems, Inc. and Oracle. | |
47 | * | |
48 | * @author Josh Bloch | |
49 | * @author Neal Gafter | |
50 | * @author RealLifeDeveloper | |
51 | */ | |
52 | @SuppressWarnings("PMD") // This builds on code from other sources | |
53 | public final class FullThreadDump { | |
54 | ||
55 | private static final Logger LOG = LoggerFactory.getLogger(FullThreadDump.class); | |
56 | ||
57 | private MBeanServerConnection server; | |
58 | ||
59 | private JMXConnector jmxc; | |
60 | ||
61 | /** | |
62 | * Creates a new {@code FullThreadDump} object that is connected to a JMX server on the given host and port. | |
63 | * | |
64 | * @param hostname the name of the host running the JMX server | |
65 | * @param port the port number the JMX server is listening on | |
66 | * | |
67 | * @throws IOException if connection to the JMX server fails | |
68 | */ | |
69 | public FullThreadDump(String hostname, int port) throws IOException { | |
70 | LOG.info("Connecting to {}:{}", hostname.replaceAll("[\r\n]", ""), port); | |
71 | ||
72 | // Create an RMI connector client and connect it to | |
73 | // the RMI connector server | |
74 | String urlPath = "/jndi/rmi://" + hostname + ":" + port + "/jmxrmi"; | |
75 | JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath); | |
76 |
1
1. <init> : removed call to com/reallifedeveloper/tools/FullThreadDump::connect → KILLED |
connect(url); |
77 | } | |
78 | ||
79 | /** | |
80 | * Creates a new {@code FullThreadDump} object that is connected to a JMX server at the given {@code JMXServiceURL}. | |
81 | * | |
82 | * @param url the {@code JMXServiceURL} of the JMX server | |
83 | * | |
84 | * @throws IOException if connection to the JMX server fails | |
85 | */ | |
86 | public FullThreadDump(JMXServiceURL url) throws IOException { | |
87 |
1
1. <init> : removed call to com/reallifedeveloper/tools/FullThreadDump::connect → KILLED |
connect(url); |
88 | } | |
89 | ||
90 | /** | |
91 | * Creates a thread dump with information about all the threads running in the Java process being monitored by the JMX server connected | |
92 | * to. | |
93 | * <p> | |
94 | * The information is meant to be read by humans and is not easily parsable. | |
95 | * | |
96 | * @return a list of strings with information about the threads, e.g., thread name, call stack an so on. | |
97 | * | |
98 | * @throws IOException if communication with the JMX server fails | |
99 | */ | |
100 | public List<String> dump() throws IOException { | |
101 | ThreadMonitor monitor = new ThreadMonitor(server); | |
102 | List<String> threadInfo = monitor.threadDump(); | |
103 |
1
1. dump : negated conditional → KILLED |
if (!monitor.findDeadlock()) { |
104 | threadInfo.add("No deadlock found."); | |
105 | } | |
106 |
1
1. dump : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/FullThreadDump::dump → KILLED |
return threadInfo; |
107 | } | |
108 | ||
109 | /** | |
110 | * Connect to a JMX agent of a given URL. | |
111 | */ | |
112 | @SuppressWarnings("BanJNDI") | |
113 | private void connect(JMXServiceURL url) throws IOException { | |
114 | Map<String, Object> env = new HashMap<>(); | |
115 | // String[] credentials = { "controlRole", "control" }; | |
116 | // env.put(JMXConnector.CREDENTIALS, credentials); | |
117 | // env.put("com.sun.jndi.rmi.factory.socket", new SslRMIClientSocketFactory()); | |
118 | this.jmxc = JMXConnectorFactory.connect(url, env); | |
119 | // this.jmxc = JMXConnectorFactory.connect(url); | |
120 | this.server = jmxc.getMBeanServerConnection(); | |
121 | } | |
122 | ||
123 | /** | |
124 | * Connects to a JMX server at the given host and port number and creates a thread dump that is logged on INFO level. | |
125 | * | |
126 | * @param args should be one string on the form "hostname:port", e.g., "localhost:4711" | |
127 | * | |
128 | * @throws IOException if communication with the JMX server fails | |
129 | */ | |
130 | public static void main(String... args) throws IOException { | |
131 |
2
1. main : negated conditional → RUN_ERROR 2. main : negated conditional → KILLED |
if (args == null || args.length != 1) { |
132 | throw new IllegalArgumentException(usage()); | |
133 | } | |
134 | ||
135 | String[] arg2 = args[0].split(":", -1); // TODO: Test this | |
136 |
1
1. main : negated conditional → KILLED |
if (arg2.length != 2) { |
137 | throw new IllegalArgumentException(usage()); | |
138 | } | |
139 | String hostname = arg2[0]; | |
140 | int port = -1; | |
141 | try { | |
142 | port = Integer.parseInt(arg2[1]); | |
143 | } catch (NumberFormatException x) { | |
144 | throw new IllegalArgumentException(usage()); | |
145 | } | |
146 |
2
1. main : changed conditional boundary → SURVIVED 2. main : negated conditional → KILLED |
if (port < 0) { |
147 | throw new IllegalArgumentException(usage()); | |
148 | } | |
149 | ||
150 | // get full thread dump and perform deadlock detection | |
151 | FullThreadDump ftd = new FullThreadDump(hostname, port); | |
152 | List<String> threadInfo = ftd.dump(); | |
153 | for (String threadInfoLine : threadInfo) { | |
154 | LOG.info(threadInfoLine.replaceAll("[\r\n]", "")); | |
155 | } | |
156 | } | |
157 | ||
158 | private static String usage() { | |
159 |
1
1. usage : replaced return value with "" for com/reallifedeveloper/tools/FullThreadDump::usage → SURVIVED |
return "Usage: java " + FullThreadDump.class.getName() + " <hostname>:<port>"; |
160 | } | |
161 | ||
162 | /** | |
163 | * Example of using the java.lang.management API to dump stack trace and to perform deadlock detection. | |
164 | */ | |
165 | private static final class ThreadMonitor { | |
166 | ||
167 | private static final List<String> THREAD_INFO = new ArrayList<>(); | |
168 | ||
169 | private static final String INDENT = " "; | |
170 | ||
171 | private ThreadMXBean tmbean; | |
172 | ||
173 | /** | |
174 | * Constructs a ThreadMonitor object to get thread information in a remote JVM. | |
175 | */ | |
176 | ThreadMonitor(MBeanServerConnection server) throws IOException { | |
177 | this.tmbean = newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME, ThreadMXBean.class); | |
178 | } | |
179 | ||
180 | /** | |
181 | * Gives the thread dump information as a list of strings, one line per string. | |
182 | */ | |
183 | List<String> threadDump() { | |
184 |
2
1. threadDump : negated conditional → KILLED 2. threadDump : negated conditional → KILLED |
if (tmbean.isObjectMonitorUsageSupported() && tmbean.isSynchronizerUsageSupported()) { |
185 | // Print lock info if both object monitor usage | |
186 | // and synchronizer usage are supported. | |
187 | // This sample code can be modified to handle if | |
188 | // either monitor usage or synchronizer usage is supported. | |
189 |
1
1. threadDump : removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::dumpThreadInfoWithLocks → KILLED |
dumpThreadInfoWithLocks(); |
190 | } | |
191 |
1
1. threadDump : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::threadDump → KILLED |
return THREAD_INFO; |
192 | } | |
193 | ||
194 | /** | |
195 | * Saves the thread dump information with locks info in THREAD_INFO. | |
196 | */ | |
197 | private void dumpThreadInfoWithLocks() { | |
198 | THREAD_INFO.add("Full Java thread dump with locks info"); | |
199 | ||
200 | ThreadInfo[] tinfos = tmbean.dumpAllThreads(true, true); | |
201 | for (ThreadInfo ti : tinfos) { | |
202 |
1
1. dumpThreadInfoWithLocks : removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::printThreadInfo → KILLED |
printThreadInfo(ti); |
203 | LockInfo[] syncs = ti.getLockedSynchronizers(); | |
204 |
1
1. dumpThreadInfoWithLocks : removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::addLockInfo → RUN_ERROR |
addLockInfo(syncs); |
205 | } | |
206 | } | |
207 | ||
208 | private void printThreadInfo(ThreadInfo ti) { | |
209 |
1
1. printThreadInfo : removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::addThreadInfo → KILLED |
addThreadInfo(ti); |
210 | ||
211 | StackTraceElement[] stacktrace = ti.getStackTrace(); | |
212 | MonitorInfo[] monitors = ti.getLockedMonitors(); | |
213 |
2
1. printThreadInfo : negated conditional → KILLED 2. printThreadInfo : changed conditional boundary → KILLED |
for (int i = 0; i < stacktrace.length; i++) { |
214 | StackTraceElement ste = stacktrace[i]; | |
215 | THREAD_INFO.add(INDENT + "at " + ste.toString()); | |
216 | for (MonitorInfo mi : monitors) { | |
217 |
1
1. printThreadInfo : negated conditional → KILLED |
if (mi.getLockedStackDepth() == i) { |
218 | THREAD_INFO.add(INDENT + " - locked " + mi); | |
219 | } | |
220 | } | |
221 | } | |
222 | } | |
223 | ||
224 | private void addThreadInfo(ThreadInfo ti) { | |
225 | StringBuilder sb = new StringBuilder( | |
226 | "\"" + ti.getThreadName() + "\"" + " Id=" + ti.getThreadId() + " in " + ti.getThreadState()); | |
227 |
1
1. addThreadInfo : negated conditional → KILLED |
if (ti.getLockName() != null) { |
228 | sb.append(" on lock=" + ti.getLockName()); | |
229 | } | |
230 |
1
1. addThreadInfo : negated conditional → KILLED |
if (ti.isSuspended()) { |
231 | sb.append(" (suspended)"); | |
232 | } | |
233 |
1
1. addThreadInfo : negated conditional → KILLED |
if (ti.isInNative()) { |
234 | sb.append(" (running in native)"); | |
235 | } | |
236 | THREAD_INFO.add(sb.toString()); | |
237 |
1
1. addThreadInfo : negated conditional → KILLED |
if (ti.getLockOwnerName() != null) { |
238 | THREAD_INFO.add(INDENT + " owned by " + ti.getLockOwnerName() + " Id=" + ti.getLockOwnerId()); | |
239 | } | |
240 | } | |
241 | ||
242 | private void addLockInfo(LockInfo[] locks) { | |
243 | THREAD_INFO.add(INDENT + "Locked synchronizers: count = " + locks.length); | |
244 | for (LockInfo li : locks) { | |
245 | THREAD_INFO.add(INDENT + " - " + li); | |
246 | } | |
247 | } | |
248 | ||
249 | /** | |
250 | * Checks if any threads are deadlocked. If any, save the thread dump information. | |
251 | */ | |
252 | boolean findDeadlock() { | |
253 | long[] tids = tmbean.findDeadlockedThreads(); | |
254 |
1
1. findDeadlock : negated conditional → KILLED |
if (tids == null) { |
255 |
1
1. findDeadlock : replaced boolean return with true for com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::findDeadlock → KILLED |
return false; |
256 | } | |
257 | ||
258 | THREAD_INFO.add("Deadlock found :-"); | |
259 | ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true); | |
260 | for (ThreadInfo ti : infos) { | |
261 |
1
1. findDeadlock : removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::printThreadInfo → SURVIVED |
printThreadInfo(ti); |
262 |
1
1. findDeadlock : removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::addLockInfo → SURVIVED |
addLockInfo(ti.getLockedSynchronizers()); |
263 | } | |
264 | ||
265 |
1
1. findDeadlock : replaced boolean return with false for com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::findDeadlock → SURVIVED |
return true; |
266 | } | |
267 | } | |
268 | } | |
Mutations | ||
76 |
1.1 |
|
87 |
1.1 |
|
103 |
1.1 |
|
106 |
1.1 |
|
131 |
1.1 2.2 |
|
136 |
1.1 |
|
146 |
1.1 2.2 |
|
159 |
1.1 |
|
184 |
1.1 2.2 |
|
189 |
1.1 |
|
191 |
1.1 |
|
202 |
1.1 |
|
204 |
1.1 |
|
209 |
1.1 |
|
213 |
1.1 2.2 |
|
217 |
1.1 |
|
227 |
1.1 |
|
230 |
1.1 |
|
233 |
1.1 |
|
237 |
1.1 |
|
254 |
1.1 |
|
255 |
1.1 |
|
261 |
1.1 |
|
262 |
1.1 |
|
265 |
1.1 |