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
35package com.sun.tools.example.debug.tty;
36
37import com.sun.jdi.ThreadReference;
38import com.sun.jdi.ThreadGroupReference;
39import com.sun.jdi.IncompatibleThreadStateException;
40import com.sun.jdi.StackFrame;
41import java.util.List;
42import java.util.ArrayList;
43import java.util.Collections;
44
45class 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
57    private ThreadInfo(ThreadReference thread) {
58        this.thread = thread;
59        if (thread == null) {
60            MessageOutput.fatalError("Internal error: null ThreadInfo created");
61        }
62    }
63
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
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
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
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
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
127    static void setThreadGroup(ThreadGroupReference tg) {
128        group = tg;
129    }
130
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
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     */
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     */
161    ThreadReference getThread() {
162        return thread;
163    }
164
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
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
188    static ThreadInfo getThreadInfo(ThreadReference tr) {
189        return getThreadInfo(tr.uniqueID());
190    }
191
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     */
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     */
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     */
230    void invalidate() {
231        currentFrameIndex = 0;
232    }
233
234    /* Throw IncompatibleThreadStateException if not suspended */
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     */
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     */
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     */
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     */
292    void down(int nFrames) throws IncompatibleThreadStateException {
293        setCurrentFrameIndex(currentFrameIndex - nFrames);
294    }
295
296}
297