/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.scheduler;
import java.util.HashMap;
import org.jikesrvm.VM;
import org.jikesrvm.runtime.StackTrace;
import org.jikesrvm.runtime.StackTrace.Element;
import org.vmmagic.pragma.Uninterruptible;
public class JMXSupport {
private static HashMap<Long, Thread> threadIdToThread;
private static final org.jikesrvm.mm.mmtk.Lock peakThreadCountLock =
new org.jikesrvm.mm.mmtk.Lock("peakThreadCount");
private static int peakThreadCount;
private static final org.jikesrvm.mm.mmtk.Lock startedThreadCountLock =
new org.jikesrvm.mm.mmtk.Lock("startedThreadCount");
private static long startedThreadCount;
/**
* Updates the current peak thread count.
* <p>
* Note: this must be uninterruptible because it's called from the
* {@link RVMThread#start()} and that method must not have yieldpoints.
*
* @param liveThreadCount the current count of live threads
* @param numActiveSystemThreads the current count of live system threads
*/
@Uninterruptible
static void updatePeakThreadCount(int liveThreadCount, int numActiveSystemThreads) {
int currentThreadCount = liveThreadCount - numActiveSystemThreads;
peakThreadCountLock.acquire();
if (currentThreadCount > peakThreadCount) {
peakThreadCount = currentThreadCount;
}
peakThreadCountLock.release();
}
public static int getPeakThreadCount() {
int currentCount = 0;
peakThreadCountLock.acquire();
currentCount = peakThreadCount;
peakThreadCountLock.release();
return currentCount;
}
public static void resetPeakThreadCount() {
peakThreadCountLock.acquire();
peakThreadCount = getLiveThreadCount();
peakThreadCountLock.release();
}
/**
* Increases the number of started threads
* <p>
* Note: this must be uninterruptible because it's called from the
* {@link RVMThread#start()} and that method must not have yieldpoints.
*/
@Uninterruptible
static void increaseStartedThreadCount() {
startedThreadCountLock.acquire();
startedThreadCount++;
startedThreadCountLock.release();
}
public static long getStartedThreadCount() {
long startedThreadCountTemp = 0;
startedThreadCountLock.acquire();
startedThreadCountTemp = startedThreadCount;
startedThreadCountLock.release();
return startedThreadCountTemp;
}
public static int getLiveThreadCount() {
return RVMThread.getNumActiveThreads() - RVMThread.getNumActiveSystemThreads();
}
public static int getLiveDaemonCount() {
return RVMThread.getNumActiveDaemons();
}
/**
* @return thread ids (those of java.lang.Thread and not of our internal
* threads!)
*/
public static synchronized long[] getAllLiveThreadIds() {
Thread[] liveThreads = RVMThread.getLiveThreadsForJMX();
int liveThreadCount = liveThreads.length;
int mapSizeEstimate = (int) (liveThreadCount * 1.5);
threadIdToThread = new HashMap<Long, Thread>(mapSizeEstimate);
long[] ids = new long[liveThreadCount];
for (int i = 0; i < liveThreadCount; i++) {
Thread liveThread = liveThreads[i];
if (liveThread == null) {
// Once a null thread was found, all following threads must also be null
if (VM.VerifyAssertions) {
for (int j = i + 1; j < liveThreadCount; j++) {
liveThread = liveThreads[j];
VM._assert(liveThread == null);
}
}
break;
}
long threadId = liveThread.getId();
ids[i] = threadId;
threadIdToThread.put(Long.valueOf(threadId), liveThread);
}
return ids;
}
public static synchronized Thread getThreadForId(long id) {
getAllLiveThreadIds();
Thread thread = threadIdToThread.get(id);
if (thread == null)
throw new IllegalArgumentException("Invalid id: " + id);
return thread;
}
/**
* Checks whether the thread is in native according to JMX.
* @param t a thread
* @return whether the thread is executing JNI code
*/
public static boolean isInNative(RVMThread t) {
t.monitor().lockNoHandshake();
boolean inNative = t.isInNativeAccordingToJMX();
t.monitor().unlock();
return inNative;
}
/**
* Checks whether the thread is currently suspended
* according to JMX.
* @param t a thread
* @return whether {@code Thread.suspend()} was called on the
* thread
*/
public static boolean isSuspended(RVMThread t) {
t.monitor().lockNoHandshake();
boolean isSuspended = t.blockedFor(RVMThread.suspendBlockAdapter);
t.monitor().unlock();
return isSuspended;
}
public static long getWaitingCount(RVMThread rvmThread) {
return rvmThread.getTotalWaitingCount();
}
public static long getWaitingTime(RVMThread rvmThread) {
return rvmThread.getTotalWaitedTime();
}
public static StackTraceElement[] getStackTraceForThread(RVMThread rvmThread) {
RVMThread currentThread = RVMThread.getCurrentThread();
Element[] elements = null;
if (rvmThread == currentThread) {
StackTrace st = new StackTrace();
// Skip 1 frame (the frame of this call)
elements = st.stackTraceNoException(1);
} else {
// Wait until other thread is blocked
while (true) {
rvmThread.safeBlock(RVMThread.stackTraceBlockAdapter);
rvmThread.monitor().lockNoHandshake();
if (rvmThread.blockedFor(RVMThread.stackTraceBlockAdapter)) {
rvmThread.monitor().unlock();
break;
}
rvmThread.monitor().unlock();
}
StackTrace st = new StackTrace(rvmThread);
// Skip 2 frames: the frames for yieldpointFrom* and yieldpoint
// TODO this assumes that the thread is blocked in Java (and not in JNI)
elements = st.stackTraceNoException(2);
rvmThread.unblock(RVMThread.stackTraceBlockAdapter);
}
return JikesRVMStackTraceSupport.convertToJavaClassLibraryStackTrace(elements);
}
}