/* * 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.ignite.internal.visor.debug; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.management.ThreadInfo; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.internal.visor.VisorDataTransferObject; /** * Data transfer object for Visor {@link ThreadInfo}. */ public class VisorThreadInfo extends VisorDataTransferObject { /** */ private static final long serialVersionUID = 0L; /** */ private static final int MAX_FRAMES = 8; /** Thread name. */ private String name; /** Thread ID. */ private long id; /** Thread state. */ private Thread.State state; /** Lock information. */ private VisorThreadLockInfo lock; /** Lock name. */ private String lockName; /** Lock owner thread ID. */ private long lockOwnerId; /** Lock owner name. */ private String lockOwnerName; /** Thread executing native code. */ private boolean inNative; /** Thread is suspended. */ private boolean suspended; /** Waited count. */ private long waitedCnt; /** Waited time. */ private long waitedTime; /** Blocked count. */ private long blockedCnt; /** Blocked time. */ private long blockedTime; /** Stack trace. */ private List<StackTraceElement> stackTrace; /** Locks info. */ private List<VisorThreadLockInfo> locks; /** Locked monitors. */ private List<VisorThreadMonitorInfo> lockedMonitors; /** * Default constructor. */ public VisorThreadInfo() { // No-op. } /** * Create data transfer object for given thread info. * * @param ti Thread info. */ public VisorThreadInfo(ThreadInfo ti) { assert ti != null; name = ti.getThreadName(); id = ti.getThreadId(); state = ti.getThreadState(); lock = ti.getLockInfo() != null ? new VisorThreadLockInfo(ti.getLockInfo()) : null; lockName =ti.getLockName(); lockOwnerId = ti.getLockOwnerId(); lockOwnerName = ti.getLockOwnerName(); inNative = ti.isInNative(); suspended = ti.isSuspended(); waitedCnt = ti.getWaitedCount(); waitedTime = ti.getWaitedTime(); blockedCnt = ti.getBlockedCount(); blockedTime = ti.getBlockedTime(); stackTrace = Arrays.asList(ti.getStackTrace()); locks = ti.getLockedSynchronizers() != null ? new ArrayList<VisorThreadLockInfo>(ti.getLockedSynchronizers().length) : null; if (ti.getLockedSynchronizers() != null) for (int i = 0; i < ti.getLockedSynchronizers().length; i++) locks.add(new VisorThreadLockInfo(ti.getLockedSynchronizers()[i])); lockedMonitors = ti.getLockedMonitors() != null ? new ArrayList<VisorThreadMonitorInfo>(ti.getLockedMonitors().length) : null; if (ti.getLockedMonitors() != null) for (int i = 0; i < ti.getLockedMonitors().length; i++) lockedMonitors.add(new VisorThreadMonitorInfo(ti.getLockedMonitors()[i])); } /** * @return Thread name. */ public String getName() { return name; } /** * @return Thread ID. */ public long getId() { return id; } /** * @return Thread state. */ public Thread.State getState() { return state; } /** * @return Lock information. */ public VisorThreadLockInfo getLock() { return lock; } /** * @return Lock name. */ public String getLockName() { return lockName; } /** * @return Lock owner thread ID. */ public long getLockOwnerId() { return lockOwnerId; } /** * @return Lock owner name. */ public String getLockOwnerName() { return lockOwnerName; } /** * @return Thread executing native code. */ public boolean isInNative() { return inNative; } /** * @return Thread is suspended. */ public boolean isSuspended() { return suspended; } /** * @return Waited count. */ public long getWaitedCount() { return waitedCnt; } /** * @return Waited time. */ public long getWaitedTime() { return waitedTime; } /** * @return Blocked count. */ public long getBlockedCount() { return blockedCnt; } /** * @return Blocked time. */ public long getBlockedTime() { return blockedTime; } /** * @return Stack trace. */ public List<StackTraceElement> getStackTrace() { return stackTrace; } /** * @return Locks info. */ public List<VisorThreadLockInfo> getLocks() { return locks; } /** * @return Locked monitors. */ public List<VisorThreadMonitorInfo> getLockedMonitors() { return lockedMonitors; } /** {@inheritDoc} */ @Override protected void writeExternalData(ObjectOutput out) throws IOException { U.writeString(out, name); out.writeLong(id); U.writeString(out, state.toString()); out.writeObject(lock); U.writeString(out, lockName); out.writeLong(lockOwnerId); U.writeString(out, lockOwnerName); out.writeBoolean(inNative); out.writeBoolean(suspended); out.writeLong(waitedCnt); out.writeLong(waitedTime); out.writeLong(blockedCnt); out.writeLong(blockedTime); U.writeCollection(out, stackTrace); U.writeCollection(out, locks); U.writeCollection(out, lockedMonitors); } /** {@inheritDoc} */ @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException { name = U.readString(in); id = in.readLong(); String statePresentation = U.readString(in); if (statePresentation != null) state = Enum.valueOf(Thread.State.class, statePresentation); lock = (VisorThreadLockInfo)in.readObject(); lockName = U.readString(in); lockOwnerId = in.readLong(); lockOwnerName = U.readString(in); inNative = in.readBoolean(); suspended = in.readBoolean(); waitedCnt = in.readLong(); waitedTime = in.readLong(); blockedCnt = in.readLong(); blockedTime = in.readLong(); stackTrace = U.readList(in); locks = U.readList(in); lockedMonitors = U.readList(in); } /** {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(512); sb.append('"').append(name).append('"').append(" Id=").append(id).append(' ').append(state); if (lockName != null) sb.append(" on ").append(lockName); if (lockOwnerName != null) sb.append(" owned by \"").append(lockOwnerName).append("\" Id=").append(lockOwnerId); if (suspended) sb.append(" (suspended)"); if (inNative) sb.append(" (in native)"); sb.append('\n'); int maxFrames = Math.min(stackTrace.size(), MAX_FRAMES); for (int i = 0; i < maxFrames; i++) { StackTraceElement ste = stackTrace.get(i); sb.append("\tat ").append(ste).append('\n'); if (i == 0 && lock != null) { switch (state) { case BLOCKED: sb.append("\t- blocked on ").append(lock).append('\n'); break; case WAITING: sb.append("\t- waiting on ").append(lock).append('\n'); break; case TIMED_WAITING: sb.append("\t- waiting on ").append(lock).append('\n'); break; default: } } for (VisorThreadMonitorInfo mi : lockedMonitors) { if (mi.getStackDepth() == i) sb.append("\t- locked ").append(mi).append('\n'); } } if (maxFrames < stackTrace.size()) sb.append("\t...").append('\n'); if (!F.isEmpty(locks)) { sb.append("\n\tNumber of locked synchronizers = ").append(locks.size()).append('\n'); for (VisorThreadLockInfo li : locks) sb.append("\t- ").append(li).append('\n'); } sb.append('\n'); return sb.toString(); } }