FullThreadDump.java

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
Location : <init>
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:mainIncorrectPort()]
removed call to com/reallifedeveloper/tools/FullThreadDump::connect → KILLED

87

1.1
Location : <init>
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dumpWithDeadlock()]
removed call to com/reallifedeveloper/tools/FullThreadDump::connect → KILLED

103

1.1
Location : dump
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

106

1.1
Location : dump
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dumpWithDeadlock()]
replaced return value with Collections.emptyList for com/reallifedeveloper/tools/FullThreadDump::dump → KILLED

131

1.1
Location : main
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:mainNoArguments()]
negated conditional → KILLED

2.2
Location : main
Killed by : none
negated conditional → RUN_ERROR

136

1.1
Location : main
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:mainIncorrectPort()]
negated conditional → KILLED

146

1.1
Location : main
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:mainNegativePort()]
negated conditional → KILLED

2.2
Location : main
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

159

1.1
Location : usage
Killed by : none
replaced return value with "" for com/reallifedeveloper/tools/FullThreadDump::usage → SURVIVED
Covering tests

184

1.1
Location : threadDump
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

2.2
Location : threadDump
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

189

1.1
Location : threadDump
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::dumpThreadInfoWithLocks → KILLED

191

1.1
Location : threadDump
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dumpWithDeadlock()]
replaced return value with Collections.emptyList for com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::threadDump → KILLED

202

1.1
Location : dumpThreadInfoWithLocks
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::printThreadInfo → KILLED

204

1.1
Location : dumpThreadInfoWithLocks
Killed by : none
removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::addLockInfo → RUN_ERROR

209

1.1
Location : printThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::addThreadInfo → KILLED

213

1.1
Location : printThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dumpWithDeadlock()]
negated conditional → KILLED

2.2
Location : printThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dumpWithDeadlock()]
changed conditional boundary → KILLED

217

1.1
Location : printThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

227

1.1
Location : addThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

230

1.1
Location : addThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

233

1.1
Location : addThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

237

1.1
Location : addThreadInfo
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

254

1.1
Location : findDeadlock
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
negated conditional → KILLED

255

1.1
Location : findDeadlock
Killed by : com.reallifedeveloper.tools.FullThreadDumpTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.tools.FullThreadDumpTest]/[method:dump()]
replaced boolean return with true for com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::findDeadlock → KILLED

261

1.1
Location : findDeadlock
Killed by : none
removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::printThreadInfo → SURVIVED
Covering tests

262

1.1
Location : findDeadlock
Killed by : none
removed call to com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::addLockInfo → SURVIVED
Covering tests

265

1.1
Location : findDeadlock
Killed by : none
replaced boolean return with false for com/reallifedeveloper/tools/FullThreadDump$ThreadMonitor::findDeadlock → SURVIVED
Covering tests

Active mutators

Tests examined


Report generated by PIT 1.20.2