| 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 |