/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.apache.ambari.server.testing; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.commons.lang.ArrayUtils; /** * * Monitoring of deadlocks thread * Please note. This class can not be used outside of tests */ public class DeadlockWarningThread extends Thread { private final List<String> errorMessages; private int MAX_STACK_DEPTH = 30; private int SLEEP_TIME_MS = 3000; private Collection<Thread> monitoredThreads = null; private boolean deadlocked = false; private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); public List<String> getErrorMessages() { return errorMessages; } public boolean isDeadlocked() { return deadlocked; } public DeadlockWarningThread(Collection<Thread> monitoredThreads, int maxStackDepth, int sleepTimeMS) { this.errorMessages = new ArrayList<>(); this.monitoredThreads = monitoredThreads; this.MAX_STACK_DEPTH = maxStackDepth; this.SLEEP_TIME_MS = sleepTimeMS; start(); } public DeadlockWarningThread(Collection<Thread> monitoredThreads) { this(monitoredThreads, 30, 3000); } public String getThreadsStacktraces(Collection<Long> ids) { StringBuilder errBuilder = new StringBuilder(); for (long id : ids) { ThreadInfo ti = mbean.getThreadInfo(id, MAX_STACK_DEPTH); errBuilder.append("Deadlocked Thread:\n"). append("------------------\n"). append(ti).append('\n'); for (StackTraceElement ste : ti.getStackTrace()) { errBuilder.append('\t').append(ste); } errBuilder.append('\n'); } return errBuilder.toString(); } @Override public void run() { while (true) { try { Thread.sleep(SLEEP_TIME_MS); } catch (InterruptedException ex) { } long[] ids = mbean.findMonitorDeadlockedThreads(); StringBuilder errBuilder = new StringBuilder(); if (ids != null && ids.length > 0) { errBuilder.append(getThreadsStacktraces(Arrays.asList(ArrayUtils.toObject(ids)))); errorMessages.add(errBuilder.toString()); System.out.append(errBuilder.toString()); //Exit if deadlocks have been found deadlocked = true; break; } else { //Exit if all monitored threads were finished boolean hasLive = false; boolean hasRunning = false; for (Thread monTh : monitoredThreads) { State state = monTh.getState(); if (state != State.TERMINATED && state != State.NEW) { hasLive = true; } if (state == State.RUNNABLE || state == State.TIMED_WAITING) { hasRunning = true; break; } } if (!hasLive) { deadlocked = false; break; } else if (!hasRunning) { List<Long> tIds = new ArrayList<>(); for (Thread monitoredThread : monitoredThreads) { State state = monitoredThread.getState(); if (state == State.WAITING || state == State.BLOCKED) { tIds.add(monitoredThread.getId()); } } errBuilder.append(getThreadsStacktraces(tIds)); errorMessages.add(errBuilder.toString()); deadlocked = true; break; } } } } }