/*
 * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * This source code is provided to illustrate the usage of a given feature
 * or technique and has been deliberately simplified. Additional steps
 * required for a production-quality application, such as security checks,
 * input validation and proper error handling, might not be present in
 * this sample code.
 */


package com.sun.tools.example.debug.tty;

import com.sun.jdi.ThreadReference;
import com.sun.jdi.ThreadGroupReference;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.StackFrame;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

class ThreadInfo {
    // This is a list of all known ThreadInfo objects. It survives
    // ThreadInfo.invalidateAll, unlike the other static fields below.
    private static List<ThreadInfo> threads = Collections.synchronizedList(new ArrayList<ThreadInfo>());
    private static boolean gotInitialThreads = false;

    private static ThreadInfo current = null;
    private static ThreadGroupReference group = null;

    private final ThreadReference thread;
    private int currentFrameIndex = 0;

    private ThreadInfo(ThreadReference thread) {
        this.thread = thread;
        if (thread == null) {
            MessageOutput.fatalError("Internal error: null ThreadInfo created");
        }
    }

    private static void initThreads() {
        if (!gotInitialThreads) {
            for (ThreadReference thread : Env.vm().allThreads()) {
                threads.add(new ThreadInfo(thread));
            }
            gotInitialThreads = true;
        }
    }

    static void addThread(ThreadReference thread) {
        synchronized (threads) {
            initThreads();
            ThreadInfo ti = new ThreadInfo(thread);
            // Guard against duplicates. Duplicates can happen during
            // initialization when a particular thread might be added both
            // by a thread start event and by the initial call to threads()
            if (getThreadInfo(thread) == null) {
                threads.add(ti);
            }
        }
    }

    static void removeThread(ThreadReference thread) {
        if (thread.equals(ThreadInfo.current)) {
            // Current thread has died.

            // Be careful getting the thread name. If its death happens
            // as part of VM termination, it may be too late to get the
            // information, and an exception will be thrown.
            String currentThreadName;
            try {
               currentThreadName = "\"" + thread.name() + "\"";
            } catch (Exception e) {
               currentThreadName = "";
            }

            setCurrentThread(null);

            MessageOutput.println();
            MessageOutput.println("Current thread died. Execution continuing...",
                                  currentThreadName);
        }
        threads.remove(getThreadInfo(thread));
    }

    static List<ThreadInfo> threads() {
        synchronized(threads) {
            initThreads();
            // Make a copy to allow iteration without synchronization
            return new ArrayList<ThreadInfo>(threads);
        }
    }

    static void invalidateAll() {
        current = null;
        group = null;
        synchronized (threads) {
            for (ThreadInfo ti : threads()) {
                ti.invalidate();
            }
        }
    }

    static void setThreadGroup(ThreadGroupReference tg) {
        group = tg;
    }

    static void setCurrentThread(ThreadReference tr) {
        if (tr == null) {
            setCurrentThreadInfo(null);
        } else {
            ThreadInfo tinfo = getThreadInfo(tr);
            setCurrentThreadInfo(tinfo);
        }
    }

    static void setCurrentThreadInfo(ThreadInfo tinfo) {
        current = tinfo;
        if (current != null) {
            current.invalidate();
        }
    }

    
Get the current ThreadInfo object.
Returns:the ThreadInfo for the current thread.
/** * Get the current ThreadInfo object. * * @return the ThreadInfo for the current thread. */
static ThreadInfo getCurrentThreadInfo() { return current; }
Get the thread from this ThreadInfo object.
Returns:the Thread wrapped by this ThreadInfo.
/** * Get the thread from this ThreadInfo object. * * @return the Thread wrapped by this ThreadInfo. */
ThreadReference getThread() { return thread; } static ThreadGroupReference group() { if (group == null) { // Current thread group defaults to the first top level // thread group. setThreadGroup(Env.vm().topLevelThreadGroups().get(0)); } return group; } static ThreadInfo getThreadInfo(long id) { ThreadInfo retInfo = null; synchronized (threads) { for (ThreadInfo ti : threads()) { if (ti.thread.uniqueID() == id) { retInfo = ti; break; } } } return retInfo; } static ThreadInfo getThreadInfo(ThreadReference tr) { return getThreadInfo(tr.uniqueID()); } static ThreadInfo getThreadInfo(String idToken) { ThreadInfo tinfo = null; if (idToken.startsWith("t@")) { idToken = idToken.substring(2); } try { long threadId = Long.decode(idToken).longValue(); tinfo = getThreadInfo(threadId); } catch (NumberFormatException e) { tinfo = null; } return tinfo; }
Get the thread stack frames.
Returns:a List of the stack frames.
/** * Get the thread stack frames. * * @return a <code>List</code> of the stack frames. */
List<StackFrame> getStack() throws IncompatibleThreadStateException { return thread.frames(); }
Get the current stackframe.
Returns:the current stackframe.
/** * Get the current stackframe. * * @return the current stackframe. */
StackFrame getCurrentFrame() throws IncompatibleThreadStateException { if (thread.frameCount() == 0) { return null; } return thread.frame(currentFrameIndex); }
Invalidate the current stackframe index.
/** * Invalidate the current stackframe index. */
void invalidate() { currentFrameIndex = 0; } /* Throw IncompatibleThreadStateException if not suspended */ private void assureSuspended() throws IncompatibleThreadStateException { if (!thread.isSuspended()) { throw new IncompatibleThreadStateException(); } }
Get the current stackframe index.
Returns:the number of the current stackframe. Frame zero is the closest to the current program counter
/** * Get the current stackframe index. * * @return the number of the current stackframe. Frame zero is the * closest to the current program counter */
int getCurrentFrameIndex() { return currentFrameIndex; }
Set the current stackframe to a specific frame.
Params:
  • nFrame – the number of the desired stackframe. Frame zero is the closest to the current program counter
Throws:
/** * Set the current stackframe to a specific frame. * * @param nFrame the number of the desired stackframe. Frame zero is the * closest to the current program counter * @exception IllegalAccessError when the thread isn't * suspended or waiting at a breakpoint * @exception ArrayIndexOutOfBoundsException when the * requested frame is beyond the stack boundary */
void setCurrentFrameIndex(int nFrame) throws IncompatibleThreadStateException { assureSuspended(); if ((nFrame < 0) || (nFrame >= thread.frameCount())) { throw new ArrayIndexOutOfBoundsException(); } currentFrameIndex = nFrame; }
Change the current stackframe to be one or more frames higher (as in, away from the current program counter).
Params:
  • nFrames – the number of stackframes
Throws:
/** * Change the current stackframe to be one or more frames higher * (as in, away from the current program counter). * * @param nFrames the number of stackframes * @exception IllegalAccessError when the thread isn't * suspended or waiting at a breakpoint * @exception ArrayIndexOutOfBoundsException when the * requested frame is beyond the stack boundary */
void up(int nFrames) throws IncompatibleThreadStateException { setCurrentFrameIndex(currentFrameIndex + nFrames); }
Change the current stackframe to be one or more frames lower (as in, toward the current program counter). *
Params:
  • nFrames – the number of stackframes
Throws:
/** * Change the current stackframe to be one or more frames lower * (as in, toward the current program counter). * * @param nFrames the number of stackframes * @exception IllegalAccessError when the thread isn't * suspended or waiting at a breakpoint * @exception ArrayIndexOutOfBoundsException when the * requested frame is beyond the stack boundary */
void down(int nFrames) throws IncompatibleThreadStateException { setCurrentFrameIndex(currentFrameIndex - nFrames); } }