/* * ApplicationInsights-Java * Copyright (c) Microsoft Corporation * All rights reserved. * * MIT License * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the ""Software""), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package com.microsoft.applicationinsights.internal.perfcounter.jvm; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.ArrayList; import static java.lang.Math.min; import com.microsoft.applicationinsights.TelemetryClient; import com.microsoft.applicationinsights.internal.perfcounter.PerformanceCounter; import com.microsoft.applicationinsights.internal.util.LocalStringsUtils; import com.microsoft.applicationinsights.telemetry.MetricTelemetry; import com.microsoft.applicationinsights.telemetry.TraceTelemetry; /** * The class uses the JVM ThreadMXBean to detect threads dead locks * A metric with value 0 is sent when there are no blocked threads, * otherwise the number of detected blocked threads is sent with a * dimension that holds information like thread id and minimal stack traces as trace telemetries * * Created by gupele on 8/7/2016. */ public final class DeadLockDetectorPerformanceCounter implements PerformanceCounter { public final static String NAME = "ThreadDeadLockDetector"; private final static String INDENT = " "; private final static String SEPERATOR = " | "; private final static String METRIC_NAME = "Suspected Deadlocked Threads"; private final static int MAX_STACK_TRACE = 3; private final ThreadMXBean threadBean; public DeadLockDetectorPerformanceCounter() { threadBean = ManagementFactory.getThreadMXBean(); } public boolean isSupported() { ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); return threadBean.isSynchronizerUsageSupported(); } @Override public String getId() { return "DeadLockDetector"; } @Override public void report(TelemetryClient telemetryClient) { MetricTelemetry mt = new MetricTelemetry(METRIC_NAME, 0.0); mt.markAsCustomPerfCounter(); long[] threadIds = threadBean.findDeadlockedThreads(); if (threadIds != null && threadIds.length > 0) { ArrayList<Long> blockedThreads = new ArrayList<Long>(); StringBuilder sb = new StringBuilder(); for (long threadId : threadIds) { ThreadInfo threadInfo = threadBean.getThreadInfo(threadId); if (threadInfo == null) { continue; } setThreadInfoAndStack(sb, threadInfo); blockedThreads.add(threadId); } if (!blockedThreads.isEmpty()) { String uuid = LocalStringsUtils.generateRandomIntegerId(); mt.setValue((double)blockedThreads.size()); mt.getContext().getOperation().setId(uuid); TraceTelemetry trace = new TraceTelemetry(String.format("%s%s", "Suspected deadlocked threads: ", sb.toString())); trace.getContext().getOperation().setId(uuid); telemetryClient.track(trace); } } telemetryClient.track(mt); } private void setThreadInfoAndStack(StringBuilder sb, ThreadInfo ti) { try { setThreadInfo(sb, ti); // Stack traces up to depth of MAX_STACK_TRACE StackTraceElement[] stacktrace = ti.getStackTrace(); MonitorInfo[] monitors = ti.getLockedMonitors(); int maxTraceToReport = min(MAX_STACK_TRACE, stacktrace.length); for (int i = 0; i < maxTraceToReport; i++) { StackTraceElement ste = stacktrace[i]; sb.append(INDENT + "at " + ste.toString()); for (MonitorInfo mi : monitors) { if (mi.getLockedStackDepth() == i) { sb.append(INDENT + " - is locked " + mi); } } } } catch (Throwable t) { } sb.append(SEPERATOR); } private void setThreadInfo(StringBuilder sb, ThreadInfo ti) { sb.append(ti.getThreadName()); sb.append(" Id="); sb.append(ti.getThreadId()); sb.append(" is in "); sb.append(ti.getThreadState()); if (ti.getLockName() != null) { sb.append(" on lock=" + ti.getLockName()); } if (ti.isSuspended()) { sb.append(" (suspended)"); } if (ti.isInNative()) { sb.append(" (running in native)"); } if (ti.getLockOwnerName() != null) { sb.append(INDENT + " is owned by " + ti.getLockOwnerName() + " Id=" + ti.getLockOwnerId()); } } }