001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.scheduler;
014
015import java.util.HashMap;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.runtime.StackTrace;
019import org.jikesrvm.runtime.StackTrace.Element;
020import org.vmmagic.pragma.Uninterruptible;
021
022public class JMXSupport {
023
024  private static HashMap<Long, Thread> threadIdToThread;
025
026  private static final org.jikesrvm.mm.mmtk.Lock peakThreadCountLock =
027      new org.jikesrvm.mm.mmtk.Lock("peakThreadCount");
028
029  private static int peakThreadCount;
030
031  private static final org.jikesrvm.mm.mmtk.Lock startedThreadCountLock =
032      new org.jikesrvm.mm.mmtk.Lock("startedThreadCount");
033
034  private static long startedThreadCount;
035
036  /**
037   * Updates the current peak thread count.
038   * <p>
039   * Note: this must be uninterruptible because it's called from the
040   * {@link RVMThread#start()} and that method must not have yieldpoints.
041   *
042   * @param liveThreadCount the current count of live threads
043   * @param numActiveSystemThreads the current count of live system threads
044   */
045  @Uninterruptible
046  static void updatePeakThreadCount(int liveThreadCount, int numActiveSystemThreads) {
047    int currentThreadCount = liveThreadCount - numActiveSystemThreads;
048    peakThreadCountLock.acquire();
049    if (currentThreadCount > peakThreadCount) {
050      peakThreadCount = currentThreadCount;
051    }
052    peakThreadCountLock.release();
053  }
054
055  public static int getPeakThreadCount() {
056    int currentCount = 0;
057    peakThreadCountLock.acquire();
058    currentCount = peakThreadCount;
059    peakThreadCountLock.release();
060    return currentCount;
061  }
062
063  public static void resetPeakThreadCount() {
064    peakThreadCountLock.acquire();
065    peakThreadCount = getLiveThreadCount();
066    peakThreadCountLock.release();
067  }
068
069  /**
070   * Increases the number of started threads
071   * <p>
072   * Note: this must be uninterruptible because it's called from the
073   * {@link RVMThread#start()} and that method must not have yieldpoints.
074   */
075  @Uninterruptible
076  static void increaseStartedThreadCount() {
077    startedThreadCountLock.acquire();
078    startedThreadCount++;
079    startedThreadCountLock.release();
080  }
081
082  public static long getStartedThreadCount() {
083    long startedThreadCountTemp = 0;
084    startedThreadCountLock.acquire();
085    startedThreadCountTemp = startedThreadCount;
086    startedThreadCountLock.release();
087    return startedThreadCountTemp;
088  }
089
090
091  public static int getLiveThreadCount() {
092    return RVMThread.getNumActiveThreads() - RVMThread.getNumActiveSystemThreads();
093  }
094
095  public static int getLiveDaemonCount() {
096    return RVMThread.getNumActiveDaemons();
097  }
098
099  /**
100   * @return thread ids (those of java.lang.Thread and not of our internal
101   *  threads!)
102   */
103  public static synchronized long[] getAllLiveThreadIds() {
104    Thread[] liveThreads = RVMThread.getLiveThreadsForJMX();
105    int liveThreadCount = liveThreads.length;
106
107    int mapSizeEstimate = (int) (liveThreadCount * 1.5);
108    threadIdToThread = new HashMap<Long, Thread>(mapSizeEstimate);
109
110    long[] ids = new long[liveThreadCount];
111    for (int i = 0; i < liveThreadCount; i++) {
112      Thread liveThread = liveThreads[i];
113      if (liveThread == null) {
114        // Once a null thread was found, all following threads must also be null
115        if (VM.VerifyAssertions) {
116          for (int j = i + 1; j < liveThreadCount; j++) {
117            liveThread = liveThreads[j];
118            VM._assert(liveThread == null);
119          }
120        }
121        break;
122      }
123      long threadId = liveThread.getId();
124      ids[i] = threadId;
125      threadIdToThread.put(Long.valueOf(threadId), liveThread);
126    }
127    return ids;
128  }
129
130  public static synchronized Thread getThreadForId(long id) {
131    getAllLiveThreadIds();
132    Thread thread = threadIdToThread.get(id);
133    if (thread == null)
134      throw new IllegalArgumentException("Invalid id: " + id);
135    return thread;
136  }
137
138
139  /**
140   * Checks whether the thread is in native according to JMX.
141   * @param t a thread
142   * @return whether the thread is executing JNI code
143   */
144  public static boolean isInNative(RVMThread t) {
145    t.monitor().lockNoHandshake();
146    boolean inNative = t.isInNativeAccordingToJMX();
147    t.monitor().unlock();
148    return inNative;
149  }
150
151  /**
152   * Checks whether the thread is currently suspended
153   * according to JMX.
154   * @param t a thread
155   * @return whether {@code Thread.suspend()} was called on the
156   *  thread
157   */
158  public static boolean isSuspended(RVMThread t) {
159    t.monitor().lockNoHandshake();
160    boolean isSuspended = t.blockedFor(RVMThread.suspendBlockAdapter);
161    t.monitor().unlock();
162    return isSuspended;
163  }
164
165  public static long getWaitingCount(RVMThread rvmThread) {
166    return rvmThread.getTotalWaitingCount();
167  }
168
169  public static long getWaitingTime(RVMThread rvmThread) {
170    return rvmThread.getTotalWaitedTime();
171  }
172
173  public static StackTraceElement[] getStackTraceForThread(RVMThread rvmThread) {
174    RVMThread currentThread = RVMThread.getCurrentThread();
175
176    Element[] elements = null;
177    if (rvmThread == currentThread) {
178      StackTrace st = new StackTrace();
179      // Skip 1 frame (the frame of this call)
180      elements = st.stackTraceNoException(1);
181    } else {
182        // Wait until other thread is blocked
183        while (true) {
184          rvmThread.safeBlock(RVMThread.stackTraceBlockAdapter);
185          rvmThread.monitor().lockNoHandshake();
186          if (rvmThread.blockedFor(RVMThread.stackTraceBlockAdapter)) {
187            rvmThread.monitor().unlock();
188            break;
189          }
190          rvmThread.monitor().unlock();
191        }
192        StackTrace st = new StackTrace(rvmThread);
193        // Skip 2 frames: the frames for yieldpointFrom* and yieldpoint
194        // TODO this assumes that the thread is blocked in Java (and not in JNI)
195        elements = st.stackTraceNoException(2);
196        rvmThread.unblock(RVMThread.stackTraceBlockAdapter);
197    }
198
199    return JikesRVMStackTraceSupport.convertToJavaClassLibraryStackTrace(elements);
200  }
201
202}