| /* | |
|  * 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. | |
|      * | |
|      * @return the ThreadInfo for the current thread. | |
| */ | |
| static ThreadInfo getCurrentThreadInfo() { | |
| return current; | |
| } | |
|     /** | |
|      * 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. | |
|      * | |
|      * @return a <code>List</code> of the stack frames. | |
| */ | |
| List<StackFrame> getStack() throws IncompatibleThreadStateException { | |
| return thread.frames(); | |
| } | |
|     /** | |
|      * 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. | |
| */ | |
|     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. | |
|      * | |
|      * @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. | |
|      * | |
|      * @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). | |
|      * | |
|      * @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).     * | |
|      * @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); | |
| } | |
| } |