1 /*
2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * This source code is provided to illustrate the usage of a given feature
28  * or technique and has been deliberately simplified. Additional steps
29  * required for a production-quality application, such as security checks,
30  * input validation and proper error handling, might not be present in
31  * this sample code.
32  */
33 
34 
35 package com.sun.tools.example.debug.tty;
36 
37 import com.sun.jdi.ThreadReference;
38 import com.sun.jdi.ThreadGroupReference;
39 import com.sun.jdi.IncompatibleThreadStateException;
40 import com.sun.jdi.StackFrame;
41 import java.util.List;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 
45 class ThreadInfo {
46     // This is a list of all known ThreadInfo objects. It survives
47     // ThreadInfo.invalidateAll, unlike the other static fields below.
48     private static List<ThreadInfo> threads = Collections.synchronizedList(new ArrayList<ThreadInfo>());
49     private static boolean gotInitialThreads = false;
50 
51     private static ThreadInfo current = null;
52     private static ThreadGroupReference group = null;
53 
54     private final ThreadReference thread;
55     private int currentFrameIndex = 0;
56 
ThreadInfo(ThreadReference thread)57     private ThreadInfo(ThreadReference thread) {
58         this.thread = thread;
59         if (thread == null) {
60             MessageOutput.fatalError("Internal error: null ThreadInfo created");
61         }
62     }
63 
initThreads()64     private static void initThreads() {
65         if (!gotInitialThreads) {
66             for (ThreadReference thread : Env.vm().allThreads()) {
67                 threads.add(new ThreadInfo(thread));
68             }
69             gotInitialThreads = true;
70         }
71     }
72 
addThread(ThreadReference thread)73     static void addThread(ThreadReference thread) {
74         synchronized (threads) {
75             initThreads();
76             ThreadInfo ti = new ThreadInfo(thread);
77             // Guard against duplicates. Duplicates can happen during
78             // initialization when a particular thread might be added both
79             // by a thread start event and by the initial call to threads()
80             if (getThreadInfo(thread) == null) {
81                 threads.add(ti);
82             }
83         }
84     }
85 
removeThread(ThreadReference thread)86     static void removeThread(ThreadReference thread) {
87         if (thread.equals(ThreadInfo.current)) {
88             // Current thread has died.
89 
90             // Be careful getting the thread name. If its death happens
91             // as part of VM termination, it may be too late to get the
92             // information, and an exception will be thrown.
93             String currentThreadName;
94             try {
95                currentThreadName = "\"" + thread.name() + "\"";
96             } catch (Exception e) {
97                currentThreadName = "";
98             }
99 
100             setCurrentThread(null);
101 
102             MessageOutput.println();
103             MessageOutput.println("Current thread died. Execution continuing...",
104                                   currentThreadName);
105         }
106         threads.remove(getThreadInfo(thread));
107     }
108 
threads()109     static List<ThreadInfo> threads() {
110         synchronized(threads) {
111             initThreads();
112             // Make a copy to allow iteration without synchronization
113             return new ArrayList<ThreadInfo>(threads);
114         }
115     }
116 
invalidateAll()117     static void invalidateAll() {
118         current = null;
119         group = null;
120         synchronized (threads) {
121             for (ThreadInfo ti : threads()) {
122                 ti.invalidate();
123             }
124         }
125     }
126 
setThreadGroup(ThreadGroupReference tg)127     static void setThreadGroup(ThreadGroupReference tg) {
128         group = tg;
129     }
130 
setCurrentThread(ThreadReference tr)131     static void setCurrentThread(ThreadReference tr) {
132         if (tr == null) {
133             setCurrentThreadInfo(null);
134         } else {
135             ThreadInfo tinfo = getThreadInfo(tr);
136             setCurrentThreadInfo(tinfo);
137         }
138     }
139 
setCurrentThreadInfo(ThreadInfo tinfo)140     static void setCurrentThreadInfo(ThreadInfo tinfo) {
141         current = tinfo;
142         if (current != null) {
143             current.invalidate();
144         }
145     }
146 
147     /**
148      * Get the current ThreadInfo object.
149      *
150      * @return the ThreadInfo for the current thread.
151      */
getCurrentThreadInfo()152     static ThreadInfo getCurrentThreadInfo() {
153         return current;
154     }
155 
156     /**
157      * Get the thread from this ThreadInfo object.
158      *
159      * @return the Thread wrapped by this ThreadInfo.
160      */
getThread()161     ThreadReference getThread() {
162         return thread;
163     }
164 
group()165     static ThreadGroupReference group() {
166         if (group == null) {
167             // Current thread group defaults to the first top level
168             // thread group.
169             setThreadGroup(Env.vm().topLevelThreadGroups().get(0));
170         }
171         return group;
172     }
173 
getThreadInfo(long id)174     static ThreadInfo getThreadInfo(long id) {
175         ThreadInfo retInfo = null;
176 
177         synchronized (threads) {
178             for (ThreadInfo ti : threads()) {
179                 if (ti.thread.uniqueID() == id) {
180                    retInfo = ti;
181                    break;
182                 }
183             }
184         }
185         return retInfo;
186     }
187 
getThreadInfo(ThreadReference tr)188     static ThreadInfo getThreadInfo(ThreadReference tr) {
189         return getThreadInfo(tr.uniqueID());
190     }
191 
getThreadInfo(String idToken)192     static ThreadInfo getThreadInfo(String idToken) {
193         ThreadInfo tinfo = null;
194         if (idToken.startsWith("t@")) {
195             idToken = idToken.substring(2);
196         }
197         try {
198             long threadId = Long.decode(idToken).longValue();
199             tinfo = getThreadInfo(threadId);
200         } catch (NumberFormatException e) {
201             tinfo = null;
202         }
203         return tinfo;
204     }
205 
206     /**
207      * Get the thread stack frames.
208      *
209      * @return a <code>List</code> of the stack frames.
210      */
getStack()211     List<StackFrame> getStack() throws IncompatibleThreadStateException {
212         return thread.frames();
213     }
214 
215     /**
216      * Get the current stackframe.
217      *
218      * @return the current stackframe.
219      */
getCurrentFrame()220     StackFrame getCurrentFrame() throws IncompatibleThreadStateException {
221         if (thread.frameCount() == 0) {
222             return null;
223         }
224         return thread.frame(currentFrameIndex);
225     }
226 
227     /**
228      * Invalidate the current stackframe index.
229      */
invalidate()230     void invalidate() {
231         currentFrameIndex = 0;
232     }
233 
234     /* Throw IncompatibleThreadStateException if not suspended */
assureSuspended()235     private void assureSuspended() throws IncompatibleThreadStateException {
236         if (!thread.isSuspended()) {
237             throw new IncompatibleThreadStateException();
238         }
239     }
240 
241     /**
242      * Get the current stackframe index.
243      *
244      * @return the number of the current stackframe.  Frame zero is the
245      * closest to the current program counter
246      */
getCurrentFrameIndex()247     int getCurrentFrameIndex() {
248         return currentFrameIndex;
249     }
250 
251     /**
252      * Set the current stackframe to a specific frame.
253      *
254      * @param nFrame    the number of the desired stackframe.  Frame zero is the
255      * closest to the current program counter
256      * @exception IllegalAccessError when the thread isn't
257      * suspended or waiting at a breakpoint
258      * @exception ArrayIndexOutOfBoundsException when the
259      * requested frame is beyond the stack boundary
260      */
setCurrentFrameIndex(int nFrame)261     void setCurrentFrameIndex(int nFrame) throws IncompatibleThreadStateException {
262         assureSuspended();
263         if ((nFrame < 0) || (nFrame >= thread.frameCount())) {
264             throw new ArrayIndexOutOfBoundsException();
265         }
266         currentFrameIndex = nFrame;
267     }
268 
269     /**
270      * Change the current stackframe to be one or more frames higher
271      * (as in, away from the current program counter).
272      *
273      * @param nFrames   the number of stackframes
274      * @exception IllegalAccessError when the thread isn't
275      * suspended or waiting at a breakpoint
276      * @exception ArrayIndexOutOfBoundsException when the
277      * requested frame is beyond the stack boundary
278      */
up(int nFrames)279     void up(int nFrames) throws IncompatibleThreadStateException {
280         setCurrentFrameIndex(currentFrameIndex + nFrames);
281     }
282 
283     /**
284      * Change the current stackframe to be one or more frames lower
285      * (as in, toward the current program counter).     *
286      * @param nFrames   the number of stackframes
287      * @exception IllegalAccessError when the thread isn't
288      * suspended or waiting at a breakpoint
289      * @exception ArrayIndexOutOfBoundsException when the
290      * requested frame is beyond the stack boundary
291      */
down(int nFrames)292     void down(int nFrames) throws IncompatibleThreadStateException {
293         setCurrentFrameIndex(currentFrameIndex - nFrames);
294     }
295 
296 }
297